sproutcore 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/Manifest.txt +269 -0
- data/README.txt +67 -0
- data/Rakefile +4 -0
- data/app_generators/sproutcore/USAGE +5 -0
- data/app_generators/sproutcore/sproutcore_generator.rb +66 -0
- data/app_generators/sproutcore/templates/README +77 -0
- data/app_generators/sproutcore/templates/environment.yml +4 -0
- data/bin/sc-build +145 -0
- data/bin/sc-gen +24 -0
- data/bin/sc-server +63 -0
- data/bin/sproutcore +21 -0
- data/clients/sc_docs/controllers/docs.js +118 -0
- data/clients/sc_docs/core.js +19 -0
- data/clients/sc_docs/english.lproj/body.css +159 -0
- data/clients/sc_docs/english.lproj/body.rhtml +33 -0
- data/clients/sc_docs/english.lproj/controls.css +0 -0
- 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/indicator.gif +0 -0
- data/clients/sc_docs/english.lproj/images/toolbar.png +0 -0
- data/clients/sc_docs/english.lproj/no_docs.rhtml +7 -0
- data/clients/sc_docs/english.lproj/strings.js +14 -0
- data/clients/sc_docs/english.lproj/warning.rhtml +6 -0
- data/clients/sc_docs/fixtures/doc.js +11 -0
- data/clients/sc_docs/main.js +21 -0
- data/clients/sc_docs/models/doc.js +9 -0
- data/clients/sc_docs/tests/controllers/docs.rhtml +21 -0
- data/clients/sc_docs/tests/models/doc.rhtml +21 -0
- data/clients/sc_docs/tests/views/doc_frame.rhtml +21 -0
- data/clients/sc_docs/tests/views/doc_label_view.rhtml +21 -0
- data/clients/sc_docs/views/doc_frame.js +33 -0
- data/clients/sc_docs/views/doc_label.js +20 -0
- data/clients/sc_test_runner/controllers/runner.js +175 -0
- data/clients/sc_test_runner/core.js +19 -0
- data/clients/sc_test_runner/english.lproj/body.css +151 -0
- data/clients/sc_test_runner/english.lproj/body.rhtml +35 -0
- data/clients/sc_test_runner/english.lproj/controls.css +0 -0
- data/clients/sc_test_runner/english.lproj/icons/small/next.png +0 -0
- data/clients/sc_test_runner/english.lproj/icons/small/reset.png +0 -0
- data/clients/sc_test_runner/english.lproj/images/gradients.png +0 -0
- data/clients/sc_test_runner/english.lproj/images/indicator.gif +0 -0
- data/clients/sc_test_runner/english.lproj/images/toolbar.png +0 -0
- data/clients/sc_test_runner/english.lproj/no_tests.rhtml +6 -0
- data/clients/sc_test_runner/english.lproj/strings.js +14 -0
- data/clients/sc_test_runner/english.lproj/warning.rhtml +6 -0
- data/clients/sc_test_runner/fixtures/test.js +12 -0
- data/clients/sc_test_runner/main.js +26 -0
- data/clients/sc_test_runner/models/test.js +11 -0
- data/clients/sc_test_runner/views/runner_frame.js +72 -0
- data/clients/sc_test_runner/views/test_label.js +20 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +17 -0
- data/environment.yml +9 -0
- data/frameworks/prototype/prototype.js +4186 -0
- data/frameworks/sproutcore/Core.js +378 -0
- data/frameworks/sproutcore/README +3 -0
- data/frameworks/sproutcore/controllers/array.js +236 -0
- data/frameworks/sproutcore/controllers/collection.js +305 -0
- data/frameworks/sproutcore/controllers/controller.js +323 -0
- data/frameworks/sproutcore/controllers/object.js +372 -0
- data/frameworks/sproutcore/drag/drag.js +549 -0
- data/frameworks/sproutcore/drag/drag_data_source.js +32 -0
- data/frameworks/sproutcore/drag/drag_source.js +64 -0
- data/frameworks/sproutcore/drag/drop_target.js +153 -0
- data/frameworks/sproutcore/english.lproj/blank.gif +0 -0
- data/frameworks/sproutcore/english.lproj/buttons.css +589 -0
- data/frameworks/sproutcore/english.lproj/buttons.png +0 -0
- data/frameworks/sproutcore/english.lproj/inline_text_editor.css +21 -0
- data/frameworks/sproutcore/english.lproj/menu.css +121 -0
- data/frameworks/sproutcore/english.lproj/panels/background-fat.jpg +0 -0
- data/frameworks/sproutcore/english.lproj/panels/background-thin.jpg +0 -0
- data/frameworks/sproutcore/english.lproj/panels/bottom-edge.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/bottom-left-corner.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/bottom-right-corner.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/left-edge.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/overlay.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/right-edge.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/top-edge.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/top-left-corner.png +0 -0
- data/frameworks/sproutcore/english.lproj/panels/top-right-corner.png +0 -0
- data/frameworks/sproutcore/english.lproj/panes.css +155 -0
- data/frameworks/sproutcore/english.lproj/picker.css +22 -0
- data/frameworks/sproutcore/english.lproj/strings.js +15 -0
- data/frameworks/sproutcore/english.lproj/tab.css +23 -0
- data/frameworks/sproutcore/english.lproj/tests.css +67 -0
- data/frameworks/sproutcore/english.lproj/theme.css +77 -0
- data/frameworks/sproutcore/foundation/animator.js +670 -0
- data/frameworks/sproutcore/foundation/application.js +199 -0
- data/frameworks/sproutcore/foundation/array.js +348 -0
- data/frameworks/sproutcore/foundation/benchmark.js +211 -0
- data/frameworks/sproutcore/foundation/binding.js +384 -0
- data/frameworks/sproutcore/foundation/date.js +357 -0
- data/frameworks/sproutcore/foundation/error.js +39 -0
- data/frameworks/sproutcore/foundation/input_manager.js +153 -0
- data/frameworks/sproutcore/foundation/json.js +296 -0
- data/frameworks/sproutcore/foundation/mock.js +42 -0
- data/frameworks/sproutcore/foundation/node_descriptor.js +56 -0
- data/frameworks/sproutcore/foundation/object.js +777 -0
- data/frameworks/sproutcore/foundation/observable.js +451 -0
- data/frameworks/sproutcore/foundation/page.js +63 -0
- data/frameworks/sproutcore/foundation/path_module.js +413 -0
- data/frameworks/sproutcore/foundation/responder.js +310 -0
- data/frameworks/sproutcore/foundation/routes.js +371 -0
- data/frameworks/sproutcore/foundation/run_loop.js +21 -0
- data/frameworks/sproutcore/foundation/server.js +491 -0
- data/frameworks/sproutcore/foundation/set.js +96 -0
- data/frameworks/sproutcore/foundation/string.js +149 -0
- data/frameworks/sproutcore/foundation/undo_manager.js +186 -0
- data/frameworks/sproutcore/foundation/unittest.js +622 -0
- data/frameworks/sproutcore/foundation/utils.js +61 -0
- data/frameworks/sproutcore/globals/panels.js +182 -0
- data/frameworks/sproutcore/globals/popups.js +60 -0
- data/frameworks/sproutcore/globals/window.js +381 -0
- data/frameworks/sproutcore/lib/index.rhtml +66 -0
- data/frameworks/sproutcore/models/collection.js +395 -0
- data/frameworks/sproutcore/models/record.js +622 -0
- data/frameworks/sproutcore/models/store.js +295 -0
- data/frameworks/sproutcore/panes/dialog.js +16 -0
- data/frameworks/sproutcore/panes/manager.js +164 -0
- data/frameworks/sproutcore/panes/menu.js +45 -0
- data/frameworks/sproutcore/panes/overlay.js +231 -0
- data/frameworks/sproutcore/panes/pane.js +90 -0
- data/frameworks/sproutcore/panes/panel.js +19 -0
- data/frameworks/sproutcore/panes/picker.js +45 -0
- data/frameworks/sproutcore/tests/controllers/array.rhtml +86 -0
- data/frameworks/sproutcore/tests/controllers/controller.rhtml +273 -0
- data/frameworks/sproutcore/tests/controllers/object.rhtml +327 -0
- data/frameworks/sproutcore/tests/foundation/application.rhtml +125 -0
- data/frameworks/sproutcore/tests/foundation/array.rhtml +221 -0
- data/frameworks/sproutcore/tests/foundation/object.rhtml +69 -0
- data/frameworks/sproutcore/tests/globals/window.rhtml +45 -0
- data/frameworks/sproutcore/tests/panes/pane.rhtml +88 -0
- data/frameworks/sproutcore/tests/views/collection.rhtml +137 -0
- data/frameworks/sproutcore/tests/views/popup_button.rhtml +115 -0
- data/frameworks/sproutcore/tests/views/text_field.rhtml +37 -0
- data/frameworks/sproutcore/validators/credit_card.js +92 -0
- data/frameworks/sproutcore/validators/date.js +36 -0
- data/frameworks/sproutcore/validators/email.js +29 -0
- data/frameworks/sproutcore/validators/not_empty.js +24 -0
- data/frameworks/sproutcore/validators/number.js +55 -0
- data/frameworks/sproutcore/validators/password.js +78 -0
- data/frameworks/sproutcore/validators/validator.js +304 -0
- data/frameworks/sproutcore/views/button.js +425 -0
- data/frameworks/sproutcore/views/checkbox_field.js +30 -0
- data/frameworks/sproutcore/views/collection.js +1521 -0
- data/frameworks/sproutcore/views/container.js +62 -0
- data/frameworks/sproutcore/views/error_explanation.js +45 -0
- data/frameworks/sproutcore/views/field.js +214 -0
- data/frameworks/sproutcore/views/filter_button.js +29 -0
- data/frameworks/sproutcore/views/form.js +591 -0
- data/frameworks/sproutcore/views/image.js +141 -0
- data/frameworks/sproutcore/views/inline_text_editor.js +96 -0
- data/frameworks/sproutcore/views/label.js +176 -0
- data/frameworks/sproutcore/views/menu_item.js +90 -0
- data/frameworks/sproutcore/views/pagination.js +54 -0
- data/frameworks/sproutcore/views/popup_button.js +86 -0
- data/frameworks/sproutcore/views/popup_menu.js +137 -0
- data/frameworks/sproutcore/views/progress.js +100 -0
- data/frameworks/sproutcore/views/radio_field.js +107 -0
- data/frameworks/sproutcore/views/radio_group.js +48 -0
- data/frameworks/sproutcore/views/segmented.js +80 -0
- data/frameworks/sproutcore/views/select_field.js +272 -0
- data/frameworks/sproutcore/views/spinner.js +11 -0
- data/frameworks/sproutcore/views/tab.js +126 -0
- data/frameworks/sproutcore/views/text_field.js +179 -0
- data/frameworks/sproutcore/views/textarea_field.js +14 -0
- data/frameworks/sproutcore/views/toolbar.js +29 -0
- data/frameworks/sproutcore/views/view.js +1389 -0
- data/frameworks/sproutcore/views/workspace.js +170 -0
- data/generators/client/README +3 -0
- data/generators/client/USAGE +12 -0
- data/generators/client/client_generator.rb +53 -0
- data/generators/client/templates/core.js +19 -0
- data/generators/client/templates/english.lproj/body.css +0 -0
- data/generators/client/templates/english.lproj/body.rhtml +3 -0
- data/generators/client/templates/english.lproj/controls.css +0 -0
- data/generators/client/templates/english.lproj/strings.js +14 -0
- data/generators/client/templates/main.js +37 -0
- data/generators/controller/USAGE +16 -0
- data/generators/controller/controller_generator.rb +51 -0
- data/generators/controller/templates/controller.js +21 -0
- data/generators/controller/templates/test.rhtml +21 -0
- data/generators/framework/README +7 -0
- data/generators/framework/USAGE +12 -0
- data/generators/framework/framework_generator.rb +53 -0
- data/generators/framework/templates/core.js +20 -0
- data/generators/framework/templates/english.lproj/body.css +0 -0
- data/generators/framework/templates/english.lproj/body.rhtml +3 -0
- data/generators/framework/templates/english.lproj/controls.css +0 -0
- data/generators/framework/templates/english.lproj/strings.js +14 -0
- data/generators/language/USAGE +16 -0
- data/generators/language/language_generator.rb +47 -0
- data/generators/language/templates/strings.js +10 -0
- data/generators/model/USAGE +24 -0
- data/generators/model/model_generator.rb +55 -0
- data/generators/model/templates/fixture.js +11 -0
- data/generators/model/templates/model.js +20 -0
- data/generators/model/templates/test.rhtml +21 -0
- data/generators/test/USAGE +16 -0
- data/generators/test/templates/test.rhtml +21 -0
- data/generators/test/test_generator.rb +47 -0
- data/generators/view/USAGE +16 -0
- data/generators/view/templates/test.rhtml +21 -0
- data/generators/view/templates/view.js +20 -0
- data/generators/view/view_generator.rb +51 -0
- data/jsdoc/README.txt +119 -0
- data/jsdoc/app/DocFile.js +137 -0
- data/jsdoc/app/DocTag.js +110 -0
- data/jsdoc/app/Doclet.js +63 -0
- data/jsdoc/app/Dumper.js +143 -0
- data/jsdoc/app/JsDoc.js +103 -0
- data/jsdoc/app/JsHilite.js +45 -0
- data/jsdoc/app/JsIO.js +163 -0
- data/jsdoc/app/JsParse.js +385 -0
- data/jsdoc/app/JsPlate.js +130 -0
- data/jsdoc/app/JsTestrun.js +129 -0
- data/jsdoc/app/JsToke.js +564 -0
- data/jsdoc/app/Symbol.js +298 -0
- data/jsdoc/app/Transformer.js +14 -0
- data/jsdoc/app/Util.js +97 -0
- data/jsdoc/app/js.jar +0 -0
- data/jsdoc/app/run.js +144 -0
- data/jsdoc/plugins/min.js +316 -0
- data/jsdoc/plugins/strip.js +20 -0
- data/jsdoc/templates/sproutcore/class.tmpl +438 -0
- data/jsdoc/templates/sproutcore/default.css +241 -0
- data/jsdoc/templates/sproutcore/index.html +13 -0
- data/jsdoc/templates/sproutcore/index.tmpl +21 -0
- data/jsdoc/templates/sproutcore/prototype.js +4186 -0
- data/jsdoc/templates/sproutcore/publish.js +236 -0
- data/jsdoc/templates/sproutcore/splash.html +7 -0
- data/lib/sproutcore/build_tools/html_builder.rb +88 -0
- data/lib/sproutcore/build_tools/resource_builder.rb +194 -0
- data/lib/sproutcore/build_tools.rb +44 -0
- data/lib/sproutcore/bundle.rb +517 -0
- data/lib/sproutcore/bundle_manifest.rb +397 -0
- data/lib/sproutcore/generator_helper.rb +170 -0
- data/lib/sproutcore/helpers/capture_helper.rb +42 -0
- data/lib/sproutcore/helpers/static_helper.rb +80 -0
- data/lib/sproutcore/helpers/tag_helper.rb +110 -0
- data/lib/sproutcore/helpers/text_helper.rb +336 -0
- data/lib/sproutcore/helpers.rb +3 -0
- data/lib/sproutcore/jsdoc.rb +40 -0
- data/lib/sproutcore/jsmin.rb +247 -0
- data/lib/sproutcore/library.rb +258 -0
- data/lib/sproutcore/merb/bundle_controller.rb +179 -0
- data/lib/sproutcore/merb/router.rb +43 -0
- data/lib/sproutcore/merb.rb +27 -0
- data/lib/sproutcore/version.rb +9 -0
- data/lib/sproutcore/view_helpers/button_views.rb +302 -0
- data/lib/sproutcore/view_helpers/core_views.rb +284 -0
- data/lib/sproutcore/view_helpers/form_views.rb +258 -0
- data/lib/sproutcore/view_helpers/menu_views.rb +94 -0
- data/lib/sproutcore/view_helpers.rb +628 -0
- data/lib/sproutcore.rb +30 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/sproutcore_spec.rb +11 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/website.rake +17 -0
- metadata +365 -0
@@ -0,0 +1,1389 @@
|
|
1
|
+
// ========================================================================
|
2
|
+
// SproutCore
|
3
|
+
// copyright 2006-2007 Sprout Systems, Inc.
|
4
|
+
// ========================================================================
|
5
|
+
|
6
|
+
require('foundation/object') ;
|
7
|
+
require('foundation/responder') ;
|
8
|
+
require('foundation/node_descriptor') ;
|
9
|
+
require('foundation/binding');
|
10
|
+
require('foundation/path_module');
|
11
|
+
|
12
|
+
BENCHMARK_OUTLETS = NO ;
|
13
|
+
SC.FIXED = 'fixed';
|
14
|
+
SC.FLEXIBLE = 'flexible';
|
15
|
+
|
16
|
+
/**
|
17
|
+
@class Manages a DOM element for display.
|
18
|
+
|
19
|
+
Views are how you interact with the DOM.
|
20
|
+
|
21
|
+
@extends SC.Responder
|
22
|
+
*/
|
23
|
+
SC.View = SC.Responder.extend(SC.PathModule,
|
24
|
+
/** @scope SC.View.prototype */
|
25
|
+
{
|
26
|
+
|
27
|
+
// ..........................................
|
28
|
+
// VIEW API
|
29
|
+
//
|
30
|
+
// The methods in this section are used to manage actual views. You can
|
31
|
+
// basically interact with child elements in two ways. One using an API
|
32
|
+
// similar to the DOM API. Alternatively, you can treat the view like an
|
33
|
+
// array and use standard iterators.
|
34
|
+
//
|
35
|
+
|
36
|
+
/*
|
37
|
+
insert the view before the specified view. pass null to insert at the
|
38
|
+
end.
|
39
|
+
*/
|
40
|
+
insertBefore: function(view, beforeView) {
|
41
|
+
this._insertBefore(view,beforeView,true);
|
42
|
+
},
|
43
|
+
|
44
|
+
_insertBefore: function(view, beforeView, updateDom) {
|
45
|
+
// verify that beforeView is a child.
|
46
|
+
if (beforeView) {
|
47
|
+
if (beforeView.parentNode != this) throw "insertBefore() beforeView must belong to the receiver" ;
|
48
|
+
if (beforeView == view) throw "insertBefore() views cannot be the same";
|
49
|
+
}
|
50
|
+
|
51
|
+
if (view.parentNode) view.removeFromParent() ;
|
52
|
+
this.willAddChild(this, beforeView) ;
|
53
|
+
view.willAddToParent(this, beforeView) ;
|
54
|
+
|
55
|
+
// patch in the view.
|
56
|
+
if (beforeView) {
|
57
|
+
view.set('previousSibling', beforeView.previousSibling) ;
|
58
|
+
view.set('nextSibling', beforeView) ;
|
59
|
+
beforeView.set('previousSibling', view) ;
|
60
|
+
} else {
|
61
|
+
view.set('previousSibling', this.lastChild) ;
|
62
|
+
view.set('nextSibling', null) ;
|
63
|
+
this.set('lastChild', view) ;
|
64
|
+
}
|
65
|
+
|
66
|
+
if (view.previousSibling) view.previousSibling.set('nextSibling',view);
|
67
|
+
if (view.previousSibling == null) this.set('firstChild',view) ;
|
68
|
+
view.set('parentNode', this) ;
|
69
|
+
|
70
|
+
// Update DOM. -- ANIMATE
|
71
|
+
// Note that this code is not called when outlets are first configured.
|
72
|
+
// The assumption is that the created view already belongs to the
|
73
|
+
// document somwhere.
|
74
|
+
if (updateDom) {
|
75
|
+
var beforeElement = (beforeView) ? beforeView.rootElement : null;
|
76
|
+
(this.containerElement || this.rootElement).insertBefore(view.rootElement,beforeElement);
|
77
|
+
|
78
|
+
// regenerate the childNodes array.
|
79
|
+
this._rebuildChildNodes();
|
80
|
+
|
81
|
+
// update parent state.
|
82
|
+
view._updateIsVisibleInWindow() ;
|
83
|
+
}
|
84
|
+
|
85
|
+
|
86
|
+
// call notices.
|
87
|
+
view.didAddToParent(this, beforeView) ;
|
88
|
+
this.didAddChild(view, beforeView) ;
|
89
|
+
|
90
|
+
return this ;
|
91
|
+
},
|
92
|
+
|
93
|
+
// remove the current child
|
94
|
+
removeChild: function(view) {
|
95
|
+
if (!view) return ;
|
96
|
+
if (view.parentNode != this) throw "removeChild: view must belong to parent";
|
97
|
+
|
98
|
+
view.willRemoveFromParent() ;
|
99
|
+
this.willRemoveChild(view) ;
|
100
|
+
|
101
|
+
// unpatch.
|
102
|
+
if (view.previousSibling) {
|
103
|
+
view.previousSibling.set('nextSibling', view.nextSibling);
|
104
|
+
} else this.set('firstChild', view.nextSibling) ;
|
105
|
+
|
106
|
+
if (view.nextSibling) {
|
107
|
+
view.nextSibling.set('previousSibling', view.previousSibling) ;
|
108
|
+
} else this.set('lastChild', view.previousSibling) ;
|
109
|
+
|
110
|
+
// Update DOM -- ANIMATE
|
111
|
+
var el = (this.containerElement || this.rootElement);
|
112
|
+
if (el && (view.rootElement.parentNode == el) && (el != document)) {
|
113
|
+
el.removeChild(view.rootElement);
|
114
|
+
}
|
115
|
+
|
116
|
+
// regenerate the childNodes array.
|
117
|
+
this._rebuildChildNodes();
|
118
|
+
|
119
|
+
// update parent state.
|
120
|
+
view._updateIsVisibleInWindow() ;
|
121
|
+
|
122
|
+
|
123
|
+
view.set('nextSibling', null);
|
124
|
+
view.set('previousSibling', null);
|
125
|
+
view.set('parentNode', null) ;
|
126
|
+
view.didRemoveFromParent(this) ;
|
127
|
+
this.didRemoveChild(view);
|
128
|
+
},
|
129
|
+
|
130
|
+
// replace the oldView with the new view.
|
131
|
+
replaceChild: function(view, oldView) {
|
132
|
+
this.insertBefore(view,oldView) ; this.removeChild(oldView) ;
|
133
|
+
},
|
134
|
+
|
135
|
+
// remove the receiver from the parent view. Safe to call even if there
|
136
|
+
// is no parent node.
|
137
|
+
removeFromParent: function() {
|
138
|
+
if (this.parentNode) this.parentNode.removeChild(this) ;
|
139
|
+
},
|
140
|
+
|
141
|
+
// add a child to the end of the current views.
|
142
|
+
appendChild: function(view) {
|
143
|
+
this.insertBefore(view,null) ;
|
144
|
+
},
|
145
|
+
|
146
|
+
// this array contains the childViews associated with this view. You should
|
147
|
+
// always access this via a GET.
|
148
|
+
childNodes: [],
|
149
|
+
|
150
|
+
// the first child in the chain.
|
151
|
+
firstChild: null,
|
152
|
+
|
153
|
+
// the last child in the chain.
|
154
|
+
lastChild: null,
|
155
|
+
|
156
|
+
// the next child view. access via a get()
|
157
|
+
nextSibling: null,
|
158
|
+
|
159
|
+
// the previous view
|
160
|
+
previousSibling: null,
|
161
|
+
|
162
|
+
// the parent node. null if not in the hierarchy.
|
163
|
+
parentNode: null,
|
164
|
+
|
165
|
+
|
166
|
+
pane: function()
|
167
|
+
{
|
168
|
+
var view = this;
|
169
|
+
while(view = view.get('parentNode'))
|
170
|
+
{
|
171
|
+
if (view.get('isPane') ) break;
|
172
|
+
}
|
173
|
+
return view;
|
174
|
+
}.property(),
|
175
|
+
|
176
|
+
|
177
|
+
// This will remove all child views.
|
178
|
+
clear: function() {
|
179
|
+
while(this.firstChild) this.removeChild(this.firstChild) ;
|
180
|
+
},
|
181
|
+
|
182
|
+
// This callback is invoke just before your view is added to a new parent.
|
183
|
+
willAddToParent: function(parent, beforeView) {},
|
184
|
+
|
185
|
+
// This callback is invoked just after your view added to a new parent.
|
186
|
+
didAddToParent: function(parent, beforeView) {},
|
187
|
+
|
188
|
+
// This callback is invoked just before your view is removed from a parent.
|
189
|
+
willRemoveFromParent: function() {},
|
190
|
+
|
191
|
+
// This callback is invoked just after your view is remove from a parent.
|
192
|
+
didRemoveFromParent: function(oldParent) {},
|
193
|
+
|
194
|
+
// This callback is invoked just before a new child is added to view.
|
195
|
+
willAddChild: function(child, beforeView) {},
|
196
|
+
|
197
|
+
// This callback is invoked just after a new child is added to a view.
|
198
|
+
didAddChild: function(child, beforeView) {},
|
199
|
+
|
200
|
+
// This callback is invoke just before a child is removed from a view.
|
201
|
+
willRemoveChild: function(child) {},
|
202
|
+
|
203
|
+
// This callback is invoked this just after a child is removed from a view.
|
204
|
+
didRemoveChild: function(child) {},
|
205
|
+
|
206
|
+
|
207
|
+
nextKeyView: null,
|
208
|
+
previousKeyView: null,
|
209
|
+
|
210
|
+
nextValidKeyView: function()
|
211
|
+
{
|
212
|
+
var view = this;
|
213
|
+
while (view = view.get('nextKeyView'))
|
214
|
+
{
|
215
|
+
if (view.get('isVisible') && view.get('acceptsFirstResponder')) return view;
|
216
|
+
}
|
217
|
+
return null;
|
218
|
+
},
|
219
|
+
previousValidKeyView: function()
|
220
|
+
{
|
221
|
+
var view = this;
|
222
|
+
while (view = view.get('previousKeyView'))
|
223
|
+
{
|
224
|
+
if (view.get('isVisible') && view.get('acceptsFirstResponder')) return view;
|
225
|
+
}
|
226
|
+
return null;
|
227
|
+
},
|
228
|
+
|
229
|
+
|
230
|
+
// ..........................................
|
231
|
+
// SC.Responder implementation
|
232
|
+
//
|
233
|
+
|
234
|
+
nextResponder: function()
|
235
|
+
{
|
236
|
+
return this.parentNode;
|
237
|
+
}.property('parentNode'),
|
238
|
+
|
239
|
+
// recursively travels down the view hierarchy looking for a view that returns true to performKeyEquivalent
|
240
|
+
performKeyEquivalent: function(keystring, evt)
|
241
|
+
{
|
242
|
+
var child = this.get('firstChild');
|
243
|
+
while (child)
|
244
|
+
{
|
245
|
+
if (child.performKeyEquivalent(keystring, evt)) return true;
|
246
|
+
child = child.get('nextSibling');
|
247
|
+
}
|
248
|
+
return false;
|
249
|
+
},
|
250
|
+
|
251
|
+
// ..........................................
|
252
|
+
// ELEMENT API
|
253
|
+
//
|
254
|
+
// The methods in this section provide compatibility with the most common
|
255
|
+
// Prototype methods used on elements. These methods are generally primitives
|
256
|
+
// for modifying the underlying DOM element. You should only use them for
|
257
|
+
// INTERNAL VIEW CODE.
|
258
|
+
|
259
|
+
// returns the CSS classNames for the element.
|
260
|
+
classNames: function() {
|
261
|
+
return Element.classNames(this.rootElement);
|
262
|
+
}.property(),
|
263
|
+
|
264
|
+
// return true if the element has the classname.
|
265
|
+
hasClassName: function(className) {
|
266
|
+
var ret = Element.hasClassName(this.rootElement,className) ;
|
267
|
+
this.propertyDidChange('classNames') ;
|
268
|
+
return ret ;
|
269
|
+
},
|
270
|
+
|
271
|
+
// add the specified class name.
|
272
|
+
addClassName: function(className) {
|
273
|
+
var ret = Element.addClassName(this.rootElement,className) ;
|
274
|
+
this.propertyDidChange('classNames') ;
|
275
|
+
return ret ;
|
276
|
+
},
|
277
|
+
|
278
|
+
// remove the specified class name.
|
279
|
+
removeClassName: function(className) {
|
280
|
+
var ret = Element.removeClassName(this.rootElement,className) ;
|
281
|
+
this.propertyDidChange('classNames') ;
|
282
|
+
return ret ;
|
283
|
+
},
|
284
|
+
|
285
|
+
setClassName: function(className, flag) {
|
286
|
+
(!!flag) ? this.addClassName(className) : this.removeClassName(className);
|
287
|
+
},
|
288
|
+
|
289
|
+
// toggler specified class name..
|
290
|
+
toggleClassName: function(className) {
|
291
|
+
var ret = Element.toggleClassName(this.rootElement,className) ;
|
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) ;
|
299
|
+
},
|
300
|
+
|
301
|
+
// get the named style. (see also style properties)
|
302
|
+
getStyle: function(style) {
|
303
|
+
return Element.getStyle(this.rootElement,style) ;
|
304
|
+
},
|
305
|
+
|
306
|
+
// set the passed styles.
|
307
|
+
setStyle: function(styles, camelized) {
|
308
|
+
return Element.setStyle(this.rootElement, styles, camelized) ;
|
309
|
+
},
|
310
|
+
|
311
|
+
// use this method to update the HTML of an element. This takes care of
|
312
|
+
// nasties like processing scripts and inserting HTML into a table. You can
|
313
|
+
// also use asHTML, which builds on this method.
|
314
|
+
update: function(html) {
|
315
|
+
Element.update((this.containerElement || this.rootElement),html) ;
|
316
|
+
this.propertyDidChange('asHTML') ;
|
317
|
+
},
|
318
|
+
|
319
|
+
// this works like the element getAttribute() except it is standardized
|
320
|
+
// across all browsers.
|
321
|
+
getAttribute: function(attrName) {
|
322
|
+
return Element.readAttribute(this.rootElement,attrName) ;
|
323
|
+
},
|
324
|
+
|
325
|
+
setAttribute: function(attrName, value) {
|
326
|
+
this.rootElement.setAttribute(atrrName, value) ;
|
327
|
+
},
|
328
|
+
|
329
|
+
hasAttribute: function(attrName) {
|
330
|
+
return Element.hasAttribute(this.rootElement, attrName) ;
|
331
|
+
},
|
332
|
+
|
333
|
+
// ..........................................
|
334
|
+
// DOM API
|
335
|
+
//
|
336
|
+
// The methods in this section give you some low-level control over how the
|
337
|
+
// view interacts with the DOM. You do not normally need to work with this.
|
338
|
+
|
339
|
+
// This is the DOM element actually managed by this view. This will be set
|
340
|
+
// by the view when it is created. Changing it afterwards will likely
|
341
|
+
// break things.
|
342
|
+
rootElement: null,
|
343
|
+
|
344
|
+
// Normally when you add child views to your view, their DOM elements will
|
345
|
+
// be set as direct children of the root element. However you can
|
346
|
+
// choose instead to designate an alertnative child node using this
|
347
|
+
// property. Set this to a selector string to begin with. The first time
|
348
|
+
// it is access, the view will convert it to an actual element. It is not
|
349
|
+
// currently safe to edit this property once the view has been created.
|
350
|
+
containerElement: null,
|
351
|
+
|
352
|
+
// ..........................................
|
353
|
+
// VIEW LAYOUT
|
354
|
+
//
|
355
|
+
// The following methods can be used to implement automatic resizing.
|
356
|
+
// The frame and bounds provides a simple way for you to compute the
|
357
|
+
// location and size of your views. You can then use the automatic
|
358
|
+
// resizing.
|
359
|
+
|
360
|
+
|
361
|
+
/**
|
362
|
+
Convert a point _from_ the offset parent of the passed view to the current view.
|
363
|
+
|
364
|
+
This is a useful utility for converting points in the coordinate system of
|
365
|
+
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 of
|
367
|
+
convertFrameToView().
|
368
|
+
|
369
|
+
Note that if your view is not visible on the screen, this may not work.
|
370
|
+
|
371
|
+
@param {Point} f The point or frame to convert
|
372
|
+
@param {SC.Vew} targetView The view to convert from. Pass null to convert from window coordinates.
|
373
|
+
|
374
|
+
@returns {Point} The converted point or frame
|
375
|
+
*/
|
376
|
+
convertFrameFromView: function(f, targetView) {
|
377
|
+
|
378
|
+
// first, convert to root level offset.
|
379
|
+
var thisOffset = Element.viewportOffset(this.get('offsetParent')) ;
|
380
|
+
var thatOffset = (targetView) ? Element.viewportOffset(targetView.get('offsetParent')) : [0,0] ;
|
381
|
+
|
382
|
+
// now get adjustment.
|
383
|
+
var adjustX = thatOffset[0] - thisOffset[0] ;
|
384
|
+
var adjustY = thatOffset[1] - thisOffset[1] ;
|
385
|
+
return { x: (f.x + adjustX), y: (f.y + adjustY), width: f.width, height: f.height };
|
386
|
+
},
|
387
|
+
|
388
|
+
/**
|
389
|
+
Convert a point _to_ the offset parent of the passed view from the current view.
|
390
|
+
|
391
|
+
This is a useful utility for converting points in the coordinate system of
|
392
|
+
the receiver to the coordinate system of another view. Pass null for
|
393
|
+
targetView to convert a point to a window offset. This is the inverse of
|
394
|
+
convertFrameFromView().
|
395
|
+
|
396
|
+
Note that if your view is not visible on the screen, this may not work.
|
397
|
+
|
398
|
+
@param {Point} f The point or frame to convert
|
399
|
+
@param {SC.Vew} targetView The view to convert to. Pass null to convert to window coordinates.
|
400
|
+
|
401
|
+
@returns {Point} The converted point or frame
|
402
|
+
*/
|
403
|
+
convertFrameToView: function(f, sourceView) {
|
404
|
+
// first, convert to root level offset.
|
405
|
+
var thisOffset = Element.viewportOffset(this.get('offsetParent')) ;
|
406
|
+
var thatOffset = (sourceView) ? Element.viewportOffset(sourceView.get('offsetParent')) : [0,0] ;
|
407
|
+
|
408
|
+
// now get adjustment.
|
409
|
+
var adjustX = thisOffset[0] - thatOffset[0] ;
|
410
|
+
var adjustY = thisOffset[1] - thatOffset[1] ;
|
411
|
+
return { x: (f.x + adjustX), y: (f.y + adjustY), width: f.width, height: f.height };
|
412
|
+
},
|
413
|
+
|
414
|
+
// if a view isPositioned, then you can manually control the size and
|
415
|
+
// origin of the view using the frame property. If isPositioned is false,
|
416
|
+
// then this view will be sized and positioned by the browser using CSS.
|
417
|
+
// You can read the current frame, but you cannot make edits.
|
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 ;
|
427
|
+
|
428
|
+
// make absolute positioned. Also get default frame.
|
429
|
+
if (isPositioned) {
|
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
|
458
|
+
|
459
|
+
var el = this.rootElement ;
|
460
|
+
this._frame = Element.getDimensions(el);
|
461
|
+
this._frame.x = el.offsetLeft ;
|
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
|
+
if (this._frameCached) {
|
470
|
+
this._frame = null ;
|
471
|
+
this._frameCached = false;
|
472
|
+
}
|
473
|
+
},
|
474
|
+
|
475
|
+
// This property returns a DOM ELEMENT that is the offset parent for
|
476
|
+
// this view's frame coordinates. Depending on your CSS, this parent
|
477
|
+
// may or may not match with the parent view.
|
478
|
+
offsetParent: function() {
|
479
|
+
return Position.offsetParent(this.rootElement) ;
|
480
|
+
}.property(),
|
481
|
+
|
482
|
+
// This property is used to set the internal padding of an element. The
|
483
|
+
// innerFrame is an offset from the outer frame. Changing these settings
|
484
|
+
// will adjust the height, width, and padding of the element.
|
485
|
+
innerFrame: function(key, value) {
|
486
|
+
|
487
|
+
// get the basic inner framce
|
488
|
+
var el = this.rootElement ;
|
489
|
+
var f = {
|
490
|
+
x: parseInt(this.getStyle('padding-left'),0) || 0,
|
491
|
+
y: parseInt(this.getStyle('padding-top'), 0) || 0,
|
492
|
+
width: parseInt(this.getStyle('width'), 0) || 0,
|
493
|
+
height: parseInt(this.getStyle('height'),0) || 0
|
494
|
+
} ;
|
495
|
+
|
496
|
+
// get the current frame size.
|
497
|
+
var size = {
|
498
|
+
width: f.x + f.width + parseInt(this.getStyle('padding-right'),0),
|
499
|
+
height: f.y + f.height + parseInt(this.getStyle('padding-bottom'),0)
|
500
|
+
};
|
501
|
+
|
502
|
+
// now update the innerFrame if needed. Change only the bits that are
|
503
|
+
// passed in.
|
504
|
+
if (value !== undefined) {
|
505
|
+
var style = {} ;
|
506
|
+
var didResize = false ;
|
507
|
+
var clearFrame = false ;
|
508
|
+
|
509
|
+
// reposition X
|
510
|
+
if (value.x !== undefined) {
|
511
|
+
f.x = value.x ;
|
512
|
+
style.paddingLeft = Math.floor(f.x) + 'px' ;
|
513
|
+
}
|
514
|
+
|
515
|
+
// reposition Y
|
516
|
+
if (value.y !== undefined) {
|
517
|
+
f.y = value.y ;
|
518
|
+
style.paddingTop = Math.floor(f.y) + 'px' ;
|
519
|
+
}
|
520
|
+
|
521
|
+
// resize Width
|
522
|
+
// adjust both the element width and padding right so that the overall
|
523
|
+
// frame size does not change.
|
524
|
+
if (value.width !== undefined) {
|
525
|
+
didResize = true ;
|
526
|
+
f.width = value.width ;
|
527
|
+
style.width = Math.floor(f.width).toString() + 'px' ;
|
528
|
+
|
529
|
+
var padding = size.width - f.width - f.x ;
|
530
|
+
if (padding < 0) {
|
531
|
+
clearFrame = true ;
|
532
|
+
padding = 0 ;
|
533
|
+
}
|
534
|
+
style.paddingRight = Math.floor(padding).toString() + 'px' ;
|
535
|
+
}
|
536
|
+
|
537
|
+
// Resize Height
|
538
|
+
// adjust both the element height and padding bottom so that the
|
539
|
+
// overall frame size does not change.
|
540
|
+
if (value.height !== undefined) {
|
541
|
+
didResize = true ;
|
542
|
+
f.height = value.height ;
|
543
|
+
style.height = Math.floor(f.height).toString() + 'px' ;
|
544
|
+
|
545
|
+
var padding = size.height - f.height - f.y ;
|
546
|
+
if (padding < 0) {
|
547
|
+
clearFrame = true ;
|
548
|
+
padding = 0 ;
|
549
|
+
}
|
550
|
+
style.paddingBottom = Math.floor(padding).toString() + 'px' ;
|
551
|
+
}
|
552
|
+
|
553
|
+
// now apply style change
|
554
|
+
this.setStyle(style) ;
|
555
|
+
|
556
|
+
// if the user sets an innerFrame size that cannot fit within the
|
557
|
+
// current outer frame, then the outer frame will be adjusted to fit.
|
558
|
+
// clear the frame so that this can happen.
|
559
|
+
if (clearFrame) {
|
560
|
+
this.propertyWillChange('frame') ;
|
561
|
+
this._frame = null ;
|
562
|
+
this.propertyDidChange('frame') ;
|
563
|
+
}
|
564
|
+
|
565
|
+
// also notify children so they can resize also.
|
566
|
+
if (didResize) this.resizeChildrenWithOldSize(size) ;
|
567
|
+
}
|
568
|
+
|
569
|
+
// finally return the frame.
|
570
|
+
return f ;
|
571
|
+
}.property('frame'),
|
572
|
+
|
573
|
+
innerSize: function(key, value) {
|
574
|
+
if (value !== undefined) {
|
575
|
+
this.set('innerFrame',{ width: value.width, height: value.height }) ;
|
576
|
+
}
|
577
|
+
return this.get('innerFrame') ;
|
578
|
+
}.property('innerFrame'),
|
579
|
+
|
580
|
+
innerOrigin: function(key, value) {
|
581
|
+
if (value !== undefined) {
|
582
|
+
this.set('innerFrame',{ x: value.x, y: value.y }) ;
|
583
|
+
}
|
584
|
+
return this.get('innerFrame') ;
|
585
|
+
}.property('innerFrame'),
|
586
|
+
|
587
|
+
// This property identifies the height and offset of your view with
|
588
|
+
// respect to the parent view and its bounds. To resize your view, edit
|
589
|
+
// this property.
|
590
|
+
//
|
591
|
+
// This method is carefully constructed to use the computed CSS style
|
592
|
+
// until you actually override it by setting your own size and location
|
593
|
+
// at which point it will use its own settings.
|
594
|
+
frame: function(key, value) {
|
595
|
+
|
596
|
+
// build frame
|
597
|
+
var el = this.rootElement ;
|
598
|
+
var f = Object.clone(this._frame) ;
|
599
|
+
if (f.x === undefined) f.x = el.offsetLeft ;
|
600
|
+
if (f.y === undefined) f.y = el.offsetTop ;
|
601
|
+
|
602
|
+
// get the current size if needed.
|
603
|
+
var size ;
|
604
|
+
if ((f.width === undefined) || (f.height === undefined)) {
|
605
|
+
var isVisibleInWindow = this.get('isVisibleInWindow') ;
|
606
|
+
|
607
|
+
// if not visible in window, move parent node into window and get
|
608
|
+
// dim and offset. If the element has no parentNode, then just move
|
609
|
+
// the element in.
|
610
|
+
if (!isVisibleInWindow) {
|
611
|
+
var pn = el.parentNode || el ;
|
612
|
+
var pnParent = pn.parentNode ;
|
613
|
+
var pnSib = pn.nextSibling ;
|
614
|
+
SC.window.rootElement.insertBefore(pn, null) ;
|
615
|
+
}
|
616
|
+
|
617
|
+
size = Element.getDimensions(el);
|
618
|
+
f.width = size.width ;
|
619
|
+
f.height = size.height;
|
620
|
+
|
621
|
+
if (!isVisibleInWindow) {
|
622
|
+
if (pnParent) {
|
623
|
+
pnParent.insertBefore(pn, pnSib) ;
|
624
|
+
} else SC.window.removeChild(pn) ;
|
625
|
+
}
|
626
|
+
} else size = f ;
|
627
|
+
|
628
|
+
// now update the frame if needed. Only actually change the style for
|
629
|
+
// those parts of the frame that were passed in.
|
630
|
+
if (value !== undefined) {
|
631
|
+
var style = {} ;
|
632
|
+
var didResize = false ;
|
633
|
+
|
634
|
+
// reposition X
|
635
|
+
if (value.x !== undefined) {
|
636
|
+
f.x = value.x ;
|
637
|
+
style.left = Math.floor(f.x) + 'px' ;
|
638
|
+
}
|
639
|
+
|
640
|
+
// reposition Y
|
641
|
+
if (value.y !== undefined) {
|
642
|
+
f.y = value.y ;
|
643
|
+
style.top = Math.floor(f.y) + 'px' ;
|
644
|
+
}
|
645
|
+
|
646
|
+
// Resize width
|
647
|
+
if (value.width !== undefined) {
|
648
|
+
didResize = true ;
|
649
|
+
f.width = value.width ;
|
650
|
+
var padding = parseInt(this.getStyle('padding-left'),0) + parseInt(this.getStyle('padding-right'),0) ;
|
651
|
+
style.width = (Math.floor(f.width) - padding).toString() + 'px' ;
|
652
|
+
}
|
653
|
+
|
654
|
+
// Resize Height
|
655
|
+
if (value.height !== undefined) {
|
656
|
+
didResize = true ;
|
657
|
+
f.height = value.height ;
|
658
|
+
var padding = parseInt(this.getStyle('padding-top'),0) + parseInt(this.getStyle('padding-bottom'),0) ;
|
659
|
+
style.height = (Math.floor(f.height) - padding).toString() + 'px' ;
|
660
|
+
}
|
661
|
+
|
662
|
+
// now apply style change and save new frame.
|
663
|
+
this.setStyle(style) ;
|
664
|
+
this._frame = Object.clone(f) ;
|
665
|
+
|
666
|
+
// also notify children so they can resize also.
|
667
|
+
if (didResize) this.resizeChildrenWithOldSize(size) ;
|
668
|
+
}
|
669
|
+
|
670
|
+
// finally return the frame.
|
671
|
+
return f ;
|
672
|
+
}.property('innerFrame'),
|
673
|
+
|
674
|
+
size: function(key, value) {
|
675
|
+
if (value !== undefined) {
|
676
|
+
this.set('frame',{ width: value.width, height: value.height }) ;
|
677
|
+
}
|
678
|
+
return this.get('frame') ;
|
679
|
+
}.property('frame'),
|
680
|
+
|
681
|
+
origin: function(key, value) {
|
682
|
+
if (value !== undefined) {
|
683
|
+
this.set('frame',{ x: value.x, y: value.y }) ;
|
684
|
+
}
|
685
|
+
return this.get('frame') ;
|
686
|
+
}.property('frame'),
|
687
|
+
|
688
|
+
/**
|
689
|
+
The current scroll frame for the view.
|
690
|
+
|
691
|
+
This will tell you the total scroll height and width of the view as well
|
692
|
+
as any current scroll offset. You can also set the x and y properties of
|
693
|
+
the scrollFrame. Any changes to height and width will be ignored.
|
694
|
+
|
695
|
+
@returns frame
|
696
|
+
*/
|
697
|
+
scrollFrame: function(key, value) {
|
698
|
+
var el = this.rootElement ;
|
699
|
+
if (value !== undefined) {
|
700
|
+
el.scrollTop = value.y ;
|
701
|
+
el.scrollLeft = value.x ;
|
702
|
+
}
|
703
|
+
|
704
|
+
return { x: el.scrollLeft, y: el.scrollTop, height: el.scrollHeight, width: el.scrollWidth } ;
|
705
|
+
}.property('frame'),
|
706
|
+
|
707
|
+
// called on the view when you need to resize your child views. Normally
|
708
|
+
// this will call resizeWithOldParentSize() on the child views, but you
|
709
|
+
// can override this to do whatever funky layout to want.
|
710
|
+
resizeChildrenWithOldSize: function(oldSize) {
|
711
|
+
var child = this.get('firstChild') ;
|
712
|
+
while(child) {
|
713
|
+
child.resizeWithOldParentSize(oldSize) ;
|
714
|
+
child = child.get('nextSibling') ;
|
715
|
+
}
|
716
|
+
},
|
717
|
+
|
718
|
+
// called by the parentNode when it is resized. If you define the
|
719
|
+
// resizeOptions property, then this will respect those properties,
|
720
|
+
// otherwise it will let the browser do all the resizing and simply informs
|
721
|
+
// the child views that they need to resize also.
|
722
|
+
resizeWithOldParentSize: function(oldSize) {
|
723
|
+
var opts = this.get('resizeOptions') ;
|
724
|
+
|
725
|
+
// if there are no options, then just notify the children and return.
|
726
|
+
if (opts == null) {
|
727
|
+
if (this.firstChild) {
|
728
|
+
var oldSize = (this._frame) ? { width: this._frame.width, height: this._frame.height } : this.get('size') ;
|
729
|
+
this.resizeChildrenWithOldSize(oldSize) ;
|
730
|
+
}
|
731
|
+
return ;
|
732
|
+
}
|
733
|
+
|
734
|
+
// if there are options, then handle the resizing. This will
|
735
|
+
// notify the children also.
|
736
|
+
if (this.get('isPositioned')) this.set('isPositioned',true) ;
|
737
|
+
|
738
|
+
var f = Object.clone(this.get('frame')) ;
|
739
|
+
var newSize = this.get('parentNode').get('size') ;
|
740
|
+
|
741
|
+
var adjust = function(props, apts, newSize, oldSize) {
|
742
|
+
var loc ;
|
743
|
+
|
744
|
+
// first, compute the dimensions for old size.
|
745
|
+
var dims = [f[apts[0]], f[apts[1]]] ;
|
746
|
+
dims.push(oldSize - (dims[0] + dims[1])) ;
|
747
|
+
|
748
|
+
// next, subtract the dimensions of fixed elements from the old and
|
749
|
+
// new sizes.
|
750
|
+
for(loc=0;loc < 3;loc++) {
|
751
|
+
if (opts[props[loc]] != SC.FLEXIBLE) {
|
752
|
+
newSize -= dims[loc]; oldSize -= dims[loc] ;
|
753
|
+
}
|
754
|
+
}
|
755
|
+
|
756
|
+
// finally, adjust the flexible area as a percentage of the limited
|
757
|
+
// dimensions.
|
758
|
+
for(loc=0;loc < 2; loc++) {
|
759
|
+
if (opts[props[loc]] == SC.FLEXIBLE) {
|
760
|
+
f[apts[loc]] = newSize * dims[loc] / oldSize ;
|
761
|
+
}
|
762
|
+
}
|
763
|
+
};
|
764
|
+
|
765
|
+
// handle horizontal
|
766
|
+
adjust(['left','width','right'], ['x','width'], newSize.width, oldSize.width) ;
|
767
|
+
|
768
|
+
adjust(['top','height','bottom'], ['y','height'], newSize.height, oldSize.height) ;
|
769
|
+
|
770
|
+
this.set('frame',f) ;
|
771
|
+
},
|
772
|
+
|
773
|
+
// These properties are provide simple control for autoresizing. If you
|
774
|
+
// set these, then resizeWithOldParentSize() will autoresize for you.
|
775
|
+
// The allowed options are: SC.FLEXIBLE, SC.FIXED.
|
776
|
+
resizeOptions: null,
|
777
|
+
|
778
|
+
// ..........................................
|
779
|
+
// PROPERTIES
|
780
|
+
//
|
781
|
+
|
782
|
+
// set isVisible to false to hide a view or true to display it. You can
|
783
|
+
// optionally setup a visibleAnimation that will be used to transition the
|
784
|
+
// view in and out.
|
785
|
+
//
|
786
|
+
// If you would instead like to be notified when the view's actual
|
787
|
+
// visibility state changes (i.e. when animations are complete) bind to
|
788
|
+
// isDisplayVisible.
|
789
|
+
isVisible: true,
|
790
|
+
isVisibleBindingDefault: SC.Binding.Flag,
|
791
|
+
|
792
|
+
// [RO] This property reflects the current display visibility of the view.
|
793
|
+
// Usually, this property will mirror the current state of the isVisible
|
794
|
+
// property. However, if your view animates its visibility in and out, then
|
795
|
+
// this will not become false until the animation completes.
|
796
|
+
displayIsVisible: true,
|
797
|
+
|
798
|
+
// This property is set to true only when the view is (a) in the main DOM
|
799
|
+
// hierarchy and (b) all parent nodes are visible and (c) the receiver node
|
800
|
+
// is visible.
|
801
|
+
isVisibleInWindow: true,
|
802
|
+
|
803
|
+
// Localize boolean. This is used if you need toolTips.
|
804
|
+
localize: false,
|
805
|
+
|
806
|
+
// Tool tip gets applied to the title attribute if set.
|
807
|
+
toolTip: '',
|
808
|
+
|
809
|
+
// set this to the HTML you want to use when creating a new element. You
|
810
|
+
// can specify the HTML as a string of text, using the NodeDescriptor, or
|
811
|
+
// by pointing directly to an element.
|
812
|
+
emptyElement: "<div></div>",
|
813
|
+
|
814
|
+
// Set to true and the view will display in a lightbox when you show it.
|
815
|
+
isPanel: false,
|
816
|
+
|
817
|
+
// Set to true if the view should be modal when shown as a panel.
|
818
|
+
isModal: true,
|
819
|
+
|
820
|
+
// Enable visible animation by default.
|
821
|
+
isAnimationEnabled: true,
|
822
|
+
|
823
|
+
// General support for animation. Just call this method and it will build
|
824
|
+
// and play an animation starting from the current state. The second param
|
825
|
+
// is optional. It should either be a hash of animator options or an
|
826
|
+
// animator object returned by a previous call to transitionTo().
|
827
|
+
//
|
828
|
+
transitionTo: function(target,animator,opts) {
|
829
|
+
var animatorOptions = opts || {} ;
|
830
|
+
|
831
|
+
// Create or reset the animator.
|
832
|
+
if (animator && !animator._isAnimator) {
|
833
|
+
var finalStyle = animator ;
|
834
|
+
if (!this.get("isAnimationEnabled")) {
|
835
|
+
animatorOptions = Object.clone(animatorOptions) ;
|
836
|
+
animatorOptions.duration = 1;
|
837
|
+
}
|
838
|
+
if (animatorOptions.duration) {
|
839
|
+
animatorOptions.duration = parseInt(animatorOptions.duration,0) ;
|
840
|
+
}
|
841
|
+
|
842
|
+
animator = Animator.apply(this.rootElement, finalStyle, animatorOptions);
|
843
|
+
animator._isAnimator = true ;
|
844
|
+
}
|
845
|
+
|
846
|
+
// trigger animation
|
847
|
+
if (animator) {
|
848
|
+
animator.jumpTo(animator.state) ;
|
849
|
+
animator.seekTo(target) ;
|
850
|
+
}
|
851
|
+
return animator ;
|
852
|
+
},
|
853
|
+
|
854
|
+
// returns the contents of the element as HTML. Accounts for browser
|
855
|
+
// bugs.
|
856
|
+
asHTML: function(key, value) {
|
857
|
+
if (value !== undefined) {
|
858
|
+
// Safari2 has a bad habit of sometimes not actually changing its
|
859
|
+
// innerHTML. This will make sure the innerHTML get's changed properly.
|
860
|
+
if (SC.isSafari() && !SC.isSafari3()) {
|
861
|
+
var el = (this.containerElement || this.rootElement) ; var reps = 0 ;
|
862
|
+
var f = function() {
|
863
|
+
el.innerHTML = '' ; el.innerHTML = value ;
|
864
|
+
if ((reps++ < 5) && (value.length>0) && (el.innerHTML == '')) setTimeout(f,1) ;
|
865
|
+
};
|
866
|
+
f();
|
867
|
+
} else (this.containerElement || this.rootElement).innerHTML = value;
|
868
|
+
} else value = (this.containerElement || this.rootElement).innerHTML ;
|
869
|
+
return value ;
|
870
|
+
}.property(),
|
871
|
+
|
872
|
+
// returns the contents of the element as plain text. Accounts for browser
|
873
|
+
// bugs.
|
874
|
+
asText: function(key, value) {
|
875
|
+
if (value !== undefined) {
|
876
|
+
if (value == null) value = '' ;
|
877
|
+
this.asHTML(key,value.toString().escapeHTML()) ;
|
878
|
+
}
|
879
|
+
return this.asHTML().unescapeHTML() ;
|
880
|
+
}.property(),
|
881
|
+
|
882
|
+
|
883
|
+
|
884
|
+
|
885
|
+
//
|
886
|
+
// Command methods (used by the command manager)
|
887
|
+
//
|
888
|
+
|
889
|
+
/**
|
890
|
+
* Queries to see if the view has function matching the passed name .
|
891
|
+
* @param {String} name The name of the function
|
892
|
+
* @return Boolean
|
893
|
+
**/
|
894
|
+
hasNamedFunction: function( name )
|
895
|
+
{
|
896
|
+
return ( this[name] && ($type(this[name]) == T_FUNCTION) );
|
897
|
+
},
|
898
|
+
/**
|
899
|
+
* Queries to see if the view has a named command.
|
900
|
+
* @param {String} name The name of the command
|
901
|
+
* @return Boolean
|
902
|
+
**/
|
903
|
+
hasCommand: function( name )
|
904
|
+
{
|
905
|
+
return this.hasNamedFunction(name);
|
906
|
+
},
|
907
|
+
/**
|
908
|
+
* Queries to see if the view has a validator for the named command.
|
909
|
+
* @param {String} name The name of the command
|
910
|
+
* @return Boolean
|
911
|
+
**/
|
912
|
+
hasCommandValidator: function( name )
|
913
|
+
{
|
914
|
+
var name = this._commandValidatorForCommand(name);
|
915
|
+
return this.hasNamedFunction(name);
|
916
|
+
},
|
917
|
+
|
918
|
+
/**
|
919
|
+
* Queries to see if the view is capable of executing a named command.
|
920
|
+
* The view must have a method named after the command and, if there is a command validator method, it must pass validation.
|
921
|
+
* @param {String} name The name of the command
|
922
|
+
* @return Boolean
|
923
|
+
**/
|
924
|
+
canExecuteCommand: function( name )
|
925
|
+
{
|
926
|
+
var hasCommand = this.hasCommand(name);
|
927
|
+
var hasCommandValidator = this.hasCommandValidator(name);
|
928
|
+
// can't execute what you haven't got...
|
929
|
+
if ( !hasCommand ) return false;
|
930
|
+
// got it and not validating before usage...
|
931
|
+
if ( hasCommand && !hasCommandValidator ) return true;
|
932
|
+
// ok... we got it, and we need to check before using...
|
933
|
+
if ( hasCommand && hasCommandValidator )
|
934
|
+
{
|
935
|
+
return this.executeCommandValidator(name);
|
936
|
+
}
|
937
|
+
},
|
938
|
+
/**
|
939
|
+
* Executes the command (if permitted).
|
940
|
+
* @param {String} name The name of the command
|
941
|
+
* @return Boolean Either the return value of executing the command, or false.
|
942
|
+
**/
|
943
|
+
executeCommand: function( name )
|
944
|
+
{
|
945
|
+
return this.canExecuteCommand(name) ? this.executeCommandWithoutValidation(name) : false;
|
946
|
+
},
|
947
|
+
/**
|
948
|
+
* Executes the command without performing any validation.
|
949
|
+
* @param {String} name The name of the command
|
950
|
+
* @return Boolean The return value of executing the command.
|
951
|
+
**/
|
952
|
+
executeCommandWithoutValidation: function( name )
|
953
|
+
{
|
954
|
+
return this[name]();
|
955
|
+
},
|
956
|
+
/**
|
957
|
+
* Executes the command validator.
|
958
|
+
* @param {String} name The name of the command to be validated.
|
959
|
+
* @return Boolean Wether or not the command can be executed.
|
960
|
+
**/
|
961
|
+
executeCommandValidator: function( name )
|
962
|
+
{
|
963
|
+
var name = this._commandValidatorForCommand(name);
|
964
|
+
return this[name]();
|
965
|
+
},
|
966
|
+
|
967
|
+
/**
|
968
|
+
* Utility to construct the command alidator method name.
|
969
|
+
* @private
|
970
|
+
* @param {String} name The name of the command
|
971
|
+
* @return String
|
972
|
+
**/
|
973
|
+
_commandValidatorForCommand: function( name )
|
974
|
+
{
|
975
|
+
return "can" + name.capitalize();
|
976
|
+
},
|
977
|
+
|
978
|
+
|
979
|
+
|
980
|
+
|
981
|
+
// ..........................................
|
982
|
+
// SUPPORT METHODS
|
983
|
+
//
|
984
|
+
init: function() {
|
985
|
+
this._frame = {} ;
|
986
|
+
arguments.callee.base.call(this) ;
|
987
|
+
|
988
|
+
// configure them outlets.
|
989
|
+
var r = SC.idt.active ; var idtStart ; var idtSt ;
|
990
|
+
if (r) { idtSt = new Date().getTime(); }
|
991
|
+
this.configureOutlets() ;
|
992
|
+
if (r) { SC.idt.conf_t += ((new Date().getTime()) - idtSt); }
|
993
|
+
|
994
|
+
var toolTip = this.get('toolTip') ;
|
995
|
+
if(toolTip && (toolTip != '')) this._updateToolTipObserver();
|
996
|
+
|
997
|
+
// despite what was written in the comments for the containerElement property, it was not being converted
|
998
|
+
// from a sring to an element on access... doing so here...
|
999
|
+
// shouldn't be a bottleneck since if containerElement is set, you are likely to need the DOM element at some point.
|
1000
|
+
if ( this.containerElement && (SC.typeOf(this.containerElement) == T_STRING) )
|
1001
|
+
{
|
1002
|
+
this.containerElement = this.$sel(this.containerElement);
|
1003
|
+
}
|
1004
|
+
|
1005
|
+
if (this.get('isDropTarget')) SC.Drag.addDropTarget(this) ;
|
1006
|
+
},
|
1007
|
+
|
1008
|
+
// this method looks through your outlets array and will try to
|
1009
|
+
// reconfigure any missing ones.
|
1010
|
+
configureOutlets: function() {
|
1011
|
+
|
1012
|
+
if (!this.outlets || (this.outlets.length <= 0)) return ;
|
1013
|
+
|
1014
|
+
// lookup outlets as selector paths or execute the function if there
|
1015
|
+
// is one.
|
1016
|
+
this.beginPropertyChanges(); // bundle changes
|
1017
|
+
for(var oloc=0;oloc < this.outlets.length;oloc++) {
|
1018
|
+
var view = this.outlet(this.outlets[oloc]) ;
|
1019
|
+
|
1020
|
+
// if the HTML for the view is already in the DOM, then walk up the
|
1021
|
+
// DOM tree to find the first parent element managed by a view (incl
|
1022
|
+
// the receiver. Add the view to the list of child views also.
|
1023
|
+
if (view && view.rootElement && view.rootElement.parentNode) {
|
1024
|
+
var node = view.rootElement.parentNode;
|
1025
|
+
var parentView ;
|
1026
|
+
while(node && (node != this.rootElement) && !(parentView = $view(node))) node = node.parentNode;
|
1027
|
+
if (node == this.rootElement) parentView = this;
|
1028
|
+
if (parentView) parentView._insertBefore(view,null,false) ;
|
1029
|
+
}
|
1030
|
+
this._rebuildChildNodes() ; // this is not done with _insertBefore.
|
1031
|
+
|
1032
|
+
// update parent state.
|
1033
|
+
if (view && view._updateIsVisibleInWindow) {
|
1034
|
+
view._updateIsVisibleInWindow() ;
|
1035
|
+
}
|
1036
|
+
}
|
1037
|
+
this.endPropertyChanges() ;
|
1038
|
+
},
|
1039
|
+
|
1040
|
+
// ..........................................
|
1041
|
+
// VISIBILITY METHODS
|
1042
|
+
//
|
1043
|
+
|
1044
|
+
// Calling this method will show the view. Don't call this method
|
1045
|
+
// directly but instead set the isVisible property to true. You can
|
1046
|
+
// override this method to provide your own show capabilities.
|
1047
|
+
show: function() {
|
1048
|
+
Element.show(this.rootElement) ;
|
1049
|
+
this.removeClassName('hidden') ;
|
1050
|
+
this.set('displayIsVisible',true) ;
|
1051
|
+
},
|
1052
|
+
|
1053
|
+
// This is the primitive method for hiding a view. It will be called when
|
1054
|
+
// isVisible is set to false after an animation runs or immediate if no
|
1055
|
+
// animation is defined.
|
1056
|
+
hide: function() {
|
1057
|
+
Element.hide(this.rootElement) ;
|
1058
|
+
this.addClassName('hidden') ;
|
1059
|
+
this.set('displayIsVisible', false) ;
|
1060
|
+
},
|
1061
|
+
|
1062
|
+
// ..........................................
|
1063
|
+
// DEPRECATED. DO NOT USE
|
1064
|
+
//
|
1065
|
+
|
1066
|
+
// deprecated. Included only for compatibility.
|
1067
|
+
animateVisible: function(key, value) {
|
1068
|
+
if (value !== undefined) return this.set('isAnimationEnabled',value) ;
|
1069
|
+
return this.get('isAnimationEnabled');
|
1070
|
+
}.property('isAnimationEnabled'),
|
1071
|
+
|
1072
|
+
|
1073
|
+
// ..........................................
|
1074
|
+
// PRIVATE METHODS
|
1075
|
+
//
|
1076
|
+
|
1077
|
+
// this will set the rootElement, cleaning up any old element.
|
1078
|
+
_attachRootElement: function(el) {
|
1079
|
+
if (this.rootElement) this.rootElement._configured = null ;
|
1080
|
+
this.rootElement = el ;
|
1081
|
+
el._configured = this._guid ;
|
1082
|
+
},
|
1083
|
+
|
1084
|
+
// This method is called internally after you add or remove a child view.
|
1085
|
+
// It will rebuild the childNodes array to reflect all children.
|
1086
|
+
_rebuildChildNodes: function() {
|
1087
|
+
var ret = [] ; var view = this.firstChild;
|
1088
|
+
while(view) { ret.push(view); view = view.nextSibling; }
|
1089
|
+
this.set('childNodes', ret) ;
|
1090
|
+
},
|
1091
|
+
|
1092
|
+
_toolTipObserver: function() {
|
1093
|
+
var toolTip = this.get('toolTip') ;
|
1094
|
+
if (this.get('localize')) toolTip = toolTip.loc() ;
|
1095
|
+
this.rootElement.title = toolTip ;
|
1096
|
+
}.observes("toolTip"),
|
1097
|
+
|
1098
|
+
_isVisibleObserver: function() {
|
1099
|
+
var flag = this.get('isVisible') ;
|
1100
|
+
if ((this._isVisible === undefined) || (flag != this._isVisible)) {
|
1101
|
+
this._isVisible = flag ;
|
1102
|
+
if (flag) {
|
1103
|
+
this._show() ;
|
1104
|
+
} else this._hide() ;
|
1105
|
+
|
1106
|
+
// update parent state.
|
1107
|
+
this._updateIsVisibleInWindow() ;
|
1108
|
+
}
|
1109
|
+
}.observes('isVisible'),
|
1110
|
+
|
1111
|
+
_updateIsVisibleInWindow: function(parentNodeState) {
|
1112
|
+
if (parentNodeState === undefined) {
|
1113
|
+
var parentNode = this.get('parentNode') ;
|
1114
|
+
parentNodeState = (parentNode) ? parentNode.get('isVisibleInWindow') : false ;
|
1115
|
+
}
|
1116
|
+
|
1117
|
+
var visible = parentNodeState && this.get('isVisible') ;
|
1118
|
+
|
1119
|
+
// if state changes, update and notify children.
|
1120
|
+
if (visible != this.get('isVisibleInWindow')) {
|
1121
|
+
this.set('isVisibleInWindow', visible) ;
|
1122
|
+
var child = this.get('firstChild') ;
|
1123
|
+
while(child) {
|
1124
|
+
child._updateIsVisibleInWindow(visible) ;
|
1125
|
+
child = child.get('nextSibling') ;
|
1126
|
+
}
|
1127
|
+
}
|
1128
|
+
},
|
1129
|
+
|
1130
|
+
// Calling this method will show the view. Don't call this method
|
1131
|
+
// directly but instead set the isVisible property to true. You can
|
1132
|
+
// override this method to provide your own show capabilities.
|
1133
|
+
_show: function(anchorView, triggerEvent) {
|
1134
|
+
// compatibility
|
1135
|
+
if (this.showView) return this.showView() ;
|
1136
|
+
|
1137
|
+
// if this is a type of pane, call the pane manager.
|
1138
|
+
var paneType = this.get('paneType') ;
|
1139
|
+
if (this.get('isPanel')) paneType = SC.PANEL_PANE; // compatibility
|
1140
|
+
if (paneType) {
|
1141
|
+
if (anchorView === undefined) anchorView = null ;
|
1142
|
+
if (triggerEvent === undefined) triggerEvent = null ;
|
1143
|
+
SC.PaneManager.manager().showPaneView(this, paneType, anchorView, triggerEvent) ;
|
1144
|
+
this.set('displayIsVisible', true) ;
|
1145
|
+
|
1146
|
+
// if an animation is defined and animations are configured, use that.
|
1147
|
+
// the displayIsVisible property will be set to true when the animation
|
1148
|
+
// completes.
|
1149
|
+
} else if (this.visibleAnimation && this.get('isAnimationEnabled')) {
|
1150
|
+
this._transitionVisibleTo(1.0) ;
|
1151
|
+
|
1152
|
+
// at this point the animation has been reset to the beginng. Run the
|
1153
|
+
// core show() method immediately so the animation will be visible.
|
1154
|
+
this.show() ;
|
1155
|
+
|
1156
|
+
// otherwise, just change over visible settings.
|
1157
|
+
} else {
|
1158
|
+
this._visibleAnimator = null ;
|
1159
|
+
this.show() ;
|
1160
|
+
}
|
1161
|
+
|
1162
|
+
return this ;
|
1163
|
+
},
|
1164
|
+
|
1165
|
+
_hide: function() {
|
1166
|
+
// compatibility
|
1167
|
+
if (this.hideView) return this.hideView() ;
|
1168
|
+
|
1169
|
+
// if this is a type of pane, call the pane manager.
|
1170
|
+
var isPane = (!!this.get('paneType')) || this.get('isPanel') ;
|
1171
|
+
if (isPane) {
|
1172
|
+
SC.PaneManager.manager().hidePaneView(this) ;
|
1173
|
+
this.set('displayIsVisible', false) ;
|
1174
|
+
|
1175
|
+
// if an animation is defined and animations are configured, use that.
|
1176
|
+
// the displayIsVisible property will be set to false when the animation
|
1177
|
+
// completes.
|
1178
|
+
} else if (this.visibleAnimation && this.get('isAnimationEnabled')) {
|
1179
|
+
this._transitionVisibleTo(0.0) ;
|
1180
|
+
|
1181
|
+
// otherwise, just change over visible settings.
|
1182
|
+
} else {
|
1183
|
+
this._visibleAnimator = null;
|
1184
|
+
this.hide();
|
1185
|
+
}
|
1186
|
+
|
1187
|
+
return this ;
|
1188
|
+
},
|
1189
|
+
|
1190
|
+
|
1191
|
+
_transitionVisibleTo: function(target) {
|
1192
|
+
var a ;
|
1193
|
+
|
1194
|
+
// if an animator already exists, just transition to the new state.
|
1195
|
+
if (this._visibleAnimator) {
|
1196
|
+
this.transitionTo(target,this._visibleAnimator);
|
1197
|
+
|
1198
|
+
// otherwise, build the animator from the options passed. Patch in our
|
1199
|
+
// own onComplete handler.
|
1200
|
+
} else {
|
1201
|
+
var opts = this.visibleAnimation ;
|
1202
|
+
var style = [opts.hidden,opts.visible] ;
|
1203
|
+
opts.onComplete =
|
1204
|
+
this._animateVisibleDidComplete.bind(this,opts.onComplete) ;
|
1205
|
+
this._visibleAnimator = this.transitionTo(target,style,opts);
|
1206
|
+
}
|
1207
|
+
},
|
1208
|
+
|
1209
|
+
// This is called when the animation completes. Finish cleaning up the
|
1210
|
+
// visibility section.
|
1211
|
+
_animateVisibleDidComplete: function(chainFunc) {
|
1212
|
+
if (!this.get('isVisible')) this.hide() ;
|
1213
|
+
if (chainFunc) chainFunc(this) ;
|
1214
|
+
},
|
1215
|
+
|
1216
|
+
_firstResponderObserver: function(target, key, value) {
|
1217
|
+
this.setClassName('focus',value) ;
|
1218
|
+
}.observes('isFirstResponder'),
|
1219
|
+
|
1220
|
+
_dropTargetObserver: function() {
|
1221
|
+
if (this.get('isDropTarget')) {
|
1222
|
+
SC.Drag.addDropTarget(this) ;
|
1223
|
+
} else SC.Drag.removeDropTarget(this) ;
|
1224
|
+
}.observes('isDropTarget'),
|
1225
|
+
|
1226
|
+
// .............................................
|
1227
|
+
// SPECIAL TYPES OF VIEWS
|
1228
|
+
//
|
1229
|
+
|
1230
|
+
// This will show the pane as a popup or picker (depending on the paneType
|
1231
|
+
// you have set.) This works just like setting isVisible to true, except
|
1232
|
+
// that it also passes the anchorView and triggerEvent you pass in.
|
1233
|
+
popup: function(anchorView, triggerEvent) {
|
1234
|
+
|
1235
|
+
// this will bypass the normal observer machinery, calling the private
|
1236
|
+
// _show method ourselves. To avoid triggering _show twice, we patch up
|
1237
|
+
// the internal _isVisible property.
|
1238
|
+
this._isVisible = true ;
|
1239
|
+
this._show(anchorView, triggerEvent) ;
|
1240
|
+
this.set('isVisible', true);
|
1241
|
+
},
|
1242
|
+
|
1243
|
+
// This can be used to manually add observers to the rootElement for the
|
1244
|
+
// methods in the passed map. You generally don't want to do this since we
|
1245
|
+
// handle event propgation through the responder chain.
|
1246
|
+
configureObserverMethods: function(methodMap) {
|
1247
|
+
for(var name in methodMap) {
|
1248
|
+
if (!methodMap.hasOwnProperty(name)) continue ;
|
1249
|
+
if (this[name]) {
|
1250
|
+
var method = this[name].bindAsEventListener(this);
|
1251
|
+
Event.observe(this.rootElement,methodMap[name],method) ;
|
1252
|
+
}
|
1253
|
+
}
|
1254
|
+
},
|
1255
|
+
|
1256
|
+
toString: function() {
|
1257
|
+
var el = this.rootElement ;
|
1258
|
+
var attrs = el.attributes ;
|
1259
|
+
attrs = (attrs) ? $A(attrs).map(function(atr) { return [atr.nodeName,atr.nodeValue].join("="); }).join(' ') : '';
|
1260
|
+
var tagName = (!!el.tagName) ? el.tagName.toLowerCase() : 'document' ;
|
1261
|
+
return "View(<%@>)".format([tagName,attrs].join(' ')) ;
|
1262
|
+
}
|
1263
|
+
|
1264
|
+
}) ;
|
1265
|
+
|
1266
|
+
// Class Methods
|
1267
|
+
SC.View.mixin({
|
1268
|
+
|
1269
|
+
// this is the global registry of views. It's used to map elements back
|
1270
|
+
// to the views that own them.
|
1271
|
+
_view: {},
|
1272
|
+
|
1273
|
+
findViewForElement: function(el) {
|
1274
|
+
var guid = el._configured ;
|
1275
|
+
return (guid) ? SC.View._view[guid] : null ;
|
1276
|
+
},
|
1277
|
+
|
1278
|
+
// ..........................................
|
1279
|
+
// SETUP
|
1280
|
+
//
|
1281
|
+
// This works much like create except that it works on the passed in
|
1282
|
+
// element instead of trying to find something new. If you pass null for
|
1283
|
+
// the first parameter, then a new element will be created with the html
|
1284
|
+
// you set in content.
|
1285
|
+
viewFor: function(el,config) {
|
1286
|
+
if (el) el = $(el) ;
|
1287
|
+
|
1288
|
+
var r = SC.idt.active ; var vStart ;
|
1289
|
+
if (r) SC.idt.v_count++;
|
1290
|
+
|
1291
|
+
if (r) vStart = new Date().getTime() ;
|
1292
|
+
|
1293
|
+
// find or build the element.
|
1294
|
+
if (!el) {
|
1295
|
+
var emptyElement = this.prototype._cachedEmptyElement || this.prototype.emptyElement;
|
1296
|
+
|
1297
|
+
// if the emptyElement is a string not starting with '<', treat it like
|
1298
|
+
// an id and find it in the doc. If an element is found, cache it for
|
1299
|
+
// future use.
|
1300
|
+
var isString = typeof(emptyElement) == 'string' ;
|
1301
|
+
if (isString && (emptyElement.slice(0,1) != '<')) {
|
1302
|
+
var el = $sel(emptyElement) ;
|
1303
|
+
if (el) {
|
1304
|
+
this.prototype.emptyElement = emptyElement = el ;
|
1305
|
+
isString = false ;
|
1306
|
+
}
|
1307
|
+
}
|
1308
|
+
|
1309
|
+
// if still a string, then use it to create HTML. Save the generated
|
1310
|
+
// element so that we can avoid doing this over again.
|
1311
|
+
if (isString) {
|
1312
|
+
SC._ViewCreator.innerHTML = emptyElement ;
|
1313
|
+
el = $(SC._ViewCreator.firstChild) ;
|
1314
|
+
SC.NodeCache.appendChild(el) ;
|
1315
|
+
this.prototype._cachedEmptyElement = el.cloneNode(true) ;
|
1316
|
+
|
1317
|
+
} else if (typeof(emptyElement) == "object") {
|
1318
|
+
if (emptyElement.tagName) {
|
1319
|
+
el = emptyElement.cloneNode(true) ;
|
1320
|
+
} else el = SC.NodeDescriptor.create(emptyElement) ;
|
1321
|
+
}
|
1322
|
+
}
|
1323
|
+
if (r) SC.idt.vc_t += (new Date().getTime()) - vStart ;
|
1324
|
+
|
1325
|
+
// configure only once.
|
1326
|
+
if (el && el._configured) return SC.View.findViewForElement(el);
|
1327
|
+
|
1328
|
+
// Now that we have found an element, instantiate the view.
|
1329
|
+
var args = $A(arguments) ; args[0] = { rootElement: el } ;
|
1330
|
+
if (r) vStart = new Date().getTime();
|
1331
|
+
var ret = new this(args,this) ; // create instance.
|
1332
|
+
if (r) SC.idt.v_t += (new Date().getTime()) - vStart;
|
1333
|
+
el._configured = ret._guid ;
|
1334
|
+
|
1335
|
+
// return the view.
|
1336
|
+
SC.View._view[ret._guid] = ret ;
|
1337
|
+
return ret ;
|
1338
|
+
},
|
1339
|
+
|
1340
|
+
// create in the view work is like viewFor but with 'null' for el
|
1341
|
+
create: function(configs) {
|
1342
|
+
var args = $A(arguments) ;
|
1343
|
+
args.unshift(null) ;
|
1344
|
+
return this.viewFor.apply(this,args) ;
|
1345
|
+
},
|
1346
|
+
|
1347
|
+
// define your view as an outlet.
|
1348
|
+
outletFor: function(path) {
|
1349
|
+
var view = this ;
|
1350
|
+
var _func = function() {
|
1351
|
+
if (path === null) return view.viewFor(null) ;
|
1352
|
+
|
1353
|
+
|
1354
|
+
var ret = (this.$$sel) ? this.$$sel(path) : $$sel(path) ;
|
1355
|
+
if (ret) {
|
1356
|
+
var owner = this ;
|
1357
|
+
var newRet = [] ;
|
1358
|
+
for(var loc=0;loc<ret.length;loc++) {
|
1359
|
+
newRet.push(view.viewFor(ret[loc], { owner: owner }));
|
1360
|
+
}
|
1361
|
+
ret = newRet ;
|
1362
|
+
ret = (ret.length == 0) ? null : ((ret.length == 1) ? ret[0] : ret);
|
1363
|
+
}
|
1364
|
+
|
1365
|
+
return ret ;
|
1366
|
+
} ;
|
1367
|
+
|
1368
|
+
var func ;
|
1369
|
+
if (BENCHMARK_OUTLETS) {
|
1370
|
+
func = function() {
|
1371
|
+
var that = this ;
|
1372
|
+
return SC.Benchmark._bench(function() {
|
1373
|
+
return _func.call(that);
|
1374
|
+
}, "OUTLET(%@)".format(path)) ;
|
1375
|
+
};
|
1376
|
+
} else func = _func ;
|
1377
|
+
func.isOutlet = true ;
|
1378
|
+
return func ;
|
1379
|
+
}
|
1380
|
+
|
1381
|
+
}) ;
|
1382
|
+
|
1383
|
+
SC.View.elementFor = SC.View.viewFor ; // Old Sprout Compatibility.
|
1384
|
+
|
1385
|
+
// This div is used to create nodes. It should normally remain empty.
|
1386
|
+
SC._ViewCreator = document.createElement('div') ;
|
1387
|
+
|
1388
|
+
// This div can be used to hold elements you don't want on the page right now.
|
1389
|
+
SC.NodeCache = document.createElement('div') ;
|