sproutcore 1.5.0.pre.4 → 1.5.0.pre.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. data/CHANGELOG +6 -0
  2. data/VERSION.yml +1 -1
  3. data/lib/Buildfile +1 -1
  4. data/lib/buildtasks/build.rake +14 -6
  5. data/lib/buildtasks/manifest.rake +171 -72
  6. data/lib/frameworks/sproutcore/Buildfile +3 -5
  7. data/lib/frameworks/sproutcore/CHANGELOG.md +9 -0
  8. data/lib/frameworks/sproutcore/apps/test_controls/resources/main_page.js +2 -1
  9. data/lib/frameworks/sproutcore/apps/test_controls/resources/progress_page.js +7 -17
  10. data/lib/frameworks/sproutcore/frameworks/bootstrap/system/browser.js +7 -1
  11. data/lib/frameworks/sproutcore/frameworks/bootstrap/system/loader.js +14 -1
  12. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/browser.js +0 -2
  13. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +1 -1
  14. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +14 -3
  15. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/template/collection.js +1 -1
  16. data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +2 -2
  17. data/lib/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +6 -0
  18. data/lib/frameworks/sproutcore/frameworks/datastore/system/store.js +1 -1
  19. data/lib/frameworks/sproutcore/frameworks/desktop/mixins/scrollable.js +1 -1
  20. data/lib/frameworks/sproutcore/frameworks/desktop/panes/alert.js +49 -4
  21. data/lib/frameworks/sproutcore/frameworks/desktop/panes/menu.js +8 -0
  22. data/lib/frameworks/sproutcore/frameworks/desktop/panes/panel.js +46 -1
  23. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/button.js +32 -5
  24. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/checkbox.js +32 -6
  25. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/disclosure.js +31 -3
  26. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/progress.js +15 -3
  27. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/radio.js +28 -7
  28. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/segmented.js +1 -1
  29. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/slider.js +20 -1
  30. data/lib/frameworks/sproutcore/frameworks/desktop/render_delegates/well.js +2 -4
  31. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/alert/ui.js +40 -34
  32. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/ui.js +35 -0
  33. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/panel/ui.js +43 -0
  34. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/button/ui.js +47 -0
  35. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/checkbox/methods.js +6 -16
  36. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/checkbox/ui.js +21 -0
  37. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/disclosure/ui.js +41 -1
  38. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/progress/ui.js +128 -0
  39. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/radio/ui.js +93 -3
  40. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroll/ui.js +13 -1
  41. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/scroller.js +28 -0
  42. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/methods.js +47 -0
  43. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/segmented/ui.js +51 -0
  44. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/toolbar/method.js +33 -0
  45. data/lib/frameworks/sproutcore/frameworks/desktop/tests/views/toolbar/ui.js +29 -0
  46. data/lib/frameworks/sproutcore/frameworks/desktop/views/checkbox.js +28 -0
  47. data/lib/frameworks/sproutcore/frameworks/desktop/views/disclosure.js +9 -1
  48. data/lib/frameworks/sproutcore/frameworks/desktop/views/list_item.js +148 -134
  49. data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +18 -0
  50. data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_scroll.js +30 -3
  51. data/lib/frameworks/sproutcore/frameworks/desktop/views/progress.js +9 -0
  52. data/lib/frameworks/sproutcore/frameworks/desktop/views/radio.js +52 -4
  53. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +2 -1
  54. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroller.js +36 -3
  55. data/lib/frameworks/sproutcore/frameworks/desktop/views/segmented.js +1 -1
  56. data/lib/frameworks/sproutcore/frameworks/desktop/views/slider.js +9 -1
  57. data/lib/frameworks/sproutcore/frameworks/desktop/views/split.js +15 -4
  58. data/lib/frameworks/sproutcore/frameworks/desktop/views/split_divider.js +1 -6
  59. data/lib/frameworks/sproutcore/frameworks/desktop/views/tab.js +4 -2
  60. data/lib/frameworks/sproutcore/frameworks/desktop/views/toolbar.js +7 -0
  61. data/lib/frameworks/sproutcore/frameworks/desktop/views/well.js +1 -2
  62. data/lib/frameworks/sproutcore/frameworks/forms/views/form.js +3 -1
  63. data/lib/frameworks/sproutcore/frameworks/foundation/english.lproj/bootstrap.rhtml +1 -1
  64. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/auto_resize.js +0 -9
  65. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/editable.js +1 -1
  66. data/lib/frameworks/sproutcore/frameworks/foundation/mixins/flowed_layout.js +2 -0
  67. data/lib/frameworks/sproutcore/frameworks/foundation/render_delegates/canvas_image.js +1 -1
  68. data/lib/frameworks/sproutcore/frameworks/foundation/render_delegates/label.js +25 -1
  69. data/lib/frameworks/sproutcore/frameworks/foundation/system/chance.js +64 -0
  70. data/lib/frameworks/sproutcore/frameworks/foundation/system/module.js +34 -27
  71. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/editable/ui.js +42 -0
  72. data/lib/frameworks/sproutcore/frameworks/foundation/tests/mixins/inline_text_field/ui.js +62 -0
  73. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/image/ui.js +0 -8
  74. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/label/ui.js +12 -0
  75. data/lib/frameworks/sproutcore/frameworks/foundation/tests/views/text_field/ui.js +48 -1
  76. data/lib/frameworks/sproutcore/frameworks/foundation/views/field.js +19 -0
  77. data/lib/frameworks/sproutcore/frameworks/foundation/views/image.js +2 -0
  78. data/lib/frameworks/sproutcore/frameworks/foundation/views/label.js +11 -5
  79. data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +152 -74
  80. data/lib/frameworks/sproutcore/frameworks/runtime/private/observer_queue.js +12 -9
  81. data/lib/frameworks/sproutcore/frameworks/runtime/system/logger.js +1476 -176
  82. data/lib/frameworks/sproutcore/frameworks/runtime/system/run_loop.js +0 -2
  83. data/lib/frameworks/sproutcore/frameworks/runtime/tests/system/logger.js +227 -32
  84. data/lib/frameworks/sproutcore/frameworks/statechart/system/state.js +11 -11
  85. data/lib/frameworks/sproutcore/frameworks/statechart/system/statechart.js +1 -1
  86. data/lib/frameworks/sproutcore/frameworks/testing/system/runner.js +10 -0
  87. data/lib/frameworks/sproutcore/lib/index.rhtml +30 -8
  88. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/18px/segmented.css +4 -4
  89. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/24px/segmented.css +4 -4
  90. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/30px/segmented.css +4 -4
  91. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/44px/segmented.css +4 -4
  92. data/lib/frameworks/sproutcore/themes/ace/resources/segmented/segmented.css +4 -0
  93. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/slider.css +5 -1
  94. data/lib/frameworks/sproutcore/themes/ace/resources/slider/ace/slider.js +11 -3
  95. data/lib/frameworks/sproutcore/themes/ace/resources/well/well.css +7 -8
  96. data/lib/frameworks/sproutcore/themes/ace/resources/well/well.js +2 -4
  97. data/lib/gen/app/templates/apps/@target_name@/Buildfile +14 -0
  98. data/lib/gen/app/templates/apps/@target_name@/theme.js +27 -0
  99. data/lib/gen/html_app/templates/apps/@target_name@/resources/stylesheets/@target_name@.css +4 -0
  100. data/lib/gen/project/templates/@filename@/Buildfile +7 -2
  101. data/lib/gen/theme/templates/themes/@target_name@/Buildfile +3 -0
  102. data/lib/gen/theme/templates/themes/@target_name@/theme.js +23 -0
  103. data/lib/sproutcore/builders.rb +2 -0
  104. data/lib/sproutcore/builders/chance.rb +64 -0
  105. data/lib/sproutcore/builders/chance_file.rb +59 -0
  106. data/lib/sproutcore/builders/combine.rb +0 -45
  107. data/lib/sproutcore/builders/html.rb +1 -1
  108. data/lib/sproutcore/builders/minify.rb +5 -17
  109. data/lib/sproutcore/builders/module.rb +2 -2
  110. data/lib/sproutcore/builders/string_wrapper.rb +13 -15
  111. data/lib/sproutcore/helpers.rb +1 -1
  112. data/lib/sproutcore/helpers/minifier.rb +56 -0
  113. data/lib/sproutcore/helpers/static_helper.rb +86 -40
  114. data/lib/sproutcore/models/generator.rb +16 -0
  115. data/lib/sproutcore/models/manifest_entry.rb +8 -1
  116. data/lib/sproutcore/models/target.rb +101 -33
  117. data/lib/sproutcore/tools.rb +0 -7
  118. data/lib/sproutcore/tools/build.rb +2 -37
  119. data/spec/buildtasks/manifest/prepare_build_tasks/chance_spec.rb +100 -0
  120. data/spec/buildtasks/manifest/prepare_build_tasks/combine_spec.rb +86 -9
  121. data/spec/buildtasks/manifest/prepare_build_tasks/css_spec.rb +1 -1
  122. data/spec/buildtasks/manifest/prepare_build_tasks/minify_spec.rb +1 -1
  123. data/spec/buildtasks/manifest/prepare_build_tasks/module_info_spec.rb +96 -0
  124. data/spec/buildtasks/manifest/prepare_build_tasks/module_spec.rb +83 -0
  125. data/spec/buildtasks/manifest/prepare_build_tasks/packed_spec.rb +37 -4
  126. data/spec/buildtasks/manifest/spec_helper.rb +4 -0
  127. data/spec/fixtures/builder_tests/Buildfile +11 -9
  128. data/spec/fixtures/builder_tests/apps/chance_test/Buildfile +14 -0
  129. data/spec/fixtures/builder_tests/apps/chance_test/core.js +27 -0
  130. data/spec/fixtures/builder_tests/apps/chance_test/main.js +30 -0
  131. data/spec/fixtures/builder_tests/apps/chance_test/resources/demo.css +6 -0
  132. data/spec/fixtures/builder_tests/apps/chance_test/resources/last_file.css +7 -0
  133. data/spec/fixtures/builder_tests/apps/chance_test/resources/loading.rhtml +9 -0
  134. data/spec/fixtures/builder_tests/apps/chance_test/resources/main_page.js +24 -0
  135. data/spec/fixtures/builder_tests/apps/chance_test/resources/z_first_file.css +6 -0
  136. data/spec/fixtures/builder_tests/apps/chance_test/theme.js +27 -0
  137. data/spec/fixtures/builder_tests/apps/module_test/module.js +1 -0
  138. data/spec/fixtures/builder_tests/apps/module_test/modules/deferred_module/english.lproj/req_style_2.css +0 -0
  139. data/spec/fixtures/builder_tests/apps/module_test/modules/deferred_module/english.lproj/test.rhtml +1 -0
  140. data/spec/fixtures/builder_tests/apps/module_test/modules/deferred_module/javascript.js +1 -0
  141. data/spec/fixtures/builder_tests/apps/module_test/modules/deferred_module/lib/alt_layout.rhtml +0 -0
  142. data/spec/fixtures/builder_tests/apps/module_test/modules/deferred_module/req_js_2.js +0 -0
  143. data/spec/fixtures/builder_tests/apps/module_test/modules/dynamic_req_target_1/dynamic_req_js_1.js +0 -0
  144. data/spec/fixtures/builder_tests/apps/module_test/modules/dynamic_req_target_1/english.lproj/dynamic_req_style_1.css +0 -0
  145. data/spec/fixtures/builder_tests/apps/module_test/modules/inlined_module/README +0 -0
  146. data/spec/fixtures/builder_tests/apps/module_test/modules/required_target/english.lproj/req_style_1.css +0 -0
  147. data/spec/fixtures/builder_tests/apps/module_test/modules/required_target/english.lproj/strings.js +4 -0
  148. data/spec/fixtures/builder_tests/apps/module_test/modules/required_target/english.lproj/test.rhtml +1 -0
  149. data/spec/fixtures/builder_tests/apps/module_test/modules/required_target/req_js_1.js +0 -0
  150. data/spec/fixtures/helper_tests/apps/minifier_test/core.js +8 -0
  151. data/spec/fixtures/real_world/Buildfile +11 -1
  152. data/spec/fixtures/real_world/apps/account/modules/preferences/README +0 -0
  153. data/spec/fixtures/real_world/apps/calendar/modules/preferences/README +0 -0
  154. data/spec/fixtures/real_world/apps/contacts/modules/preferences/README +0 -0
  155. data/spec/fixtures/real_world/apps/contacts/modules/printing/README +0 -0
  156. data/spec/fixtures/real_world/apps/mail/modules/preferences/README +0 -0
  157. data/spec/fixtures/real_world/apps/mail/modules/printing/README +0 -0
  158. data/spec/fixtures/real_world/apps/photos/modules/email/README +0 -0
  159. data/spec/fixtures/real_world/apps/photos/modules/preferences/README +0 -0
  160. data/spec/lib/builders/chance_file_spec.rb +63 -0
  161. data/spec/lib/builders/chance_spec.rb +81 -0
  162. data/spec/lib/builders/module_spec.rb +133 -0
  163. data/spec/lib/helpers/minifier.rb +31 -0
  164. data/spec/lib/models/project/find_targets_for_spec.rb +1 -1
  165. data/vendor/chance/lib/chance/imagers/data_url.rb +68 -17
  166. data/vendor/chance/lib/chance/instance.rb +55 -30
  167. data/vendor/chance/lib/chance/parser.rb +1 -1
  168. data/vendor/chance/lib/chance/slicing.rb +39 -3
  169. metadata +52 -7
  170. data/lib/frameworks/sproutcore/frameworks/foundation/system/logger.js +0 -163
  171. data/lib/frameworks/sproutcore/frameworks/foundation/tests/system/logger.js +0 -44
  172. data/spec/buildtasks/manifest/prepare_build_tasks/bundle_spec.rb +0 -254
  173. data/spec/fixtures/builder_tests/apps/bundle_test/bundle.js +0 -1
  174. data/spec/lib/builders/bundle_spec.rb +0 -295
