sproutcore 0.9.1 → 0.9.2
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/History.txt +233 -0
- data/Manifest.txt +67 -34
- data/bin/sc-build +12 -1
- data/bin/sc-gen +1 -1
- data/bin/sproutcore +14 -0
- data/clients/sc_docs/controllers/docs.js +38 -8
- data/clients/sc_docs/english.lproj/body.css +80 -127
- data/clients/sc_docs/english.lproj/body.rhtml +43 -23
- data/clients/sc_docs/english.lproj/no_docs.rhtml +2 -1
- data/clients/sc_docs/english.lproj/tabs.rhtml +16 -0
- data/clients/sc_docs/main.js +14 -9
- data/clients/sc_docs/models/doc.js +1 -1
- data/clients/sc_docs/tests/controllers/docs.rhtml +1 -2
- data/clients/sc_docs/tests/models/doc.rhtml +1 -2
- data/clients/sc_docs/tests/views/doc_frame.rhtml +1 -2
- data/clients/sc_docs/tests/views/doc_label_view.rhtml +1 -2
- data/clients/sc_docs/views/doc_frame.js +1 -1
- data/clients/sc_test_runner/controllers/runner.js +31 -8
- data/clients/sc_test_runner/english.lproj/body.css +62 -122
- data/clients/sc_test_runner/english.lproj/body.rhtml +62 -26
- data/clients/sc_test_runner/main.js +1 -6
- data/clients/sc_test_runner/models/test.js +14 -1
- data/clients/sc_test_runner/views/runner_frame.js +4 -2
- data/clients/view_builder/builders/builder.js +339 -0
- data/clients/view_builder/builders/button.js +81 -0
- data/clients/view_builder/controllers/document.js +21 -0
- data/clients/view_builder/core.js +19 -0
- data/clients/view_builder/english.lproj/body.css +77 -0
- data/clients/view_builder/english.lproj/body.rhtml +41 -0
- data/clients/{sc_docs → view_builder}/english.lproj/controls.css +0 -0
- data/clients/view_builder/english.lproj/strings.js +14 -0
- data/clients/view_builder/main.js +38 -0
- data/clients/view_builder/tests/controllers/document.rhtml +20 -0
- data/clients/view_builder/tests/views/builder.rhtml +20 -0
- data/clients/view_builder/views/builder.js +23 -0
- data/frameworks/prototype/prototype.js +1 -1
- data/frameworks/sproutcore/Core.js +32 -7
- data/frameworks/sproutcore/README +1 -1
- data/frameworks/sproutcore/animation/animation.js +411 -0
- data/frameworks/sproutcore/controllers/array.js +17 -9
- data/frameworks/sproutcore/controllers/collection.js +9 -110
- data/frameworks/sproutcore/controllers/controller.js +1 -1
- data/frameworks/sproutcore/controllers/object.js +2 -1
- data/frameworks/sproutcore/drag/drag.js +267 -56
- data/frameworks/sproutcore/drag/drag_data_source.js +24 -16
- data/frameworks/sproutcore/drag/drag_source.js +53 -42
- data/frameworks/sproutcore/drag/drop_target.js +2 -2
- data/frameworks/sproutcore/english.lproj/buttons.css +337 -236
- data/frameworks/sproutcore/english.lproj/core.css +115 -0
- data/frameworks/sproutcore/english.lproj/icons.css +227 -0
- data/{clients/sc_docs → frameworks/sproutcore}/english.lproj/images/indicator.gif +0 -0
- data/frameworks/sproutcore/english.lproj/images/sc-theme-sprite.png +0 -0
- data/frameworks/sproutcore/english.lproj/images/sc-theme-ysprite.png +0 -0
- data/frameworks/sproutcore/english.lproj/images/shared-icons.png +0 -0
- data/frameworks/sproutcore/english.lproj/menu.css +1 -1
- data/frameworks/sproutcore/english.lproj/strings.js +1 -1
- data/frameworks/sproutcore/english.lproj/theme.css +405 -31
- data/frameworks/sproutcore/foundation/application.js +15 -11
- data/frameworks/sproutcore/foundation/benchmark.js +1 -1
- data/frameworks/sproutcore/foundation/binding.js +2 -2
- data/frameworks/sproutcore/foundation/date.js +1 -1
- data/frameworks/sproutcore/foundation/error.js +1 -1
- data/frameworks/sproutcore/foundation/input_manager.js +32 -21
- data/frameworks/sproutcore/foundation/mock.js +1 -1
- data/frameworks/sproutcore/foundation/node_descriptor.js +9 -6
- data/frameworks/sproutcore/foundation/object.js +249 -177
- data/frameworks/sproutcore/foundation/page.js +5 -2
- data/frameworks/sproutcore/foundation/path_module.js +11 -10
- data/frameworks/sproutcore/foundation/responder.js +5 -2
- data/frameworks/sproutcore/foundation/routes.js +17 -13
- data/frameworks/sproutcore/foundation/run_loop.js +249 -11
- data/frameworks/sproutcore/foundation/server.js +1 -1
- data/frameworks/sproutcore/foundation/set.js +3 -3
- data/frameworks/sproutcore/foundation/string.js +5 -3
- data/frameworks/sproutcore/foundation/timer.js +371 -0
- data/frameworks/sproutcore/foundation/undo_manager.js +1 -1
- data/frameworks/sproutcore/foundation/unittest.js +3 -3
- data/frameworks/sproutcore/foundation/utils.js +161 -2
- data/frameworks/sproutcore/globals/panels.js +1 -1
- data/frameworks/sproutcore/globals/popups.js +4 -3
- data/frameworks/sproutcore/globals/window.js +44 -4
- data/frameworks/sproutcore/lib/button_views.rb +328 -0
- data/frameworks/sproutcore/lib/collection_view.rb +80 -0
- data/frameworks/sproutcore/lib/core_views.rb +281 -0
- data/frameworks/sproutcore/lib/form_views.rb +253 -0
- data/frameworks/sproutcore/lib/index.rhtml +2 -0
- data/frameworks/sproutcore/lib/menu_views.rb +88 -0
- data/frameworks/sproutcore/{foundation → mixins}/array.js +60 -29
- data/frameworks/sproutcore/mixins/control.js +265 -0
- data/frameworks/sproutcore/mixins/delegate_support.js +66 -0
- data/frameworks/sproutcore/{foundation → mixins}/observable.js +176 -6
- data/frameworks/sproutcore/mixins/scrollable.js +245 -0
- data/frameworks/sproutcore/mixins/selection_support.js +148 -0
- data/frameworks/sproutcore/mixins/validatable.js +152 -0
- data/frameworks/sproutcore/models/collection.js +5 -5
- data/frameworks/sproutcore/models/record.js +1 -1
- data/frameworks/sproutcore/models/store.js +1 -1
- data/frameworks/sproutcore/panes/dialog.js +1 -1
- data/frameworks/sproutcore/panes/manager.js +1 -1
- data/frameworks/sproutcore/panes/menu.js +1 -1
- data/frameworks/sproutcore/panes/overlay.js +2 -2
- data/frameworks/sproutcore/panes/panel.js +1 -1
- data/frameworks/sproutcore/panes/picker.js +1 -1
- data/frameworks/sproutcore/tests/controllers/array.rhtml +44 -4
- data/frameworks/sproutcore/tests/foundation/timer/invalidate.rhtml +33 -0
- data/frameworks/sproutcore/tests/foundation/timer/invokeLater.rhtml +145 -0
- data/frameworks/sproutcore/tests/foundation/timer/isPaused.rhtml +70 -0
- data/frameworks/sproutcore/tests/foundation/timer/schedule.rhtml +145 -0
- data/frameworks/sproutcore/tests/views/{scroll.rhtml → checkbox.rhtml} +3 -3
- data/frameworks/sproutcore/tests/views/{collection.rhtml → collection/base.rhtml} +33 -32
- data/frameworks/sproutcore/tests/views/collection/incremental_rendering.rhtml +260 -0
- data/frameworks/sproutcore/tests/views/image_cell.rhtml +19 -0
- data/frameworks/sproutcore/tests/views/label_item.rhtml +2 -4
- data/frameworks/sproutcore/tests/views/list.rhtml +2 -3
- data/frameworks/sproutcore/tests/views/list_item.rhtml +20 -0
- data/frameworks/sproutcore/tests/views/slider.rhtml +20 -0
- data/frameworks/sproutcore/tests/views/text_cell.rhtml +19 -0
- data/frameworks/sproutcore/tests/views/view/clippingFrame.rhtml +395 -0
- data/frameworks/sproutcore/tests/views/view/frame.rhtml +353 -0
- data/frameworks/sproutcore/tests/views/view/innerFrame.rhtml +347 -0
- data/frameworks/sproutcore/tests/views/view/isVisibleInWindow.rhtml +148 -0
- data/frameworks/sproutcore/tests/views/view/scrollFrame.rhtml +468 -0
- data/frameworks/sproutcore/validators/credit_card.js +33 -13
- data/frameworks/sproutcore/validators/date.js +26 -6
- data/frameworks/sproutcore/validators/email.js +21 -3
- data/frameworks/sproutcore/validators/not_empty.js +11 -1
- data/frameworks/sproutcore/validators/number.js +18 -4
- data/frameworks/sproutcore/validators/password.js +12 -1
- data/frameworks/sproutcore/validators/validator.js +204 -194
- data/frameworks/sproutcore/views/{button.js → button/button.js} +96 -94
- data/frameworks/sproutcore/views/button/checkbox.js +29 -0
- data/frameworks/sproutcore/views/button/disclosure.js +42 -0
- data/frameworks/sproutcore/views/button/radio.js +29 -0
- data/frameworks/sproutcore/views/{collection.js → collection/collection.js} +1373 -1024
- data/frameworks/sproutcore/views/collection/grid.js +124 -46
- data/frameworks/sproutcore/views/collection/image_cell.js +17 -46
- data/frameworks/sproutcore/views/collection/list.js +45 -35
- data/frameworks/sproutcore/views/collection/source_list.js +386 -0
- data/frameworks/sproutcore/views/collection/table.js +118 -0
- data/frameworks/sproutcore/views/container.js +7 -2
- data/frameworks/sproutcore/views/error_explanation.js +23 -10
- data/frameworks/sproutcore/views/{checkbox_field.js → field/checkbox_field.js} +16 -6
- data/frameworks/sproutcore/views/field/field.js +219 -0
- data/frameworks/sproutcore/views/{radio_field.js → field/radio_field.js} +27 -12
- data/frameworks/sproutcore/views/{select_field.js → field/select_field.js} +116 -90
- data/frameworks/sproutcore/views/{text_field.js → field/text_field.js} +57 -8
- data/frameworks/sproutcore/views/{textarea_field.js → field/textarea_field.js} +13 -3
- data/frameworks/sproutcore/views/filter_button.js +2 -2
- data/frameworks/sproutcore/views/form.js +3 -3
- data/frameworks/sproutcore/views/image.js +128 -21
- data/frameworks/sproutcore/views/inline_text_editor.js +1 -1
- data/frameworks/sproutcore/views/label.js +149 -92
- data/frameworks/sproutcore/views/list_item.js +225 -0
- data/frameworks/sproutcore/views/menu_item.js +10 -4
- data/frameworks/sproutcore/views/pagination.js +11 -4
- data/frameworks/sproutcore/views/popup_button.js +25 -21
- data/frameworks/sproutcore/views/popup_menu.js +10 -4
- data/frameworks/sproutcore/views/progress.js +29 -16
- data/frameworks/sproutcore/views/radio_group.js +1 -1
- data/frameworks/sproutcore/views/scroll.js +60 -20
- data/frameworks/sproutcore/views/segmented.js +1 -1
- data/frameworks/sproutcore/views/slider.js +132 -0
- data/frameworks/sproutcore/views/source_list_group.js +130 -0
- data/frameworks/sproutcore/views/spinner.js +1 -1
- data/frameworks/sproutcore/views/split.js +292 -0
- data/frameworks/sproutcore/views/split_divider.js +109 -0
- data/frameworks/sproutcore/views/tab.js +1 -1
- data/frameworks/sproutcore/views/toolbar.js +1 -1
- data/frameworks/sproutcore/views/view.js +1272 -591
- data/generators/client/templates/english.lproj/body.css +1 -1
- data/generators/controller/controller_generator.rb +1 -1
- data/generators/controller/templates/test.rhtml +2 -1
- data/generators/model/templates/test.rhtml +1 -1
- data/generators/test/templates/test.rhtml +1 -1
- data/generators/view/templates/test.rhtml +1 -1
- data/jsdoc/templates/sproutcore/class.tmpl +241 -338
- data/jsdoc/templates/sproutcore/default.css +105 -155
- data/jsdoc/templates/sproutcore/index.tmpl +43 -8
- data/jsdoc/templates/sproutcore/publish.js +9 -4
- data/lib/sproutcore/build_tools/html_builder.rb +29 -13
- data/lib/sproutcore/build_tools/resource_builder.rb +1 -1
- data/lib/sproutcore/bundle.rb +86 -25
- data/lib/sproutcore/jsdoc.rb +2 -0
- data/lib/sproutcore/version.rb +1 -1
- data/lib/sproutcore/view_helpers.rb +36 -3
- data/tasks/deployment.rake +1 -1
- metadata +69 -36
- data/clients/sc_docs/english.lproj/icons/small/next.png +0 -0
- data/clients/sc_docs/english.lproj/icons/small/reset.png +0 -0
- data/clients/sc_docs/english.lproj/images/gradients.png +0 -0
- data/clients/sc_docs/english.lproj/images/toolbar.png +0 -0
- data/clients/sc_docs/english.lproj/warning.rhtml +0 -6
- data/clients/sc_test_runner/english.lproj/warning.rhtml +0 -6
- data/frameworks/sproutcore/english.lproj/buttons.png +0 -0
- data/frameworks/sproutcore/english.lproj/collections.css +0 -82
- data/frameworks/sproutcore/english.lproj/images/buttons-sprite.png +0 -0
- data/frameworks/sproutcore/views/collection/collection_item.js +0 -36
- data/frameworks/sproutcore/views/collection/text_cell.js +0 -128
- data/frameworks/sproutcore/views/field.js +0 -214
- data/frameworks/sproutcore/views/workspace.js +0 -170
- data/generators/client/templates/english.lproj/controls.css +0 -0
- data/generators/framework/templates/english.lproj/body.css +0 -0
- data/generators/framework/templates/english.lproj/body.rhtml +0 -3
- data/generators/framework/templates/english.lproj/controls.css +0 -0
- data/lib/sproutcore/view_helpers/button_views.rb +0 -302
- data/lib/sproutcore/view_helpers/core_views.rb +0 -292
- data/lib/sproutcore/view_helpers/form_views.rb +0 -258
- data/lib/sproutcore/view_helpers/menu_views.rb +0 -94
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// ========================================================================
|
|
2
|
+
// SproutCore
|
|
3
|
+
// copyright 2006-2008 Sprout Systems, Inc.
|
|
4
|
+
// ========================================================================
|
|
5
|
+
|
|
6
|
+
require('views/view') ;
|
|
7
|
+
require('views/split');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
@class
|
|
11
|
+
|
|
12
|
+
A SplitDividerView displays a divider between two split views. Clicking
|
|
13
|
+
and dragging the divider will change the thickness of the view either to
|
|
14
|
+
the left or right of the divider, depending on which side of the flexible
|
|
15
|
+
view the divider is on.
|
|
16
|
+
|
|
17
|
+
Double-clicking will try to collapse the same view so it is not visible
|
|
18
|
+
unless you have canCollapse disabled on the SplitView.
|
|
19
|
+
|
|
20
|
+
This view must be a direct child of the split view it works with.
|
|
21
|
+
|
|
22
|
+
@extends SC.View
|
|
23
|
+
|
|
24
|
+
@author Charles Jolley
|
|
25
|
+
*/
|
|
26
|
+
SC.SplitDividerView = SC.View.extend(
|
|
27
|
+
/** @scope SC.SplitDividerView.prototype */ {
|
|
28
|
+
|
|
29
|
+
emptyElement: '<div class="sc-split-divider-view"></div>',
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
Returns the view to be managed by the divider view.
|
|
33
|
+
*/
|
|
34
|
+
targetView: function() {
|
|
35
|
+
var splitView = this.get('parentNode') ;
|
|
36
|
+
if (!splitView) return null ;
|
|
37
|
+
|
|
38
|
+
var flexibleView = splitView.computeFlexibleView() ;
|
|
39
|
+
var views = splitView.get('childNodes') ;
|
|
40
|
+
var myIndex = views.indexOf(this) ;
|
|
41
|
+
var flexibleIndex = views.indexOf(flexibleView) ;
|
|
42
|
+
|
|
43
|
+
if (myIndex < 0) throw "SplitDividerView must belong to the SplitView";
|
|
44
|
+
|
|
45
|
+
return (myIndex <= flexibleIndex) ? this.get('previousSibling') : this.get('nextSibling') ;
|
|
46
|
+
|
|
47
|
+
}.property(),
|
|
48
|
+
|
|
49
|
+
mouseDown: function(evt) {
|
|
50
|
+
|
|
51
|
+
var splitView = this.get('parentNode') ;
|
|
52
|
+
if (!splitView) return ;
|
|
53
|
+
|
|
54
|
+
// cache some info for later use.
|
|
55
|
+
this._mouseDownLocation = Event.pointerLocation(evt) ;
|
|
56
|
+
|
|
57
|
+
this._targetView = this.get('targetView') ;
|
|
58
|
+
|
|
59
|
+
// determine the view to change.
|
|
60
|
+
this._originalThickness = splitView.thicknessForView(this._targetView);
|
|
61
|
+
|
|
62
|
+
this._direction = splitView.get('layoutDirection') ;
|
|
63
|
+
|
|
64
|
+
// return true so we can track mouse dragged.
|
|
65
|
+
return true ;
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
mouseDragged: function(evt) {
|
|
69
|
+
|
|
70
|
+
// calculate new thickness
|
|
71
|
+
var loc = Event.pointerLocation(evt) ;
|
|
72
|
+
|
|
73
|
+
if (this._direction == SC.HORIZONTAL) {
|
|
74
|
+
var offset = loc.x - this._mouseDownLocation.x ;
|
|
75
|
+
} else {
|
|
76
|
+
var offset = loc.y - this._mouseDownLocation.y ;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
var thickness = this._originalThickness + offset ;
|
|
80
|
+
var splitView = this.get('parentNode') ;
|
|
81
|
+
splitView.setThicknessForView(this._targetView, thickness) ;
|
|
82
|
+
|
|
83
|
+
return true ;
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// clear left overs.
|
|
87
|
+
mouseUp: function(evt) {
|
|
88
|
+
this._targetView = this._originalThickness = this._direction = this._mouseDownLocation = null ;
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
doubleClick: function(evt) {
|
|
92
|
+
var splitView = this.get('parentNode') ;
|
|
93
|
+
if (!splitView) return; // nothing to do.
|
|
94
|
+
|
|
95
|
+
// try to collapse or un-collapse.
|
|
96
|
+
var targetView = this.get('targetView');
|
|
97
|
+
var isCollapsed = targetView.get('isCollapsed') || NO;
|
|
98
|
+
|
|
99
|
+
// do not collapse if not allowed.
|
|
100
|
+
if (!isCollapsed && !splitView.canCollapseView(targetView)) return;
|
|
101
|
+
|
|
102
|
+
// now set the collapsed state and layout.
|
|
103
|
+
targetView.set('isCollapsed', !isCollapsed) ;
|
|
104
|
+
splitView.layout() ;
|
|
105
|
+
|
|
106
|
+
return true ;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ========================================================================
|
|
2
2
|
// SproutCore
|
|
3
|
-
// copyright 2006-
|
|
3
|
+
// copyright 2006-2008 Sprout Systems, Inc.
|
|
4
4
|
// ========================================================================
|
|
5
5
|
|
|
6
6
|
require('views/view') ;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ========================================================================
|
|
2
2
|
// SproutCore
|
|
3
|
-
// copyright 2006-
|
|
3
|
+
// copyright 2006-2008 Sprout Systems, Inc.
|
|
4
4
|
// ========================================================================
|
|
5
5
|
|
|
6
6
|
// A menu is not a view exactly, but you can use it to bundle together
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ========================================================================
|
|
2
2
|
// SproutCore
|
|
3
|
-
// copyright 2006-
|
|
3
|
+
// copyright 2006-2008 Sprout Systems, Inc.
|
|
4
4
|
// ========================================================================
|
|
5
5
|
|
|
6
6
|
require('foundation/object') ;
|
|
@@ -9,20 +9,33 @@ require('foundation/node_descriptor') ;
|
|
|
9
9
|
require('foundation/binding');
|
|
10
10
|
require('foundation/path_module');
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
SC.
|
|
12
|
+
require('mixins/delegate_support') ;
|
|
13
|
+
|
|
14
|
+
SC.BENCHMARK_OUTLETS = NO ;
|
|
15
|
+
SC.BENCHMARK_CONFIGURE_OUTLETS = NO ;
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
|
-
@class
|
|
18
|
+
@class
|
|
19
|
+
|
|
20
|
+
A view is the root class you use to manage the web page DOM in your
|
|
21
|
+
application. You can use views to render visible content on your page,
|
|
22
|
+
provide animations, and to capture and respond to events.
|
|
23
|
+
|
|
24
|
+
You can use SC.View directly to manage DOM elements or you can extend one
|
|
25
|
+
of the many subclasses provided by SproutCore. This documentation describes
|
|
26
|
+
the general concepts you need to understand when working with views, though
|
|
27
|
+
most often you will want to work with one of the subclasses instead.
|
|
18
28
|
|
|
19
|
-
Views are how you interact with the DOM.
|
|
20
29
|
|
|
21
30
|
@extends SC.Responder
|
|
31
|
+
@extends SC.PathModule
|
|
32
|
+
@extends SC.DelegateSupport
|
|
33
|
+
|
|
34
|
+
@author Charles Jolley
|
|
35
|
+
@version 1.0
|
|
22
36
|
*/
|
|
23
|
-
SC.View = SC.Responder.extend(SC.PathModule,
|
|
24
|
-
|
|
25
|
-
{
|
|
37
|
+
SC.View = SC.Responder.extend(SC.PathModule, SC.DelegateSupport,
|
|
38
|
+
/** @scope SC.View.prototype */ {
|
|
26
39
|
|
|
27
40
|
// ..........................................
|
|
28
41
|
// VIEW API
|
|
@@ -33,14 +46,27 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
33
46
|
// array and use standard iterators.
|
|
34
47
|
//
|
|
35
48
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
/**
|
|
50
|
+
Insert the view into the the receiver's childNodes array.
|
|
51
|
+
|
|
52
|
+
The view will be added to the childNodes array before the beforeView. If
|
|
53
|
+
beforeView is null, then the view will be added to the end of the array.
|
|
54
|
+
This will also add the view's rootElement DOM node to the receivers
|
|
55
|
+
containerElement DOM node as a child.
|
|
56
|
+
|
|
57
|
+
If the specified view already belongs to another parent, it will be
|
|
58
|
+
removed from that view first.
|
|
59
|
+
|
|
60
|
+
@param view {SC.View} the view to insert as a child node.
|
|
61
|
+
@param beforeView {SC.View} view to insert before, or null to insert at
|
|
62
|
+
end
|
|
63
|
+
@returns {void}
|
|
39
64
|
*/
|
|
40
65
|
insertBefore: function(view, beforeView) {
|
|
41
66
|
this._insertBefore(view,beforeView,true);
|
|
42
67
|
},
|
|
43
|
-
|
|
68
|
+
|
|
69
|
+
/** @private */
|
|
44
70
|
_insertBefore: function(view, beforeView, updateDom) {
|
|
45
71
|
// verify that beforeView is a child.
|
|
46
72
|
if (beforeView) {
|
|
@@ -77,11 +103,12 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
77
103
|
|
|
78
104
|
// regenerate the childNodes array.
|
|
79
105
|
this._rebuildChildNodes();
|
|
80
|
-
|
|
81
|
-
// update parent state.
|
|
82
|
-
view._updateIsVisibleInWindow() ;
|
|
83
106
|
}
|
|
84
107
|
|
|
108
|
+
// update cached states.
|
|
109
|
+
view._updateIsVisibleInWindow() ;
|
|
110
|
+
view._flushInternalCaches() ;
|
|
111
|
+
view._invalidateClippingFrame() ;
|
|
85
112
|
|
|
86
113
|
// call notices.
|
|
87
114
|
view.didAddToParent(this, beforeView) ;
|
|
@@ -89,8 +116,15 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
89
116
|
|
|
90
117
|
return this ;
|
|
91
118
|
},
|
|
92
|
-
|
|
93
|
-
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
Remove the view from the receiver's childNodes array.
|
|
122
|
+
|
|
123
|
+
This will also remove the view's DOM element from the recievers DOM.
|
|
124
|
+
|
|
125
|
+
@param view {SC.View} the view to remove
|
|
126
|
+
@returns {void}
|
|
127
|
+
*/
|
|
94
128
|
removeChild: function(view) {
|
|
95
129
|
if (!view) return ;
|
|
96
130
|
if (view.parentNode != this) throw "removeChild: view must belong to parent";
|
|
@@ -116,53 +150,126 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
116
150
|
// regenerate the childNodes array.
|
|
117
151
|
this._rebuildChildNodes();
|
|
118
152
|
|
|
119
|
-
// update parent state.
|
|
120
|
-
view._updateIsVisibleInWindow() ;
|
|
121
|
-
|
|
122
|
-
|
|
123
153
|
view.set('nextSibling', null);
|
|
124
154
|
view.set('previousSibling', null);
|
|
125
155
|
view.set('parentNode', null) ;
|
|
156
|
+
|
|
157
|
+
// update parent state.
|
|
158
|
+
view._updateIsVisibleInWindow() ;
|
|
159
|
+
view._flushInternalCaches();
|
|
160
|
+
view._invalidateClippingFrame() ;
|
|
161
|
+
|
|
126
162
|
view.didRemoveFromParent(this) ;
|
|
127
163
|
this.didRemoveChild(view);
|
|
128
164
|
},
|
|
129
|
-
|
|
130
|
-
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
Replace the oldView with the specified view in the receivers childNodes
|
|
168
|
+
array. This will also replace the DOM node of the oldView with the DOM
|
|
169
|
+
node of the new view in the receivers DOM.
|
|
170
|
+
|
|
171
|
+
If the specified view already belongs to another parent, it will be
|
|
172
|
+
removed from that view first.
|
|
173
|
+
|
|
174
|
+
@param view {SC.View} the view to insert in the DOM
|
|
175
|
+
@param view {SC.View} the view to remove from the DOM.
|
|
176
|
+
@returns {void}
|
|
177
|
+
*/
|
|
131
178
|
replaceChild: function(view, oldView) {
|
|
132
179
|
this.insertBefore(view,oldView) ; this.removeChild(oldView) ;
|
|
133
180
|
},
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
Removes the receiver from its parentNode. If the receiver does not belong
|
|
184
|
+
to a parentNode, this method does nothing.
|
|
185
|
+
|
|
186
|
+
@returns {void}
|
|
187
|
+
*/
|
|
137
188
|
removeFromParent: function() {
|
|
138
189
|
if (this.parentNode) this.parentNode.removeChild(this) ;
|
|
139
190
|
},
|
|
140
|
-
|
|
141
|
-
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
Appends the specified view to the end of the receivers childNodes array.
|
|
194
|
+
This is equivalent to calling insertBefore(view, null);
|
|
195
|
+
|
|
196
|
+
@param view {SC.View} the view to insert
|
|
197
|
+
@returns {void}
|
|
198
|
+
*/
|
|
142
199
|
appendChild: function(view) {
|
|
143
200
|
this.insertBefore(view,null) ;
|
|
144
201
|
},
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
The array of views that are direct children of the receiver view. The DOM
|
|
205
|
+
elements managed by the views are also directl children of the
|
|
206
|
+
containerElement for the receiver.
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
@type Array
|
|
210
|
+
*/
|
|
148
211
|
childNodes: [],
|
|
149
|
-
|
|
150
|
-
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
The first child view in the childNodes array. If the view does not have
|
|
215
|
+
any children, this property will be null.
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
@type SC.View
|
|
219
|
+
*/
|
|
151
220
|
firstChild: null,
|
|
152
221
|
|
|
153
|
-
|
|
222
|
+
/**
|
|
223
|
+
The last child view in the childNodes array. If the view does not have any children,
|
|
224
|
+
this property will be null.
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
@type SC.View
|
|
228
|
+
*/
|
|
154
229
|
lastChild: null,
|
|
155
230
|
|
|
156
|
-
|
|
231
|
+
/**
|
|
232
|
+
The next sibling view in the childNodes array of the receivers parentNode.
|
|
233
|
+
If the receiver is the last view in the array or if the receiver does not
|
|
234
|
+
belong to a parent view this property will be null.
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
@type SC.View
|
|
238
|
+
*/
|
|
157
239
|
nextSibling: null,
|
|
158
240
|
|
|
159
|
-
|
|
241
|
+
/**
|
|
242
|
+
The previous sibling view in the childNodes array of the receivers
|
|
243
|
+
parentNode. If the receiver is the first view in the array or if the
|
|
244
|
+
receiver does not belong to a parent view this property will be null.
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
@type SC.View
|
|
248
|
+
*/
|
|
160
249
|
previousSibling: null,
|
|
161
250
|
|
|
162
|
-
|
|
251
|
+
/**
|
|
252
|
+
The parent view this view belongs to. If the receiver does not belong to a parent view
|
|
253
|
+
then this property is null.
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
@type SC.View
|
|
257
|
+
*/
|
|
163
258
|
parentNode: null,
|
|
164
259
|
|
|
165
|
-
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
The pane this view belongs to. The pane is the root of the responder
|
|
263
|
+
chain that this view belongs to. Typically a view's pane will be the
|
|
264
|
+
SC.window object. However, if you have added the view to a dialog, panel,
|
|
265
|
+
popup or other pane, this property will point to that pane instead.
|
|
266
|
+
|
|
267
|
+
If the view does not belong to a parentNode or if the view is not
|
|
268
|
+
onscreen, this property will be null.
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
@type SC.View
|
|
272
|
+
*/
|
|
166
273
|
pane: function()
|
|
167
274
|
{
|
|
168
275
|
var view = this;
|
|
@@ -173,34 +280,125 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
173
280
|
return view;
|
|
174
281
|
}.property(),
|
|
175
282
|
|
|
176
|
-
|
|
177
|
-
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
Removes all child views from the receiver.
|
|
286
|
+
|
|
287
|
+
@returns {void}
|
|
288
|
+
*/
|
|
178
289
|
clear: function() {
|
|
179
290
|
while(this.firstChild) this.removeChild(this.firstChild) ;
|
|
180
291
|
},
|
|
181
|
-
|
|
182
|
-
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
This method is called on the view just before it is added to a new parent
|
|
295
|
+
view.
|
|
296
|
+
|
|
297
|
+
You can override this method to do any setup you need on your view or to
|
|
298
|
+
reset any cached values that are impacted by being added to a view. The
|
|
299
|
+
default implementation does nothing.
|
|
300
|
+
|
|
301
|
+
@param parent {SC.View} the new parent
|
|
302
|
+
@paran beforeView {SC.View} the view in the parent's childNodes array that
|
|
303
|
+
will follow this view once it is added. If the view is being added to
|
|
304
|
+
the end of the array, this will be null.
|
|
305
|
+
@returns {void}
|
|
306
|
+
*/
|
|
183
307
|
willAddToParent: function(parent, beforeView) {},
|
|
184
308
|
|
|
185
|
-
|
|
309
|
+
/**
|
|
310
|
+
This method is called on the view just after it is added to a new parent
|
|
311
|
+
view.
|
|
312
|
+
|
|
313
|
+
You can override this method to do any setup you need on your view or to
|
|
314
|
+
reset any cached values that are impacted by being added to a view. The
|
|
315
|
+
default implementation does nothing.
|
|
316
|
+
|
|
317
|
+
@param parent {SC.View} the new parent
|
|
318
|
+
@paran beforeView {SC.View} the view in the parent's childNodes array that
|
|
319
|
+
will follow this view once it is added. If the view is being added to
|
|
320
|
+
the end of the array, this will be null.
|
|
321
|
+
@returns {void}
|
|
322
|
+
*/
|
|
186
323
|
didAddToParent: function(parent, beforeView) {},
|
|
187
324
|
|
|
188
|
-
|
|
325
|
+
/**
|
|
326
|
+
This method is called on the view just before it is removed from a parent
|
|
327
|
+
view.
|
|
328
|
+
|
|
329
|
+
You can override this method to clear out any values that depend on the
|
|
330
|
+
view belonging to the current parentNode. The default implementation does
|
|
331
|
+
nothing.
|
|
332
|
+
|
|
333
|
+
@returns {void}
|
|
334
|
+
*/
|
|
189
335
|
willRemoveFromParent: function() {},
|
|
190
336
|
|
|
191
|
-
|
|
337
|
+
/**
|
|
338
|
+
This method is called on the view just after it is removed from a parent
|
|
339
|
+
view.
|
|
340
|
+
|
|
341
|
+
You can override this method to clear out any values that depend on the
|
|
342
|
+
view belonging to the current parentNode. The default implementation does
|
|
343
|
+
nothing.
|
|
344
|
+
|
|
345
|
+
@param oldParent {SC.View} the old parent view
|
|
346
|
+
@returns {void}
|
|
347
|
+
*/
|
|
192
348
|
didRemoveFromParent: function(oldParent) {},
|
|
193
349
|
|
|
194
|
-
|
|
350
|
+
/**
|
|
351
|
+
This method is called just before a new child view is added to the
|
|
352
|
+
receiver's childNodes array. You can use this to prepare for any layout
|
|
353
|
+
or other cleanup you might need to do.
|
|
354
|
+
|
|
355
|
+
The default implementation does nothing.
|
|
356
|
+
|
|
357
|
+
@param child {SC.View} the view to be added
|
|
358
|
+
@param beforeView {SC.View} and existing child view that will follow the
|
|
359
|
+
child view in the array once it is added. If adding to the end of the
|
|
360
|
+
array, this param will be null.
|
|
361
|
+
@returns {void}
|
|
362
|
+
*/
|
|
195
363
|
willAddChild: function(child, beforeView) {},
|
|
196
364
|
|
|
197
|
-
|
|
365
|
+
/**
|
|
366
|
+
This method is called just after a new child view is added to the
|
|
367
|
+
receiver's childNodes array. You can use this to prepare for any layout
|
|
368
|
+
or other cleanup you might need to do.
|
|
369
|
+
|
|
370
|
+
The default implementation does nothing.
|
|
371
|
+
|
|
372
|
+
@param child {SC.View} the view that was added
|
|
373
|
+
@param beforeView {SC.View} and existing child view that will follow the
|
|
374
|
+
child view in the array once it is added. If adding to the end of the
|
|
375
|
+
array, this param will be null.
|
|
376
|
+
@returns {void}
|
|
377
|
+
*/
|
|
198
378
|
didAddChild: function(child, beforeView) {},
|
|
199
379
|
|
|
200
|
-
|
|
380
|
+
/**
|
|
381
|
+
This method is called just before a child view is removed from the
|
|
382
|
+
receiver's childNodes array. You can use this to prepare for any layout
|
|
383
|
+
or other cleanup you might need to do.
|
|
384
|
+
|
|
385
|
+
The default implementation does nothing.
|
|
386
|
+
|
|
387
|
+
@param child {SC.View} the view to be removed
|
|
388
|
+
@returns {void}
|
|
389
|
+
*/
|
|
201
390
|
willRemoveChild: function(child) {},
|
|
202
391
|
|
|
203
|
-
|
|
392
|
+
/**
|
|
393
|
+
This method is called just after a child view is removed from the
|
|
394
|
+
receiver's childNodes array. You can use this to prepare for any layout
|
|
395
|
+
or other cleanup you might need to do.
|
|
396
|
+
|
|
397
|
+
The default implementation does nothing.
|
|
398
|
+
|
|
399
|
+
@param child {SC.View} the view that was removed
|
|
400
|
+
@returns {void}
|
|
401
|
+
*/
|
|
204
402
|
didRemoveChild: function(child) {},
|
|
205
403
|
|
|
206
404
|
|
|
@@ -212,7 +410,9 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
212
410
|
var view = this;
|
|
213
411
|
while (view = view.get('nextKeyView'))
|
|
214
412
|
{
|
|
215
|
-
if (view.get('isVisible') && view.get('acceptsFirstResponder'))
|
|
413
|
+
if (view.get('isVisible') && view.get('acceptsFirstResponder')) {
|
|
414
|
+
return view;
|
|
415
|
+
}
|
|
216
416
|
}
|
|
217
417
|
return null;
|
|
218
418
|
},
|
|
@@ -221,11 +421,25 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
221
421
|
var view = this;
|
|
222
422
|
while (view = view.get('previousKeyView'))
|
|
223
423
|
{
|
|
224
|
-
if (view.get('isVisible') && view.get('acceptsFirstResponder'))
|
|
424
|
+
if (view.get('isVisible') && view.get('acceptsFirstResponder')) {
|
|
425
|
+
return view;
|
|
426
|
+
}
|
|
225
427
|
}
|
|
226
428
|
return null;
|
|
227
429
|
},
|
|
228
430
|
|
|
431
|
+
/** @private
|
|
432
|
+
Invoked whenever the child hierarchy changes and any internally cached
|
|
433
|
+
values might need to be recalculated.
|
|
434
|
+
*/
|
|
435
|
+
_flushInternalCaches: function() {
|
|
436
|
+
// only flush cache for parent if this item was cached since the top level
|
|
437
|
+
// cached can only be populated if this one is populated also...
|
|
438
|
+
if ((this._needsClippingFrame != null) || (this._needsFrameChanges != null)) {
|
|
439
|
+
this._needsClippingFrame = this._needsFrameChanges = null ;
|
|
440
|
+
if (this.parentNode) this.parentNode._flushInternalCaches() ;
|
|
441
|
+
}
|
|
442
|
+
},
|
|
229
443
|
|
|
230
444
|
// ..........................................
|
|
231
445
|
// SC.Responder implementation
|
|
@@ -258,49 +472,77 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
258
472
|
|
|
259
473
|
// returns the CSS classNames for the element.
|
|
260
474
|
classNames: function() {
|
|
261
|
-
|
|
475
|
+
if (!this._classNames) {
|
|
476
|
+
var classNames = this.rootElement.className;
|
|
477
|
+
this._classNames = (classNames && classNames.length > 0) ? classNames.split(' ') : [] ;
|
|
478
|
+
}
|
|
479
|
+
return this._classNames ;
|
|
262
480
|
}.property(),
|
|
263
481
|
|
|
264
482
|
// return true if the element has the classname.
|
|
265
483
|
hasClassName: function(className) {
|
|
266
|
-
|
|
267
|
-
this.propertyDidChange('classNames') ;
|
|
268
|
-
return ret ;
|
|
484
|
+
return (this._classNames || this.get('classNames')).indexOf(className) >= 0 ;
|
|
269
485
|
},
|
|
270
486
|
|
|
271
487
|
// add the specified class name.
|
|
272
488
|
addClassName: function(className) {
|
|
273
|
-
|
|
489
|
+
if (this.hasClassName(className)) return ; // nothing to do
|
|
490
|
+
|
|
491
|
+
this.propertyWillChange('classNames') ;
|
|
492
|
+
var classNames = this._classNames || this.get('classNames') ;
|
|
493
|
+
classNames.push(className) ;
|
|
494
|
+
if (this.rootElement) this.rootElement.className = classNames.join(' ');
|
|
274
495
|
this.propertyDidChange('classNames') ;
|
|
275
|
-
return
|
|
496
|
+
return className ;
|
|
276
497
|
},
|
|
277
498
|
|
|
278
499
|
// remove the specified class name.
|
|
279
500
|
removeClassName: function(className) {
|
|
280
|
-
|
|
501
|
+
if (!this.hasClassName(className)) return ; // nothing to do
|
|
502
|
+
|
|
503
|
+
this.propertyWillChange('classNames') ;
|
|
504
|
+
var classNames = this._classNames || this.get('classNames') ;
|
|
505
|
+
classNames = this._classNames = classNames.without(className) ;
|
|
506
|
+
if (this.rootElement) this.rootElement.className = classNames.join(' ');
|
|
281
507
|
this.propertyDidChange('classNames') ;
|
|
282
|
-
return
|
|
508
|
+
return className ;
|
|
283
509
|
},
|
|
284
510
|
|
|
285
511
|
setClassName: function(className, flag) {
|
|
286
|
-
(!!flag) ? this.addClassName(className) : this.removeClassName(className);
|
|
512
|
+
return (!!flag) ? this.addClassName(className) : this.removeClassName(className);
|
|
287
513
|
},
|
|
288
514
|
|
|
289
515
|
// toggler specified class name..
|
|
290
516
|
toggleClassName: function(className) {
|
|
291
|
-
|
|
292
|
-
this.propertyDidChange('classNames') ;
|
|
293
|
-
return ret ;
|
|
294
|
-
},
|
|
295
|
-
|
|
296
|
-
// scroll the main window to the selected element.
|
|
297
|
-
scrollTo: function() {
|
|
298
|
-
Element.scrollTo(this.rootElement) ;
|
|
517
|
+
return this.setClassName(className, !this.hasClassName(className)) ;
|
|
299
518
|
},
|
|
300
519
|
|
|
301
520
|
// get the named style. (see also style properties)
|
|
302
521
|
getStyle: function(style) {
|
|
303
|
-
|
|
522
|
+
var element = this.rootElement ;
|
|
523
|
+
if (!this._computedStyle) {
|
|
524
|
+
this._computedStyle = document.defaultView.getComputedStyle(element, null) ;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
//if (style == 'float') style = 'cssFloat' ;
|
|
528
|
+
style = (style == 'float') ? 'cssFloat' : style.camelize() ;
|
|
529
|
+
var value = element.style[style];
|
|
530
|
+
if (!value) {
|
|
531
|
+
value = this._computedStyle ? this._computedStyle[style] : null ;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
switch(style) {
|
|
535
|
+
case 'opacity':
|
|
536
|
+
value = value ? parseFloat(value) : 1.0;
|
|
537
|
+
break ;
|
|
538
|
+
case 'auto':
|
|
539
|
+
value = null;
|
|
540
|
+
break ;
|
|
541
|
+
default:
|
|
542
|
+
break ;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return value ;
|
|
304
546
|
},
|
|
305
547
|
|
|
306
548
|
// set the passed styles.
|
|
@@ -310,10 +552,10 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
310
552
|
|
|
311
553
|
// use this method to update the HTML of an element. This takes care of
|
|
312
554
|
// nasties like processing scripts and inserting HTML into a table. You can
|
|
313
|
-
// also use
|
|
555
|
+
// also use innerHTML, which builds on this method.
|
|
314
556
|
update: function(html) {
|
|
315
557
|
Element.update((this.containerElement || this.rootElement),html) ;
|
|
316
|
-
this.propertyDidChange('
|
|
558
|
+
this.propertyDidChange('innerHTML') ;
|
|
317
559
|
},
|
|
318
560
|
|
|
319
561
|
// this works like the element getAttribute() except it is standardized
|
|
@@ -336,17 +578,37 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
336
578
|
// The methods in this section give you some low-level control over how the
|
|
337
579
|
// view interacts with the DOM. You do not normally need to work with this.
|
|
338
580
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
581
|
+
/**
|
|
582
|
+
This is the DOM element actually managed by this view. This will be set
|
|
583
|
+
by the view when it is created. You should rarely need to access this
|
|
584
|
+
property directly. When you do access it, you should only do so from
|
|
585
|
+
within methods you write on your SC.View subclasses, never from outside
|
|
586
|
+
the view.
|
|
587
|
+
|
|
588
|
+
Unlike most properties, you do not need to use get()/set() to access this
|
|
589
|
+
property. It is not currently safe to edit this property once the view
|
|
590
|
+
has been createde.
|
|
591
|
+
|
|
592
|
+
@property
|
|
593
|
+
@type {Element}
|
|
594
|
+
*/
|
|
342
595
|
rootElement: null,
|
|
343
596
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
597
|
+
/**
|
|
598
|
+
Normally when you add child views to your view, their DOM elements will
|
|
599
|
+
be set as direct children of the root element. However you can
|
|
600
|
+
choose instead to designate an alertnative child node using this
|
|
601
|
+
property. Set this to a selector string to begin with. The first time
|
|
602
|
+
it is accessed, the view will convert it to an actual element. It is not
|
|
603
|
+
currently safe to edit this property once the view has been created.
|
|
604
|
+
|
|
605
|
+
Like rootElement, you should only access this property from within
|
|
606
|
+
methods you write on an SC.View subclass, never from outside the view.
|
|
607
|
+
Unlike most properties, it is not necessary to use get()/set().
|
|
608
|
+
|
|
609
|
+
@property
|
|
610
|
+
@type {Element}
|
|
611
|
+
*/
|
|
350
612
|
containerElement: null,
|
|
351
613
|
|
|
352
614
|
// ..........................................
|
|
@@ -357,14 +619,79 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
357
619
|
// location and size of your views. You can then use the automatic
|
|
358
620
|
// resizing.
|
|
359
621
|
|
|
622
|
+
/**
|
|
623
|
+
Returns true if the view or any of its contained views implement the
|
|
624
|
+
clippingFrameDidChange method.
|
|
625
|
+
|
|
626
|
+
If this property returns false, then notifications about changes to the
|
|
627
|
+
clippingFrame will probably not be called on the receiver. Normally if you
|
|
628
|
+
do not need to worry about this property since implementing the clippingFrameDidChange()
|
|
629
|
+
method will change its value and cause your method to be invoked.
|
|
630
|
+
|
|
631
|
+
This property is automatically updated whenever you add or remove a child view.
|
|
632
|
+
*/
|
|
633
|
+
needsClippingFrame: function() {
|
|
634
|
+
if (this._needsClippingFrame == null) {
|
|
635
|
+
var ret = this.clippingFrameDidChange != SC.View.prototype.clippingFrameDidChange;
|
|
636
|
+
var view = this.get('firstChild') ;
|
|
637
|
+
while(!ret && view) {
|
|
638
|
+
ret = view.get('needsClippingFrame') ;
|
|
639
|
+
view = view.get('nextSibling') ;
|
|
640
|
+
}
|
|
641
|
+
this._needsClippingFrame = ret ;
|
|
642
|
+
}
|
|
643
|
+
return this._needsClippingFrame ;
|
|
644
|
+
}.property(),
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
Returns true if the view or any of its contained views implements any resize
|
|
648
|
+
methods.
|
|
649
|
+
|
|
650
|
+
If this property returns false, changes to your frame view may not be
|
|
651
|
+
relayed to child methods. This may mean that your various frame properties could
|
|
652
|
+
become stale unless you call refreshFrames() first.
|
|
653
|
+
|
|
654
|
+
If you want you make sure your frames are up to date, see hasManualLayout.
|
|
655
|
+
|
|
656
|
+
This property is automatically updated whenever you add or remove a child view. It
|
|
657
|
+
returns true if you implement any of the resize methods or if hasManualLayout is true.
|
|
658
|
+
*/
|
|
659
|
+
needsFrameChanges: function() {
|
|
660
|
+
if (this._needsFrameChanges == null) {
|
|
661
|
+
var ret = this.get('needsClippingFrame') || this.get('hasManualLayout') ;
|
|
662
|
+
var view = this.get('firstChild') ;
|
|
663
|
+
while(!ret && view) {
|
|
664
|
+
ret = view.get('needsFrameChanges') ;
|
|
665
|
+
view = view.get('nextSibling') ;
|
|
666
|
+
}
|
|
667
|
+
this._needsFrameChanges = ret ;
|
|
668
|
+
}
|
|
669
|
+
return this._needsFrameChanges ;
|
|
670
|
+
}.property(),
|
|
360
671
|
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
Returns true if the receiver manages the layout for itself or its children.
|
|
675
|
+
|
|
676
|
+
Normally this property returns true automatically if you implement
|
|
677
|
+
resizeChildrenWithOldSize() or resizeWithOldParentSize() or clippingFrameDidChange().
|
|
678
|
+
|
|
679
|
+
If you do not implement these methods but need to make sure your frame is always up-to-date
|
|
680
|
+
anyway, set this property to true.
|
|
681
|
+
*/
|
|
682
|
+
hasManualLayout: function() {
|
|
683
|
+
return (this.resizeChildrenWithOldSize != SC.View.prototype.resizeChildrenWithOldSize) ||
|
|
684
|
+
(this.resizeWithOldParentSize != SC.View.prototype.resizeWithOldParentSize) ||
|
|
685
|
+
(this.clippingFrameDidChange != SC.View.prototype.clippingFrameDidChange) ;
|
|
686
|
+
}.property(),
|
|
687
|
+
|
|
361
688
|
/**
|
|
362
689
|
Convert a point _from_ the offset parent of the passed view to the current view.
|
|
363
690
|
|
|
364
691
|
This is a useful utility for converting points in the coordinate system of
|
|
365
692
|
another view to the coordinate system of the receiver. Pass null for
|
|
366
|
-
targetView to convert a point from a window offset. This is the inverse
|
|
367
|
-
convertFrameToView().
|
|
693
|
+
targetView to convert a point from a window offset. This is the inverse
|
|
694
|
+
of convertFrameToView().
|
|
368
695
|
|
|
369
696
|
Note that if your view is not visible on the screen, this may not work.
|
|
370
697
|
|
|
@@ -376,12 +703,12 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
376
703
|
convertFrameFromView: function(f, targetView) {
|
|
377
704
|
|
|
378
705
|
// first, convert to root level offset.
|
|
379
|
-
var thisOffset =
|
|
380
|
-
var thatOffset = (targetView) ?
|
|
706
|
+
var thisOffset = SC.viewportOffset(this.get('offsetParent')) ;
|
|
707
|
+
var thatOffset = (targetView) ? SC.viewportOffset(targetView.get('offsetParent')) : SC.ZERO_POINT;
|
|
381
708
|
|
|
382
709
|
// now get adjustment.
|
|
383
|
-
var adjustX = thatOffset
|
|
384
|
-
var adjustY = thatOffset
|
|
710
|
+
var adjustX = thatOffset.x - thisOffset.x ;
|
|
711
|
+
var adjustY = thatOffset.y - thisOffset.y ;
|
|
385
712
|
return { x: (f.x + adjustX), y: (f.y + adjustY), width: f.width, height: f.height };
|
|
386
713
|
},
|
|
387
714
|
|
|
@@ -402,309 +729,566 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
402
729
|
*/
|
|
403
730
|
convertFrameToView: function(f, sourceView) {
|
|
404
731
|
// first, convert to root level offset.
|
|
405
|
-
var thisOffset =
|
|
406
|
-
var thatOffset = (sourceView) ?
|
|
732
|
+
var thisOffset = SC.viewportOffset(this.get('offsetParent')) ;
|
|
733
|
+
var thatOffset = (sourceView) ? SC.viewportOffset(sourceView.get('offsetParent')) : SC.ZERO_POINT ;
|
|
407
734
|
|
|
408
735
|
// now get adjustment.
|
|
409
|
-
var adjustX = thisOffset
|
|
410
|
-
var adjustY = thisOffset
|
|
736
|
+
var adjustX = thisOffset.x - thatOffset.x ;
|
|
737
|
+
var adjustY = thisOffset.y - thatOffset.y ;
|
|
411
738
|
return { x: (f.x + adjustX), y: (f.y + adjustY), width: f.width, height: f.height };
|
|
412
739
|
},
|
|
413
740
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
//
|
|
419
|
-
// You can edit the innerFrame of a view anytime, even if the element is
|
|
420
|
-
// not positioned.
|
|
421
|
-
//
|
|
422
|
-
isPositioned: false,
|
|
423
|
-
|
|
424
|
-
changePositionObserver: function() {
|
|
425
|
-
var isPositioned = this.get('isPositioned') ;
|
|
426
|
-
if (this._wasPositioned == isPositioned) return ;
|
|
741
|
+
/**
|
|
742
|
+
This property returns a DOM ELEMENT that is the offset parent for
|
|
743
|
+
this view's frame coordinates. Depending on your CSS, this parent
|
|
744
|
+
may or may not match with the parent view.
|
|
427
745
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
var el = this.rootElement;
|
|
431
|
-
this.cacheFrame();
|
|
432
|
-
|
|
433
|
-
this.setStyle({
|
|
434
|
-
position: 'absolute',
|
|
435
|
-
top: Math.floor(this._frame.y) + 'px',
|
|
436
|
-
left: Math.floor(this._frame.x) + 'px',
|
|
437
|
-
width: Math.floor(this._frame.width) + 'px',
|
|
438
|
-
height: Math.floor(this._frame.height) + 'px'
|
|
439
|
-
}) ;
|
|
440
|
-
|
|
441
|
-
} else {
|
|
442
|
-
var el = this.rootElement;
|
|
443
|
-
el.style.position =
|
|
444
|
-
el.style.top =
|
|
445
|
-
el.style.left =
|
|
446
|
-
el.style.width =
|
|
447
|
-
el.style.height = '' ;
|
|
448
|
-
this._frame = null ;
|
|
449
|
-
}
|
|
450
|
-
this._wasPositioned = isPositioned ;
|
|
451
|
-
}.observes('isPositioned'),
|
|
452
|
-
|
|
453
|
-
// Normally we don't get the dimensions of a view until you actually ask
|
|
454
|
-
// for them. However, sometimes you need to get the frame before you
|
|
455
|
-
// remove the view from the parent, etc. This will cache the frame.
|
|
456
|
-
cacheFrame: function() {
|
|
457
|
-
if (this._frame || this._frameCached) return ; // don't cache twice
|
|
746
|
+
@example
|
|
747
|
+
offsetView = $view(this.get('offsetParent')) ;
|
|
458
748
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
this._frame.y = el.offsetTop ;
|
|
463
|
-
this._frameCached = true ;
|
|
464
|
-
},
|
|
465
|
-
|
|
466
|
-
// if you cached the frame, you can use this to clear that cache so that it
|
|
467
|
-
// will now track with the frame in the document.
|
|
468
|
-
flushFrameCache: function() {
|
|
469
|
-
this._frame = null ;
|
|
470
|
-
this._frameCached = false;
|
|
471
|
-
},
|
|
472
|
-
|
|
473
|
-
// This property returns a DOM ELEMENT that is the offset parent for
|
|
474
|
-
// this view's frame coordinates. Depending on your CSS, this parent
|
|
475
|
-
// may or may not match with the parent view.
|
|
749
|
+
@property
|
|
750
|
+
@type {Element}
|
|
751
|
+
*/
|
|
476
752
|
offsetParent: function() {
|
|
477
753
|
return Position.offsetParent(this.rootElement) ;
|
|
478
754
|
}.property(),
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
The inner bounds for the content shown inside of this frame. Reflects scroll position
|
|
758
|
+
and other properties.
|
|
759
|
+
|
|
760
|
+
The inner frame returns the actual available frame for child elements, less any borders
|
|
761
|
+
or scroll bars.
|
|
762
|
+
|
|
763
|
+
This value can change when:
|
|
764
|
+
- the receiver's frame changes
|
|
765
|
+
- the receiver's child views change, adding or removing scrollbars
|
|
766
|
+
- You can the CSS or applied style that effects the borders or scrollbar visibility
|
|
767
|
+
*/
|
|
483
768
|
innerFrame: function(key, value) {
|
|
484
769
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
770
|
+
var f ;
|
|
771
|
+
if (this._innerFrame == null) {
|
|
772
|
+
|
|
773
|
+
// get the base frame
|
|
774
|
+
var el = this.rootElement ;
|
|
775
|
+
f = this._collectFrame(function() {
|
|
776
|
+
return {
|
|
777
|
+
x: el.offsetLeft,
|
|
778
|
+
y: el.offsetTop,
|
|
779
|
+
width: Math.min(el.scrollWidth, el.clientWidth),
|
|
780
|
+
height: Math.min(el.scrollHeight, el.clientHeight)
|
|
781
|
+
};
|
|
782
|
+
}) ;
|
|
493
783
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
784
|
+
// bizarely for FireFox if your offsetParent has a border, then it can
|
|
785
|
+
// impact the offset
|
|
786
|
+
if (SC.Platform.Firefox) {
|
|
787
|
+
var parent = el.offsetParent ;
|
|
788
|
+
if (parent && (Element.getStyle(parent, 'overflow') != 'visible')) {
|
|
789
|
+
var left = parseInt(Element.getStyle(parent, 'borderLeftWidth'),0) || 0 ;
|
|
790
|
+
var top = parseInt(Element.getStyle(parent, 'borderTopWidth'),0) || 0 ;
|
|
791
|
+
f.x += left; f.y += top ;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// fix the x & y with the clientTop/clientLeft
|
|
796
|
+
var clientLeft, clientTop ;
|
|
797
|
+
if (el.clientLeft == null) {
|
|
798
|
+
clientLeft = parseInt(this.getStyle('border-left-width'),0) || 0 ;
|
|
799
|
+
} else clientLeft = el.clientLeft ;
|
|
499
800
|
|
|
500
|
-
|
|
501
|
-
|
|
801
|
+
if (el.clientTop == null) {
|
|
802
|
+
clientTop = parseInt(this.getStyle('border-top-width'),0) || 0 ;
|
|
803
|
+
} else clientTop = el.clientTop ;
|
|
804
|
+
|
|
805
|
+
f.x += clientLeft; f.y += clientTop;
|
|
806
|
+
|
|
807
|
+
// cache this frame if using manual layout mode
|
|
808
|
+
this._innerFrame = SC.cloneRect(f);
|
|
809
|
+
} else f = SC.cloneRect(this._innerFrame) ;
|
|
810
|
+
return f ;
|
|
811
|
+
}.property('frame'),
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
The outside bounds of your view, offset top/left from its offsetParent
|
|
815
|
+
|
|
816
|
+
The frame rect is the area actually occupied by a view including any
|
|
817
|
+
borders or padding, but excluding margins.
|
|
818
|
+
|
|
819
|
+
The frame is calculated and cached the first time you get it. Afer that,
|
|
820
|
+
the frame cache should automatically update when you make changes that
|
|
821
|
+
will effect the view frames unless you change the frame indirectly, such
|
|
822
|
+
as through changing CSS classes or by-passing the view to edit the DOM.
|
|
823
|
+
|
|
824
|
+
If you make a change like this, be sure to wrap the code that makes this
|
|
825
|
+
change with calls to viewFrameWillChange() and viewFrameDidChange() on the
|
|
826
|
+
highest-level view that will be impacted by the change. Calling this
|
|
827
|
+
method will automatically update child frames as well.
|
|
828
|
+
|
|
829
|
+
When you set the frame property, it will update the left, top, height,
|
|
830
|
+
and width CSS attributes on the element. Since the height and width in
|
|
831
|
+
the frame rect includes borders and padding, the view will automatically
|
|
832
|
+
adjust the height and width CSS it sets to account for this.
|
|
833
|
+
|
|
834
|
+
If you would prefer to edit the CSS attributes for the frame directly
|
|
835
|
+
instead, you can do so by using the styleTop, styleLeft, styleRight,
|
|
836
|
+
styleBottom, styleWidth, and styleHeight properties on the view. These
|
|
837
|
+
properties will update the CSS attributes and call viewFrameDidChange()/
|
|
838
|
+
viewFrameWillChange().
|
|
839
|
+
|
|
840
|
+
@field
|
|
841
|
+
*/
|
|
842
|
+
frame: function(key, value) {
|
|
843
|
+
|
|
844
|
+
// if value was passed, set the values in the style
|
|
845
|
+
// now update the frame if needed. Only actually change the style for
|
|
846
|
+
// those parts of the frame that were passed in.
|
|
502
847
|
if (value !== undefined) {
|
|
848
|
+
|
|
849
|
+
this.viewFrameWillChange() ;
|
|
850
|
+
|
|
851
|
+
var f= value ;
|
|
503
852
|
var style = {} ;
|
|
504
853
|
var didResize = false ;
|
|
505
|
-
var clearFrame = false ;
|
|
506
854
|
|
|
855
|
+
// collect required info
|
|
507
856
|
// reposition X
|
|
508
857
|
if (value.x !== undefined) {
|
|
509
|
-
|
|
510
|
-
style.
|
|
858
|
+
style.left = Math.floor(f.x) + 'px' ;
|
|
859
|
+
style.right = 'auto';
|
|
511
860
|
}
|
|
512
861
|
|
|
513
862
|
// reposition Y
|
|
514
863
|
if (value.y !== undefined) {
|
|
515
|
-
|
|
516
|
-
style.
|
|
864
|
+
style.top = Math.floor(f.y) + 'px' ;
|
|
865
|
+
style.bottom = 'auto';
|
|
517
866
|
}
|
|
518
|
-
|
|
519
|
-
//
|
|
520
|
-
// adjust both the element width and padding right so that the overall
|
|
521
|
-
// frame size does not change.
|
|
867
|
+
|
|
868
|
+
// Resize width
|
|
522
869
|
if (value.width !== undefined) {
|
|
523
870
|
didResize = true ;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (padding < 0) {
|
|
529
|
-
clearFrame = true ;
|
|
530
|
-
padding = 0 ;
|
|
871
|
+
var padding = 0 ;
|
|
872
|
+
var idx = SC.View.WIDTH_PADDING_STYLES.length;
|
|
873
|
+
while(--idx >= 0) {
|
|
874
|
+
padding += parseInt(this.getStyle(SC.View.WIDTH_PADDING_STYLES[idx]), 0) || 0;
|
|
531
875
|
}
|
|
532
|
-
style.
|
|
876
|
+
style.width = (Math.floor(f.width) - padding).toString() + 'px' ;
|
|
533
877
|
}
|
|
534
878
|
|
|
535
879
|
// Resize Height
|
|
536
|
-
// adjust both the element height and padding bottom so that the
|
|
537
|
-
// overall frame size does not change.
|
|
538
880
|
if (value.height !== undefined) {
|
|
539
881
|
didResize = true ;
|
|
540
|
-
|
|
541
|
-
|
|
882
|
+
var padding = 0 ;
|
|
883
|
+
var idx = SC.View.HEIGHT_PADDING_STYLES.length;
|
|
884
|
+
while(--idx >= 0) {
|
|
885
|
+
padding += parseInt(this.getStyle(SC.View.HEIGHT_PADDING_STYLES[idx]), 0) || 0;
|
|
886
|
+
}
|
|
887
|
+
style.height = (Math.floor(f.height) - padding).toString() + 'px' ;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// now apply style change and clear the cached frame
|
|
891
|
+
this.setStyle(style) ;
|
|
892
|
+
|
|
893
|
+
// notify for a resize only.
|
|
894
|
+
this.viewFrameDidChange() ;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// build frame. We can use a cached version but only
|
|
898
|
+
// if layoutMode == SC.MANUAL_MODE
|
|
899
|
+
var f;
|
|
900
|
+
if (this._frame == null) {
|
|
901
|
+
var el = this.rootElement ;
|
|
902
|
+
f = this._collectFrame(function() {
|
|
903
|
+
return {
|
|
904
|
+
x: el.offsetLeft,
|
|
905
|
+
y: el.offsetTop,
|
|
906
|
+
width: el.offsetWidth,
|
|
907
|
+
height: el.offsetHeight
|
|
908
|
+
};
|
|
909
|
+
}) ;
|
|
910
|
+
|
|
911
|
+
// bizarely for FireFox if your offsetParent has a border, then it can
|
|
912
|
+
// impact the offset
|
|
913
|
+
if (SC.Platform.Firefox) {
|
|
914
|
+
var parent = el.offsetParent ;
|
|
915
|
+
if (parent && (Element.getStyle(parent, 'overflow') != 'visible')) {
|
|
916
|
+
var left = parseInt(Element.getStyle(parent, 'borderLeftWidth'),0) || 0 ;
|
|
917
|
+
var top = parseInt(Element.getStyle(parent, 'borderTopWidth'),0) || 0 ;
|
|
918
|
+
f.x += left; f.y += top ;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// cache this frame if using manual layout mode
|
|
923
|
+
this._frame = SC.cloneRect(f);
|
|
924
|
+
} else f = SC.cloneRect(this._frame) ;
|
|
925
|
+
|
|
926
|
+
// finally return the frame.
|
|
927
|
+
return f ;
|
|
928
|
+
}.property(),
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
The current frame size.
|
|
932
|
+
|
|
933
|
+
This property will actually return the same value as the frame property,
|
|
934
|
+
however setting this property will set only the frame size and ignore any
|
|
935
|
+
origin you might pass.
|
|
936
|
+
|
|
937
|
+
@field
|
|
938
|
+
*/
|
|
939
|
+
size: function(key, value) {
|
|
940
|
+
if (value !== undefined) {
|
|
941
|
+
this.set('frame',{ width: value.width, height: value.height }) ;
|
|
942
|
+
}
|
|
943
|
+
return this.get('frame') ;
|
|
944
|
+
}.property('frame'),
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
The current frame origin.
|
|
948
|
+
|
|
949
|
+
This property will actually return the same value as the frame property,
|
|
950
|
+
however setting this property will set only the frame origin and ignore
|
|
951
|
+
any size you might pass.
|
|
952
|
+
|
|
953
|
+
@field
|
|
954
|
+
*/
|
|
955
|
+
origin: function(key, value) {
|
|
956
|
+
if (value !== undefined) {
|
|
957
|
+
this.set('frame',{ x: value.x, y: value.y }) ;
|
|
958
|
+
}
|
|
959
|
+
return this.get('frame') ;
|
|
960
|
+
}.property('frame'),
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
Style-width for the views rootElement
|
|
964
|
+
|
|
965
|
+
Setting this property will also notify of a view frame change.
|
|
966
|
+
|
|
967
|
+
@field
|
|
968
|
+
*/
|
|
969
|
+
styleWidth: function(key, value) {
|
|
970
|
+
if (value !== undefined) {
|
|
971
|
+
this.viewFrameWillChange() ;
|
|
972
|
+
this.setStyle({ width: ((value != null) ? value+'px' : 'auto') }) ;
|
|
973
|
+
this.viewFrameDidChange() ;
|
|
974
|
+
this._styleWidth = null;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
var ret = this.getStyle('width') ;
|
|
978
|
+
ret = (ret == 'auto') ? null : parseInt(ret, 0) ;
|
|
979
|
+
}.property(),
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
Style-height for the views rootElement
|
|
983
|
+
|
|
984
|
+
Setting this property will also notify of a view frame change.
|
|
985
|
+
|
|
986
|
+
@field
|
|
987
|
+
*/
|
|
988
|
+
styleHeight: function(key, value) {
|
|
989
|
+
if (value !== undefined) {
|
|
990
|
+
this.viewFrameWillChange() ;
|
|
991
|
+
this.setStyle({ height: ((value != null) ? value+'px' : 'auto') }) ;
|
|
992
|
+
this.viewFrameDidChange() ;
|
|
993
|
+
}
|
|
994
|
+
var ret = this.getStyle('height') ;
|
|
995
|
+
return (ret == 'auto') ? null : parseInt(ret, 0) ;
|
|
996
|
+
}.property(),
|
|
997
|
+
|
|
998
|
+
/**
|
|
999
|
+
Style-top for the views rootElement
|
|
1000
|
+
|
|
1001
|
+
Setting this property will also notify of a view frame change.
|
|
1002
|
+
|
|
1003
|
+
@field
|
|
1004
|
+
*/
|
|
1005
|
+
styleTop: function(key, value) {
|
|
1006
|
+
if (value !== undefined) {
|
|
1007
|
+
this.viewFrameWillChange() ;
|
|
1008
|
+
this.setStyle({ top: ((value != null) ? value+'px' : 'auto') }) ;
|
|
1009
|
+
this.viewFrameDidChange() ;
|
|
1010
|
+
}
|
|
1011
|
+
var ret = this.getStyle('top') ;
|
|
1012
|
+
return (ret == 'auto') ? null : parseInt(ret, 0) ;
|
|
1013
|
+
}.property(),
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
Style-bottom for the views rootElement
|
|
1017
|
+
|
|
1018
|
+
Setting this property will also notify of a view frame change.
|
|
1019
|
+
|
|
1020
|
+
@field
|
|
1021
|
+
*/
|
|
1022
|
+
styleBottom: function(key, value) {
|
|
1023
|
+
if (value !== undefined) {
|
|
1024
|
+
this.viewFrameWillChange() ;
|
|
1025
|
+
this.setStyle({ bottom: ((value != null) ? value+'px' : 'auto') }) ;
|
|
1026
|
+
this.viewFrameDidChange() ;
|
|
1027
|
+
}
|
|
1028
|
+
var ret = this.getStyle('bottom') ;
|
|
1029
|
+
return (ret == 'auto') ? null : parseInt(ret, 0) ;
|
|
1030
|
+
}.property(),
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
Style-left for the views rootElement
|
|
1034
|
+
|
|
1035
|
+
Setting this property will also notify of a view frame change.
|
|
1036
|
+
|
|
1037
|
+
@field
|
|
1038
|
+
*/
|
|
1039
|
+
styleLeft: function(key, value) {
|
|
1040
|
+
if (value !== undefined) {
|
|
1041
|
+
this.viewFrameWillChange() ;
|
|
1042
|
+
this.setStyle({ left: ((value != null) ? value+'px' : 'auto') }) ;
|
|
1043
|
+
this.viewFrameDidChange() ;
|
|
1044
|
+
}
|
|
1045
|
+
var ret = this.getStyle('left') ;
|
|
1046
|
+
return (ret == 'auto') ? null : parseInt(ret, 0) ;
|
|
1047
|
+
}.property(),
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
Style-right for the views rootElement
|
|
1051
|
+
|
|
1052
|
+
Setting this property will also notify of a view frame change.
|
|
1053
|
+
|
|
1054
|
+
@field
|
|
1055
|
+
*/
|
|
1056
|
+
styleRight: function(key, value) {
|
|
1057
|
+
if (value !== undefined) {
|
|
1058
|
+
this.viewFrameWillChange() ;
|
|
1059
|
+
this.setStyle({ right: ((value != null) ? value+'px' : 'auto') }) ;
|
|
1060
|
+
this.viewFrameDidChange() ;
|
|
1061
|
+
}
|
|
1062
|
+
var ret = this.getStyle('right') ;
|
|
1063
|
+
return (ret == 'auto') ? null : parseInt(ret, 0) ;
|
|
1064
|
+
}.property(),
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
Call this method before you make a change that will impact the frame of
|
|
1069
|
+
the view such as changing the border thickness or adding/removing a CSS
|
|
1070
|
+
style.
|
|
1071
|
+
|
|
1072
|
+
Once you finish making your changes, be sure to call viewFrameDidChange()
|
|
1073
|
+
as well. This will deliver any relevant resizing and other notifications.
|
|
1074
|
+
It is safe to nest multiple calls to this method.
|
|
1075
|
+
|
|
1076
|
+
This method is called automatically anytime you set the frame.
|
|
1077
|
+
|
|
1078
|
+
@returns {void}
|
|
1079
|
+
*/
|
|
1080
|
+
viewFrameWillChange: function() {
|
|
1081
|
+
if (this._frameChangeLevel++ <= 0) {
|
|
1082
|
+
this._frameChangeLevel = 1 ;
|
|
1083
|
+
|
|
1084
|
+
// save frame information if view has manual layout.
|
|
1085
|
+
if (this.get('needsFrameChanges')) {
|
|
1086
|
+
this._cachedFrames = this.getEach('innerFrame', 'clippingFrame', 'frame') ;
|
|
1087
|
+
} else this._cachedFrames = null ;
|
|
1088
|
+
this.beginPropertyChanges(); // suspend change notifications
|
|
1089
|
+
}
|
|
1090
|
+
},
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
Call this method just after you finish making changes that will impace the frame
|
|
1094
|
+
of the view such as changing the border thickness or adding/removing a CSS style.
|
|
1095
|
+
|
|
1096
|
+
It is safe to next multiple calls to this method. This method is called automatically
|
|
1097
|
+
anytime you set the frame.
|
|
1098
|
+
|
|
1099
|
+
@returns {void}
|
|
1100
|
+
*/
|
|
1101
|
+
viewFrameDidChange: function(force) {
|
|
1102
|
+
|
|
1103
|
+
// clear the frame caches
|
|
1104
|
+
this.recacheFrames() ;
|
|
1105
|
+
|
|
1106
|
+
// if this is a top-level call then also deliver notifications as needed.
|
|
1107
|
+
if (--this._frameChangeLevel <= 0) {
|
|
1108
|
+
this._frameChangeLevel = 0 ;
|
|
1109
|
+
if (this._cachedFrames) {
|
|
1110
|
+
var newFrames = this.getEach('innerFrame', 'clippingFrame') ;
|
|
1111
|
+
|
|
1112
|
+
// notify if clippingFrame has changed and clippingFrameDidChange is
|
|
1113
|
+
// implemented.
|
|
1114
|
+
var nf = newFrames[1]; var of = this._cachedFrames[1] ;
|
|
1115
|
+
if (force || (nf.width != of.width) || (nf.height != of.height)) {
|
|
1116
|
+
this._invalidateClippingFrame() ;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// notify children if the size of the innerFrame has changed.
|
|
1120
|
+
var nf = newFrames[0]; var of = this._cachedFrames[0] ;
|
|
1121
|
+
if (force || (nf.width != of.width) || (nf.height != of.height)) {
|
|
1122
|
+
this.resizeChildrenWithOldSize(this._cachedFrames.last()) ;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// clear parent scrollFrame if needed
|
|
1126
|
+
var parent = this.parentNode ;
|
|
1127
|
+
while (parent && parent != SC.window) {
|
|
1128
|
+
if (parent._scrollFrame) parent._scrollFrame = null ;
|
|
1129
|
+
parent = parent.parentNode ;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
this.notifyPropertyChange('frame') ; // trigger notifications.
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// allow notifications again
|
|
1136
|
+
this.endPropertyChanges() ;
|
|
1137
|
+
}
|
|
1138
|
+
},
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
Clears any cached frames so the next get will recompute them.
|
|
1143
|
+
|
|
1144
|
+
This method does not notify any observers of changes to the frames. It should
|
|
1145
|
+
only be used when you need to make sure your frame info is up to date but you do
|
|
1146
|
+
not expect anything to have happened that frame observers would be interested in.
|
|
1147
|
+
*/
|
|
1148
|
+
recacheFrames: function() {
|
|
1149
|
+
this._innerFrame = this._frame = this._clippingFrame = this._scrollFrame = null ;
|
|
1150
|
+
},
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
Set to true if you expect this view to have scrollable content.
|
|
542
1154
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
padding = 0 ;
|
|
547
|
-
}
|
|
548
|
-
style.paddingBottom = Math.floor(padding).toString() + 'px' ;
|
|
549
|
-
}
|
|
1155
|
+
Normally views do not monitor their onscroll event. If you set this property to true,
|
|
1156
|
+
however, the view will observe its onscroll event and update its scrollFrame and
|
|
1157
|
+
clippedFrame.
|
|
550
1158
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
1159
|
+
This will also register the view as a scrollable area that can be auto-scrolled during
|
|
1160
|
+
a drag/drop event.
|
|
1161
|
+
*/
|
|
1162
|
+
isScrollable: false,
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
The frame used to control scrolling of content.
|
|
1166
|
+
|
|
1167
|
+
x,y => offset from the innerFrame root.
|
|
1168
|
+
width,height => total size of the frame
|
|
1169
|
+
|
|
1170
|
+
If the frame does not have scrollable content, then the size will be equal to the
|
|
1171
|
+
innerFrame size.
|
|
1172
|
+
|
|
1173
|
+
This frame changes when:
|
|
1174
|
+
- the receiver's innerFrame changes
|
|
1175
|
+
- the scroll location is changed programatically
|
|
1176
|
+
- the size of child views changes
|
|
1177
|
+
- the user scrolls the view
|
|
1178
|
+
|
|
1179
|
+
@field
|
|
1180
|
+
*/
|
|
1181
|
+
scrollFrame: function(key, value) {
|
|
562
1182
|
|
|
563
|
-
|
|
564
|
-
|
|
1183
|
+
// if value was passed, update the scroll x,y only.
|
|
1184
|
+
if (value != undefined) {
|
|
1185
|
+
var el = this.rootElement ;
|
|
1186
|
+
if (value.x != null) el.scrollLeft = 0-value.x ;
|
|
1187
|
+
if (value.y != null) el.scrollTop = 0-value.y ;
|
|
1188
|
+
this._scrollFrame = null ;
|
|
1189
|
+
this._invalidateClippingFrame() ;
|
|
565
1190
|
}
|
|
1191
|
+
|
|
1192
|
+
// build frame. We can use a cached version but only
|
|
1193
|
+
var f;
|
|
1194
|
+
if (this._scrollFrame == null) {
|
|
1195
|
+
var el = this.rootElement ;
|
|
1196
|
+
f = this._collectFrame(function() {
|
|
1197
|
+
return {
|
|
1198
|
+
x: 0 - el.scrollLeft,
|
|
1199
|
+
y: 0 - el.scrollTop,
|
|
1200
|
+
width: el.scrollWidth,
|
|
1201
|
+
height: el.scrollHeight
|
|
1202
|
+
};
|
|
1203
|
+
}) ;
|
|
1204
|
+
|
|
1205
|
+
// cache this frame if using manual layout mode
|
|
1206
|
+
this._scrollFrame = SC.cloneRect(f);
|
|
1207
|
+
} else f = SC.cloneRect(this._scrollFrame) ;
|
|
566
1208
|
|
|
567
1209
|
// finally return the frame.
|
|
568
|
-
return f ;
|
|
1210
|
+
return f ;
|
|
569
1211
|
}.property('frame'),
|
|
570
1212
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
this.set('innerFrame',{ width: value.width, height: value.height }) ;
|
|
574
|
-
}
|
|
575
|
-
return this.get('innerFrame') ;
|
|
576
|
-
}.property('innerFrame'),
|
|
577
|
-
|
|
578
|
-
innerOrigin: function(key, value) {
|
|
579
|
-
if (value !== undefined) {
|
|
580
|
-
this.set('innerFrame',{ x: value.x, y: value.y }) ;
|
|
581
|
-
}
|
|
582
|
-
return this.get('innerFrame') ;
|
|
583
|
-
}.property('innerFrame'),
|
|
584
|
-
|
|
585
|
-
// This property identifies the height and offset of your view with
|
|
586
|
-
// respect to the parent view and its bounds. To resize your view, edit
|
|
587
|
-
// this property.
|
|
588
|
-
//
|
|
589
|
-
// This method is carefully constructed to use the computed CSS style
|
|
590
|
-
// until you actually override it by setting your own size and location
|
|
591
|
-
// at which point it will use its own settings.
|
|
592
|
-
frame: function(key, value) {
|
|
593
|
-
|
|
594
|
-
// build frame
|
|
595
|
-
var el = this.rootElement ;
|
|
596
|
-
var f = Object.clone(this._frame) ;
|
|
597
|
-
if (f.x === undefined) f.x = el.offsetLeft ;
|
|
598
|
-
if (f.y === undefined) f.y = el.offsetTop ;
|
|
599
|
-
|
|
600
|
-
// get the current size if needed.
|
|
601
|
-
var size ;
|
|
602
|
-
if ((f.width === undefined) || (f.height === undefined)) {
|
|
603
|
-
var isVisibleInWindow = this.get('isVisibleInWindow') ;
|
|
604
|
-
|
|
605
|
-
// if not visible in window, move parent node into window and get
|
|
606
|
-
// dim and offset. If the element has no parentNode, then just move
|
|
607
|
-
// the element in.
|
|
608
|
-
if (!isVisibleInWindow) {
|
|
609
|
-
var pn = el.parentNode || el ;
|
|
610
|
-
var pnParent = pn.parentNode ;
|
|
611
|
-
var pnSib = pn.nextSibling ;
|
|
612
|
-
SC.window.rootElement.insertBefore(pn, null) ;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
size = Element.getDimensions(el);
|
|
616
|
-
f.width = size.width ;
|
|
617
|
-
f.height = size.height;
|
|
618
|
-
|
|
619
|
-
if (!isVisibleInWindow) {
|
|
620
|
-
if (pnParent) {
|
|
621
|
-
pnParent.insertBefore(pn, pnSib) ;
|
|
622
|
-
} else SC.window.removeChild(pn) ;
|
|
623
|
-
}
|
|
624
|
-
} else size = f ;
|
|
625
|
-
|
|
626
|
-
// now update the frame if needed. Only actually change the style for
|
|
627
|
-
// those parts of the frame that were passed in.
|
|
628
|
-
if (value !== undefined) {
|
|
629
|
-
var style = {} ;
|
|
630
|
-
var didResize = false ;
|
|
1213
|
+
/**
|
|
1214
|
+
The visible portion of the view.
|
|
631
1215
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
1216
|
+
Returns the subset of the receivers frame that is actually visible on screen.
|
|
1217
|
+
This frame is automatically updated whenever one of the following changes:
|
|
1218
|
+
|
|
1219
|
+
- A parent view is resized
|
|
1220
|
+
- A parent view's scrollFrame changes.
|
|
1221
|
+
- The receiver is moved or resized
|
|
1222
|
+
- The receiver or a parent view is added to or removed from the window.
|
|
1223
|
+
|
|
1224
|
+
@field
|
|
1225
|
+
*/
|
|
1226
|
+
clippingFrame: function() {
|
|
1227
|
+
var f ;
|
|
1228
|
+
if (this._clippingFrame == null) {
|
|
637
1229
|
|
|
638
|
-
//
|
|
639
|
-
if (value.y !== undefined) {
|
|
640
|
-
f.y = value.y ;
|
|
641
|
-
style.top = Math.floor(f.y) + 'px' ;
|
|
642
|
-
}
|
|
1230
|
+
//if (this instanceof SC.SplitView) debugger ;
|
|
643
1231
|
|
|
644
|
-
//
|
|
645
|
-
|
|
646
|
-
didResize = true ;
|
|
647
|
-
f.width = value.width ;
|
|
648
|
-
var padding = parseInt(this.getStyle('padding-left'),0) + parseInt(this.getStyle('padding-right'),0) ;
|
|
649
|
-
style.width = (Math.floor(f.width) - padding).toString() + 'px' ;
|
|
650
|
-
}
|
|
1232
|
+
// my clipping frame is usually my frame
|
|
1233
|
+
f = this.get('frame') ;
|
|
651
1234
|
|
|
652
|
-
//
|
|
653
|
-
if (
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
var
|
|
657
|
-
|
|
1235
|
+
// scope to my parents clipping frame.
|
|
1236
|
+
if (this.parentNode) {
|
|
1237
|
+
|
|
1238
|
+
// use only the visible portion of the parent's innerFrame.
|
|
1239
|
+
var parent = this.parentNode ;
|
|
1240
|
+
var prect = SC.intersectRects(parent.get('clippingFrame'), parent.get('innerFrame'));
|
|
1241
|
+
|
|
1242
|
+
// convert the local view's coordinates
|
|
1243
|
+
prect = this.convertFrameFromView(prect, parent) ;
|
|
1244
|
+
|
|
1245
|
+
// if parent is scrollable, then adjust by scroll frame also.
|
|
1246
|
+
if (this.parentNode.get('isScrollable')) {
|
|
1247
|
+
var scrollFrame = this.get('scrollFrame') ;
|
|
1248
|
+
prect.x -= scrollFrame.x ;
|
|
1249
|
+
prect.y -= scrollFrame.y ;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// blend with current frame
|
|
1253
|
+
f = SC.intersectRects(f, prect) ;
|
|
1254
|
+
} else {
|
|
1255
|
+
f.width = f.height = 0 ;
|
|
658
1256
|
}
|
|
1257
|
+
|
|
1258
|
+
this._clippingFrame = SC.cloneRect(f) ;
|
|
659
1259
|
|
|
660
|
-
|
|
661
|
-
this.setStyle(style) ;
|
|
662
|
-
this._frame = Object.clone(f) ;
|
|
663
|
-
|
|
664
|
-
// also notify children so they can resize also.
|
|
665
|
-
if (didResize) this.resizeChildrenWithOldSize(size) ;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// finally return the frame.
|
|
1260
|
+
} else f = SC.cloneRect(this._clippingFrame) ;
|
|
669
1261
|
return f ;
|
|
670
|
-
}.property('
|
|
671
|
-
|
|
672
|
-
size: function(key, value) {
|
|
673
|
-
if (value !== undefined) {
|
|
674
|
-
this.set('frame',{ width: value.width, height: value.height }) ;
|
|
675
|
-
}
|
|
676
|
-
return this.get('frame') ;
|
|
677
|
-
}.property('frame'),
|
|
1262
|
+
}.property('frame', 'scrollFrame'),
|
|
678
1263
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
1264
|
+
/**
|
|
1265
|
+
Called whenever the receivers clippingFrame has changed. You can override this
|
|
1266
|
+
method to perform partial rendering or other clippingFrame-dependent actions.
|
|
1267
|
+
|
|
1268
|
+
The default implementation does nothing (and may not even be called do to optimizations).
|
|
1269
|
+
Note that this is the preferred way to respond to changes in the clippingFrame
|
|
1270
|
+
of using an observer since this method is gauranteed to happen in the correct
|
|
1271
|
+
order. You can use observers and bindings as well if you wish to handle anything
|
|
1272
|
+
that need not be handled synchronously.
|
|
1273
|
+
*/
|
|
1274
|
+
clippingFrameDidChange: function() {
|
|
1275
|
+
|
|
1276
|
+
},
|
|
685
1277
|
|
|
686
1278
|
/**
|
|
687
|
-
|
|
1279
|
+
Called whenever the view's innerFrame size changes. You can override this
|
|
1280
|
+
method to perform your own layout of your child views.
|
|
1281
|
+
|
|
1282
|
+
If you do not override this method, the view will assume you are using
|
|
1283
|
+
CSS to layout your child views. As an optimization the view may not always
|
|
1284
|
+
call this method if it determines that you have not overridden it.
|
|
688
1285
|
|
|
689
|
-
This
|
|
690
|
-
|
|
691
|
-
the scrollFrame. Any changes to height and width will be ignored.
|
|
1286
|
+
This default version simply calls resizeWithOldParentSize() on all of its
|
|
1287
|
+
children.
|
|
692
1288
|
|
|
693
|
-
@
|
|
1289
|
+
@param oldSize {Size} The old frame size of the view.
|
|
1290
|
+
@returns {void}
|
|
694
1291
|
*/
|
|
695
|
-
scrollFrame: function(key, value) {
|
|
696
|
-
var el = this.rootElement ;
|
|
697
|
-
if (value !== undefined) {
|
|
698
|
-
el.scrollTop = value.y ;
|
|
699
|
-
el.scrollLeft = value.x ;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
return { x: el.scrollLeft, y: el.scrollTop, height: el.scrollHeight, width: el.scrollWidth } ;
|
|
703
|
-
}.property('frame'),
|
|
704
|
-
|
|
705
|
-
// called on the view when you need to resize your child views. Normally
|
|
706
|
-
// this will call resizeWithOldParentSize() on the child views, but you
|
|
707
|
-
// can override this to do whatever funky layout to want.
|
|
708
1292
|
resizeChildrenWithOldSize: function(oldSize) {
|
|
709
1293
|
var child = this.get('firstChild') ;
|
|
710
1294
|
while(child) {
|
|
@@ -712,117 +1296,234 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
712
1296
|
child = child.get('nextSibling') ;
|
|
713
1297
|
}
|
|
714
1298
|
},
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
Called whenever the parent's innerFrame size has changed. You can
|
|
1302
|
+
override this method to change how your view responds to this change.
|
|
1303
|
+
|
|
1304
|
+
If you do not override this method, the view will assume you are using CSS
|
|
1305
|
+
to control your layout and it will simply relay the change information to
|
|
1306
|
+
your child views. As an optmization, the view may not always call this
|
|
1307
|
+
method if it determines that you have not overridden it.
|
|
1308
|
+
|
|
1309
|
+
@param oldSize {Size} The old frame size of the parent view.
|
|
1310
|
+
*/
|
|
720
1311
|
resizeWithOldParentSize: function(oldSize) {
|
|
721
|
-
|
|
1312
|
+
this.viewFrameWillChange() ;
|
|
1313
|
+
this.viewFrameDidChange(YES) ;
|
|
1314
|
+
},
|
|
1315
|
+
|
|
1316
|
+
/** @private
|
|
1317
|
+
Handler for the onscroll event. Hooked in on init if isScrollable is true.
|
|
1318
|
+
Notify children that their clipping frame has changed.
|
|
1319
|
+
*/
|
|
1320
|
+
_onscroll: function() {
|
|
1321
|
+
this._scrollFrame = null ;
|
|
1322
|
+
this.notifyPropertyChange('scrollFrame') ;
|
|
1323
|
+
SC.Benchmark.start('%@.onscroll'.fmt(this)) ;
|
|
1324
|
+
this._invalidateClippingFrame() ;
|
|
1325
|
+
SC.Benchmark.end('%@.onscroll'.fmt(this)) ;
|
|
1326
|
+
},
|
|
1327
|
+
|
|
1328
|
+
_frameChangeLevel: 0,
|
|
1329
|
+
|
|
1330
|
+
/** @private
|
|
1331
|
+
Used internally to collect client offset and location info. If the element is
|
|
1332
|
+
not in the main window or hidden, it will be added temporarily and then the passed
|
|
1333
|
+
function will be called.
|
|
1334
|
+
*/
|
|
1335
|
+
_collectFrame: function(func) {
|
|
1336
|
+
var el = this.rootElement ;
|
|
722
1337
|
|
|
723
|
-
// if
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
1338
|
+
// if not visible in window, move parent node into window and get
|
|
1339
|
+
// dim and offset. If the element has no parentNode, then just move
|
|
1340
|
+
// the element in.
|
|
1341
|
+
var isVisibleInWindow = this.get('isVisibleInWindow') ;
|
|
1342
|
+
if (!isVisibleInWindow) {
|
|
1343
|
+
var pn = el.parentNode || el ;
|
|
1344
|
+
var pnParent = pn.parentNode ; // cache former parent node
|
|
1345
|
+
var pnSib = pn.nextSibling ; // cache next sibling
|
|
1346
|
+
SC.window.rootElement.insertBefore(pn, null) ;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// if view is not displayed, temporarily display it also
|
|
1350
|
+
var display = this.getStyle('display') ;
|
|
1351
|
+
var isHidden = !(display != 'none' && display != null) ;
|
|
1352
|
+
|
|
1353
|
+
// All *Width and *Height properties give 0 on elements with display none,
|
|
1354
|
+
// so enable the element temporarily
|
|
1355
|
+
if (isHidden) {
|
|
1356
|
+
var els = this.rootElement.style;
|
|
1357
|
+
var originalVisibility = els.visibility;
|
|
1358
|
+
var originalPosition = els.position;
|
|
1359
|
+
var originalDisplay = els.display;
|
|
1360
|
+
els.visibility = 'hidden';
|
|
1361
|
+
els.position = 'absolute';
|
|
1362
|
+
els.display = 'block';
|
|
730
1363
|
}
|
|
731
1364
|
|
|
732
|
-
|
|
733
|
-
// notify the children also.
|
|
734
|
-
if (this.get('isPositioned')) this.set('isPositioned',true) ;
|
|
1365
|
+
var ret = func.call(this) ;
|
|
735
1366
|
|
|
736
|
-
|
|
737
|
-
|
|
1367
|
+
if (isHidden) {
|
|
1368
|
+
els.display = originalDisplay;
|
|
1369
|
+
els.position = originalPosition;
|
|
1370
|
+
els.visibility = originalVisibility;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
if (!isVisibleInWindow) {
|
|
1374
|
+
if (pnParent) {
|
|
1375
|
+
pnParent.insertBefore(pn, pnSib) ;
|
|
1376
|
+
} else SC.window.rootElement.removeChild(pn) ;
|
|
1377
|
+
}
|
|
738
1378
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
// next, subtract the dimensions of fixed elements from the old and
|
|
747
|
-
// new sizes.
|
|
748
|
-
for(loc=0;loc < 3;loc++) {
|
|
749
|
-
if (opts[props[loc]] != SC.FLEXIBLE) {
|
|
750
|
-
newSize -= dims[loc]; oldSize -= dims[loc] ;
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
// finally, adjust the flexible area as a percentage of the limited
|
|
755
|
-
// dimensions.
|
|
756
|
-
for(loc=0;loc < 2; loc++) {
|
|
757
|
-
if (opts[props[loc]] == SC.FLEXIBLE) {
|
|
758
|
-
f[apts[loc]] = newSize * dims[loc] / oldSize ;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
};
|
|
1379
|
+
return ret;
|
|
1380
|
+
},
|
|
1381
|
+
|
|
1382
|
+
/** @private
|
|
1383
|
+
Called whenever some aspect of the receiver's frames have changed that
|
|
1384
|
+
probably has invalidated the child views clippingFrames. Events that cause
|
|
1385
|
+
this include:
|
|
762
1386
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1387
|
+
- change to the innerFrame size
|
|
1388
|
+
- change to the scrollFrame
|
|
1389
|
+
- change to the clippingFrame
|
|
1390
|
+
|
|
1391
|
+
For performance reasons, this only passes onto children if they or a decendent
|
|
1392
|
+
implements the clippingFrameDidChange method.
|
|
1393
|
+
*/
|
|
1394
|
+
_invalidateChildrenClippingFrames: function() {
|
|
1395
|
+
var view = this.get('firstChild') ;
|
|
1396
|
+
while(view) {
|
|
1397
|
+
view._invalidateClippingFrame() ;
|
|
1398
|
+
view = view.get('nextSibling') ;
|
|
1399
|
+
}
|
|
1400
|
+
},
|
|
767
1401
|
|
|
768
|
-
|
|
1402
|
+
/** @private
|
|
1403
|
+
Called by a parentNode whenever the clippingFrame needs to be recalculated.
|
|
1404
|
+
*/
|
|
1405
|
+
_invalidateClippingFrame: function() {
|
|
1406
|
+
if (this.get('needsClippingFrame')) {
|
|
1407
|
+
this._clippingFrame = null ;
|
|
1408
|
+
this.clippingFrameDidChange() ;
|
|
1409
|
+
this.notifyPropertyChange('clippingFrame') ;
|
|
1410
|
+
this._invalidateChildrenClippingFrames() ;
|
|
1411
|
+
}
|
|
769
1412
|
},
|
|
770
1413
|
|
|
771
|
-
// These properties are provide simple control for autoresizing. If you
|
|
772
|
-
// set these, then resizeWithOldParentSize() will autoresize for you.
|
|
773
|
-
// The allowed options are: SC.FLEXIBLE, SC.FIXED.
|
|
774
|
-
resizeOptions: null,
|
|
775
|
-
|
|
776
1414
|
// ..........................................
|
|
777
1415
|
// PROPERTIES
|
|
778
1416
|
//
|
|
779
1417
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1418
|
+
/**
|
|
1419
|
+
Makes the view visible.
|
|
1420
|
+
|
|
1421
|
+
If false, sets display: none on the DOM element as well. You will
|
|
1422
|
+
often want to bind this property to some setting in your application
|
|
1423
|
+
to make various parts of your app visible as needed.
|
|
1424
|
+
|
|
1425
|
+
If you have animation enabled, then changing this property will actually
|
|
1426
|
+
trigger the animation to bring the view in or out.
|
|
1427
|
+
|
|
1428
|
+
The default binding format is SC.Binding.Bool
|
|
1429
|
+
|
|
1430
|
+
@property
|
|
1431
|
+
@type Boolean
|
|
1432
|
+
*/
|
|
787
1433
|
isVisible: true,
|
|
788
|
-
isVisibleBindingDefault: SC.Binding.Flag,
|
|
789
1434
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
1435
|
+
/** @private */
|
|
1436
|
+
isVisibleBindingDefault: SC.Binding.Bool,
|
|
1437
|
+
|
|
1438
|
+
/**
|
|
1439
|
+
(Read Only) The current display visibility of the view.
|
|
1440
|
+
|
|
1441
|
+
Usually, this property will mirror the current state of the isVisible
|
|
1442
|
+
property. However, if your view animates its visibility in and out, then
|
|
1443
|
+
this will not become false until the animation completes.
|
|
1444
|
+
|
|
1445
|
+
@type {Boolean}
|
|
1446
|
+
*/
|
|
794
1447
|
displayIsVisible: true,
|
|
795
1448
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1449
|
+
/**
|
|
1450
|
+
true when the view is actually visible in the DOM window.
|
|
1451
|
+
|
|
1452
|
+
This property is set to true only when the view is (a) in the main DOM
|
|
1453
|
+
hierarchy and (b) all parent nodes are visible and (c) the receiver node
|
|
1454
|
+
is visible.
|
|
1455
|
+
|
|
1456
|
+
@type {Boolean}
|
|
1457
|
+
@property
|
|
1458
|
+
*/
|
|
1459
|
+
isVisibleInWindow: YES,
|
|
800
1460
|
|
|
801
|
-
|
|
1461
|
+
/**
|
|
1462
|
+
If true, the tooltip will be localized. Also used by some subclasses.
|
|
1463
|
+
|
|
1464
|
+
@type {Boolean}
|
|
1465
|
+
@property
|
|
1466
|
+
*/
|
|
802
1467
|
localize: false,
|
|
803
|
-
|
|
804
|
-
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
Applied to the title attribute of the rootElement DOM if set.
|
|
1471
|
+
|
|
1472
|
+
If localize is true, then the toolTip will be localized first.
|
|
1473
|
+
|
|
1474
|
+
@type {String}
|
|
1475
|
+
@property
|
|
1476
|
+
*/
|
|
805
1477
|
toolTip: '',
|
|
806
1478
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
1479
|
+
|
|
1480
|
+
/**
|
|
1481
|
+
The HTML you want to use when creating a new element.
|
|
1482
|
+
|
|
1483
|
+
You can specify the HTML as a string of text, using the NodeDescriptor, or
|
|
1484
|
+
by pointing directly to an element.
|
|
1485
|
+
|
|
1486
|
+
Note that as an optimization, SC.View will actually convert the value of this
|
|
1487
|
+
property to an actual DOM structure the first time you create a view and then
|
|
1488
|
+
clone the DOM structure for future views.
|
|
1489
|
+
|
|
1490
|
+
This means that in general you should only set the value of emptyElement when
|
|
1491
|
+
you create a view subclass. Changing this property value at other times will
|
|
1492
|
+
often have no effect.
|
|
1493
|
+
|
|
1494
|
+
@property
|
|
1495
|
+
@type {String}
|
|
1496
|
+
*/
|
|
810
1497
|
emptyElement: "<div></div>",
|
|
811
1498
|
|
|
812
|
-
|
|
1499
|
+
/**
|
|
1500
|
+
If true, view will display in a lightbox when you show it.
|
|
1501
|
+
|
|
1502
|
+
@property
|
|
1503
|
+
@type {Boolean}
|
|
1504
|
+
*/
|
|
813
1505
|
isPanel: false,
|
|
814
1506
|
|
|
815
|
-
|
|
1507
|
+
/**
|
|
1508
|
+
If true, the view should be modal when shown as a panel.
|
|
1509
|
+
|
|
1510
|
+
@property
|
|
1511
|
+
@type {Boolean}
|
|
1512
|
+
*/
|
|
816
1513
|
isModal: true,
|
|
817
1514
|
|
|
818
|
-
|
|
1515
|
+
/**
|
|
1516
|
+
Enable visible animation by default.
|
|
1517
|
+
*/
|
|
819
1518
|
isAnimationEnabled: true,
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1519
|
+
|
|
1520
|
+
/**
|
|
1521
|
+
General support for animation. Just call this method and it will build
|
|
1522
|
+
and play an animation starting from the current state. The second param
|
|
1523
|
+
is optional. It should either be a hash of animator options or an
|
|
1524
|
+
animator object returned by a previous call to transitionTo().
|
|
1525
|
+
|
|
1526
|
+
*/
|
|
826
1527
|
transitionTo: function(target,animator,opts) {
|
|
827
1528
|
var animatorOptions = opts || {} ;
|
|
828
1529
|
|
|
@@ -848,159 +1549,91 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
848
1549
|
}
|
|
849
1550
|
return animator ;
|
|
850
1551
|
},
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1552
|
+
|
|
1553
|
+
/**
|
|
1554
|
+
The contents of the view as HTML. You can use this property to both
|
|
1555
|
+
retrieve the content and to change it. Use this property instead of
|
|
1556
|
+
manually changing the content of your view as this property works around
|
|
1557
|
+
certain cross-browser bugs.
|
|
1558
|
+
|
|
1559
|
+
@field
|
|
1560
|
+
*/
|
|
1561
|
+
innerHTML: function(key, value) {
|
|
855
1562
|
if (value !== undefined) {
|
|
1563
|
+
|
|
1564
|
+
// Clear the text node.
|
|
1565
|
+
this._textNode = null ;
|
|
1566
|
+
|
|
856
1567
|
// Safari2 has a bad habit of sometimes not actually changing its
|
|
857
1568
|
// innerHTML. This will make sure the innerHTML get's changed properly.
|
|
858
1569
|
if (SC.isSafari() && !SC.isSafari3()) {
|
|
859
1570
|
var el = (this.containerElement || this.rootElement) ; var reps = 0 ;
|
|
860
1571
|
var f = function() {
|
|
861
1572
|
el.innerHTML = '' ; el.innerHTML = value ;
|
|
862
|
-
if ((reps++ < 5) && (value.length>0) && (el.innerHTML == ''))
|
|
1573
|
+
if ((reps++ < 5) && (value.length>0) && (el.innerHTML == '')) {
|
|
1574
|
+
f.invokeLater() ;
|
|
1575
|
+
}
|
|
863
1576
|
};
|
|
864
1577
|
f();
|
|
865
1578
|
} else (this.containerElement || this.rootElement).innerHTML = value;
|
|
866
1579
|
} else value = (this.containerElement || this.rootElement).innerHTML ;
|
|
867
1580
|
return value ;
|
|
868
1581
|
}.property(),
|
|
1582
|
+
|
|
1583
|
+
/**
|
|
1584
|
+
The contents of the view as plain text. You can use this property to
|
|
1585
|
+
both retrieve the content and to change it. Use this property instead of
|
|
1586
|
+
the innerHTML property when you want to set plain text only as this
|
|
1587
|
+
property is much faster.
|
|
869
1588
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1589
|
+
@field
|
|
1590
|
+
*/
|
|
1591
|
+
innerText: function(key, value) {
|
|
873
1592
|
if (value !== undefined) {
|
|
874
1593
|
if (value == null) value = '' ;
|
|
875
|
-
this.asHTML(key,value.toString().escapeHTML()) ;
|
|
876
|
-
}
|
|
877
|
-
return this.asHTML().unescapeHTML() ;
|
|
878
|
-
}.property(),
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
//
|
|
884
|
-
// Command methods (used by the command manager)
|
|
885
|
-
//
|
|
886
|
-
|
|
887
|
-
/**
|
|
888
|
-
* Queries to see if the view has function matching the passed name .
|
|
889
|
-
* @param {String} name The name of the function
|
|
890
|
-
* @return Boolean
|
|
891
|
-
**/
|
|
892
|
-
hasNamedFunction: function( name )
|
|
893
|
-
{
|
|
894
|
-
return ( this[name] && ($type(this[name]) == T_FUNCTION) );
|
|
895
|
-
},
|
|
896
|
-
/**
|
|
897
|
-
* Queries to see if the view has a named command.
|
|
898
|
-
* @param {String} name The name of the command
|
|
899
|
-
* @return Boolean
|
|
900
|
-
**/
|
|
901
|
-
hasCommand: function( name )
|
|
902
|
-
{
|
|
903
|
-
return this.hasNamedFunction(name);
|
|
904
|
-
},
|
|
905
|
-
/**
|
|
906
|
-
* Queries to see if the view has a validator for the named command.
|
|
907
|
-
* @param {String} name The name of the command
|
|
908
|
-
* @return Boolean
|
|
909
|
-
**/
|
|
910
|
-
hasCommandValidator: function( name )
|
|
911
|
-
{
|
|
912
|
-
var name = this._commandValidatorForCommand(name);
|
|
913
|
-
return this.hasNamedFunction(name);
|
|
914
|
-
},
|
|
915
1594
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
{
|
|
924
|
-
var hasCommand = this.hasCommand(name);
|
|
925
|
-
var hasCommandValidator = this.hasCommandValidator(name);
|
|
926
|
-
// can't execute what you haven't got...
|
|
927
|
-
if ( !hasCommand ) return false;
|
|
928
|
-
// got it and not validating before usage...
|
|
929
|
-
if ( hasCommand && !hasCommandValidator ) return true;
|
|
930
|
-
// ok... we got it, and we need to check before using...
|
|
931
|
-
if ( hasCommand && hasCommandValidator )
|
|
932
|
-
{
|
|
933
|
-
return this.executeCommandValidator(name);
|
|
1595
|
+
// add a textNode if necessary
|
|
1596
|
+
if (this._textNode == null) {
|
|
1597
|
+
this._textNode = document.createTextNode(value) ;
|
|
1598
|
+
var el = this.rootElement || this.containerElement ;
|
|
1599
|
+
while(el.firstChild) el.removeChild(el.firstChild) ;
|
|
1600
|
+
el.appendChild(this._textNode) ;
|
|
1601
|
+
} else this._textNode.data = value ;
|
|
934
1602
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
**/
|
|
941
|
-
executeCommand: function( name )
|
|
942
|
-
{
|
|
943
|
-
return this.canExecuteCommand(name) ? this.executeCommandWithoutValidation(name) : false;
|
|
944
|
-
},
|
|
945
|
-
/**
|
|
946
|
-
* Executes the command without performing any validation.
|
|
947
|
-
* @param {String} name The name of the command
|
|
948
|
-
* @return Boolean The return value of executing the command.
|
|
949
|
-
**/
|
|
950
|
-
executeCommandWithoutValidation: function( name )
|
|
951
|
-
{
|
|
952
|
-
return this[name]();
|
|
953
|
-
},
|
|
954
|
-
/**
|
|
955
|
-
* Executes the command validator.
|
|
956
|
-
* @param {String} name The name of the command to be validated.
|
|
957
|
-
* @return Boolean Wether or not the command can be executed.
|
|
958
|
-
**/
|
|
959
|
-
executeCommandValidator: function( name )
|
|
960
|
-
{
|
|
961
|
-
var name = this._commandValidatorForCommand(name);
|
|
962
|
-
return this[name]();
|
|
963
|
-
},
|
|
964
|
-
|
|
965
|
-
/**
|
|
966
|
-
* Utility to construct the command alidator method name.
|
|
967
|
-
* @private
|
|
968
|
-
* @param {String} name The name of the command
|
|
969
|
-
* @return String
|
|
970
|
-
**/
|
|
971
|
-
_commandValidatorForCommand: function( name )
|
|
972
|
-
{
|
|
973
|
-
return "can" + name.capitalize();
|
|
974
|
-
},
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1603
|
+
|
|
1604
|
+
return (this._textNode) ? this._textNode.data : this.innerHTML().unescapeHTML() ;
|
|
1605
|
+
|
|
1606
|
+
}.property(),
|
|
1607
|
+
|
|
978
1608
|
|
|
979
1609
|
// ..........................................
|
|
980
1610
|
// SUPPORT METHODS
|
|
981
1611
|
//
|
|
982
1612
|
init: function() {
|
|
983
|
-
this._frame = {} ;
|
|
984
1613
|
arguments.callee.base.call(this) ;
|
|
985
1614
|
|
|
986
1615
|
// configure them outlets.
|
|
987
|
-
|
|
988
|
-
if (r) { idtSt = new Date().getTime(); }
|
|
1616
|
+
if (SC.BENCHMARK_CONFIGURE_OUTLETS) SC.Benchmark.start('SC.View.configureOutlets') ;
|
|
989
1617
|
this.configureOutlets() ;
|
|
990
|
-
if (
|
|
1618
|
+
if (SC.BENCHMARK_CONFIGURE_OUTLETS) SC.Benchmark.end('SC.View.configureOutlets') ;
|
|
991
1619
|
|
|
992
1620
|
var toolTip = this.get('toolTip') ;
|
|
993
1621
|
if(toolTip && (toolTip != '')) this._updateToolTipObserver();
|
|
994
|
-
|
|
995
|
-
//
|
|
996
|
-
|
|
997
|
-
// shouldn't be a bottleneck since if containerElement is set, you are likely to need the DOM element at some point.
|
|
998
|
-
if ( this.containerElement && (SC.typeOf(this.containerElement) == T_STRING) )
|
|
999
|
-
{
|
|
1622
|
+
|
|
1623
|
+
// if container element is a string, convert it to an actual DOM element.
|
|
1624
|
+
if (this.containerElement && ($type(this.containerElement) == T_STRING)) {
|
|
1000
1625
|
this.containerElement = this.$sel(this.containerElement);
|
|
1001
1626
|
}
|
|
1002
|
-
|
|
1627
|
+
|
|
1628
|
+
// register as a drop target and scrollable.
|
|
1003
1629
|
if (this.get('isDropTarget')) SC.Drag.addDropTarget(this) ;
|
|
1630
|
+
if (this.get('isScrollable')) SC.Drag.addScrollableView(this) ;
|
|
1631
|
+
|
|
1632
|
+
// add scrollable handler
|
|
1633
|
+
if (this.isScrollable) this.rootElement.onscroll = SC.View._onscroll ;
|
|
1634
|
+
|
|
1635
|
+
// setup isVisibleInWindow ;
|
|
1636
|
+
this.isVisibleInWindow = (this.parentNode) ? this.parentNode.get('isVisibleInWindow') : NO;
|
|
1004
1637
|
},
|
|
1005
1638
|
|
|
1006
1639
|
// this method looks through your outlets array and will try to
|
|
@@ -1014,23 +1647,6 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
1014
1647
|
this.beginPropertyChanges(); // bundle changes
|
|
1015
1648
|
for(var oloc=0;oloc < this.outlets.length;oloc++) {
|
|
1016
1649
|
var view = this.outlet(this.outlets[oloc]) ;
|
|
1017
|
-
|
|
1018
|
-
// if the HTML for the view is already in the DOM, then walk up the
|
|
1019
|
-
// DOM tree to find the first parent element managed by a view (incl
|
|
1020
|
-
// the receiver. Add the view to the list of child views also.
|
|
1021
|
-
if (view && view.rootElement && view.rootElement.parentNode) {
|
|
1022
|
-
var node = view.rootElement.parentNode;
|
|
1023
|
-
var parentView ;
|
|
1024
|
-
while(node && (node != this.rootElement) && !(parentView = $view(node))) node = node.parentNode;
|
|
1025
|
-
if (node == this.rootElement) parentView = this;
|
|
1026
|
-
if (parentView) parentView._insertBefore(view,null,false) ;
|
|
1027
|
-
}
|
|
1028
|
-
this._rebuildChildNodes() ; // this is not done with _insertBefore.
|
|
1029
|
-
|
|
1030
|
-
// update parent state.
|
|
1031
|
-
if (view && view._updateIsVisibleInWindow) {
|
|
1032
|
-
view._updateIsVisibleInWindow() ;
|
|
1033
|
-
}
|
|
1034
1650
|
}
|
|
1035
1651
|
this.endPropertyChanges() ;
|
|
1036
1652
|
},
|
|
@@ -1249,15 +1865,20 @@ SC.View = SC.Responder.extend(SC.PathModule,
|
|
|
1249
1865
|
Event.observe(this.rootElement,methodMap[name],method) ;
|
|
1250
1866
|
}
|
|
1251
1867
|
}
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
toString: function() {
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1868
|
+
}//,
|
|
1869
|
+
|
|
1870
|
+
// toString: function() {
|
|
1871
|
+
// var el = this.rootElement ;
|
|
1872
|
+
// var tagName = (!!el.tagName) ? el.tagName.toLowerCase() : 'document' ;
|
|
1873
|
+
//
|
|
1874
|
+
// var className = el.className ;
|
|
1875
|
+
// className = (className && className.length>0) ? 'class=%@'.fmt(className) : null;
|
|
1876
|
+
//
|
|
1877
|
+
// var idName = el.id ;
|
|
1878
|
+
// idName = (idName && idName.length>0) ? 'id=%@'.fmt(idName) : null;
|
|
1879
|
+
//
|
|
1880
|
+
// return "%@:%@<%@>".fmt(this._type, this._guid, [tagName,idName, className].compact().join(' ')) ;
|
|
1881
|
+
// }
|
|
1261
1882
|
|
|
1262
1883
|
}) ;
|
|
1263
1884
|
|
|
@@ -1349,43 +1970,105 @@ SC.View.mixin({
|
|
|
1349
1970
|
ret.prototype._cachedEmptyElement = null ;
|
|
1350
1971
|
return ret ;
|
|
1351
1972
|
},
|
|
1352
|
-
|
|
1353
|
-
|
|
1973
|
+
|
|
1974
|
+
/**
|
|
1975
|
+
Defines a view as an outlet. This will return an function that
|
|
1976
|
+
can be executed at a later time to actually create itself as an outlet.
|
|
1977
|
+
*/
|
|
1354
1978
|
outletFor: function(path) {
|
|
1355
|
-
var
|
|
1356
|
-
var
|
|
1357
|
-
if (
|
|
1979
|
+
var viewClass = this ; // save the view class
|
|
1980
|
+
var func = function() {
|
|
1981
|
+
if (SC.BENCHMARK_OUTLETS) SC.Benchmark.start("OUTLET(%@)".format(path)) ;
|
|
1358
1982
|
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1983
|
+
// if no path was passed, then create the view from scratch
|
|
1984
|
+
if (path == null) {
|
|
1985
|
+
var ret = viewClass.viewFor(null) ;
|
|
1986
|
+
|
|
1987
|
+
// otherwise, try to find the HTML element identified by the path.
|
|
1988
|
+
// If the element cannot be found in the caller (the owner view), then
|
|
1989
|
+
// search the entire document.
|
|
1990
|
+
} else {
|
|
1991
|
+
var ret = (this.$$sel) ? this.$$sel(path) : $$sel(path) ;
|
|
1992
|
+
|
|
1993
|
+
// if some HTML has been found, then loop through and create views for each
|
|
1994
|
+
// one. Be sure to setup the proper parent view.
|
|
1995
|
+
if (ret) {
|
|
1996
|
+
var owner = this ; var views = [] ;
|
|
1997
|
+
for(var loc=0;loc<ret.length;loc++) {
|
|
1998
|
+
|
|
1999
|
+
// create the new view instance
|
|
2000
|
+
var view = viewClass.viewFor(ret[loc], { owner: owner }) ;
|
|
2001
|
+
|
|
2002
|
+
// if successful, then we need to determine the new parentNode.
|
|
2003
|
+
// then walk up the DOM tree to find the first parent element
|
|
2004
|
+
// managed by a view (including this).
|
|
2005
|
+
//
|
|
2006
|
+
// If a matching view is not found, but the view IS in a DOM
|
|
2007
|
+
// somewhere then make the view a child of either SC.page or
|
|
2008
|
+
// SC.window.
|
|
2009
|
+
//
|
|
2010
|
+
// Add the view to the list of child views also.
|
|
2011
|
+
//
|
|
2012
|
+
if (view && view.rootElement && view.rootElement.parentNode) {
|
|
2013
|
+
var node = view.rootElement.parentNode;
|
|
2014
|
+
var parentView = null ;
|
|
2015
|
+
|
|
2016
|
+
// go up the chain. stop when we find a parent view, or the rootElement
|
|
2017
|
+
// for SC.page.
|
|
2018
|
+
while(node && !parentView) {
|
|
2019
|
+
switch(node) {
|
|
2020
|
+
case this.rootElement:
|
|
2021
|
+
parentView = this;
|
|
2022
|
+
break ;
|
|
2023
|
+
case SC.page.rootElement:
|
|
2024
|
+
parentView = SC.page ;
|
|
2025
|
+
break;
|
|
2026
|
+
case SC.window.rootElement:
|
|
2027
|
+
parentView = SC.window ;
|
|
2028
|
+
default:
|
|
2029
|
+
node = node.parentNode ;
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
// if a parentView was found, then add to parentView.
|
|
2034
|
+
if (parentView) {
|
|
2035
|
+
parentView._insertBefore(view,null,false) ;
|
|
2036
|
+
parentView._rebuildChildNodes() ; // this is not done with _insertBefore.
|
|
2037
|
+
view._updateIsVisibleInWindow();
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
// view is not in a DOM. nothing to do.
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// add to return array
|
|
2044
|
+
views[views.length] = view ;
|
|
2045
|
+
|
|
2046
|
+
}
|
|
2047
|
+
ret = views ;
|
|
2048
|
+
ret = (ret.length == 0) ? null : ((ret.length == 1) ? ret[0] : ret);
|
|
1366
2049
|
}
|
|
1367
|
-
|
|
1368
|
-
ret = (ret.length == 0) ? null : ((ret.length == 1) ? ret[0] : ret);
|
|
2050
|
+
|
|
1369
2051
|
}
|
|
1370
|
-
|
|
2052
|
+
|
|
2053
|
+
if (SC.BENCHMARK_OUTLETS) SC.Benchmark.end("OUTLET(%@)".format(path)) ;
|
|
1371
2054
|
return ret ;
|
|
1372
2055
|
} ;
|
|
1373
|
-
|
|
1374
|
-
var func ;
|
|
1375
|
-
if (BENCHMARK_OUTLETS) {
|
|
1376
|
-
func = function() {
|
|
1377
|
-
var that = this ;
|
|
1378
|
-
return SC.Benchmark._bench(function() {
|
|
1379
|
-
return _func.call(that);
|
|
1380
|
-
}, "OUTLET(%@)".format(path)) ;
|
|
1381
|
-
};
|
|
1382
|
-
} else func = _func ;
|
|
1383
2056
|
func.isOutlet = true ;
|
|
1384
2057
|
return func ;
|
|
1385
2058
|
}
|
|
1386
2059
|
|
|
1387
2060
|
}) ;
|
|
1388
2061
|
|
|
2062
|
+
// this handler goes through the guid to avoid any potential memory leaks
|
|
2063
|
+
SC.View._onscroll = function(evt) { $view(this)._onscroll(evt); } ;
|
|
2064
|
+
|
|
2065
|
+
SC.View.WIDTH_PADDING_STYLES = ['paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth'];
|
|
2066
|
+
|
|
2067
|
+
SC.View.HEIGHT_PADDING_STYLES = ['paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'];
|
|
2068
|
+
|
|
2069
|
+
SC.View.SCROLL_WIDTH_PADDING_STYLES = ['borderLeftWidth', 'borderRightWidth'];
|
|
2070
|
+
SC.View.SCROLL_HEIGHT_PADDING_STYLES = ['borderTopWidth', 'borderBottomWidth'];
|
|
2071
|
+
|
|
1389
2072
|
SC.View.elementFor = SC.View.viewFor ; // Old Sprout Compatibility.
|
|
1390
2073
|
|
|
1391
2074
|
// This div is used to create nodes. It should normally remain empty.
|
|
@@ -1393,5 +2076,3 @@ SC._ViewCreator = document.createElement('div') ;
|
|
|
1393
2076
|
|
|
1394
2077
|
// This div can be used to hold elements you don't want on the page right now.
|
|
1395
2078
|
SC.NodeCache = document.createElement('div') ;
|
|
1396
|
-
|
|
1397
|
-
console.log('SC.View = %@'.fmt(SC.View)) ;
|