sproutcore 1.6.0.1-java → 1.7.1.beta-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. data/CHANGELOG +21 -0
  2. data/Gemfile +5 -0
  3. data/Rakefile +26 -13
  4. data/VERSION.yml +2 -2
  5. data/lib/Buildfile +43 -4
  6. data/lib/buildtasks/build.rake +10 -0
  7. data/lib/buildtasks/helpers/file_rule.rb +22 -0
  8. data/lib/buildtasks/helpers/file_rule_list.rb +137 -0
  9. data/lib/buildtasks/manifest.rake +133 -122
  10. data/lib/frameworks/sproutcore/CHANGELOG.md +69 -2
  11. data/lib/frameworks/sproutcore/apps/tests/english.lproj/strings.js +1 -0
  12. data/lib/frameworks/sproutcore/frameworks/bootstrap/system/browser.js +28 -22
  13. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/array.js +9 -5
  14. data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/controller.js +1 -1
  15. data/lib/frameworks/sproutcore/frameworks/core_foundation/controls/button.js +18 -13
  16. data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/bind.js +5 -3
  17. data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/collection.js +2 -0
  18. data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/action_support.js +80 -0
  19. data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/template_helpers/text_field_support.js +84 -116
  20. data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane.js +8 -5
  21. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +157 -157
  22. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +5 -3
  23. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +6 -6
  24. data/lib/frameworks/sproutcore/frameworks/core_foundation/system/sparse_array.js +10 -7
  25. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/mixins/action_support.js +106 -0
  26. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/collection.js +18 -0
  27. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/handlebars.js +71 -1
  28. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/attribute_bindings_test.js +38 -0
  29. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/class_name_bindings_test.js +47 -0
  30. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutChildViews.js +18 -18
  31. data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutStyle.js +42 -10
  32. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +158 -1
  33. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/keyboard.js +26 -1
  34. data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout_style.js +14 -8
  35. data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +15 -2
  36. data/lib/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +108 -108
  37. data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +1 -1
  38. data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +2 -4
  39. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record/error_methods.js +2 -2
  40. data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/single_attribute.js +26 -0
  41. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/query/builders.js +7 -0
  42. data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/record_array/error_methods.js +1 -1
  43. data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/system/datetime.js +4 -1
  44. data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/tests/system/datetime.js +6 -0
  45. data/lib/frameworks/sproutcore/frameworks/desktop/panes/menu.js +26 -5
  46. data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +97 -96
  47. data/lib/frameworks/sproutcore/frameworks/desktop/system/drag.js +4 -3
  48. data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/ui.js +17 -4
  49. data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +7 -7
  50. data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +7 -5
  51. data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +12 -3
  52. data/lib/frameworks/sproutcore/frameworks/desktop/views/web.js +23 -14
  53. data/lib/frameworks/sproutcore/frameworks/experimental/Buildfile +5 -1
  54. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/render_delegates/menu_scroller.js +28 -0
  55. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/tests/menu/scroll.js +235 -0
  56. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroll.js +363 -0
  57. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroller.js +250 -0
  58. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/desktop_scroller.js +92 -0
  59. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/native_scroll.js +25 -0
  60. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/scroll.js +33 -0
  61. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/touch_scroller.js +76 -0
  62. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/integration.js +50 -0
  63. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/methods.js +143 -0
  64. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/ui.js +258 -0
  65. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroll.js +1164 -0
  66. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroller.js +332 -0
  67. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroll.js +236 -0
  68. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroller.js +347 -0
  69. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroll.js +15 -0
  70. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroller.js +10 -0
  71. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroll.js +804 -0
  72. data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroller.js +133 -0
  73. data/lib/frameworks/sproutcore/frameworks/foundation/resources/text_field.css +3 -3
  74. data/lib/frameworks/sproutcore/frameworks/foundation/validators/number.js +3 -1
  75. data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +3 -3
  76. data/lib/frameworks/sproutcore/frameworks/media/views/audio.js +2 -1
  77. data/lib/frameworks/sproutcore/frameworks/media/views/controls.js +2 -1
  78. data/lib/frameworks/sproutcore/frameworks/media/views/media_slider.js +2 -4
  79. data/lib/frameworks/sproutcore/frameworks/media/views/mini_controls.js +2 -4
  80. data/lib/frameworks/sproutcore/frameworks/media/views/simple_controls.js +2 -4
  81. data/lib/frameworks/sproutcore/frameworks/media/views/video.js +2 -2
  82. data/lib/frameworks/sproutcore/frameworks/routing/system/routes.js +29 -3
  83. data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
  84. data/lib/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/replace.js +1 -1
  85. data/lib/frameworks/sproutcore/frameworks/runtime/private/property_chain.js +2 -1
  86. data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +3 -3
  87. data/lib/frameworks/sproutcore/frameworks/runtime/system/index_set.js +2 -2
  88. data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +1 -1
  89. data/lib/frameworks/sproutcore/themes/ace/resources/collection/normal/list_item.css +2 -2
  90. data/lib/frameworks/sproutcore/themes/legacy_theme/english.lproj/segmented.css +1 -1
  91. data/lib/gen/app/templates/apps/@target_name@/Buildfile +3 -5
  92. data/lib/gen/app/templates/apps/@target_name@/resources/_theme.css +18 -0
  93. data/lib/gen/project/templates/@filename@/Buildfile +2 -2
  94. data/lib/sproutcore.rb +30 -5
  95. data/lib/sproutcore/builders.rb +1 -0
  96. data/lib/sproutcore/builders/chance_file.rb +9 -16
  97. data/lib/sproutcore/builders/html.rb +2 -1
  98. data/lib/sproutcore/builders/minify.rb +4 -35
  99. data/lib/sproutcore/builders/module.rb +38 -1
  100. data/lib/sproutcore/builders/split.rb +63 -0
  101. data/lib/sproutcore/builders/strings.rb +7 -1
  102. data/lib/sproutcore/helpers.rb +1 -1
  103. data/lib/sproutcore/helpers/css_split.rb +190 -0
  104. data/lib/sproutcore/helpers/entry_sorter.rb +2 -0
  105. data/lib/sproutcore/helpers/minifier.rb +40 -16
  106. data/lib/sproutcore/helpers/static_helper.rb +35 -17
  107. data/lib/sproutcore/models/manifest.rb +26 -0
  108. data/lib/sproutcore/models/target.rb +12 -1
  109. data/lib/sproutcore/rack.rb +1 -0
  110. data/lib/sproutcore/rack/proxy.rb +244 -225
  111. data/lib/sproutcore/rack/restrict_ip.rb +67 -0
  112. data/lib/sproutcore/rack/service.rb +8 -2
  113. data/lib/sproutcore/tools.rb +102 -46
  114. data/lib/sproutcore/tools/build.rb +91 -43
  115. data/lib/sproutcore/tools/gen.rb +2 -3
  116. data/lib/sproutcore/tools/manifest.rb +22 -16
  117. data/lib/sproutcore/tools/server.rb +21 -0
  118. data/spec/buildtasks/helpers/accept_list +22 -0
  119. data/spec/buildtasks/helpers/accept_list.rb +128 -0
  120. data/spec/buildtasks/helpers/list.json +11 -0
  121. data/spec/buildtasks/manifest/prepare_build_tasks/chance_2x_spec.rb +1 -39
  122. data/spec/buildtasks/manifest/prepare_build_tasks/chance_spec.rb +0 -38
  123. data/spec/buildtasks/manifest/prepare_build_tasks/combine_spec.rb +4 -4
  124. data/spec/buildtasks/manifest/prepare_build_tasks/module_spec.rb +2 -2
  125. data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_indirect_spec.rb +7 -16
  126. data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_spec.rb +7 -17
  127. data/spec/buildtasks/manifest/prepare_build_tasks/packed_spec.rb +11 -6
  128. data/spec/fixtures/builder_tests/Buildfile +2 -1
  129. data/spec/fixtures/builder_tests/apps/module_test/modules/required_module/core.js +0 -0
  130. data/spec/lib/builders/module_spec.rb +1 -1
  131. data/spec/spec_helper.rb +1 -0
  132. data/sproutcore.gemspec +4 -9
  133. data/vendor/chance/lib/chance.rb +25 -6
  134. data/vendor/chance/lib/chance/factory.rb +45 -0
  135. data/vendor/chance/lib/chance/instance.rb +173 -28
  136. data/vendor/chance/lib/chance/instance/data_url.rb +0 -29
  137. data/vendor/chance/lib/chance/instance/slicing.rb +57 -4
  138. data/vendor/chance/lib/chance/instance/spriting.rb +112 -21
  139. data/vendor/chance/lib/chance/parser.rb +80 -52
  140. data/vendor/sproutcore/SCCompiler.jar +0 -0
  141. data/vendor/sproutcore/lib/args4j-2.0.12.jar +0 -0
  142. data/vendor/sproutcore/lib/yuicompressor-2.4.2.jar +0 -0
  143. metadata +84 -25