@@ -46,8 +46,8 @@ SC.Observers = {
46
46
  tuple = propertyPath;
47
47
  }
48
48
 
49
- // if a tuple was found, add the observer immediately...
50
- if (tuple) {
49
+ // if a tuple was found and is observable, add the observer immediately...
50
+ if (tuple && tuple[0].addObserver) {
51
51
  tuple[0].addObserver(tuple[1],target, method) ;
52
52
 
53
53
  // otherwise, save this in the queue.
@@ -98,23 +98,26 @@ SC.Observers = {
98
98
  flush: function(object) {
99
99
 
100
100
  // flush any observers that we tried to setup but didn't have a path yet
101
- var oldQueue = this.queue ;
102
- if (oldQueue && oldQueue.length > 0) {
101
+ var oldQueue = this.queue, i,
102
+ queueLen = oldQueue.length;
103
+
104
+ if (oldQueue && queueLen > 0) {
103
105
  var newQueue = (this.queue = []) ;
104
106
 
105
- for ( var i=0,l=oldQueue.length; i<l; i++ ) {
107
+ for (i=0; i<queueLen; i++ ) {
106
108
  var item = oldQueue[i];
107
109
  if ( !item ) continue;
108
-
110
+
109
111
  var tuple = SC.tupleForPropertyPath( item[0], item[3] );
110
- if( tuple ) {
112
+ // check if object is observable (yet) before adding an observer
113
+ if( tuple && tuple[0].addObserver ) {
111
114
  tuple[0].addObserver( tuple[1], item[1], item[2] );
112
115
  } else {
113
116
  newQueue.push( item );
114
117
  }
115
118
  }
116
119
  }
117
-
120
+
118
121
  // if this object needsRangeObserver then see if any pending range
119
122
  // observers need it.
120
123
  if ( object._kvo_needsRangeObserver ) {
@@ -123,7 +126,7 @@ SC.Observers = {
123
126
  out = this._TMP_OUT,
124
127
  ro;
125
128
 
126
- for ( var i=0; i<len; i++ ) {
129
+ for ( i=0; i<len; i++ ) {
127
130
  ro = set[i]; // get the range observer
128
131
  if ( ro.setupPending(object) ) {
129
132
  out.push(ro); // save to remove later
@@ -6,9 +6,129 @@
6
6
  // ==========================================================================
7
7
 
8
8
 
9
+ // ..........................................................
10
+ // CONSTANTS
11
+ //
12
+
13
+ // Implementation note: We use two spaces after four-letter prefixes and one
14
+ // after five-letter prefixes so things align in monospaced consoles.
15
+
16
+ /**
17
+ If {@link SC.Logger.format} is true, this delimiter will be put between arguments.
18
+
19
+ @property {String}
20
+ */
21
+ SC.LOGGER_LOG_DELIMITER = ", ";
22
+
23
+ /**
24
+ If {@link SC.Logger.error} falls back onto {@link SC.Logger.log}, this will be
25
+ prepended to the output.
26
+
27
+ @property {String}
28
+ */
29
+ SC.LOGGER_LOG_ERROR = "ERROR: ";
30
+
31
+ /**
32
+ If {@link SC.Logger.info} falls back onto {@link SC.Logger.log}, this will be
33
+ prepended to the output.
34
+
35
+ @property {String}
36
+ */
37
+ SC.LOGGER_LOG_INFO = "INFO: ";
38
+
39
+ /**
40
+ If {@link SC.Logger.warn} falls back onto {@link SC.Logger.log}, this will be
41
+ prepended to the output.
42
+
43
+ @property {String}
44
+ */
45
+ SC.LOGGER_LOG_WARN = "WARN: ";
46
+
47
+ /**
48
+ If {@link SC.Logger.debug} falls back onto {@link SC.Logger.log}, this will be
49
+ prepended to the output.
50
+
51
+ @property {String}
52
+ */
53
+ SC.LOGGER_LOG_DEBUG = "DEBUG: ";
54
+
55
+ /**
56
+ If {@link SC.Logger.group} falls back onto {@link SC.Logger.log}, this will
57
+ be prepended to the output.
58
+
59
+ @property {String}
60
+ */
61
+ SC.LOGGER_LOG_GROUP_HEADER = "** %@"; // The variable is the group title
62
+
63
+ /**
64
+ If the reporter does not support group(), then we’ll add our own indentation
65
+ to our output. This constant represents one level of indentation.
66
+
67
+ @property {String}
68
+ */
69
+ SC.LOGGER_LOG_GROUP_INDENTATION = " ";
70
+
71
+ /**
72
+ When reporting recorded log messages, the timestamp is included with this
73
+ prefix.
74
+
75
+ @property {String}
76
+ */
77
+ SC.LOGGER_RECORDED_LOG_TIMESTAMP_PREFIX = "%@: ";
78
+
79
+
80
+ SC.LOGGER_LEVEL_DEBUG = 'debug';
81
+ SC.LOGGER_LEVEL_INFO = 'info';
82
+ SC.LOGGER_LEVEL_WARN = 'warn';
83
+ SC.LOGGER_LEVEL_ERROR = 'error';
84
+ SC.LOGGER_LEVEL_NONE = 'none';
85
+
86
+
87
+
9
88
  /** @class
10
89
 
11
90
  Object to allow for safe logging actions, such as using the browser console.
91
+ In addition to being output to the console, logs can be optionally recorded
92
+ in memory, to be accessed by your application as appropriate.
93
+
94
+ This class also adds in the concept of a “current log level”, which allows
95
+ your application to potentially determine a subset of logging messages to
96
+ output and/or record. The order of levels is:
97
+
98
+ * debug SC.LOGGER_LEVEL_DEBUG
99
+ * info SC.LOGGER_LEVEL_INFO
100
+ * warn SC.LOGGER_LEVEL_WARN
101
+ * error SC.LOGGER_LEVEL_ERROR
102
+
103
+ All messages at the level or “above” will be output/recorded. So, for
104
+ example, if you set the level to 'info', all 'info', 'warn', and 'error'
105
+ messages will be output/recorded, but no 'debug' messages will be. Also,
106
+ there are two separate log levels: one for output, and one for recording.
107
+ You may wish to only output, say, 'warn' and above, but record everything
108
+ from 'debug' on up. (You can also limit the number log messages to record.)
109
+
110
+ This mechanism allows your application to avoid needless output (which has a
111
+ non-zero cost in many browsers) in the general case, but turning up the log
112
+ level when necessary for debugging. Note that there can still be a
113
+ performance cost for preparing log messages (calling {@link String.fmt},
114
+ etc.), so it’s still a good idea to be selective about what log messages are
115
+ output even to 'debug', especially in hot code.
116
+
117
+ Similarly, you should be aware that if you wish to log objects without
118
+ stringification — using the {@link SC.Logger.debugWithoutFmt} variants — and
119
+ you enable recording, the “recorded messages” array will hold onto a
120
+ reference to the arguments, potentially increasing the amount of memory
121
+ used.
122
+
123
+ As a convenience, this class also adds some shorthand methods to SC:
124
+
125
+ * SC.debug() ==> SC.Logger.debug()
126
+ * SC.info() ==> SC.Logger.info()
127
+ * SC.warn() ==> SC.Logger.warn()
128
+ * SC.error() ==> SC.Logger.error()
129
+
130
+ …although note that no shorthand versions exist for the less-common
131
+ functions, such as defining groups.
12
132
 
13
133
  The FireFox plugin Firebug was used as a function reference. Please see
14
134
  {@link <a href="http://getfirebug.com/logging.html">Firebug Logging Reference</a>}
@@ -16,267 +136,1447 @@
16
136
 
17
137
  @author Colin Campbell
18
138
  @author Benedikt Böhm
139
+ @author William Kakes
19
140
  @extends SC.Object
20
- @since Sproutcore 1.0
141
+ @since SproutCore 1.0
21
142
  @see <a href="http://getfirebug.com/logging.html">Firebug Logging Reference</a>
22
143
  */
23
- SC.Logger = SC.Object.create(
24
- /** @scope SC.Logger */ {
144
+ SC.Logger = SC.Object.create({
25
145
 
26
146
  // ..........................................................
27
147
  // PROPERTIES
28
148
  //
29
149
 
30
150
  /**
31
- If console.log does not exist, SC.Logger will use window.alert instead.
151
+ The current log level determining what is output to the reporter object
152
+ (usually your browser’s console). Valid values are:
153
+
154
+ * SC.LOGGER_LEVEL_DEBUG
155
+ * SC.LOGGER_LEVEL_INFO
156
+ * SC.LOGGER_LEVEL_WARN
157
+ * SC.LOGGER_LEVEL_ERROR
158
+ * SC.LOGGER_LEVEL_NONE
159
+
160
+ If you do not specify this value, it will default to SC.LOGGER_LEVEL_DEBUG
161
+ when running in development mode and SC.LOGGER_LEVEL_INFO when running in
162
+ production mode.
163
+
164
+ @property: {Constant}
165
+ */
166
+ logOutputLevel: null, // If null, set appropriately during init()
167
+
168
+
169
+ /**
170
+ The current log level determining what is output to the reporter object
171
+ (usually your browser’s console). Valid values are the same as with
172
+ 'logOutputLevel':
173
+
174
+ * SC.LOGGER_LEVEL_DEBUG
175
+ * SC.LOGGER_LEVEL_INFO
176
+ * SC.LOGGER_LEVEL_WARN
177
+ * SC.LOGGER_LEVEL_ERROR
178
+ * SC.LOGGER_LEVEL_NONE
179
+
180
+ If you do not specify this value, it will default to SC.LOGGER_LEVEL_NONE.
181
+
182
+ @property: {Constant}
183
+ */
184
+ logRecordingLevel: SC.LOGGER_LEVEL_NONE,
185
+
186
+
187
+ /**
188
+ All recorded log messages. You generally should not need to interact with
189
+ this array, as most commonly-used functionality can be achieved via the
190
+ {@link SC.Logger.outputRecordedLogMessages} and
191
+ {@link SC.Logger.stringifyRecordedLogMessages} methods.
192
+
193
+ This array will be lazily created when the first message is recorded.
194
+
195
+ Format:
196
+
197
+ For efficiency, each entry in the array is a simple hash rather than a
198
+ full SC.Object instance. Furthermore, to minimize memory usage, niceties
199
+ like “type of entry: message” are avoided; if you need to parse this
200
+ structure, you can determine which type of entry you’re looking at by
201
+ checking for the 'message' and 'indentation' fields.
202
+
203
+ Log entry:
204
+ {
205
+ type: {Constant} (SC.LOGGER_LEVEL_DEBUG, etc.)
206
+ message: {String | Boolean}
207
+ originalArguments: {Arguments} // optional
208
+ timestamp: {Date}
209
+ }
210
+
211
+ Group entry (either beginning or end of):
212
+ {
213
+ type: {Constant} SC.LOGGER_LEVEL_DEBUG, etc.
214
+ indentation: {Number} The value is the new group indentation level
215
+ beginGroup: {Boolean} Whether this entry is the beginning of a new group (as opposed to the end)
216
+ title: {String} Optional for new groups, and never present for end-of-group
217
+ timestamp: {Date}
218
+ }
219
+
220
+ @property {Array}
221
+ */
222
+ recordedLogMessages: null,
32
223
 
33
- This property is only used inside {@link SC.Logger.log}. If fallBackOnLog is
34
- false and you call a different function, an alert will not be opened.
224
+
225
+ /**
226
+ If the recording level is set such that messages will be recorded, this is
227
+ the maximum number of messages that will be saved in the
228
+ 'recordedLogMessages' array. Any further recorded messages will push
229
+ older messages out of the array, so the most recent messages will be
230
+ saved.
231
+
232
+ @property {Number}
233
+ */
234
+ recordedLogMessagesMaximumLength: 500,
235
+
236
+
237
+ /**
238
+ If the recording level is set such that messages will be recorded, this is
239
+ the minimum number of messages that will be saved whenever the recordings
240
+ are pruned. (They are pruned whenever you hit the maximum length, as
241
+ specified via the 'recordedLogMessagesMaximumLength' property. This
242
+ mechanism avoids thrashing the array for each log message once the
243
+ maximum is reached.) When pruning, the most recent messages will be saved.
244
+
245
+ @property {Number}
246
+ */
247
+ recordedLogMessagesPruningMinimumLength: 100,
248
+
249
+
250
+ /**
251
+ Whether or not to enable debug logging. This property exists for
252
+ backwards compatibility with previous versions of SC.Logger. In newer
253
+ code, you should instead set the appropriate output/recording log levels.
254
+
255
+ If this property is set to YES, it will set 'logOutputLevel' to
256
+ SC.LOGGER_LEVEL_DEBUG. Otherwise, it will have no effect.
257
+
258
+ @deprecated
259
+ @property: {Boolean}
260
+ */
261
+ debugEnabled: NO,
262
+
263
+
264
+ /**
265
+ Computed property that checks for the existence of the reporter object.
35
266
 
36
267
  @property {Boolean}
37
268
  */
38
- fallBackOnAlert: NO,
269
+ exists: function() {
270
+ return !SC.none(this.get('reporter'));
271
+ }.property('reporter').cacheable(),
272
+
39
273
 
40
274
  /**
41
- Whether or not to format multiple arguments together
42
- or let the browser deal with that.
275
+ If console.log does not exist, SC.Logger will use window.alert instead
276
+ when {@link SC.Logger.log} is invoked.
277
+
278
+ Note that this property has no effect for messages initiated via the
279
+ debug/info/warn/error methods, on the assumption that it is better to
280
+ simply utilize the message recording mechanism than put up a bunch of
281
+ alerts when there is no browser console.
43
282
 
44
283
  @property {Boolean}
45
284
  */
46
- format: YES,
285
+ fallBackOnAlert: NO,
47
286
 
48
- /** @private
49
- Provide backwards compatibility for developers setting the reporter object.
287
+
288
+ /**
289
+ The reporter is the object which implements the actual logging functions.
290
+
291
+ @default The browser’s console
292
+ @property {Object}
50
293
  */
51
- reporter: function(key, value) {
52
- if (value !== undefined) {
53
- this.set('reporters', [value]);
54
- }
55
- return this.get('reporters')[0];
56
- }.property(),
294
+ reporter: console,
295
+
296
+
297
+
298
+
299
+ // ..........................................................
300
+ // METHODS
301
+ //
57
302
 
58
303
  /**
59
- An array of objects which can report messages. This will contain the 'console'
60
- object, if it exists, by default.
61
-
62
- @property {Array}
63
- @default []
304
+ Logs a debug message to the console and potentially to the recorded
305
+ array, provided the respective log levels are set appropriately.
306
+
307
+ The first argument must be a string, and if there are any additional
308
+ arguments, it is assumed to be a format string. Thus, you can (and
309
+ should) use it like:
310
+
311
+ SC.Logger.debug("%@: My debug message", this); // good
312
+
313
+ …and not:
314
+
315
+ SC.Logger.debug("%@: My debug message".fmt(this)); // bad
316
+
317
+ The former method can be more efficient because if the log levels are set
318
+ in such a way that the debug() invocation will be ignored, then the
319
+ String.fmt() call will never actually be performed.
320
+
321
+ @param {String} A message or a format string
322
+ @param {…} (optional) Other arguments to pass to String.fmt() when using a format string
64
323
  */
65
- reporters: [],
324
+ debug: function(message, optionalFormatArgs) {
325
+ // Implementation note: To avoid having to put the SC.debug() shorthand
326
+ // variant inside a function wrapper, we'll avoid 'this'.
327
+ SC.Logger._handleMessage(SC.LOGGER_LEVEL_DEBUG, YES, message, arguments);
328
+ },
66
329
 
67
- init: function() {
68
- if (window.console !== undefined) this.get('reporters').push(window.console);
330
+
331
+ /**
332
+ Logs a debug message to the console and potentially to the recorded
333
+ array, provided the respective log levels are set appropriately.
334
+
335
+ Unlike simply debug(), this method does not try to apply String.fmt() to
336
+ the arguments, and instead passes them directly to the reporter (and
337
+ stringifies them if recording). This can be useful if the browser formats
338
+ a type in a manner more useful to you than you can achieve with
339
+ String.fmt().
340
+
341
+ @param {String|Array|Function|Object}
342
+ */
343
+ debugWithoutFmt: function() {
344
+ this._handleMessage(SC.LOGGER_LEVEL_DEBUG, NO, null, arguments);
69
345
  },
70
346
 
71
347
 
72
- // ..........................................................
73
- // REPORTERS SUPPORT
74
- //
348
+ /**
349
+ Begins a new group in the console and/or in the recorded array provided
350
+ the respective log levels are set to ouput/record 'debug' messages.
351
+ Every message after this call (at any log level) will be indented for
352
+ readability until a matching {@link SC.Logger.debugGroupEnd} is invoked,
353
+ and you can create as many levels as you want.
354
+
355
+ Assuming you are using 'debug' messages elsewhere, it is preferable to
356
+ group them using this method over simply {@link SC.Logger.group} — the log
357
+ levels could be set such that the 'debug' messages are never seen, and you
358
+ wouldn’t want an empty/needless group!
359
+
360
+ You can optionally provide a title for the group. If there are any
361
+ additional arguments, the first argument is assumed to be a format string.
362
+ Thus, you can (and should) use it like:
363
+
364
+ SC.Logger.debugGroup("%@: My debug group", this); // good
365
+
366
+ …and not:
367
+
368
+ SC.Logger.debugGroup("%@: My debug group".fmt(this)); // bad
369
+
370
+ The former method can be more efficient because if the log levels are set
371
+ in such a way that the debug() invocation will be ignored, then the
372
+ String.fmt() call will never actually be performed.
373
+
374
+ @param {String} (optional) A title or format string to display above the group
375
+ @param {…} (optional) Other arguments to pass to String.fmt() when using a format string as the title
376
+ */
377
+ debugGroup: function(message, optionalFormatArgs) {
378
+ // Implementation note: To avoid having to put the SC.debugGroup()
379
+ // shorthand variant inside a function wrapper, we'll avoid 'this'.
380
+ SC.Logger._handleGroup(SC.LOGGER_LEVEL_DEBUG, message, arguments);
381
+ },
382
+
75
383
 
76
384
  /**
77
- @since SproutCore 1.5
78
- @param {Object} reporter The report object to add
79
- @returns {SC.Logger}
385
+ Ends a group initiated with {@link SC.Logger.debugGroup}, provided the
386
+ respective output/recording log levels are set appropriately.
387
+
388
+ @see SC.Logger.debugGroup
80
389
  */
81
- addReporter: function(reporter) {
82
- this.get('reporters').push(reporter);
83
- return this;
390
+ debugGroupEnd: function() {
391
+ // Implementation note: To avoid having to put the SC.debugGroupEnd()
392
+ // shorthand variant inside a function wrapper, we'll avoid 'this'.
393
+ SC.Logger._handleGroupEnd(SC.LOGGER_LEVEL_DEBUG);
84
394
  },
85
395
 
396
+
397
+
86
398
  /**
87
- @since SproutCore 1.5
88
- @param {Boolean} keepConsole If NO, remove console from reporters as well
89
- @returns {SC.Logger}
399
+ Logs an informational message to the console and potentially to the
400
+ recorded array, provided the respective log levels are set appropriately.
401
+
402
+ The first argument must be a string, and if there are any additional
403
+ arguments, it is assumed to be a format string. Thus, you can (and
404
+ should) use it like:
405
+
406
+ SC.Logger.info("%@: My info message", this); // good
407
+
408
+ …and not:
409
+
410
+ SC.Logger.info("%@: My info message".fmt(this)); // bad
411
+
412
+ The former method can be more efficient because if the log levels are set
413
+ in such a way that the info() invocation will be ignored, then the
414
+ String.fmt() call will never actually be performed.
415
+
416
+ @param {String} A message or a format string
417
+ @param {…} (optional) Other arguments to pass to String.fmt() when using a format string
90
418
  */
91
- clearReporters: function(keepConsole) {
92
- var ret = [];
93
- if (keepConsole) ret.push(console);
94
- this.set('reporters', ret);
95
- return this;
419
+ info: function(message, optionalFormatArgs) {
420
+ // Implementation note: To avoid having to put the SC.info() shorthand
421
+ // variant inside a function wrapper, we'll avoid 'this'.
422
+ SC.Logger._handleMessage(SC.LOGGER_LEVEL_INFO, YES, message, arguments);
96
423
  },
97
424
 
98
- // ..........................................................
99
- // LOGGING SUPPORT
100
- //
101
425
 
102
426
  /**
103
- Log output to the console, but only if it exists.
427
+ Logs an information message to the console and potentially to the recorded
428
+ array, provided the respective log levels are set appropriately.
429
+
430
+ Unlike simply info(), this method does not try to apply String.fmt() to
431
+ the arguments, and instead passes them directly to the reporter (and
432
+ stringifies them if recording). This can be useful if the browser formats
433
+ a type in a manner more useful to you than you can achieve with
434
+ String.fmt().
104
435
 
105
436
  @param {String|Array|Function|Object}
106
- @returns {SC.Logger}
107
437
  */
108
- log: function() {
109
- var reporters = this.get('reporters'),
110
- args = arguments,
111
- f;
438
+ infoWithoutFmt: function() {
439
+ this._handleMessage(SC.LOGGER_LEVEL_INFO, NO, null, arguments);
440
+ },
441
+
442
+
443
+ /**
444
+ Begins a new group in the console and/or in the recorded array provided
445
+ the respective log levels are set to ouput/record 'info' messages.
446
+ Every message after this call (at any log level) will be indented for
447
+ readability until a matching {@link SC.Logger.infoGroupEnd} is invoked,
448
+ and you can create as many levels as you want.
449
+
450
+ Assuming you are using 'info' messages elsewhere, it is preferable to
451
+ group them using this method over simply {@link SC.Logger.group} — the log
452
+ levels could be set such that the 'info' messages are never seen, and you
453
+ wouldn’t want an empty/needless group!
454
+
455
+ You can optionally provide a title for the group. If there are any
456
+ additional arguments, the first argument is assumed to be a format string.
457
+ Thus, you can (and should) use it like:
458
+
459
+ SC.Logger.infoGroup("%@: My info group", this); // good
460
+
461
+ …and not:
462
+
463
+ SC.Logger.infoGroup("%@: My info group".fmt(this)); // bad
464
+
465
+ The former method can be more efficient because if the log levels are set
466
+ in such a way that the info() invocation will be ignored, then the
467
+ String.fmt() call will never actually be performed.
468
+
469
+ @param {String} (optional) A title or format string to display above the group
470
+ @param {…} (optional) Other arguments to pass to String.fmt() when using a format string as the title
471
+ */
472
+ infoGroup: function(message, optionalFormatArgs) {
473
+ // Implementation note: To avoid having to put the SC.infoGroup()
474
+ // shorthand variant inside a function wrapper, we'll avoid 'this'.
475
+ SC.Logger._handleGroup(SC.LOGGER_LEVEL_INFO, message, arguments);
476
+ },
477
+
478
+
479
+ /**
480
+ Ends a group initiated with {@link SC.Logger.infoGroup}, provided the
481
+ respective output/recording log levels are set appropriately.
482
+
483
+ @see SC.Logger.infoGroup
484
+ */
485
+ infoGroupEnd: function() {
486
+ // Implementation note: To avoid having to put the SC.infoGroupEnd()
487
+ // shorthand variant inside a function wrapper, we'll avoid 'this'.
488
+ SC.Logger._handleGroupEnd(SC.LOGGER_LEVEL_INFO);
489
+ },
490
+
491
+
492
+
493
+ /**
494
+ Logs a warning message to the console and potentially to the recorded
495
+ array, provided the respective log levels are set appropriately.
496
+
497
+ The first argument must be a string, and if there are any additional
498
+ arguments, it is assumed to be a format string. Thus, you can (and
499
+ should) use it like:
500
+
501
+ SC.Logger.warn("%@: My warning message", this); // good
112
502
 
113
- // log through the reporter
114
- reporters.forEach(function(reporter) {
115
- if (reporter.log !== undefined && reporter.log.apply !== undefined) f = reporter.log;
116
- else if (this.get('fallBackOnAlert')) f = alert;
503
+ …and not:
117
504
 
118
- if (this.get('format')) args = [SC.A(args).join(", ")];
119
- f.apply(reporter, args);
120
- }, this);
505
+ SC.Logger.warn("%@: My warning message".fmt(this)); // bad
506
+
507
+ The former method can be more efficient because if the log levels are set
508
+ in such a way that the warn() invocation will be ignored, then the
509
+ String.fmt() call will never actually be performed.
510
+
511
+ @param {String} A message or a format string
512
+ @param {…} (optional) Other arguments to pass to String.fmt() when using a format string
513
+ */
514
+ warn: function(message, optionalFormatArgs) {
515
+ // Implementation note: To avoid having to put the SC.warn() shorthand
516
+ // variant inside a function wrapper, we'll avoid 'this'.
517
+ SC.Logger._handleMessage(SC.LOGGER_LEVEL_WARN, YES, message, arguments);
121
518
 
122
- return this;
123
519
  },
124
520
 
521
+
125
522
  /**
126
- Log a debug message to the console.
523
+ Logs a warning message to the console and potentially to the recorded
524
+ array, provided the respective log levels are set appropriately.
127
525
 
128
- Logs the response using {@link SC.Logger.log} if reporter.debug does not exist and
129
- {@link SC.Logger.fallBackOnLog} is true.
526
+ Unlike simply warn(), this method does not try to apply String.fmt() to
527
+ the arguments, and instead passes them directly to the reporter (and
528
+ stringifies them if recording). This can be useful if the browser formats
529
+ a type in a manner more useful to you than you can achieve with
530
+ String.fmt().
130
531
 
131
532
  @param {String|Array|Function|Object}
132
- @returns {SC.Logger}
133
- */
134
- debug: function() {
135
- var reporters = this.get('reporters'),
136
- args = SC.A(arguments),
137
- f;
138
-
139
- reporters.forEach(function(reporter) {
140
- if (reporter.debug !== undefined && reporter.debug.apply !== undefined) {
141
- f = reporter.debug;
142
- } else if (reporter.log !== undefined && reporter.log.apply !== undefined) {
143
- args.unshift('DEBUG');
144
- f = reporter.log;
145
- } else if (this.get('fallBackOnAlert')) {
146
- args.unshift('DEBUG');
147
- f = alert;
148
- }
533
+ */
534
+ warnWithoutFmt: function() {
535
+ this._handleMessage(SC.LOGGER_LEVEL_WARN, NO, null, arguments);
536
+ },
149
537
 
150
- if (this.get('format')) args = [args.join(", ")];
151
- f.apply(reporter, args);
152
- }, this);
153
538
 
154
- return this;
539
+ /**
540
+ Begins a new group in the console and/or in the recorded array provided
541
+ the respective log levels are set to ouput/record 'warn' messages.
542
+ Every message after this call (at any log level) will be indented for
543
+ readability until a matching {@link SC.Logger.warnGroupEnd} is invoked,
544
+ and you can create as many levels as you want.
545
+
546
+ Assuming you are using 'warn' messages elsewhere, it is preferable to
547
+ group them using this method over simply {@link SC.Logger.group} — the log
548
+ levels could be set such that the 'warn' messages are never seen, and you
549
+ wouldn’t want an empty/needless group!
550
+
551
+ You can optionally provide a title for the group. If there are any
552
+ additional arguments, the first argument is assumed to be a format string.
553
+ Thus, you can (and should) use it like:
554
+
555
+ SC.Logger.warnGroup("%@: My warn group", this); // good
556
+
557
+ …and not:
558
+
559
+ SC.Logger.warnGroup("%@: My warn group".fmt(this)); // bad
560
+
561
+ The former method can be more efficient because if the log levels are set
562
+ in such a way that the warn() invocation will be ignored, then the
563
+ String.fmt() call will never actually be performed.
564
+
565
+ @param {String} (optional) A title or format string to display above the group
566
+ @param {…} (optional) Other arguments to pass to String.fmt() when using a format string as the title
567
+ */
568
+ warnGroup: function(message, optionalFormatArgs) {
569
+ // Implementation note: To avoid having to put the SC.warnGroup()
570
+ // shorthand variant inside a function wrapper, we'll avoid 'this'.
571
+ SC.Logger._handleGroup(SC.LOGGER_LEVEL_WARN, message, arguments);
155
572
  },
156
573
 
574
+
157
575
  /**
158
- Prints the properties of an object.
576
+ Ends a group initiated with {@link SC.Logger.warnGroup}, provided the
577
+ respective output/recording log levels are set appropriately.
159
578
 
160
- @param {Object}
161
- @returns {SC.Logger}
579
+ @see SC.Logger.warnGroup
162
580
  */
163
- dir: function() {
164
- var reporters = this.get('reporters'),
165
- args = SC.A(arguments),
166
- f;
167
-
168
- reporters.forEach(function(reporter) {
169
- if (reporter.dir !== undefined && reporter.dir.apply !== undefined) {
170
- f = reporter.dir;
171
- } else if (reporter.log !== undefined && reporter.log.apply !== undefined) {
172
- f = reporter.log;
173
- args = args.map(function(obj) { return SC.inspect(obj); });
174
- } else if (this.get('fallBackOnAlert')) {
175
- f = alert;
176
- args = args.map(function(obj) { return SC.inspect(obj); });
177
- }
581
+ warnGroupEnd: function() {
582
+ // Implementation note: To avoid having to put the SC.warnGroupEnd()
583
+ // shorthand variant inside a function wrapper, we'll avoid 'this'.
584
+ SC.Logger._handleGroupEnd(SC.LOGGER_LEVEL_WARN);
585
+ },
586
+
178
587
 
179
- f.apply(reporter, args); // we don't want to format these results
180
- }, this);
588
+ /**
589
+ Logs an error message to the console and potentially to the recorded
590
+ array, provided the respective log levels are set appropriately.
181
591
 
182
- return this;
592
+ The first argument must be a string, and if there are any additional
593
+ arguments, it is assumed to be a format string. Thus, you can (and
594
+ should) use it like:
595
+
596
+ SC.Logger.error("%@: My error message", this); // good
597
+
598
+ …and not:
599
+
600
+ SC.Logger.warn("%@: My error message".fmt(this)); // bad
601
+
602
+ The former method can be more efficient because if the log levels are set
603
+ in such a way that the warn() invocation will be ignored, then the
604
+ String.fmt() call will never actually be performed.
605
+
606
+ @param {String} A message or a format string
607
+ @param {…} (optional) Other arguments to pass to String.fmt() when using a format string
608
+ */
609
+ error: function(message, optionalFormatArgs) {
610
+ // Implementation note: To avoid having to put the SC.error() shorthand
611
+ // variant inside a function wrapper, we'll avoid 'this'.
612
+ SC.Logger._handleMessage(SC.LOGGER_LEVEL_ERROR, YES, message, arguments);
183
613
  },
184
614
 
615
+
185
616
  /**
186
- Log an error to the console
617
+ Logs an error message to the console and potentially to the recorded
618
+ array, provided the respective log levels are set appropriately.
619
+
620
+ Unlike simply error(), this method does not try to apply String.fmt() to
621
+ the arguments, and instead passes them directly to the reporter (and
622
+ stringifies them if recording). This can be useful if the browser formats
623
+ a type in a manner more useful to you than you can achieve with
624
+ String.fmt().
187
625
 
188
626
  @param {String|Array|Function|Object}
189
- @returns {SC.Logger}
190
- */
191
- error: function() {
192
- var reporters = this.get('reporters'),
193
- args = SC.A(arguments),
194
- f;
195
-
196
- reporters.forEach(function(reporter) {
197
- if (reporter.error !== undefined && reporter.error.apply !== undefined) {
198
- f = reporter.error;
199
- } else if (reporter.log !== undefined && reporter.log.apply !== undefined) {
200
- args.unshift('ERROR');
201
- f = reporter.log;
202
- } else if (this.get('fallBackOnAlert')) {
203
- args.unshift('ERROR');
204
- f = alert;
627
+ */
628
+ errorWithoutFmt: function() {
629
+ this._handleMessage(SC.LOGGER_LEVEL_ERROR, NO, null, arguments);
630
+ },
631
+
632
+
633
+ /**
634
+ Begins a new group in the console and/or in the recorded array provided
635
+ the respective log levels are set to ouput/record 'error' messages.
636
+ Every message after this call (at any log level) will be indented for
637
+ readability until a matching {@link SC.Logger.errorGroupEnd} is invoked,
638
+ and you can create as many levels as you want.
639
+
640
+ Assuming you are using 'error' messages elsewhere, it is preferable to
641
+ group them using this method over simply {@link SC.Logger.group} — the log
642
+ levels could be set such that the 'error' messages are never seen, and you
643
+ wouldn’t want an empty/needless group!
644
+
645
+ You can optionally provide a title for the group. If there are any
646
+ additional arguments, the first argument is assumed to be a format string.
647
+ Thus, you can (and should) use it like:
648
+
649
+ SC.Logger.errorGroup("%@: My error group", this); // good
650
+
651
+ …and not:
652
+
653
+ SC.Logger.errorGroup("%@: My error group".fmt(this)); // bad
654
+
655
+ The former method can be more efficient because if the log levels are set
656
+ in such a way that the error() invocation will be ignored, then the
657
+ String.fmt() call will never actually be performed.
658
+
659
+ @param {String} (optional) A title or format string to display above the group
660
+ @param {…} (optional) Other arguments to pass to String.fmt() when using a format string as the title
661
+ */
662
+ errorGroup: function(message, optionalFormatArgs) {
663
+ // Implementation note: To avoid having to put the SC.errorGroup()
664
+ // shorthand variant inside a function wrapper, we'll avoid 'this'.
665
+ SC.Logger._handleGroup(SC.LOGGER_LEVEL_ERROR, message, arguments);
666
+ },
667
+
668
+
669
+ /**
670
+ Ends a group initiated with {@link SC.Logger.errorGroup}, provided the
671
+ respective output/recording log levels are set appropriately.
672
+
673
+ @see SC.Logger.errorGroup
674
+ */
675
+ errorGroupEnd: function() {
676
+ // Implementation note: To avoid having to put the SC.errorGroupEnd()
677
+ // shorthand variant inside a function wrapper, we'll avoid 'this'.
678
+ SC.Logger._handleGroupEnd(SC.LOGGER_LEVEL_ERROR);
679
+ },
680
+
681
+
682
+
683
+ /**
684
+ This method will output all recorded log messages to the reporter. This
685
+ provides a convenient way to see the messages “on-demand” without having
686
+ to have them always output. The timestamp of each message will be
687
+ included as a prefix if you specify 'includeTimestamps' as YES, although
688
+ in some browsers the native group indenting can make the timestamp
689
+ formatting less than ideal.
690
+
691
+ @param {Boolean} (optional) Whether to include timestamps in the output
692
+ */
693
+ outputRecordedLogMessages: function(includeTimestamps) {
694
+ // If we have no reporter, there's nothing we can do.
695
+ if (!this.get('exists')) return;
696
+
697
+ var reporter = this.get('reporter'),
698
+ entries = this.get('recordedLogMessages'),
699
+ indentation = 0,
700
+ timestampFormat = SC.LOGGER_RECORDED_LOG_TIMESTAMP_PREFIX,
701
+ i, iLen, entry, type, timestampStr, message, originalArguments,
702
+ output, title, newIndentation, disparity, j, jLen;
703
+
704
+ if (entries) {
705
+ for (i = 0, iLen = entries.length; i < iLen; ++i) {
706
+ entry = entries[i];
707
+ type = entry.type;
708
+
709
+ if (includeTimestamps) {
710
+ timestampStr = timestampFormat.fmt(entry.timestamp.utcFormat());
711
+ }
712
+
713
+ // Is this a message or a group directive?
714
+ message = entry.message;
715
+ if (message) {
716
+ // It's a message entry. Were the original arguments stored? If
717
+ // so, we need to use those instead of the message.
718
+ originalArguments = entry.originalArguments;
719
+ this._outputMessage(type, timestampStr, indentation, message, originalArguments);
720
+ }
721
+ else {
722
+ // It's a group directive. Update our indentation appropriately.
723
+ newIndentation = entry.indentation;
724
+ title = entry.title;
725
+ disparity = newIndentation - indentation;
726
+
727
+ // If the reporter implements group() and the indentation level
728
+ // changes by more than 1, that implies that some earlier “begin
729
+ // group” / “end group” directives were pruned from the beginning of
730
+ // the buffer and we need to insert empty groups to compensate.
731
+ if (reporter.group) {
732
+ if (Math.abs(disparity) > 1) {
733
+ for (j = 0, jLen = (disparity - 1); j < jLen; ++j) {
734
+ if (disparity > 0) {
735
+ reporter.group();
736
+ }
737
+ else {
738
+ reporter.groupEnd();
739
+ }
740
+ }
741
+ }
742
+
743
+ if (disparity > 0) {
744
+ output = timestampStr ? timestampStr : "";
745
+ output += title;
746
+ reporter.group(output);
747
+ }
748
+ else {
749
+ reporter.groupEnd();
750
+ }
751
+ }
752
+ else {
753
+ // The reporter doesn't implement group()? Then simulate it using
754
+ // log(), assuming it implements that.
755
+ if (disparity > 0) {
756
+ // We're beginning a group. Output the header at an indentation
757
+ // that is one smaller.
758
+ this._outputGroup(type, timestampStr, newIndentation - 1, title);
759
+ }
760
+ // else {} (There is no need to simulate a group ending.)
761
+ }
762
+
763
+ // Update our indentation.
764
+ indentation = newIndentation;
765
+ }
205
766
  }
767
+ }
768
+ },
769
+
770
+
771
+ /**
772
+ This method will return a string representation of all recorded log
773
+ messages to the reporter, which can be convenient for saving logs and so
774
+ forth. The timestamp of each message will be included in the string.
206
775
 
207
- if (this.get('format')) args = [args.join(", ")];
208
- f.apply(reporter, args);
209
- }, this);
776
+ If there are no recorded log messages, an empty string will be returned
777
+ (as opposed to null).
210
778
 
211
- return this;
779
+ @returns {String}
780
+ */
781
+ stringifyRecordedLogMessages: function() {
782
+ var ret = "",
783
+ entries = this.get('recordedLogMessages'),
784
+ indentation = 0,
785
+ timestampFormat = SC.LOGGER_RECORDED_LOG_TIMESTAMP_PREFIX,
786
+ prefixMapping = this._LOG_FALLBACK_PREFIX_MAPPING,
787
+ groupHeader = SC.LOGGER_LOG_GROUP_HEADER,
788
+ i, iLen, entry, type, message, originalArguments, prefix, line,
789
+ title, newIndentation, disparity;
790
+
791
+ if (entries) {
792
+ for (i = 0, iLen = entries.length; i < iLen; ++i) {
793
+ entry = entries[i];
794
+ type = entry.type;
795
+
796
+ // First determine the prefix.
797
+ prefix = timestampFormat.fmt(entry.timestamp.utcFormat());
798
+ prefix += prefixMapping[type] || "";
799
+
800
+ // Is this a message or a group directive?
801
+ message = entry.message;
802
+ if (message) {
803
+ // It's a message entry. Were arguments used, or did we format a
804
+ // message? If arguments were used, we need to stringfy those
805
+ // instead of using the message.
806
+ originalArguments = entry.originalArguments;
807
+ line = prefix + this._indentation(indentation);
808
+ line += originalArguments ? this._argumentsToString(originalArguments) : message;
809
+ }
810
+ else {
811
+ // It's a group directive, so we need to update our indentation
812
+ // appropriately. Also, if it's the beginning of the group and it
813
+ // has a title, then we need to include an appropriate header.
814
+ newIndentation = entry.indentation;
815
+ title = entry.title;
816
+ disparity = newIndentation - indentation;
817
+ if (disparity > 0) {
818
+ // We're beginning a group. Output the header at an indentation
819
+ // that is one smaller.
820
+ line = prefix + this._indentation(indentation) + groupHeader.fmt(title);
821
+ }
822
+
823
+ // Update our indentation.
824
+ indentation = newIndentation;
825
+ }
826
+
827
+ // Add the line to our string.
828
+ ret += line + "\n";
829
+ }
830
+ }
831
+ return ret;
212
832
  },
213
833
 
834
+
835
+
214
836
  /**
215
- Log an information response to the reporter.
837
+ Log output to the console, but only if it exists.
838
+
839
+ IMPORTANT: Unlike debug(), info(), warn(), and error(), messages sent to
840
+ this method do not consult the log level and will always be output.
841
+ Similarly, they will never be recorded.
216
842
 
217
- Logs the response using {@link SC.Logger.log} if reporter.info does not exist and
218
- {@link SC.Logger.fallBackOnLog} is true.
843
+ In general, you should avoid this method and instead choose the
844
+ appropriate categorization for your message, choosing the appropriate
845
+ method.
219
846
 
220
847
  @param {String|Array|Function|Object}
221
- @returns {SC.Logger}
222
- */
223
- info: function() {
224
- var reporters = this.get('reporters'),
225
- args = SC.A(arguments),
226
- f;
227
-
228
- reporters.forEach(function(reporter) {
229
- if (reporter.info !== undefined && reporter.info.apply !== undefined) {
230
- f = reporter.info;
231
- } else if (reporter.log !== undefined && reporter.log.apply !== undefined) {
232
- args.unshift('INFO');
233
- f = reporter.log;
234
- } else if (this.get('fallBackOnAlert')) {
235
- args.unshift('INFO');
236
- f = alert;
848
+ @returns {Boolean} Whether or not anything was logged
849
+ */
850
+ log: function() {
851
+ var reporter = this.get('reporter'),
852
+ ret = NO;
853
+
854
+ // Log through the reporter.
855
+ if (this.get('exists')) {
856
+ if (typeof reporter.log === "function") {
857
+ reporter.log.apply(reporter, arguments);
858
+ ret = YES;
237
859
  }
860
+ else if (reporter.log) {
861
+ // IE8 implements console.log but reports the type of console.log as
862
+ // "object", so we cannot use apply(). Because of this, the best we
863
+ // can do is call it directly with an array of our arguments.
864
+ reporter.log(this._argumentsToArray(arguments));
865
+ ret = YES;
866
+ }
867
+ }
868
+
869
+ // log through alert
870
+ if (!ret && this.get('fallBackOnAlert')) {
871
+ // include support for overriding the alert through the reporter
872
+ // if it has come this far, it's likely this will fail
873
+ if (this.get('exists') && (typeof reporter.alert === "function")) {
874
+ reporter.alert(arguments);
875
+ ret = YES;
876
+ }
877
+ else {
878
+ alert(arguments);
879
+ ret = YES;
880
+ }
881
+ }
882
+ return ret;
883
+ },
884
+
238
885
 
239
- if (this.get('format')) args = [args.join(", ")];
240
- f.apply(reporter, args);
241
- }, this);
886
+ /**
887
+ Every log after this call until {@link SC.Logger.groupEnd} is called
888
+ will be indented for readability. You can create as many levels
889
+ as you want.
890
+
891
+ IMPORTANT: Unlike debugGroup(), infoGroup(), warnGroup(), and
892
+ errorGroup(), this method do not consult the log level and will always
893
+ result in output when the reporter supports it. Similarly, group messages
894
+ logged via this method will never be recorded.
895
+
896
+ @param {String} (optional) An optional title to display above the group
897
+ */
898
+ group: function(title) {
899
+ var reporter = this.get('reporter');
242
900
 
243
- return this;
901
+ if (this.get('exists') && (typeof reporter.group === "function")) {
902
+ reporter.group(title);
903
+ }
244
904
  },
245
905
 
246
906
  /**
247
- Log a warning to the console.
907
+ Ends a group declared with {@link SC.Logger.group}.
248
908
 
249
- Logs the warning using {@link SC.Logger.log} if reporter.warning does not exist and
250
- {@link SC.Logger.fallBackOnLog} is true.
909
+ @see SC.Logger.group
910
+ */
911
+ groupEnd: function() {
912
+ var reporter = this.get('reporter');
251
913
 
252
- @param {String|Array|Function|Object}
253
- @returns {SC.Logger}
254
- */
255
- warn: function() {
256
- var reporters = this.get('reporters'),
257
- args = SC.A(arguments),
258
- f;
259
-
260
- reporters.forEach(function(reporter) {
261
- if (reporter.warn !== undefined && reporter.warn.apply !== undefined) {
262
- f = reporter.warn;
263
- } else if (reporter.log !== undefined && reporter.log.apply !== undefined) {
264
- args.unshift('WARN');
265
- f = reporter.log;
266
- } else if (this.get('fallBackOnAlert')) {
267
- args.unshift('WARN');
268
- f = alert;
914
+ if (this.get('exists') && (typeof reporter.groupEnd === "function")) {
915
+ reporter.groupEnd();
916
+ }
917
+ },
918
+
919
+
920
+
921
+ /**
922
+ Outputs the properties of an object.
923
+
924
+ Logs the object using {@link SC.Logger.log} if the reporter.dir function
925
+ does not exist.
926
+
927
+ @param {Object}
928
+ */
929
+ dir: function() {
930
+ var reporter = this.get('reporter');
931
+
932
+ if (this.get('exists') && (typeof reporter.dir === "function")) {
933
+ // Firebug's console.dir doesn't support multiple objects here
934
+ // but maybe custom reporters will
935
+ reporter.dir.apply(reporter, arguments);
936
+ }
937
+ else {
938
+ this.log.apply(this, arguments);
939
+ }
940
+ },
941
+
942
+
943
+ /**
944
+ Prints an XML outline for any HTML or XML object.
945
+
946
+ Logs the object using {@link SC.Logger.log} if reporter.dirxml function
947
+ does not exist.
948
+
949
+ @param {Object}
950
+ */
951
+ dirxml: function() {
952
+ var reporter = this.get('reporter');
953
+
954
+ if (this.get('exists') && (typeof reporter.dirxml === "function")) {
955
+ // Firebug's console.dirxml doesn't support multiple objects here
956
+ // but maybe custom reporters will
957
+ reporter.dirxml.apply(reporter, arguments);
958
+ }
959
+ else {
960
+ this.log.apply(this, arguments);
961
+ }
962
+ },
963
+
964
+
965
+
966
+ /**
967
+ Begins the JavaScript profiler, if it exists. Call {@link SC.Logger.profileEnd}
968
+ to end the profiling process and receive a report.
969
+
970
+ @param {String} (optional) A title to associate with the profile
971
+ @returns {Boolean} YES if reporter.profile exists, NO otherwise
972
+ */
973
+ profile: function(title) {
974
+ var reporter = this.get('reporter');
975
+
976
+ if (this.get('exists') && (typeof reporter.profile === "function")) {
977
+ reporter.profile(title);
978
+ return YES;
979
+ }
980
+ return NO;
981
+ },
982
+
983
+ /**
984
+ Ends the JavaScript profiler, if it exists. If you specify a title, the
985
+ profile with that title will be ended.
986
+
987
+ @param {String} (optional) A title to associate with the profile
988
+ @returns {Boolean} YES if reporter.profileEnd exists, NO otherwise
989
+ @see SC.Logger.profile
990
+ */
991
+ profileEnd: function(title) {
992
+ var reporter = this.get('reporter');
993
+
994
+ if (this.get('exists') && (typeof reporter.profileEnd === "function")) {
995
+ reporter.profileEnd(title);
996
+ return YES;
997
+ }
998
+ return NO;
999
+ },
1000
+
1001
+
1002
+ /**
1003
+ Measure the time between when this function is called and
1004
+ {@link SC.Logger.timeEnd} is called.
1005
+
1006
+ @param {String} The name of the profile to begin
1007
+ @returns {Boolean} YES if reporter.time exists, NO otherwise
1008
+ @see SC.Logger.timeEnd
1009
+ */
1010
+ time: function(name) {
1011
+ var reporter = this.get('reporter');
1012
+
1013
+ if (this.get('exists') && (typeof reporter.time === "function")) {
1014
+ reporter.time(name);
1015
+ return YES;
1016
+ }
1017
+ return NO;
1018
+ },
1019
+
1020
+ /**
1021
+ Ends the profile specified.
1022
+
1023
+ @param {String} The name of the profile to end
1024
+ @returns {Boolean} YES if reporter.timeEnd exists, NO otherwise
1025
+ @see SC.Logger.time
1026
+ */
1027
+ timeEnd: function(name) {
1028
+ var reporter = this.get('reporter');
1029
+
1030
+ if (this.get('exists') && (typeof reporter.timeEnd === "function")) {
1031
+ reporter.timeEnd(name);
1032
+ return YES;
1033
+ }
1034
+ return NO;
1035
+ },
1036
+
1037
+
1038
+ /**
1039
+ Prints a stack-trace.
1040
+
1041
+ @returns {Boolean} YES if reporter.trace exists, NO otherwise
1042
+ */
1043
+ trace: function() {
1044
+ var reporter = this.get('reporter');
1045
+
1046
+ if (this.get('exists') && (typeof reporter.trace === "function")) {
1047
+ reporter.trace();
1048
+ return YES;
1049
+ }
1050
+ return NO;
1051
+ },
1052
+
1053
+
1054
+
1055
+
1056
+ // ..........................................................
1057
+ // INTERNAL SUPPORT
1058
+ //
1059
+
1060
+ init: function() {
1061
+ sc_super();
1062
+
1063
+ // Set a reasonable default value if none has been set.
1064
+ if (!this.get('logOutputLevel')) {
1065
+ if (SC.buildMode === "debug") {
1066
+ this.set('logOutputLevel', SC.LOGGER_LEVEL_DEBUG);
1067
+ }
1068
+ else {
1069
+ this.set('logOutputLevel', SC.LOGGER_LEVEL_INFO);
1070
+ }
1071
+ }
1072
+
1073
+ this.debugEnabledDidChange();
1074
+ },
1075
+
1076
+
1077
+ /** @private
1078
+ For backwards compatibility with the older 'debugEnabled' property, set
1079
+ our log output level to SC.LOGGER_LEVEL_DEBUG if 'debugEnabled' is set to
1080
+ YES.
1081
+ */
1082
+ debugEnabledDidChange: function() {
1083
+ if (this.get('debugEnabled')) {
1084
+ this.set('logOutputLevel', SC.LOGGER_LEVEL_DEBUG);
1085
+ }
1086
+ }.observes('debugEnabled'),
1087
+
1088
+
1089
+
1090
+ /** @private
1091
+ Outputs and/or records the specified message of the specified type if the
1092
+ respective current log levels allow for it. Assuming
1093
+ 'automaticallyFormat' is specified, then String.fmt() will be called
1094
+ automatically on the message, but only if at least one of the log levels
1095
+ is such that the result will be used.
1096
+
1097
+ @param {String} type Expected to be SC.LOGGER_LEVEL_DEBUG, etc.
1098
+ @param {Boolean} automaticallyFormat Whether or not to treat 'message' as a format string if there are additional arguments
1099
+ @param {String} message Expected to a string format (for String.fmt()) if there are other arguments
1100
+ @param {String} (optional) originalArguments All arguments passed into debug(), etc. (which includes 'message'; for efficiency, we don’t copy it)
1101
+ */
1102
+ _handleMessage: function(type, automaticallyFormat, message, originalArguments) {
1103
+ // Are we configured to show this type?
1104
+ var shouldOutput = this._shouldOutputType(type),
1105
+ shouldRecord = this._shouldRecordType(type),
1106
+ hasOtherArguments, i, len, args, output, entry;
1107
+
1108
+ // If we're neither going to output nor record the message, then stop now.
1109
+ if (!(shouldOutput || shouldRecord)) return;
1110
+
1111
+ // Do we have arguments other than 'message'? (Remember that
1112
+ // 'originalArguments' contains the message here, too, hence the > 1.)
1113
+ hasOtherArguments = (originalArguments && originalArguments.length > 1);
1114
+
1115
+ // If we're automatically formatting and there is no message (or it is
1116
+ // not a string), then don't automatically format after all.
1117
+ if (automaticallyFormat && (SC.none(message) || (typeof message !== "string"))) {
1118
+ automaticallyFormat = NO;
1119
+ }
1120
+
1121
+ // If we should automatically format, and the client specified other
1122
+ // arguments in addition to the message, then we'll call .fmt() assuming
1123
+ // that the message is a format string.
1124
+ if (automaticallyFormat) {
1125
+ if (hasOtherArguments) {
1126
+ args = [];
1127
+ for (i = 1, len = originalArguments.length; i < len; ++i) {
1128
+ args.push(originalArguments[i]);
1129
+ }
1130
+ message = message.fmt.apply(message, args);
1131
+ }
1132
+ }
1133
+
1134
+ if (shouldOutput) {
1135
+ // We only want to pass the original arguments to _outputMessage() if we
1136
+ // didn't format the message ourselves.
1137
+ args = automaticallyFormat ? null : originalArguments;
1138
+ this._outputMessage(type, null, this._outputIndentationLevel, message, args);
1139
+ }
1140
+
1141
+ // If we're recording the log, append the message now.
1142
+ if (shouldRecord) {
1143
+ entry = {
1144
+ type: type,
1145
+ message: message ? message : YES,
1146
+ timestamp: new Date()
1147
+ };
1148
+
1149
+ // If we didn't automatically format, and we have other arguments, then
1150
+ // be sure to record them, too.
1151
+ if (!automaticallyFormat && hasOtherArguments) {
1152
+ entry.originalArguments = originalArguments;
1153
+ }
1154
+
1155
+ this._addRecordedMessageEntry(entry);
1156
+ }
1157
+ },
1158
+
1159
+
1160
+ /** @private
1161
+ Outputs and/or records a group with the (optional) specified title
1162
+ assuming the respective current log levels allow for it. This will output
1163
+ the title (if there is one) and indent all further messages (of any type)
1164
+ until _handleGroupEnd() is invoked.
1165
+
1166
+ If additional arguments beyond a title are passed in, then String.fmt()
1167
+ will be called automatically on the title, but only if at least one of the
1168
+ log levels is such that the result will be used.
1169
+
1170
+ @param {String} type Expected to be SC.LOGGER_LEVEL_DEBUG, etc.
1171
+ @param {String} (optional) title Expected to a string format (for String.fmt()) if there are other arguments
1172
+ @param {String} (optional) originalArguments All arguments passed into debug(), etc. (which includes 'title'; for efficiency, we don’t copy it)
1173
+ */
1174
+ _handleGroup: function(type, title, originalArguments) {
1175
+ // Are we configured to show this type?
1176
+ var shouldOutput = this._shouldOutputType(type),
1177
+ shouldRecord = this._shouldRecordType(type),
1178
+ hasOtherArguments, i, len, args, arg, reporter, func, header, output,
1179
+ indentation, entry;
1180
+
1181
+ // If we're neither going to output nor record the group, then stop now.
1182
+ if (!(shouldOutput || shouldRecord)) return;
1183
+
1184
+ // Do we have arguments other than 'title'? (Remember that
1185
+ // 'originalArguments' contains the title here, too, hence the > 1.)
1186
+ hasOtherArguments = (originalArguments && originalArguments.length > 1);
1187
+
1188
+ // If the client specified a title as well other arguments, then we'll
1189
+ // call .fmt() assuming that the title is a format string.
1190
+ if (title && hasOtherArguments) {
1191
+ args = [];
1192
+ for (i = 1, len = originalArguments.length; i < len; ++i) {
1193
+ args.push(originalArguments[i]);
1194
+ }
1195
+ title = title.fmt.apply(title, args);
1196
+ }
1197
+
1198
+ if (shouldOutput) {
1199
+ this._outputGroup(type, null, this._outputIndentationLevel, title);
1200
+
1201
+ // Increase our indentation level to accommodate the group.
1202
+ this._outputIndentationLevel++;
1203
+ }
1204
+
1205
+ // If we're recording the group, append the entry now.
1206
+ if (shouldRecord) {
1207
+ // Increase our indentation level to accommodate the group.
1208
+ indentation = ++this._recordingIndentationLevel;
1209
+
1210
+ entry = {
1211
+ type: type,
1212
+ indentation: indentation,
1213
+ beginGroup: YES,
1214
+ title: title,
1215
+ timestamp: new Date()
1216
+ };
1217
+
1218
+ this._addRecordedMessageEntry(entry);
1219
+ }
1220
+ },
1221
+
1222
+
1223
+ /** @private
1224
+ Outputs and/or records a “group end” assuming the respective current log
1225
+ levels allow for it. This will remove one level of indentation from all
1226
+ further messages (of any type).
1227
+
1228
+ @param {String} type Expected to be SC.LOGGER_LEVEL_DEBUG, etc.
1229
+ */
1230
+ _handleGroupEnd: function(type) {
1231
+ // Are we configured to show this type?
1232
+ var shouldOutput = this._shouldOutputType(type),
1233
+ shouldRecord = this._shouldRecordType(type),
1234
+ reporter, func, indentation, entry;
1235
+
1236
+ // If we're neither going to output nor record the "group end", then stop
1237
+ // now.
1238
+ if (!(shouldOutput || shouldRecord)) return;
1239
+
1240
+ if (shouldOutput) {
1241
+ // Decrease our indentation level to accommodate the group.
1242
+ this._outputIndentationLevel--;
1243
+
1244
+ if (this.get('exists')) {
1245
+ // Do we have reporter.groupEnd defined as a function? If not, we
1246
+ // simply won't output anything.
1247
+ reporter = this.get('reporter');
1248
+ func = reporter.groupEnd;
1249
+ if (func) {
1250
+ func.call(reporter);
1251
+ }
1252
+ }
1253
+ }
1254
+
1255
+ // If we're recording the “group end”, append the entry now.
1256
+ if (shouldRecord) {
1257
+ // Decrease our indentation level to accommodate the group.
1258
+ indentation = --this._recordingIndentationLevel;
1259
+
1260
+ entry = {
1261
+ type: type,
1262
+ indentation: indentation,
1263
+ timestamp: new Date()
1264
+ };
1265
+
1266
+ this._addRecordedMessageEntry(entry);
1267
+ }
1268
+ },
1269
+
1270
+
1271
+ /** @private
1272
+ Returns whether a message of the specified type ('debug', etc.) should be
1273
+ output to the reporter based on the current value of 'logOutputLevel'.
1274
+
1275
+ @param {Constant} type
1276
+ @returns {Boolean}
1277
+ */
1278
+ _shouldOutputType: function(type) {
1279
+ var logLevelMapping = this._LOG_LEVEL_MAPPING,
1280
+ level = logLevelMapping[type] || 0,
1281
+ currentLevel = logLevelMapping[this.get('logOutputLevel')] || 0;
1282
+
1283
+ return (level <= currentLevel);
1284
+ },
1285
+
1286
+
1287
+ /** @private
1288
+ Returns whether a message of the specified type ('debug', etc.) should be
1289
+ recorded based on the current value of 'logRecordingLevel'.
1290
+
1291
+ @param {Constant} type
1292
+ @returns {Boolean}
1293
+ */
1294
+ _shouldRecordType: function(type) {
1295
+ // This is the same code as in _shouldOutputType(), but inlined to
1296
+ // avoid yet another function call.
1297
+ var logLevelMapping = this._LOG_LEVEL_MAPPING,
1298
+ level = logLevelMapping[type] || 0,
1299
+ currentLevel = logLevelMapping[this.get('logRecordingLevel')] || 0;
1300
+
1301
+ return (level <= currentLevel);
1302
+ },
1303
+
1304
+
1305
+ /** @private
1306
+ Outputs the specified message to the current reporter. If the reporter
1307
+ does not handle the specified type of message, it will fall back to using
1308
+ log() if possible.
1309
+
1310
+ @param {Constant} type
1311
+ @param {String} timestampStr An optional timestamp prefix for the line, or null for none
1312
+ @param {Number} indentation The current indentation level
1313
+ @param {String} message
1314
+ @param {Arguments} (optional) originalArguments If specified, the assumption is that the message was not automatically formatted
1315
+ */
1316
+ _outputMessage: function(type, timestampStr, indentation, message, originalArguments) {
1317
+ if (!this.get('exists')) return;
1318
+
1319
+ // Do we have reporter[type] defined as a function? If not, we'll fall
1320
+ // back to reporter.log if that exists.
1321
+ var reporter = this.get('reporter'),
1322
+ output, shouldIndent, func, prefix, args, arg;
1323
+
1324
+ // If the reporter doesn't support group(), then we need to manually
1325
+ // include indentation for the group. (It it does, we'll assume that
1326
+ // we're currently at the correct group level.)
1327
+ shouldIndent = !reporter.group;
1328
+
1329
+ // Note: Normally we wouldn't do the hash dereference twice, but
1330
+ // storing the result like this:
1331
+ //
1332
+ // var nativeFunction = console[type];
1333
+ // nativeFunction(output);
1334
+ //
1335
+ // …doesn't work in Safari 4, and:
1336
+ //
1337
+ // nativeFunction.call(console, output);
1338
+ //
1339
+ // …doesn't work in IE8 because the console.* methods are
1340
+ // reported as being objects.
1341
+ func = reporter[type];
1342
+ if (func) {
1343
+ // If we formatted, just include the message. Otherwise, include all
1344
+ // the original arguments.
1345
+ if (!originalArguments) {
1346
+ output = "";
1347
+ if (timestampStr) output = timestampStr;
1348
+ if (shouldIndent) output =+ this._indentation(indentation);
1349
+ output += message;
1350
+ reporter[type](output);
1351
+ }
1352
+ else {
1353
+ // We have arguments? Then pass them along to the reporter function
1354
+ // so that it can format them appropriately. We'll use the timestamp
1355
+ // string (if there is one) and the indentation as the first
1356
+ // arguments.
1357
+ args = this._argumentsToArray(originalArguments);
1358
+ prefix = "";
1359
+ if (timestampStr) prefix = timestampStr;
1360
+ if (shouldIndent) prefix += this._indentation(indentation);
1361
+ if (prefix) args.splice(0, 0, prefix);
1362
+
1363
+ if (func.apply) {
1364
+ func.apply(reporter, args);
1365
+ }
1366
+ else {
1367
+ // In IE8, passing the arguments as an array isn't ideal, but it's
1368
+ // pretty much all we can do because we can't call apply().
1369
+ reporter[type](args);
1370
+ }
1371
+ }
1372
+ }
1373
+ else {
1374
+ // The reporter doesn't support the requested function? If it at least
1375
+ // support log(), fall back to that.
1376
+ if (reporter.log) {
1377
+ prefix = "";
1378
+ if (timestampStr) prefix = timestampStr;
1379
+ prefix += this._LOG_FALLBACK_PREFIX_MAPPING[type] || "";
1380
+ if (shouldIndent) prefix += this._indentation(indentation);
1381
+
1382
+ // If we formatted, just include the message. Otherwise, include
1383
+ // all the original arguments.
1384
+ if (!originalArguments) {
1385
+ reporter.log(prefix + message);
1386
+ }
1387
+ else {
1388
+ args = this._argumentsToArray(originalArguments);
1389
+ if (prefix) args.splice(0, 0, prefix);
1390
+ reporter.log(args);
1391
+ }
269
1392
  }
1393
+ }
1394
+ },
1395
+
1396
+
1397
+ /** @private
1398
+ Outputs the specified “begin group” directive to the current reporter. If
1399
+ the reporter does not handle the group() method, it will fall back to
1400
+ simulating using log() if possible.
1401
+
1402
+ @param {Constant} type
1403
+ @param {String} timestampStr An optional timestamp prefix for the line, or null for none
1404
+ @param {Number} indentation The current indentation level, not including what the group will set it to
1405
+ @param {String} (optional) title
1406
+ */
1407
+ _outputGroup: function(type, timestampStr, indentation, title) {
1408
+ if (!this.get('exists')) return;
270
1409
 
271
- if (this.get('format')) args = [args.join(", ")];
272
- f.apply(reporter, args);
273
- }, this);
1410
+ // Do we have reporter.group defined as a function? If not, we'll fall
1411
+ // back to reporter.log if that exists. (Thankfully, we can avoid the IE8
1412
+ // special-casing we have in _outputMessage() because IE8 doesn't support
1413
+ // console.group(), anyway.)
1414
+ var reporter = this.get('reporter'),
1415
+ func = reporter.group,
1416
+ output;
274
1417
 
275
- return this;
1418
+ if (func) {
1419
+ output = timestampStr ? timestampStr : "";
1420
+ output += title;
1421
+ func.call(reporter, output);
1422
+ }
1423
+ else if (reporter.log) {
1424
+ // The reporter doesn't support group()? Then simulate with log().
1425
+ // (We'll live with the duplicitous dereference rather than using
1426
+ // apply() to work around the IE8 issue described in _outputMessage().)
1427
+ output = "";
1428
+ if (timestampStr) output = timestampStr;
1429
+ output += this._LOG_FALLBACK_PREFIX_MAPPING[type] || "";
1430
+ output += this._indentation(indentation);
1431
+ output += SC.LOGGER_LOG_GROUP_HEADER.fmt(title);
1432
+ reporter.log(output);
1433
+ }
1434
+ },
1435
+
1436
+
1437
+ /** @private
1438
+ This method will add the specified entry to the recorded log messages
1439
+ array and also prune array as necessary according to the current values of
1440
+ 'recordedLogMessagesMaximumLength' and
1441
+ 'recordedLogMessagesPruningMinimumLength'.
1442
+ */
1443
+ _addRecordedMessageEntry: function(entry) {
1444
+ var recordedMessages = this.get('recordedLogMessages'),
1445
+ len;
1446
+
1447
+ // Lazily create the array.
1448
+ if (!recordedMessages) {
1449
+ recordedMessages = [];
1450
+ this.set('recordedLogMessages', recordedMessages);
1451
+ }
1452
+
1453
+ recordedMessages.push(entry);
1454
+
1455
+ // Have we exceeded the maximum size? If so, do some pruning.
1456
+ len = recordedMessages.length;
1457
+ if (len > this.get('recordedLogMessagesMaximumLength')) {
1458
+ recordedMessages.splice(0, (len - this.get('recordedLogMessagesPruningMinimumLength')));
1459
+ }
1460
+
1461
+ // Notify that the array content changed.
1462
+ recordedMessages.enumerableContentDidChange();
1463
+ },
1464
+
1465
+
1466
+
1467
+ /** @private
1468
+ The arguments function property doesn't support Array#unshift. This helper
1469
+ copies the elements of arguments to a blank array.
1470
+
1471
+ @param {Array} arguments The arguments property of a function
1472
+ @returns {Array} An array containing the elements of arguments parameter
1473
+ */
1474
+ _argumentsToArray: function(args) {
1475
+ var ret = [],
1476
+ i, len;
1477
+
1478
+ if (args) {
1479
+ for (i = 0, len = args.length; i < len; ++i) {
1480
+ ret[i] = args[i];
1481
+ }
1482
+ }
1483
+ return ret;
1484
+ },
1485
+
1486
+
1487
+ /** @private
1488
+ Formats the arguments array of a function by creating a string with
1489
+ SC.LOGGER_LOG_DELIMITER between the elements.
1490
+ */
1491
+ _argumentsToString: function() {
1492
+ var ret = "",
1493
+ delimeter = SC.LOGGER_LOG_DELIMITER,
1494
+ i, len;
1495
+
1496
+ for (i = 0, len = (arguments.length - 1); i < len; ++i) {
1497
+ ret += arguments[i] + delimeter;
1498
+ }
1499
+ ret += arguments[len];
1500
+ return ret;
1501
+ },
1502
+
1503
+
1504
+ /** @private
1505
+ Returns a string containing the appropriate indentation for the specified
1506
+ indentation level.
1507
+
1508
+ @param {Number} The indentation level
1509
+ @returns {String}
1510
+ */
1511
+ _indentation: function(level) {
1512
+ if (!level || level < 0) {
1513
+ level = 0;
1514
+ }
1515
+
1516
+ var ret = "",
1517
+ indent = SC.LOGGER_LOG_GROUP_INDENTATION,
1518
+ i;
1519
+
1520
+ for (i = 0; i < level; ++i) {
1521
+ ret += indent;
1522
+ }
1523
+ return ret;
1524
+ },
1525
+
1526
+
1527
+
1528
+ /** @private
1529
+ The current “for output” indentation level. The reporter (browser
1530
+ console) is expected to keep track of this for us for output, but we need
1531
+ to do our own bookkeeping if the browser doesn’t support console.group.
1532
+ This is incremented by _debugGroup() and friends, and decremented by
1533
+ _debugGroupEnd() and friends.
1534
+ */
1535
+ _outputIndentationLevel: 0,
1536
+
1537
+
1538
+ /** @private
1539
+ The current “for recording” indentation level. This can be different than
1540
+ the “for output” indentation level if the respective log levels are set
1541
+ differently. This is incremented by _debugGroup() and friends, and
1542
+ decremented by _debugGroupEnd() and friends.
1543
+ */
1544
+ _recordingIndentationLevel: 0,
1545
+
1546
+
1547
+ /** @private
1548
+ A mapping of the log level constants (SC.LOGGER_LEVEL_DEBUG, etc.) to
1549
+ their priority. This makes it easy to determine which levels are “higher”
1550
+ than the current level.
1551
+
1552
+ Implementation note: We’re hardcoding the values of the constants defined
1553
+ earlier here for a tiny bit of efficiency (we can create the hash all at
1554
+ once rather than having to push in keys).
1555
+ */
1556
+ _LOG_LEVEL_MAPPING: { debug: 4, info: 3, warn: 2, error: 1, none: 0 },
1557
+
1558
+
1559
+ /** @private
1560
+ If the current reporter does not support a particular type of log message
1561
+ (for example, some older browsers’ consoles support console.log but not
1562
+ console.debug), we’ll use the specified prefixes.
1563
+
1564
+ Implementation note: We’re hardcoding the values of the constants defined
1565
+ earlier here for a tiny bit of efficiency (we can create the hash all at
1566
+ once rather than having to push in keys).
1567
+ */
1568
+ _LOG_FALLBACK_PREFIX_MAPPING: {
1569
+ debug: SC.LOGGER_LOG_DEBUG,
1570
+ info: SC.LOGGER_LOG_INFO,
1571
+ warn: SC.LOGGER_LOG_WARN,
1572
+ error: SC.LOGGER_LOG_ERROR
276
1573
  }
277
1574
 
278
1575
  });
279
1576
 
280
- SC.log = function() {
281
- return SC.Logger.log.apply(SC.Logger, arguments);
282
- };
1577
+
1578
+ // Add convenient shorthands methods to SC.
1579
+ SC.debug = SC.Logger.debug;
1580
+ SC.info = SC.Logger.info;
1581
+ SC.warn = SC.Logger.warn;
1582
+ SC.error = SC.Logger.error;