sproutcore 0.9.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- 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)) ;
|