@@ -0,0 +1,1164 @@
1
+ // ==========================================================================
2
+ // Project: SproutCore - JavaScript Application Framework
3
+ // Copyright: ©2006-2011 Strobe Inc. and contributors.
4
+ // Portions ©2008-2011 Apple Inc. All rights reserved.
5
+ // License: Licensed under MIT license (see license.js)
6
+ // ==========================================================================
7
+
8
+ sc_require('views/scroller');
9
+
10
+ /** @class
11
+ The core scroll view component.
12
+
13
+ This class is an input-agnostic scroll view implementation.
14
+
15
+ Important Events:
16
+
17
+ - contentView frame size changes (to autoshow/hide scrollbar - adjust scrollbar size)
18
+ - horizontalScrollOffset change
19
+ - verticalScrollOffsetChanges
20
+
21
+ @extends SC.View
22
+ @since SproutCore 1.6
23
+ */
24
+ SC.CoreScrollView = SC.View.extend(
25
+ /** @scope SC.ScrollView.prototype */{
26
+
27
+ /**
28
+ @type Array
29
+ @default ['sc-scroll-view']
30
+ @see SC.View#classNames
31
+ */
32
+ classNames: ['sc-scroll-view'],
33
+
34
+ // ..........................................................
35
+ // PROPERTIES
36
+ //
37
+
38
+ /**
39
+ Walk like a duck
40
+
41
+ @type Boolean
42
+ @default YES
43
+ @readOnly
44
+ */
45
+ isScrollable: YES,
46
+
47
+ /**
48
+ The content view you want the scroll view to manage.
49
+ This will be assigned to the `contentView` of the `clipView` also.
50
+
51
+ @type SC.View
52
+ @default null
53
+ */
54
+ contentView: null,
55
+
56
+ /**
57
+ The horizontal alignment for non-filling content inside of the `ScrollView`.
58
+ Possible values are:
59
+
60
+ - `SC.ALIGN_LEFT`
61
+ - `SC.ALIGN_RIGHT`
62
+ - `SC.ALIGN_CENTER`
63
+
64
+ @type String
65
+ @default SC.ALIGN_LEFT
66
+ */
67
+ horizontalAlign: SC.ALIGN_LEFT,
68
+
69
+ /**
70
+ The vertical alignment for non-filling content inside of the `ScrollView`.
71
+ Possible values are:
72
+
73
+ - `SC.ALIGN_TOP`
74
+ - `SC.ALIGN_BOTTOM`
75
+ - `SC.ALIGN_MIDDLE`
76
+
77
+ @type String
78
+ @default SC.ALIGN_TOP
79
+ */
80
+ verticalAlign: SC.ALIGN_TOP,
81
+
82
+ /**
83
+ The current horizontal scroll offset. Changing this value will update both
84
+ the `contentView` and the horizontal scroller, if there is one.
85
+
86
+ @field
87
+ @type Number
88
+ @default 0
89
+ */
90
+ horizontalScrollOffset: function (key, value) {
91
+ if (typeof value !== "undefined") {
92
+ var minOffset = this.minimumHorizontalScrollOffset(),
93
+ maxOffset = this.get('maximumHorizontalScrollOffset');
94
+ this._scroll_horizontalScrollOffset = Math.max(minOffset, Math.min(maxOffset, value));
95
+ }
96
+
97
+ return this._scroll_horizontalScrollOffset || 0;
98
+ }.property().cacheable(),
99
+
100
+ /**
101
+ The current vertical scroll offset. Changing this value will update both
102
+ the `contentView` and the vertical scroller, if there is one.
103
+
104
+ @field
105
+ @type Number
106
+ @default 0
107
+ */
108
+ verticalScrollOffset: function (key, value) {
109
+ if (typeof value !== "undefined") {
110
+ var minOffset = this.get('minimumVerticalScrollOffset'),
111
+ maxOffset = this.get('maximumVerticalScrollOffset');
112
+ this._scroll_verticalScrollOffset = Math.max(minOffset, Math.min(maxOffset, value));
113
+ }
114
+
115
+ return this._scroll_verticalScrollOffset || 0;
116
+ }.property().cacheable(),
117
+
118
+ /** @private
119
+ Calculates the maximum offset given content and container sizes, and the
120
+ alignment.
121
+ */
122
+ maximumScrollOffset: function (contentSize, containerSize, align) {
123
+ // If the content is larger than the container, the maximum offset is
124
+ // the location of the `contentView` when scrolled all the way down.
125
+ var delta = contentSize - containerSize;
126
+ if (contentSize >= containerSize) return delta;
127
+
128
+ return (align === SC.ALIGN_LEFT || align === SC.ALIGN_TOP) ? 0 : // top-left aligned
129
+ (align === SC.ALIGN_MIDDLE || align === SC.ALIGN_CENTER) ?
130
+ Math.round(delta / 2) : // center aligned
131
+ delta; // right aligned
132
+ },
133
+
134
+ /** @private
135
+ Calculates the minimum offset given content and container sizes, and the
136
+ alignment.
137
+ */
138
+ minimumScrollOffset: function (contentSize, containerSize, align) {
139
+ var delta = contentSize - containerSize;
140
+
141
+ return (contentSize > containerSize || align === SC.ALIGN_LEFT || align === SC.ALIGN_TOP) ?
142
+ 0 : // top-left aligned or larger than the container.
143
+ (align === SC.ALIGN_MIDDLE || align === SC.ALIGN_CENTER) ?
144
+ Math.round(delta / 2) : // center aligned
145
+ delta; // right aligned
146
+ },
147
+
148
+ /**
149
+ The maximum horizontal scroll offset allowed given the current contentView
150
+ size and the size of the scroll view. If horizontal scrolling is
151
+ disabled, this will always return 0.
152
+
153
+ @field
154
+ @type Number
155
+ @default 0
156
+ */
157
+ maximumHorizontalScrollOffset: function () {
158
+ var view = this.get('contentView'),
159
+ contentWidth = view ? view.get('frame').width : 0,
160
+ calculatedWidth = view ? view.get('calculatedWidth') : 0,
161
+ containerWidth = this.get('containerView').get('frame').width;
162
+
163
+ // The following code checks if there is a calculatedWidth (collections)
164
+ // to avoid looking at the incorrect value calculated by frame.
165
+ if (calculatedWidth) {
166
+ contentWidth = view.calculatedWidth;
167
+ }
168
+ contentWidth *= this._scale;
169
+
170
+ // we still must go through minimumScrollOffset even if we can't scroll
171
+ // because we need to adjust for alignment. So, just make sure it won't allow scrolling.
172
+ if (!this.get('canScrollHorizontal')) contentWidth = Math.min(contentWidth, containerWidth);
173
+ return this.maximumScrollOffset(contentWidth, containerWidth, this.get("horizontalAlign"));
174
+ }.property(),
175
+
176
+ /**
177
+ The maximum vertical scroll offset allowed given the current contentView
178
+ size and the size of the scroll view. If vertical scrolling is disabled,
179
+ this will always return 0 (or whatever alignment dictates).
180
+
181
+ @field
182
+ @type Number
183
+ @default 0
184
+ */
185
+ maximumVerticalScrollOffset: function () {
186
+ var view = this.get('contentView'),
187
+ contentHeight = (view && view.get('frame')) ? view.get('frame').height : 0,
188
+ calculatedHeight = view ? view.get('calculatedHeight') : 0,
189
+ containerHeight = this.get('containerView').get('frame').height;
190
+
191
+ // The following code checks if there is a calculatedWidth (collections)
192
+ // to avoid looking at the incorrect value calculated by frame.
193
+ if (calculatedHeight) {
194
+ contentHeight = calculatedHeight;
195
+ }
196
+ contentHeight *= this._scale;
197
+
198
+ // we still must go through minimumScrollOffset even if we can't scroll
199
+ // because we need to adjust for alignment. So, just make sure it won't allow scrolling.
200
+ if (!this.get('canScrollVertical')) contentHeight = Math.min(contentHeight, containerHeight);
201
+ return this.maximumScrollOffset(contentHeight, containerHeight, this.get("verticalAlign"));
202
+ }.property(),
203
+
204
+
205
+ /**
206
+ The minimum horizontal scroll offset allowed given the current contentView
207
+ size and the size of the scroll view. If horizontal scrolling is
208
+ disabled, this will always return 0 (or whatever alignment dictates).
209
+
210
+ @field
211
+ @type Number
212
+ @default 0
213
+ */
214
+ minimumHorizontalScrollOffset: function () {
215
+ var view = this.get('contentView'),
216
+ contentWidth = view ? view.get('frame').width : 0,
217
+ calculatedWidth = view ? view.get('calculatedWidth') : 0,
218
+ containerWidth = this.get('containerView').get('frame').width;
219
+
220
+ // The following code checks if there is a calculatedWidth (collections)
221
+ // to avoid looking at the incorrect value calculated by frame.
222
+ if (calculatedWidth) {
223
+ contentWidth = calculatedWidth;
224
+ }
225
+ contentWidth *= this._scale;
226
+
227
+ // we still must go through minimumScrollOffset even if we can't scroll
228
+ // because we need to adjust for alignment. So, just make sure it won't allow scrolling.
229
+ if (!this.get('canScrollHorizontal')) contentWidth = Math.min(contentWidth, containerWidth);
230
+ return this.minimumScrollOffset(contentWidth, containerWidth, this.get("horizontalAlign"));
231
+ }.property(),
232
+
233
+ /**
234
+ The minimum vertical scroll offset allowed given the current contentView
235
+ size and the size of the scroll view. If vertical scrolling is disabled,
236
+ this will always return 0 (or whatever alignment dictates).
237
+
238
+ @field
239
+ @type Number
240
+ @default 0
241
+ */
242
+ minimumVerticalScrollOffset: function () {
243
+ var view = this.get('contentView'),
244
+ contentHeight = (view && view.get('frame')) ? view.get('frame').height : 0,
245
+ calculatedHeight = view ? view.get('calculatedHeight') : 0,
246
+ containerHeight = this.get('containerView').get('frame').height;
247
+
248
+ // The following code checks if there is a calculatedWidth (collections)
249
+ // to avoid looking at the incorrect value calculated by frame.
250
+ if (calculatedHeight) {
251
+ contentHeight = view.calculatedHeight;
252
+ }
253
+ contentHeight *= this._scale;
254
+
255
+ // we still must go through minimumScrollOffset even if we can't scroll
256
+ // because we need to adjust for alignment. So, just make sure it won't allow scrolling.
257
+ if (!this.get('canScrollVertical')) contentHeight = Math.min(contentHeight, containerHeight);
258
+ return this.minimumScrollOffset(contentHeight, containerHeight, this.get("verticalAlign"));
259
+ }.property(),
260
+
261
+
262
+ /**
263
+ Amount to scroll one vertical line.
264
+
265
+ Used by the default implementation of scrollDownLine() and scrollUpLine().
266
+
267
+ @type Number
268
+ @default 20
269
+ */
270
+ verticalLineScroll: 20,
271
+
272
+ /**
273
+ Amount to scroll one horizontal line.
274
+
275
+ Used by the default implementation of scrollLeftLine() and
276
+ scrollRightLine().
277
+
278
+ @type Number
279
+ @default 20
280
+ */
281
+ horizontalLineScroll: 20,
282
+
283
+ /**
284
+ Amount to scroll one vertical page.
285
+
286
+ Used by the default implementation of scrollUpPage() and scrollDownPage().
287
+
288
+ @field
289
+ @type Number
290
+ @default value of frame.height
291
+ @observes frame
292
+ */
293
+ verticalPageScroll: function () {
294
+ return this.get('frame').height;
295
+ }.property('frame'),
296
+
297
+ /**
298
+ Amount to scroll one horizontal page.
299
+
300
+ Used by the default implementation of scrollLeftPage() and
301
+ scrollRightPage().
302
+
303
+ @field
304
+ @type Number
305
+ @default value of frame.width
306
+ @observes frame
307
+ */
308
+ horizontalPageScroll: function () {
309
+ return this.get('frame').width;
310
+ }.property('frame'),
311
+
312
+ /**
313
+ Whether or not native scrollbars are wanted.
314
+
315
+ @type Boolean
316
+ @default NO
317
+ */
318
+ wantsNativeScrollbars: NO,
319
+
320
+ // ..........................................................
321
+ // SCROLLERS
322
+ //
323
+
324
+ /**
325
+ YES if the view should maintain a horizontal scroller.
326
+ This property must be set when the view is created.
327
+
328
+ @type Boolean
329
+ @default YES
330
+ */
331
+ hasHorizontalScroller: YES,
332
+
333
+ /**
334
+ The horizontal scroller view class.
335
+
336
+ This will be replaced with a view instance when the
337
+ ScrollView is created unless hasHorizontalScroller is NO.
338
+
339
+ @type SC.View
340
+ @default null
341
+ */
342
+ horizontalScrollerView: null,
343
+
344
+ /**
345
+ YES if the horizontal scroller should be visible. You can change this
346
+ property value anytime to show or hide the horizontal scroller. If you
347
+ do not want to use a horizontal scroller at all, you should instead set
348
+ hasHorizontalScroller to NO to avoid creating a scroller view in the
349
+ first place.
350
+
351
+ @type Boolean
352
+ @default YES
353
+ */
354
+ isHorizontalScrollerVisible: YES,
355
+
356
+ /**
357
+ Returns YES if the view both has a horizontal scroller, the scroller is
358
+ visible.
359
+
360
+ @field
361
+ @type Boolean
362
+ @default YES
363
+ */
364
+ canScrollHorizontal: function () {
365
+ return !!(this.get('hasHorizontalScroller') &&
366
+ this.get('horizontalScrollerView') &&
367
+ this.get('isHorizontalScrollerVisible'));
368
+ }.property('isHorizontalScrollerVisible', 'horizontalScrollerView').cacheable(),
369
+
370
+ /**
371
+ If YES, the horizontal scroller will autohide if the contentView is
372
+ smaller than the visible area. You must set hasHorizontalScroller to YES
373
+ for this property to have any effect.
374
+
375
+ @type Boolean
376
+ @default YES
377
+ */
378
+ autohidesHorizontalScroller: YES,
379
+
380
+ /**
381
+ YES if the view shuld maintain a vertical scroller. This property must
382
+ be set when the view is created.
383
+
384
+ @type Boolean
385
+ @default YES
386
+ */
387
+ hasVerticalScroller: YES,
388
+
389
+ /**
390
+ The vertical scroller view class. This will be replaced with a view
391
+ instance when the ScrollView is created unless hasVerticalScroller is NO.
392
+
393
+ @type SC.View
394
+ @default null
395
+ */
396
+ verticalScrollerView: null,
397
+
398
+ /**
399
+ YES if the vertical scroller should be visible. You can change this
400
+ property value anytime to show or hide the vertical scroller. If you do
401
+ not want to use a vertical scroller at all, you should instead set
402
+ hasVerticalScroller to NO to avoid creating a scroller view in the first
403
+ place.
404
+
405
+ @type Boolean
406
+ @default YES
407
+ */
408
+ isVerticalScrollerVisible: YES,
409
+
410
+ /**
411
+ Returns YES if the view both has a horizontal scroller, the scroller is
412
+ visible.
413
+
414
+ @field
415
+ @type Boolean
416
+ @default YES
417
+ */
418
+ canScrollVertical: function () {
419
+ return !!(this.get('hasVerticalScroller') &&
420
+ this.get('verticalScrollerView') &&
421
+ this.get('isVerticalScrollerVisible'));
422
+ }.property('isVerticalScrollerVisible', 'verticalScrollerView').cacheable(),
423
+
424
+ /**
425
+ If YES, the vertical scroller will autohide if the contentView is
426
+ smaller than the visible area. You must set hasVerticalScroller to YES
427
+ for this property to have any effect.
428
+
429
+ @type Boolean
430
+ @default YES
431
+ */
432
+ autohidesVerticalScroller: YES,
433
+
434
+ /**
435
+ Use this property to set the 'bottom' offset of your vertical scroller,
436
+ to make room for a thumb view or other accessory view. Default is 0.
437
+
438
+ @type Number
439
+ @default 0
440
+ */
441
+ verticalScrollerBottom: 0,
442
+
443
+ /**
444
+ Use this to overlay the vertical scroller.
445
+
446
+ This ensures that the container frame will not resize to accomodate the
447
+ vertical scroller, hence overlaying the scroller on top of
448
+ the container.
449
+
450
+ @type Boolean
451
+ @default NO
452
+ */
453
+ verticalOverlay: NO,
454
+
455
+ /**
456
+ Use this to overlay the horizontal scroller.
457
+
458
+ This ensures that the container frame will not resize to accomodate the
459
+ horizontal scroller, hence overlaying the scroller on top of
460
+ the container
461
+
462
+ @type Boolean
463
+ @default NO
464
+ */
465
+ horizontalOverlay: NO,
466
+
467
+ /**
468
+ Use to control the positioning of the vertical scroller. If you do not
469
+ set 'verticalOverlay' to YES, then the content view will be automatically
470
+ sized to meet the left edge of the vertical scroller, wherever it may be.
471
+ This allows you to easily, for example, have “one pixel higher and one
472
+ pixel lower” scroll bars that blend into their parent views.
473
+
474
+ If you do set 'verticalOverlay' to YES, then the scroller view will
475
+ “float on top” of the content view.
476
+
477
+ Example: { top: -1, bottom: -1, right: 0 }
478
+
479
+ @type Hash
480
+ @default null
481
+ */
482
+ verticalScrollerLayout: null,
483
+
484
+ /**
485
+ Use to control the positioning of the horizontal scroller. If you do not
486
+ set 'horizontalOverlay' to YES, then the content view will be
487
+ automatically sized to meet the top edge of the horizontal scroller,
488
+ wherever it may be.
489
+
490
+ If you do set 'horizontalOverlay' to YES, then the scroller view will
491
+ “float on top” of the content view.
492
+
493
+ Example: { left: 0, bottom: 0, right: 0 }
494
+
495
+ @type Hash
496
+ @default null
497
+ */
498
+ horizontalScrollerLayout: null,
499
+
500
+ // ..........................................................
501
+ // CUSTOM VIEWS
502
+ //
503
+
504
+ /**
505
+ The container view that will contain your main content view. You can
506
+ replace this property with your own custom subclass if you prefer.
507
+
508
+ @type SC.ContainerView
509
+ @default SC.ConainerView
510
+ */
511
+ containerView: SC.ContainerView.extend({}),
512
+
513
+
514
+ // ..........................................................
515
+ // METHODS
516
+ //
517
+
518
+ /**
519
+ Scrolls the receiver to the specified x,y coordinate. This should be the
520
+ offset into the contentView you want to appear at the top-left corner of
521
+ the scroll view.
522
+
523
+ This method will contrain the actual scroll based on whether the view
524
+ can scroll in the named direction and the maximum distance it can
525
+ scroll.
526
+
527
+ If you only want to scroll in one direction, pass null for the other
528
+ direction. You can also optionally pass a Hash for the first parameter
529
+ with x and y coordinates.
530
+
531
+ @param {Number} x the x scroll location
532
+ @param {Number} y the y scroll location
533
+ @returns {SC.ScrollView} receiver
534
+ */
535
+ scrollTo: function (x, y) {
536
+ // normalize params
537
+ if (typeof y === "undefined" && SC.typeOf(x) === SC.T_HASH) {
538
+ y = x.y; x = x.x;
539
+ }
540
+
541
+ if (!SC.none(x)) {
542
+ this.set('horizontalScrollOffset', x);
543
+ }
544
+
545
+ if (!SC.none(y)) {
546
+ this.set('verticalScrollOffset', y);
547
+ }
548
+
549
+ return this;
550
+ },
551
+
552
+ /**
553
+ Scrolls the receiver in the horizontal and vertical directions by the
554
+ amount specified, if allowed. The actual scroll amount will be
555
+ constrained by the current scroll view settings.
556
+
557
+ If you only want to scroll in one direction, pass null or 0 for the other
558
+ direction. You can also optionally pass a Hash for the first parameter
559
+ with x and y coordinates.
560
+
561
+ @param {Number} x change in the x direction (or hash)
562
+ @param {Number} y change in the y direction
563
+ @returns {SC.ScrollView} receiver
564
+ */
565
+ scrollBy: function (x, y) {
566
+ // normalize params
567
+ if (typeof y === "undefined" && SC.typeOf(x) === SC.T_HASH) {
568
+ y = x.y;
569
+ x = x.x;
570
+ }
571
+
572
+ // if null, undefined, or 0, pass null; otherwise just add current offset
573
+ x = (x) ? this.get('horizontalScrollOffset') + x : null;
574
+ y = (y) ? this.get('verticalScrollOffset') + y : null;
575
+ return this.scrollTo(x, y);
576
+ },
577
+
578
+ /**
579
+ Scroll the view to make the view's frame visible. For this to make sense,
580
+ the view should be a subview of the contentView. Otherwise the results
581
+ will be undefined.
582
+
583
+ @param {SC.View} view view to scroll or null to scroll receiver visible
584
+ @returns {Boolean} YES if scroll position was changed
585
+ */
586
+ scrollToVisible: function (view) {
587
+ // if no view is passed, do default
588
+ if (typeof view === "undefined") return sc_super();
589
+
590
+ var contentView = this.get('contentView');
591
+ if (!contentView) return NO; // nothing to do if no contentView.
592
+
593
+ // get the frame for the view - should work even for views with static
594
+ // layout, assuming it has been added to the screen.
595
+ var vf = view.get('frame');
596
+ if (!vf) return NO; // nothing to do
597
+
598
+ // convert view's frame to an offset from the contentView origin. This
599
+ // will become the new scroll offset after some adjustment.
600
+ vf = contentView.convertFrameFromView(vf, view.get('parentView'));
601
+
602
+ return this.scrollToRect(vf);
603
+ },
604
+
605
+ /**
606
+ Scroll to the supplied rectangle.
607
+ @param {Rect} rect Rectangle to scroll to.
608
+ @returns {Boolean} YES if scroll position was changed.
609
+ */
610
+ scrollToRect: function (rect) {
611
+ // find current visible frame.
612
+ var vo = SC.cloneRect(this.get('containerView').get('frame')),
613
+ origX = this.get('horizontalScrollOffset'),
614
+ origY = this.get('verticalScrollOffset');
615
+
616
+ vo.x = origX;
617
+ vo.y = origY;
618
+
619
+ // if top edge is not visible, shift origin
620
+ vo.y -= Math.max(0, SC.minY(vo) - SC.minY(rect));
621
+ vo.x -= Math.max(0, SC.minX(vo) - SC.minX(rect));
622
+
623
+ // if bottom edge is not visible, shift origin
624
+ vo.y += Math.max(0, SC.maxY(rect) - SC.maxY(vo));
625
+ vo.x += Math.max(0, SC.maxX(rect) - SC.maxX(vo));
626
+
627
+ // scroll to that origin.
628
+ if ((origX !== vo.x) || (origY !== vo.y)) {
629
+ this.scrollTo(vo.x, vo.y);
630
+ return YES;
631
+ } else return NO;
632
+ },
633
+
634
+
635
+ /**
636
+ Scrolls the receiver down one or more lines if allowed. If number of
637
+ lines is not specified, scrolls one line.
638
+
639
+ @param {Number} lines number of lines
640
+ @returns {SC.ScrollView} receiver
641
+ */
642
+ scrollDownLine: function (lines) {
643
+ if (typeof lines === "undefined") lines = 1;
644
+ return this.scrollBy(null, this.get('verticalLineScroll') * lines);
645
+ },
646
+
647
+ /**
648
+ Scrolls the receiver up one or more lines if allowed. If number of
649
+ lines is not specified, scrolls one line.
650
+
651
+ @param {Number} lines number of lines
652
+ @returns {SC.ScrollView} receiver
653
+ */
654
+ scrollUpLine: function (lines) {
655
+ if (typeof lines === "undefined") lines = 1;
656
+ return this.scrollBy(null, -1 * this.get('verticalLineScroll') * lines);
657
+ },
658
+
659
+ /**
660
+ Scrolls the receiver right one or more lines if allowed. If number of
661
+ lines is not specified, scrolls one line.
662
+
663
+ @param {Number} lines number of lines
664
+ @returns {SC.ScrollView} receiver
665
+ */
666
+ scrollRightLine: function (lines) {
667
+ if (typeof lines === "undefined") lines = 1;
668
+ return this.scrollTo(this.get('horizontalLineScroll') * lines, null);
669
+ },
670
+
671
+ /**
672
+ Scrolls the receiver left one or more lines if allowed. If number of
673
+ lines is not specified, scrolls one line.
674
+
675
+ @param {Number} lines number of lines
676
+ @returns {SC.ScrollView} receiver
677
+ */
678
+ scrollLeftLine: function (lines) {
679
+ if (typeof lines === "undefined") lines = 1;
680
+ return this.scrollTo(-1 * this.get('horizontalLineScroll') * lines, null);
681
+ },
682
+
683
+ /**
684
+ Scrolls the receiver down one or more page if allowed. If number of
685
+ pages is not specified, scrolls one page. The page size is determined by
686
+ the verticalPageScroll value. By default this is the size of the current
687
+ scrollable area.
688
+
689
+ @param {Number} pages number of lines
690
+ @returns {SC.ScrollView} receiver
691
+ */
692
+ scrollDownPage: function (pages) {
693
+ if (typeof pages === "undefined") pages = 1;
694
+ return this.scrollBy(null, this.get('verticalPageScroll') * pages);
695
+ },
696
+
697
+ /**
698
+ Scrolls the receiver up one or more page if allowed. If number of
699
+ pages is not specified, scrolls one page. The page size is determined by
700
+ the verticalPageScroll value. By default this is the size of the current
701
+ scrollable area.
702
+
703
+ @param {Number} pages number of lines
704
+ @returns {SC.ScrollView} receiver
705
+ */
706
+ scrollUpPage: function (pages) {
707
+ if (typeof pages === "undefined") pages = 1;
708
+ return this.scrollBy(null, -1 * this.get('verticalPageScroll') * pages);
709
+ },
710
+
711
+ /**
712
+ Scrolls the receiver right one or more page if allowed. If number of
713
+ pages is not specified, scrolls one page. The page size is determined by
714
+ the verticalPageScroll value. By default this is the size of the current
715
+ scrollable area.
716
+
717
+ @param {Number} pages number of lines
718
+ @returns {SC.ScrollView} receiver
719
+ */
720
+ scrollRightPage: function (pages) {
721
+ if (typeof pages === "undefined") pages = 1;
722
+ return this.scrollBy(this.get('horizontalPageScroll') * pages, null);
723
+ },
724
+
725
+ /**
726
+ Scrolls the receiver left one or more page if allowed. If number of
727
+ pages is not specified, scrolls one page. The page size is determined by
728
+ the verticalPageScroll value. By default this is the size of the current
729
+ scrollable area.
730
+
731
+ @param {Number} pages number of lines
732
+ @returns {SC.ScrollView} receiver
733
+ */
734
+ scrollLeftPage: function (pages) {
735
+ if (typeof pages === "undefined") pages = 1;
736
+ return this.scrollBy(-1 * this.get('horizontalPageScroll') * pages, null);
737
+ },
738
+
739
+ /** @private
740
+ Adjusts the layout for the various internal views. This method is called
741
+ once when the scroll view is first configured and then anytime a scroller
742
+ is shown or hidden. You can call this method yourself as well to retile.
743
+
744
+ You may also want to override this method to handle layout for any
745
+ additional controls you have added to the view.
746
+ */
747
+ tile: function () {
748
+ if (this.get('wantsNativeScrollbars')) return; // Let the browser retile
749
+
750
+ var hasHorizontal = this.get('canScrollHorizontal'),
751
+ hasVertical = this.get('canScrollVertical'),
752
+ hScroll = this.get('hasHorizontalScroller') ? this.get('horizontalScrollerView') : null,
753
+ vScroll = this.get('hasVerticalScroller') ? this.get('verticalScrollerView') : null,
754
+ clipView = this.get('containerView'),
755
+ clipLayout = { left: 0, top: 0 }, layout;
756
+
757
+ var ht = hasHorizontal ? hScroll.get('scrollbarThickness') : 0;
758
+ var vt = hasVertical ? vScroll.get('scrollbarThickness') : 0;
759
+
760
+ if (hasHorizontal) {
761
+ layout = this.get('horizontalScrollerLayout');
762
+ layout = {
763
+ left: layout ? layout.left : 0,
764
+ bottom: layout ? layout.bottom : 0,
765
+ right: layout ? layout.right + vt-1 : vt-1,
766
+ height: ht
767
+ };
768
+ hScroll.set('layout', layout);
769
+ clipLayout.bottom = layout.bottom + (hScroll.get('isTranslucent') ? 0 : layout.height);
770
+ }
771
+
772
+ if ((hasHorizontal && this.get('horizontalOverlay')) || !hasHorizontal) {
773
+ clipLayout.bottom = 0;
774
+ }
775
+
776
+ if (hScroll) hScroll.set('isVisible', hasHorizontal);
777
+
778
+ if (hasVertical) {
779
+ ht = ht + this.get('verticalScrollerBottom');
780
+ layout = this.get('verticalScrollerLayout');
781
+ layout = {
782
+ top: layout ? layout.top : 0,
783
+ bottom: layout ? layout.bottom + ht : ht,
784
+ right: layout ? layout.right : 0,
785
+ width: vt
786
+ };
787
+ vScroll.set('layout', layout);
788
+ clipLayout.right = layout.right + (vScroll.get('isTranslucent') ? 0 : layout.width);
789
+ }
790
+
791
+ if ((hasVertical && this.get('verticalOverlay')) || !hasVertical) {
792
+ clipLayout.right = 0;
793
+ }
794
+
795
+ if (vScroll) vScroll.set('isVisible', hasVertical);
796
+
797
+ clipView.adjust(clipLayout);
798
+ },
799
+
800
+ /** @private
801
+ Called whenever a scroller visibility changes.
802
+ Calls the tile() method.
803
+ */
804
+ scrollerVisibilityDidChange: function () {
805
+ this.tile();
806
+ }.observes('isVerticalScrollerVisible', 'isHorizontalScrollerVisible'),
807
+
808
+ // ..............................................
809
+ // SCALING SUPPORT
810
+ //
811
+
812
+ /**
813
+ Determines whether scaling is allowed.
814
+
815
+ @type Boolean
816
+ @default NO
817
+ */
818
+ canScale: NO,
819
+
820
+ /** @private
821
+ The current scale.
822
+ */
823
+ _scale: 1.0,
824
+
825
+ /**
826
+ @field
827
+ @type Number
828
+ @default 1.0
829
+ */
830
+ scale: function (key, value) {
831
+ if (typeof value !== "undefined") {
832
+ this._scale = Math.min(Math.max(this.get("minimumScale"), value), this.get("maximumScale"));
833
+ }
834
+ return this._scale;
835
+ }.property().cacheable(),
836
+
837
+ /**
838
+ The minimum scale.
839
+
840
+ @type Number
841
+ @default 0.25
842
+ */
843
+ minimumScale: 0.25,
844
+
845
+ /**
846
+ The maximum scale.
847
+
848
+ @type Number
849
+ @default 2.0
850
+ */
851
+ maximumScale: 2.0,
852
+
853
+ /**
854
+ Whether to automatically determine the scale range based on the size of the content.
855
+
856
+ @type Boolean
857
+ @default NO
858
+ */
859
+ autoScaleRange: NO,
860
+
861
+ /** @private */
862
+ _scale_css: "",
863
+
864
+ /** @private */
865
+ updateScale: function (scale) {
866
+ var contentView = this.get("contentView");
867
+ if (!contentView) return;
868
+
869
+ if (contentView.isScalable) {
870
+ this.get("contentView").applyScale(scale);
871
+ this._scale_css = "";
872
+ } else {
873
+ this._scale_css = "scale3d(" + scale + ", " + scale + ", 1)";
874
+ }
875
+ },
876
+
877
+ // ..........................................................
878
+ // INTERNAL SUPPORT
879
+ //
880
+
881
+ /** @private
882
+ Instantiate scrollers & container views as needed. Replace their classes
883
+ in the regular properties.
884
+ */
885
+ createChildViews: function () {
886
+ var childViews = [], view;
887
+
888
+ // create the containerView. We must always have a container view.
889
+ // also, setup the contentView as the child of the containerView...
890
+ if (SC.none(view = this.containerView)) view = SC.ContainerView;
891
+
892
+ childViews.push(this.containerView = this.createChildView(view, {
893
+ contentView: this.contentView,
894
+ isScrollContainer: YES
895
+ }));
896
+
897
+ // and replace our own contentView...
898
+ this.contentView = this.containerView.get('contentView');
899
+
900
+ if (!this.get('wantsNativeScrollbars')) {
901
+ // create a horizontal scroller view if needed...
902
+ view = this.get("horizontalScrollerView");
903
+ if (view) {
904
+ if (this.get('hasHorizontalScroller')) {
905
+ view = this.createChildView(view, {
906
+ layoutDirection: SC.LAYOUT_HORIZONTAL,
907
+ valueBinding: '*owner.horizontalScrollOffset'
908
+ });
909
+ this.set('horizontalScrollerView', view);
910
+ childViews.push(view);
911
+ } else this.horizontalScrollerView = null;
912
+ }
913
+
914
+ // create a vertical scroller view if needed...
915
+ view = this.get("verticalScrollerView");
916
+ if (view) {
917
+ if (this.get('hasVerticalScroller')) {
918
+ view = this.createChildView(view, {
919
+ layoutDirection: SC.LAYOUT_VERTICAL,
920
+ valueBinding: '*owner.verticalScrollOffset'
921
+ });
922
+ this.set('verticalScrollerView', view);
923
+ childViews.push(view);
924
+ } else this.verticalScrollerView = null;
925
+ }
926
+ }
927
+
928
+ // set childViews array.
929
+ this.childViews = childViews;
930
+
931
+ this.contentViewDidChange(); // setup initial display...
932
+ this.tile(); // set up initial tiling
933
+ },
934
+
935
+ /** @private */
936
+ init: function () {
937
+ sc_super();
938
+
939
+ // start observing initial content view. The content view's frame has
940
+ // already been setup in prepareDisplay so we don't need to call
941
+ // viewFrameDidChange...
942
+ this._scroll_contentView = this.get('contentView');
943
+ var contentView = this._scroll_contentView;
944
+
945
+ if (contentView) {
946
+ contentView.addObserver('frame', this, this.contentViewFrameDidChange);
947
+ contentView.addObserver('calculatedWidth', this, this.contentViewFrameDidChange);
948
+ contentView.addObserver('calculatedHeight', this, this.contentViewFrameDidChange);
949
+ }
950
+
951
+ if (this.get('isVisibleInWindow')) this._scsv_registerAutoscroll();
952
+ },
953
+
954
+ /** @private
955
+ Registers/deregisters view with SC.Drag for autoscrolling
956
+ */
957
+ _scsv_registerAutoscroll: function () {
958
+ this.get('isVisibleInWindow') ? SC.Drag.addScrollableView(this) :
959
+ SC.Drag.removeScrollableView(this);
960
+ }.observes('isVisibleInWindow'),
961
+
962
+ /** @private
963
+ Whenever the contentView is changed, we need to observe the content view's
964
+ frame to be notified whenever it's size changes.
965
+ */
966
+ contentViewDidChange: function () {
967
+ var newView = this.get('contentView'),
968
+ oldView = this._scroll_contentView,
969
+ frameObserver = this.contentViewFrameDidChange,
970
+ layerObserver = this.contentViewLayerDidChange;
971
+
972
+ if (newView !== oldView) {
973
+
974
+ // stop observing old content view
975
+ if (oldView) {
976
+ oldView.removeObserver('calculatedWidth', this, this.contentViewFrameDidChange);
977
+ oldView.removeObserver('calculatedHeight', this, this.contentViewFrameDidChange);
978
+ oldView.removeObserver('frame', this, frameObserver);
979
+ oldView.removeObserver('layer', this, layerObserver);
980
+ }
981
+
982
+ // update cache
983
+ this._scroll_contentView = newView;
984
+ if (newView) {
985
+ newView.addObserver('frame', this, frameObserver);
986
+ newView.addObserver('calculatedWidth', this, this.contentViewFrameDidChange);
987
+ newView.addObserver('calculatedHeight', this, this.contentViewFrameDidChange);
988
+ newView.addObserver('layer', this, layerObserver);
989
+ }
990
+
991
+ // replace container
992
+ this.containerView.set('contentView', newView);
993
+
994
+ this.contentViewFrameDidChange();
995
+ }
996
+ }.observes('contentView'),
997
+
998
+ /** @private */
999
+ oldMaxHOffset: 0,
1000
+
1001
+ /** @private */
1002
+ oldMaxVOffset: 0,
1003
+
1004
+ /** @private
1005
+ Invoked whenever the contentView's frame changes. This will update the
1006
+ scroller maxmimum and optionally update the scroller visibility if the
1007
+ size of the contentView changes. We don't care about the origin since
1008
+ that is tracked separately from the offset values.
1009
+
1010
+ @param {Boolean} [force] Re-calculate everything even if the contentView’s frame didn’t change size
1011
+ */
1012
+ contentViewFrameDidChange: function (force) {
1013
+ var view = this.get('contentView'),
1014
+ f = (view) ? view.get('frame') : null,
1015
+ scale = this._scale,
1016
+ width = 0,
1017
+ height = 0,
1018
+ dim, dimWidth, dimHeight, calculatedWidth, calculatedHeight;
1019
+
1020
+ // If no view has been set yet, or it doesn't have a frame,
1021
+ // we can avoid doing any work.
1022
+ if (!view || !f) { return; }
1023
+
1024
+ width = view.get('calculatedWidth') || f.width || 0;
1025
+ height = view.get('calculatedHeight') || f.height || 0;
1026
+
1027
+ width *= scale;
1028
+ height *= scale;
1029
+
1030
+ // cache out scroll settings...
1031
+ if (!force && (width === this._scroll_contentWidth) && (height === this._scroll_contentHeight)) return;
1032
+ this._scroll_contentWidth = width;
1033
+ this._scroll_contentHeight = height;
1034
+
1035
+ dim = this.getPath('containerView.frame');
1036
+ dimWidth = dim.width;
1037
+ dimHeight = dim.height;
1038
+
1039
+ if (this.get('hasHorizontalScroller') && (view = this.get('horizontalScrollerView'))) {
1040
+ // decide if it should be visible or not
1041
+ if (this.get('autohidesHorizontalScroller')) {
1042
+ this.set('isHorizontalScrollerVisible', width > dimWidth);
1043
+ }
1044
+ if (!this.get('wantsNativeScrollbars')) {
1045
+ view.setIfChanged('maximum', width-dimWidth);
1046
+ view.setIfChanged('proportion', dimWidth/width);
1047
+ }
1048
+ }
1049
+
1050
+ if (this.get('hasVerticalScroller') && (view = this.get('verticalScrollerView'))) {
1051
+ // decide if it should be visible or not
1052
+ if (this.get('autohidesVerticalScroller')) {
1053
+ this.set('isVerticalScrollerVisible', height > dimHeight);
1054
+ }
1055
+ if (!this.get('wantsNativeScrollbars')) {
1056
+ view.setIfChanged('maximum', height - dimHeight);
1057
+ view.setIfChanged('proportion', dimHeight / height);
1058
+ }
1059
+ }
1060
+
1061
+ // If there is no vertical scroller and auto hiding is on, make
1062
+ // sure we are at the top if not already there
1063
+ if (!this.get('isVerticalScrollerVisible') && (this.get('verticalScrollOffset') !== 0) &&
1064
+ this.get('autohidesVerticalScroller')) {
1065
+ this.set('verticalScrollOffset', 0);
1066
+ }
1067
+
1068
+ // Same thing for horizontal scrolling.
1069
+ if (!this.get('isHorizontalScrollerVisible') && (this.get('horizontalScrollOffset') !== 0) &&
1070
+ this.get('autohidesHorizontalScroller')) {
1071
+ this.set('horizontalScrollOffset', 0);
1072
+ }
1073
+
1074
+ // This forces to recalculate the height of the frame when is at the bottom
1075
+ // of the scroll and the content dimension are smaller that the previous one
1076
+ var mxVOffSet = this.get('maximumVerticalScrollOffset'),
1077
+ vOffSet = this.get('verticalScrollOffset'),
1078
+ mxHOffSet = this.get('maximumHorizontalScrollOffset'),
1079
+ hOffSet = this.get('horizontalScrollOffset'),
1080
+ forceHeight = mxVOffSet < vOffSet,
1081
+ forceWidth = mxHOffSet < hOffSet;
1082
+
1083
+ if (forceHeight || forceWidth) {
1084
+ this.forceDimensionsRecalculation(forceWidth, forceHeight, vOffSet, hOffSet);
1085
+ }
1086
+ },
1087
+
1088
+ /** @private
1089
+ If our frame changes, then we need to re-calculate the visiblility of our
1090
+ scrollers, etc.
1091
+ */
1092
+ frameDidChange: function () {
1093
+ this.contentViewFrameDidChange(YES);
1094
+ }.observes('frame'),
1095
+
1096
+ /** @private
1097
+ If the layer of the content view changes, we need to readjust the
1098
+ scrollTop and scrollLeft properties on the new DOM element.
1099
+ */
1100
+ contentViewLayerDidChange: function () {
1101
+ // Invalidate these cached values, as they're no longer valid
1102
+ if (this._verticalScrollOffset !== 0) this._verticalScrollOffset = -1;
1103
+ if (this._horizontalScrollOffset !== 0) this._horizontalScrollOffset = -1;
1104
+ this.invokeLast(this.adjustElementScroll);
1105
+ },
1106
+
1107
+ willScroll: function () {
1108
+ var content = this.get('contentView');
1109
+ content && content.willScroll && content.willScroll(this);
1110
+ },
1111
+
1112
+ didScroll: function () {
1113
+ var content = this.get('contentView');
1114
+ content && content.didScroll && content.didScroll(this);
1115
+ },
1116
+
1117
+ /** @private
1118
+ Whenever the horizontal scroll offset changes, update the scrollers and
1119
+ edit the location of the contentView.
1120
+ */
1121
+ _scsv_offsetDidChange: function () {
1122
+ this.invokeLast(this.adjustElementScroll);
1123
+ }.observes('horizontalScrollOffset', 'verticalScrollOffset'),
1124
+
1125
+ /** @private
1126
+ Called at the end of the run loop to actually adjust the scrollTop
1127
+ and scrollLeft properties of the container view.
1128
+ */
1129
+ adjustElementScroll: function () {
1130
+ var content = this.get('contentView');
1131
+
1132
+ // We notify the content view that its frame property has changed
1133
+ // before we actually update the scrollTop/scrollLeft properties.
1134
+ // This gives views that use incremental rendering a chance to render
1135
+ // newly-appearing elements before they come into view.
1136
+ if (content && content._viewFrameDidChange) {
1137
+ content._viewFrameDidChange();
1138
+ }
1139
+ },
1140
+
1141
+ /** @private */
1142
+ forceDimensionsRecalculation: function (forceWidth, forceHeight, vOffSet, hOffSet) {
1143
+ var oldScrollHOffset = hOffSet;
1144
+ var oldScrollVOffset = vOffSet;
1145
+ this.scrollTo(0, 0);
1146
+ if (forceWidth && forceHeight) {
1147
+ this.scrollTo(this.get('maximumHorizontalScrollOffset'),
1148
+ this.get('maximumVerticalScrollOffset'));
1149
+ }
1150
+ if (forceWidth && !forceHeight) {
1151
+ this.scrollTo(this.get('maximumHorizontalScrollOffset'), oldScrollVOffset);
1152
+ }
1153
+ if (!forceWidth && forceHeight) {
1154
+ this.scrollTo(oldScrollHOffset, this.get('maximumVerticalScrollOffset'));
1155
+ }
1156
+ },
1157
+
1158
+ /** @private */
1159
+ _scroll_verticalScrollOffset: 0,
1160
+
1161
+ /** @private */
1162
+ _scroll_horizontalScrollOffset: 0
1163
+
1164
+ });