sproutcore 1.6.0.1-java → 1.7.1.beta-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +21 -0
- data/Gemfile +5 -0
- data/Rakefile +26 -13
- data/VERSION.yml +2 -2
- data/lib/Buildfile +43 -4
- data/lib/buildtasks/build.rake +10 -0
- data/lib/buildtasks/helpers/file_rule.rb +22 -0
- data/lib/buildtasks/helpers/file_rule_list.rb +137 -0
- data/lib/buildtasks/manifest.rake +133 -122
- data/lib/frameworks/sproutcore/CHANGELOG.md +69 -2
- data/lib/frameworks/sproutcore/apps/tests/english.lproj/strings.js +1 -0
- data/lib/frameworks/sproutcore/frameworks/bootstrap/system/browser.js +28 -22
- data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/array.js +9 -5
- data/lib/frameworks/sproutcore/frameworks/core_foundation/controllers/controller.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/controls/button.js +18 -13
- data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/bind.js +5 -3
- data/lib/frameworks/sproutcore/frameworks/core_foundation/ext/handlebars/collection.js +2 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/action_support.js +80 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/mixins/template_helpers/text_field_support.js +84 -116
- data/lib/frameworks/sproutcore/frameworks/core_foundation/panes/pane.js +8 -5
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/event.js +157 -157
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/platform.js +5 -3
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/root_responder.js +6 -6
- data/lib/frameworks/sproutcore/frameworks/core_foundation/system/sparse_array.js +10 -7
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/mixins/action_support.js +106 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/collection.js +18 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/template/handlebars.js +71 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/attribute_bindings_test.js +38 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/class_name_bindings_test.js +47 -0
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutChildViews.js +18 -18
- data/lib/frameworks/sproutcore/frameworks/core_foundation/tests/views/view/layoutStyle.js +42 -10
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view.js +158 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/keyboard.js +26 -1
- data/lib/frameworks/sproutcore/frameworks/core_foundation/views/view/layout_style.js +14 -8
- data/lib/frameworks/sproutcore/frameworks/datastore/models/record.js +15 -2
- data/lib/frameworks/sproutcore/frameworks/datastore/models/record_attribute.js +108 -108
- data/lib/frameworks/sproutcore/frameworks/datastore/system/query.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/datastore/system/record_array.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/record/error_methods.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/models/single_attribute.js +26 -0
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/query/builders.js +7 -0
- data/lib/frameworks/sproutcore/frameworks/datastore/tests/system/record_array/error_methods.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/system/datetime.js +4 -1
- data/lib/frameworks/sproutcore/frameworks/datetime/frameworks/core/tests/system/datetime.js +6 -0
- data/lib/frameworks/sproutcore/frameworks/desktop/panes/menu.js +26 -5
- data/lib/frameworks/sproutcore/frameworks/desktop/panes/picker.js +97 -96
- data/lib/frameworks/sproutcore/frameworks/desktop/system/drag.js +4 -3
- data/lib/frameworks/sproutcore/frameworks/desktop/tests/panes/menu/ui.js +17 -4
- data/lib/frameworks/sproutcore/frameworks/desktop/views/collection.js +7 -7
- data/lib/frameworks/sproutcore/frameworks/desktop/views/menu_item.js +7 -5
- data/lib/frameworks/sproutcore/frameworks/desktop/views/scroll.js +12 -3
- data/lib/frameworks/sproutcore/frameworks/desktop/views/web.js +23 -14
- data/lib/frameworks/sproutcore/frameworks/experimental/Buildfile +5 -1
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/render_delegates/menu_scroller.js +28 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/tests/menu/scroll.js +235 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroll.js +363 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/menu/views/menu/scroller.js +250 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/desktop_scroller.js +92 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/native_scroll.js +25 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/scroll.js +33 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/render_delegates/touch_scroller.js +76 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/integration.js +50 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/methods.js +143 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/tests/scroll/ui.js +258 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroll.js +1164 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroller.js +332 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroll.js +236 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/desktop/scroller.js +347 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroll.js +15 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/scroller.js +10 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroll.js +804 -0
- data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/touch/scroller.js +133 -0
- data/lib/frameworks/sproutcore/frameworks/foundation/resources/text_field.css +3 -3
- data/lib/frameworks/sproutcore/frameworks/foundation/validators/number.js +3 -1
- data/lib/frameworks/sproutcore/frameworks/foundation/views/text_field.js +3 -3
- data/lib/frameworks/sproutcore/frameworks/media/views/audio.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/controls.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/media/views/media_slider.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/media/views/mini_controls.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/media/views/simple_controls.js +2 -4
- data/lib/frameworks/sproutcore/frameworks/media/views/video.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/routing/system/routes.js +29 -3
- data/lib/frameworks/sproutcore/frameworks/runtime/core.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/runtime/debug/test_suites/array/replace.js +1 -1
- data/lib/frameworks/sproutcore/frameworks/runtime/private/property_chain.js +2 -1
- data/lib/frameworks/sproutcore/frameworks/runtime/system/binding.js +3 -3
- data/lib/frameworks/sproutcore/frameworks/runtime/system/index_set.js +2 -2
- data/lib/frameworks/sproutcore/frameworks/runtime/system/object.js +1 -1
- data/lib/frameworks/sproutcore/themes/ace/resources/collection/normal/list_item.css +2 -2
- data/lib/frameworks/sproutcore/themes/legacy_theme/english.lproj/segmented.css +1 -1
- data/lib/gen/app/templates/apps/@target_name@/Buildfile +3 -5
- data/lib/gen/app/templates/apps/@target_name@/resources/_theme.css +18 -0
- data/lib/gen/project/templates/@filename@/Buildfile +2 -2
- data/lib/sproutcore.rb +30 -5
- data/lib/sproutcore/builders.rb +1 -0
- data/lib/sproutcore/builders/chance_file.rb +9 -16
- data/lib/sproutcore/builders/html.rb +2 -1
- data/lib/sproutcore/builders/minify.rb +4 -35
- data/lib/sproutcore/builders/module.rb +38 -1
- data/lib/sproutcore/builders/split.rb +63 -0
- data/lib/sproutcore/builders/strings.rb +7 -1
- data/lib/sproutcore/helpers.rb +1 -1
- data/lib/sproutcore/helpers/css_split.rb +190 -0
- data/lib/sproutcore/helpers/entry_sorter.rb +2 -0
- data/lib/sproutcore/helpers/minifier.rb +40 -16
- data/lib/sproutcore/helpers/static_helper.rb +35 -17
- data/lib/sproutcore/models/manifest.rb +26 -0
- data/lib/sproutcore/models/target.rb +12 -1
- data/lib/sproutcore/rack.rb +1 -0
- data/lib/sproutcore/rack/proxy.rb +244 -225
- data/lib/sproutcore/rack/restrict_ip.rb +67 -0
- data/lib/sproutcore/rack/service.rb +8 -2
- data/lib/sproutcore/tools.rb +102 -46
- data/lib/sproutcore/tools/build.rb +91 -43
- data/lib/sproutcore/tools/gen.rb +2 -3
- data/lib/sproutcore/tools/manifest.rb +22 -16
- data/lib/sproutcore/tools/server.rb +21 -0
- data/spec/buildtasks/helpers/accept_list +22 -0
- data/spec/buildtasks/helpers/accept_list.rb +128 -0
- data/spec/buildtasks/helpers/list.json +11 -0
- data/spec/buildtasks/manifest/prepare_build_tasks/chance_2x_spec.rb +1 -39
- data/spec/buildtasks/manifest/prepare_build_tasks/chance_spec.rb +0 -38
- data/spec/buildtasks/manifest/prepare_build_tasks/combine_spec.rb +4 -4
- data/spec/buildtasks/manifest/prepare_build_tasks/module_spec.rb +2 -2
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_indirect_spec.rb +7 -16
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_2x_spec.rb +7 -17
- data/spec/buildtasks/manifest/prepare_build_tasks/packed_spec.rb +11 -6
- data/spec/fixtures/builder_tests/Buildfile +2 -1
- data/spec/fixtures/builder_tests/apps/module_test/modules/required_module/core.js +0 -0
- data/spec/lib/builders/module_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/sproutcore.gemspec +4 -9
- data/vendor/chance/lib/chance.rb +25 -6
- data/vendor/chance/lib/chance/factory.rb +45 -0
- data/vendor/chance/lib/chance/instance.rb +173 -28
- data/vendor/chance/lib/chance/instance/data_url.rb +0 -29
- data/vendor/chance/lib/chance/instance/slicing.rb +57 -4
- data/vendor/chance/lib/chance/instance/spriting.rb +112 -21
- data/vendor/chance/lib/chance/parser.rb +80 -52
- data/vendor/sproutcore/SCCompiler.jar +0 -0
- data/vendor/sproutcore/lib/args4j-2.0.12.jar +0 -0
- data/vendor/sproutcore/lib/yuicompressor-2.4.2.jar +0 -0
- metadata +84 -25
data/lib/frameworks/sproutcore/frameworks/experimental/frameworks/scroll_view/views/core_scroll.js
ADDED
@@ -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
|
+
});
|