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,305 @@
|
|
1
|
+
// ========================================================================
|
2
|
+
// SproutCore
|
3
|
+
// copyright 2006-2007 Sprout Systems, Inc.
|
4
|
+
// ========================================================================
|
5
|
+
|
6
|
+
require('controllers/object') ;
|
7
|
+
|
8
|
+
/** @class
|
9
|
+
|
10
|
+
A CollectionController works just like an ObjectController except that
|
11
|
+
it includes support for a few extra items including the arrangedObjects
|
12
|
+
property (which may have a filter applied), and a selection.
|
13
|
+
|
14
|
+
This expects the content object to be a Collection object.
|
15
|
+
|
16
|
+
@extends SC.ObjectController
|
17
|
+
*/
|
18
|
+
SC.CollectionController = SC.ObjectController.extend(
|
19
|
+
/** @scope SC.CollectionController.prototype */
|
20
|
+
{
|
21
|
+
|
22
|
+
// ...................................
|
23
|
+
// PROPERTIES
|
24
|
+
//
|
25
|
+
|
26
|
+
/**
|
27
|
+
This is the current set of objects for the UI.
|
28
|
+
|
29
|
+
@type Array
|
30
|
+
*/
|
31
|
+
arrangedObjects: [],
|
32
|
+
|
33
|
+
/**
|
34
|
+
@property {Array} selection
|
35
|
+
This is the current selection. You can make this selection and another
|
36
|
+
controller's selection work in concert by binding them together. You
|
37
|
+
generally have a master selection that relays changes TO all the others.
|
38
|
+
*/
|
39
|
+
selection: function(key,value)
|
40
|
+
{
|
41
|
+
if (value !== undefined)
|
42
|
+
{
|
43
|
+
// selection must be an array... force if needed....
|
44
|
+
value = (value) ? ((value instanceof Array) ? value : [value]) : [];
|
45
|
+
|
46
|
+
var allowsSelection = this.get('allowsSelection');
|
47
|
+
var allowsEmptySelection = this.get('allowsEmptySelection');
|
48
|
+
var allowsMultipleSelection = this.get('allowsMultipleSelection');
|
49
|
+
|
50
|
+
// are we even allowing selection at all?
|
51
|
+
// if not, then bail out.
|
52
|
+
if ( !allowsSelection ) return this._selection;
|
53
|
+
|
54
|
+
// ok, new decide if the *type* of seleciton is allowed...
|
55
|
+
switch ( value.length )
|
56
|
+
{
|
57
|
+
case 0:
|
58
|
+
// check to see if we're attemting to set an empty array
|
59
|
+
// if that's not allowed, set to the first available item in arrangedObjects
|
60
|
+
this._selection = allowsEmptySelection ? value : (this.get('arrangedObjects') || []).first();
|
61
|
+
break;
|
62
|
+
case 1:
|
63
|
+
// check to see id we've just taken focus away from a new record
|
64
|
+
// if so, queue up the callback to be triggered...
|
65
|
+
if ( this._editingNewRecord && (this._editingNewRecord != value[0]) )
|
66
|
+
{
|
67
|
+
setTimeout(this._newRecordDidLoseFocus.bind(this,this._editingNewRecord),1);
|
68
|
+
this._editingNewRecord = null;
|
69
|
+
}
|
70
|
+
this._selection = value;
|
71
|
+
break;
|
72
|
+
default:
|
73
|
+
// fall through for >= 2 items...
|
74
|
+
// only allow if configured for multi-select
|
75
|
+
this._selection = allowsMultipleSelection ? value : this._selection;
|
76
|
+
break;
|
77
|
+
}
|
78
|
+
|
79
|
+
if ( this._selection && (this._selection.length > 1) )
|
80
|
+
{
|
81
|
+
var collection = this.get('content');
|
82
|
+
var order = collection ? collection.get('orderBy') : null;
|
83
|
+
if ( !order ) order = ['guid'];
|
84
|
+
this._selection = this._selection.sort(function(a,b) { return a.compareTo(b,order); });
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
return this._selection;
|
89
|
+
}.property(),
|
90
|
+
|
91
|
+
/**
|
92
|
+
If true, selection is allowed.
|
93
|
+
|
94
|
+
@type bool
|
95
|
+
*/
|
96
|
+
allowsSelection: true,
|
97
|
+
|
98
|
+
/**
|
99
|
+
If true, multiple selection is allowed.
|
100
|
+
|
101
|
+
@type bool
|
102
|
+
*/
|
103
|
+
allowsMultipleSelection: true,
|
104
|
+
|
105
|
+
/**
|
106
|
+
If true, allow empty selection
|
107
|
+
|
108
|
+
@type bool
|
109
|
+
*/
|
110
|
+
allowsEmptySelection: true,
|
111
|
+
|
112
|
+
/**
|
113
|
+
If true, new, add, remove will work.
|
114
|
+
|
115
|
+
@type bool
|
116
|
+
*/
|
117
|
+
canEditCollection: false,
|
118
|
+
|
119
|
+
/**
|
120
|
+
Set to the total number of items to show on a single page. If set to
|
121
|
+
zero, then no pagination will be performed.
|
122
|
+
|
123
|
+
@type number
|
124
|
+
*/
|
125
|
+
pageSize: 0,
|
126
|
+
|
127
|
+
/**
|
128
|
+
[RO] read only property with the current total number of pages.
|
129
|
+
*/
|
130
|
+
pageCount: function() {
|
131
|
+
var pageSize = this.get('pageSize') ;
|
132
|
+
if (pageSize <= 0) return 1 ;
|
133
|
+
|
134
|
+
var content = this.get('content') ;
|
135
|
+
var count = (content && content.get) ? content.get('count') : 0 ;
|
136
|
+
if (count === null) count = 0 ;
|
137
|
+
return Math.ceil(count / pageSize) ;
|
138
|
+
}.property(),
|
139
|
+
|
140
|
+
/**
|
141
|
+
Set to the current page. This will change the offset and limit shown in
|
142
|
+
the collection. If you try to set the page to a number greater than the
|
143
|
+
maximum, then it will be set to the last page.
|
144
|
+
*/
|
145
|
+
currentPage: function(key, value) {
|
146
|
+
if (value !== undefined) {
|
147
|
+
// constrain pages.
|
148
|
+
if (this._currentPage != value) {
|
149
|
+
var pc = Math.max(this.get('pageCount')-1,0);
|
150
|
+
if (value > pc) value = pc ;
|
151
|
+
if (value < 0) value = 0 ;
|
152
|
+
this._currentPage = value ;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
return this._currentPage || 0 ;
|
156
|
+
}.property(),
|
157
|
+
|
158
|
+
// ...................................
|
159
|
+
// METHODS
|
160
|
+
//
|
161
|
+
|
162
|
+
// adds a new object to the current collection, if allowed, and sets it
|
163
|
+
// as the current selection.
|
164
|
+
newObject: function(settings) {
|
165
|
+
var content = this.get('content') ;
|
166
|
+
if (!content || !this.get('canEditCollection')) return ; // only if allowed
|
167
|
+
try {
|
168
|
+
if (content.newRecord) {
|
169
|
+
var rec = content.newRecord(settings) ;
|
170
|
+
var controller = this ;
|
171
|
+
setTimeout(function() {
|
172
|
+
controller.set('selection',(rec) ? [rec] : []) ;
|
173
|
+
controller._editingNewRecord = rec ;
|
174
|
+
},1) ;
|
175
|
+
return rec;
|
176
|
+
}
|
177
|
+
}
|
178
|
+
catch (e) {
|
179
|
+
// DO SOMETHING
|
180
|
+
}
|
181
|
+
},
|
182
|
+
|
183
|
+
addObjects: function(objects) {
|
184
|
+
var content = this.get('content') ;
|
185
|
+
if (!content || !this.get('canEditCollection')) return ; // only if allowed
|
186
|
+
try {
|
187
|
+
objects = $A(arguments).flatten() ;
|
188
|
+
if (content.addRecords) {
|
189
|
+
content.addRecords(objects) ;
|
190
|
+
this.set('selection',(objects) ? objects : []) ;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
catch (e) {
|
194
|
+
// DO SOMETHING
|
195
|
+
}
|
196
|
+
},
|
197
|
+
|
198
|
+
addSelection: function() {
|
199
|
+
return this.addObjects(this.get('selection'));
|
200
|
+
},
|
201
|
+
|
202
|
+
// adds a new object to the current collection, if allowed, and sets it
|
203
|
+
// as the current selection.
|
204
|
+
removeObjects: function(objects) {
|
205
|
+
var content = this.get('content') ;
|
206
|
+
if (!content || !this.get('canEditCollection')) return ; // only if allowed
|
207
|
+
try {
|
208
|
+
objects = $A(arguments).flatten() ;
|
209
|
+
if (content.removeRecords) {
|
210
|
+
var rec = content.removeRecords(objects) ;
|
211
|
+
var sel = (this.get('selection') || []).without(objects) ;
|
212
|
+
this.set('selection',(sel) ? sel : []) ;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
catch (e) {
|
216
|
+
// DO SOMETHING
|
217
|
+
}
|
218
|
+
},
|
219
|
+
|
220
|
+
removeSelection: function() {
|
221
|
+
return this.removeObjects(this.get('selection')) ;
|
222
|
+
},
|
223
|
+
|
224
|
+
// this method is called if a new object was created through the controller
|
225
|
+
// and then the selection was changed from the new record without the
|
226
|
+
// record being saved first. By default, this will remove the object, but
|
227
|
+
// you could override it to just do a commit.
|
228
|
+
newObjectDidLoseFocus: function(rec) { rec.destroy() ; },
|
229
|
+
|
230
|
+
// ...................................
|
231
|
+
// PRIVATE
|
232
|
+
//
|
233
|
+
|
234
|
+
_newRecordDidLoseFocus: function(rec) {
|
235
|
+
if (rec.get('newRecord')) this.newObjectDidLoseFocus(rec) ;
|
236
|
+
},
|
237
|
+
|
238
|
+
// Update the current page.
|
239
|
+
_pageObserver: function() {
|
240
|
+
// get content -- nothing to do if no content.
|
241
|
+
var content = this.get('content') ;
|
242
|
+
if (content instanceof Array) content = content[0] ;
|
243
|
+
if (!content) return ;
|
244
|
+
|
245
|
+
var curOffset = content.get('offset') || 0 ;
|
246
|
+
var curLimit = content.get('limit') || 0 ;
|
247
|
+
var count = content.get('count') || 0 ;
|
248
|
+
|
249
|
+
// calculate the offset and limit.
|
250
|
+
var currentPage = this.get('currentPage') ;
|
251
|
+
var pageSize = this.get('pageSize') ;
|
252
|
+
var newOffset, newLimit ;
|
253
|
+
if (pageSize == 0) {
|
254
|
+
newOffset = 0; newLimit = 0 ;
|
255
|
+
} else {
|
256
|
+
newOffset = currentPage * pageSize ;
|
257
|
+
newLimit = pageSize ;
|
258
|
+
}
|
259
|
+
|
260
|
+
// set new page info.
|
261
|
+
if ((newOffset != curOffset) || (newLimit != curLimit)) {
|
262
|
+
content.beginPropertyChanges() ;
|
263
|
+
content.set('offset',newOffset) ;
|
264
|
+
content.set('limit',newLimit) ;
|
265
|
+
content.endPropertyChanges() ;
|
266
|
+
}
|
267
|
+
}.observes('currentPage','pageCount','pageSize'),
|
268
|
+
|
269
|
+
// invoked whenever the list changes. Updated the arrangedObjects and
|
270
|
+
// potentially the selection.
|
271
|
+
_recordsObserver: function(target,key,value) {
|
272
|
+
var old = this.get('arrangedObjects') ;
|
273
|
+
value = Array.asArray(target.get(key)) ;
|
274
|
+
|
275
|
+
this.set('arrangedObjects',value.slice()) ;
|
276
|
+
// update selection.
|
277
|
+
var objects = this.get('arrangedObjects') || [] ;
|
278
|
+
if (!(objects instanceof Array)) objects = [objects] ;
|
279
|
+
|
280
|
+
var currentSelection = this.get('selection') || [] ;
|
281
|
+
var sel = [] ;
|
282
|
+
|
283
|
+
// the new selection is the current selection that exists in
|
284
|
+
// arrangedObjects.
|
285
|
+
currentSelection.each(function(obj) {
|
286
|
+
if (objects.include(obj)) sel.push(obj) ;
|
287
|
+
}) ;
|
288
|
+
|
289
|
+
// make new selection match current selection settings.
|
290
|
+
if (!this.allowsSelection) {
|
291
|
+
sel = [] ;
|
292
|
+
}
|
293
|
+
|
294
|
+
if ((sel.length>1) && !this.allowsMultipleSelection) {
|
295
|
+
sel = [sel[0]] ;
|
296
|
+
}
|
297
|
+
|
298
|
+
if ((sel.length == 0) && !this.allowsEmptySelection) {
|
299
|
+
if (objects.length > 0) sel = [objects[0]] ;
|
300
|
+
}
|
301
|
+
|
302
|
+
this.set('selection',sel) ;
|
303
|
+
}.observes('records')
|
304
|
+
|
305
|
+
}) ;
|
@@ -0,0 +1,323 @@
|
|
1
|
+
// ========================================================================
|
2
|
+
// SproutCore
|
3
|
+
// copyright 2006-2007 Sprout Systems, Inc.
|
4
|
+
// ========================================================================
|
5
|
+
|
6
|
+
require('foundation/object') ;
|
7
|
+
|
8
|
+
/**
|
9
|
+
@class SC.Controller
|
10
|
+
|
11
|
+
The controller base class provides some common functions you will need
|
12
|
+
for controllers in your applications, especially related to maintaining
|
13
|
+
an editing context.
|
14
|
+
|
15
|
+
In general you will not use this class, but you can use a subclass such
|
16
|
+
as ObjectController, CollectionController, or ArrayController.
|
17
|
+
|
18
|
+
h2. EDITING CONTEXTS
|
19
|
+
|
20
|
+
One major function of a controller is to mediate between changes in the
|
21
|
+
UI and changes in the model. In particular, you usually do not want
|
22
|
+
changes you make in the UI to be applied to a model object directly.
|
23
|
+
Instead, you often will want to collect changes to an object and then
|
24
|
+
apply them only when the user is ready to commit their changes.
|
25
|
+
|
26
|
+
The editing contact support in the controller class will help you
|
27
|
+
provide this capability.
|
28
|
+
|
29
|
+
@extends SC.Object
|
30
|
+
*/
|
31
|
+
SC.Controller = SC.Object.extend(
|
32
|
+
/** @scope SC.Controller.prototype */
|
33
|
+
{
|
34
|
+
|
35
|
+
/**
|
36
|
+
The controller will set this property to true whenever there are
|
37
|
+
changes that need to be committed. This property is true whenever
|
38
|
+
the controller itself has uncommitted changes or when any dependent
|
39
|
+
editors have uncommitted changes. In your own subclass, call
|
40
|
+
this.objectDidChange(this) to register changes.
|
41
|
+
|
42
|
+
@type Boolean
|
43
|
+
*/
|
44
|
+
hasChanges: false,
|
45
|
+
|
46
|
+
/**
|
47
|
+
This is the controller's parent controller usually. The controller will
|
48
|
+
notify this controller when its changes are committed or discarded.
|
49
|
+
|
50
|
+
@type SC.Controller
|
51
|
+
*/
|
52
|
+
context: null,
|
53
|
+
|
54
|
+
/**
|
55
|
+
If this is false, then the controller will only commit changes when you
|
56
|
+
explicitly call commitChanges. Otherwise it will commit them
|
57
|
+
immediately. You usually want this set to false. It is initially set to
|
58
|
+
true for compatibility.
|
59
|
+
|
60
|
+
@type Boolean
|
61
|
+
*/
|
62
|
+
commitChangesImmediately: true,
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Sets the commitChangesImmediately to the parent context's value if a context was passed.
|
66
|
+
* The Controller also observes changes to the context property and adjusts the commitChangesImmediately prop
|
67
|
+
*/
|
68
|
+
init: function()
|
69
|
+
{
|
70
|
+
arguments.callee.base.apply(this,arguments);
|
71
|
+
this._contextObserver();
|
72
|
+
},
|
73
|
+
|
74
|
+
/**
|
75
|
+
* @private
|
76
|
+
*/
|
77
|
+
_contextObserver: function()
|
78
|
+
{
|
79
|
+
if ( this.context )
|
80
|
+
{
|
81
|
+
// inherit the parent contexts inherit property
|
82
|
+
this.commitChangesImmediately = this.context.commitChangesImmediately;
|
83
|
+
}
|
84
|
+
}.observes('context'),
|
85
|
+
|
86
|
+
/**
|
87
|
+
If the controller has uncommitted changes, call this method to
|
88
|
+
commit them. This method will commit the changes for any dependent
|
89
|
+
editors as well. This will return true if the commit completed and
|
90
|
+
false or an error object if it failed.
|
91
|
+
*/
|
92
|
+
commitChanges: function() {
|
93
|
+
this._commitTimeout = null ; // clear timeout
|
94
|
+
var ret = this._canCommitChanges() ;
|
95
|
+
if (!$ok(ret)) return ret ;
|
96
|
+
return this._performCommitChanges() ;
|
97
|
+
},
|
98
|
+
|
99
|
+
/**
|
100
|
+
If this controller has uncommitted changes that you do not want to keep,
|
101
|
+
call this method to discard them. This method will also discard
|
102
|
+
changes for any dependent editors as well.
|
103
|
+
*/
|
104
|
+
discardChanges: function() {
|
105
|
+
var ret = this._canDiscardChanges() ;
|
106
|
+
if (!$ok(ret)) return ret ;
|
107
|
+
return this._performDiscardChanges() ;
|
108
|
+
},
|
109
|
+
|
110
|
+
/**
|
111
|
+
This method will return an appropriate controller object for the
|
112
|
+
value of the property you name. This will return one of:
|
113
|
+
|
114
|
+
<table>
|
115
|
+
<tr> <th>Value Type</th> <th>Returns</th> </tr>
|
116
|
+
<tr> <td>Array-compatible</td> <td>SC.ArrayController</td></tr>
|
117
|
+
<tr> <td>SC.Collection</td> <td>SC.CollectionController</td></tr>
|
118
|
+
<tr> <td>Kind of SC.Object</td> <td>SC.ObjectController</td></tr>
|
119
|
+
<tr> <td>other</td> <td>value</td></tr>
|
120
|
+
</table>
|
121
|
+
|
122
|
+
This is a helper method used by subclasses to create the appropriate
|
123
|
+
type of controller.
|
124
|
+
|
125
|
+
*/
|
126
|
+
controllerForValue: function(value) {
|
127
|
+
var ret = null ;
|
128
|
+
switch($type(value)) {
|
129
|
+
case T_OBJECT:
|
130
|
+
if (value.kindOf(SC.Collection)) {
|
131
|
+
ret = SC.CollectionController ;
|
132
|
+
} else ret = SC.ObjectController ;
|
133
|
+
break ;
|
134
|
+
case T_ARRAY:
|
135
|
+
ret = SC.ArrayController ;
|
136
|
+
break ;
|
137
|
+
default:
|
138
|
+
ret = null ;
|
139
|
+
}
|
140
|
+
|
141
|
+
return (ret) ? ret.create({ content: value, context: this }) : value;
|
142
|
+
},
|
143
|
+
|
144
|
+
/**
|
145
|
+
Call this method whenever you have uncommitted changes. This will
|
146
|
+
handle notifying your parent context as well.
|
147
|
+
|
148
|
+
@param {SC.Controller} editor
|
149
|
+
This is the object that has uncommitted changes. Normally you should
|
150
|
+
not pass a value. If you do pass an object, then that object will
|
151
|
+
become a dependent editor of the receiver.
|
152
|
+
*/
|
153
|
+
editorDidChange: function(editor) {
|
154
|
+
if (!editor) editor = this ; // set default value
|
155
|
+
|
156
|
+
// if this is another editor, add it to the list of editors that need
|
157
|
+
// to be notified of a change.
|
158
|
+
if (editor != this) {
|
159
|
+
if (!this._dirtyEditors) this._dirtyEditors = SC.Set.create();
|
160
|
+
this._dirtyEditors.add(editor) ;
|
161
|
+
} else {
|
162
|
+
this._hasLocalChanges = true ;
|
163
|
+
}
|
164
|
+
if (!this.get('hasChanges')) {
|
165
|
+
this.set('hasChanges', true) ;
|
166
|
+
|
167
|
+
// if we have a parent context notify them
|
168
|
+
if (this.context) {
|
169
|
+
this.context.editorDidChange(this) ;
|
170
|
+
|
171
|
+
// otherwise, if commit changes immediately is true, schedule commit.
|
172
|
+
// commit is only done once per cycle so that at least all the
|
173
|
+
// changes you might make at one time will be batched.
|
174
|
+
} else if (this.get('commitChangesImmediately')) {
|
175
|
+
if (!this._commitTimeout) {
|
176
|
+
this._commitTimeout = this.commitChanges.bind(this).defer();
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
},
|
181
|
+
|
182
|
+
/**
|
183
|
+
Call this method when your object no longer has uncommitted changes.
|
184
|
+
This will clear your hasChanges property and notify your parent context.
|
185
|
+
This is called automatically whenever changes are committed or discarded
|
186
|
+
on your controller.
|
187
|
+
*/
|
188
|
+
editorDidClearChanges: function(editor) {
|
189
|
+
if (!editor) editor = this ; // set default value
|
190
|
+
|
191
|
+
if (editor != this) {
|
192
|
+
// if we are currently clearing changes, then we will clean up the
|
193
|
+
// hasChanges state and dirtyeditors in bulk when this is all done.
|
194
|
+
// so do nothing.
|
195
|
+
if (this._clearingChanges) return ;
|
196
|
+
if (this._dirtyEditors) this._dirtyEditors.remove(editor) ;
|
197
|
+
} else {
|
198
|
+
this._hasLocalChanges = false ;
|
199
|
+
}
|
200
|
+
|
201
|
+
// _dirtyEditors may be undefined so use !! to force this to a bool value.
|
202
|
+
var hasChanges = !!(this._hasLocalChanges || (this._dirtyEditors && this._dirtyEditors.length > 0)) ;
|
203
|
+
|
204
|
+
if (this.get('hasChanges') != hasChanges) {
|
205
|
+
this.set('hasChanges', hasChanges) ;
|
206
|
+
if (this.context) this.context.editorDidClearChanges(editor) ;
|
207
|
+
}
|
208
|
+
},
|
209
|
+
|
210
|
+
/**
|
211
|
+
Override this method to determine if your controller can commit the
|
212
|
+
changes. This should validate your changes. Return false or an error
|
213
|
+
object if you cannot commit the change. This method will not be called
|
214
|
+
unless hasChanges is true and all your dependent editors are return
|
215
|
+
true as well.
|
216
|
+
*/
|
217
|
+
canCommitChanges: function() {
|
218
|
+
return true ;
|
219
|
+
},
|
220
|
+
|
221
|
+
/**
|
222
|
+
Override this method to actually commit the changes for your controller.
|
223
|
+
This will only be called if all controllers indicate that they can
|
224
|
+
commit. Return true if you succeeded or false or an error if you failed.
|
225
|
+
*/
|
226
|
+
performCommitChanges: function() {
|
227
|
+
return $error('performCommitChanges is not implemented') ;
|
228
|
+
},
|
229
|
+
|
230
|
+
/**
|
231
|
+
Override this method to determine if your controller can discard the
|
232
|
+
changes it has built up. This method will not be called unless you
|
233
|
+
have set hasChanges to true. Return false or an error object if you
|
234
|
+
cannot discard the change.
|
235
|
+
*/
|
236
|
+
canDiscardChanges: function() {
|
237
|
+
return true ;
|
238
|
+
},
|
239
|
+
|
240
|
+
/**
|
241
|
+
Override this method to actually discard the changes for your controller.
|
242
|
+
This will only be called if all controllers indicate that they can discard
|
243
|
+
their changes. Return true if you succeed or false or an error if you
|
244
|
+
failed.
|
245
|
+
*/
|
246
|
+
performDiscardChanges: function() {
|
247
|
+
return $error('performDiscardChanges is not implemented');
|
248
|
+
},
|
249
|
+
|
250
|
+
// ....................................
|
251
|
+
// PRIVATE
|
252
|
+
|
253
|
+
_canCommitChanges: function() {
|
254
|
+
if (!this.get('hasChanges')) return false ;
|
255
|
+
|
256
|
+
// validate editors.
|
257
|
+
var ret = true ;
|
258
|
+
if (this._dirtyEditors) {
|
259
|
+
ret = this._dirtyEditors.invokeWhile(true, '_canCommitChanges') ;
|
260
|
+
if (!$ok(ret)) return ret ;
|
261
|
+
}
|
262
|
+
|
263
|
+
// then validate receiver
|
264
|
+
return this.canCommitChanges() ;
|
265
|
+
},
|
266
|
+
|
267
|
+
_performCommitChanges: function() {
|
268
|
+
if (!this.get('hasChanges')) return true ;
|
269
|
+
|
270
|
+
// first commit any editors. If not successful, return. otherwise,
|
271
|
+
// clear editors.
|
272
|
+
var ret = true ;
|
273
|
+
if (this._dirtyEditors) {
|
274
|
+
this._clearingChanges = true ;
|
275
|
+
ret = this._dirtyEditors.invokeWhile(true, '_performCommitChanges') ;
|
276
|
+
this._clearingChanges = false ;
|
277
|
+
|
278
|
+
if ($ok(ret)) {
|
279
|
+
this._dirtyEditors = null ;
|
280
|
+
} else return ret ;
|
281
|
+
}
|
282
|
+
|
283
|
+
// now commit changes for the receiver.
|
284
|
+
ret = this.performCommitChanges() ;
|
285
|
+
if ($ok(ret)) this.editorDidClearChanges() ;
|
286
|
+
return ret ;
|
287
|
+
},
|
288
|
+
|
289
|
+
_canDiscardChanges: function() {
|
290
|
+
if (!this.get('hasChanges')) return false ;
|
291
|
+
// validate editors.
|
292
|
+
var ret = true ;
|
293
|
+
if (this._dirtyEditors) {
|
294
|
+
ret = this._dirtyEditors.invokeWhile(true, '_canDiscardChanges') ;
|
295
|
+
if (!$ok(ret)) return ret ;
|
296
|
+
}
|
297
|
+
|
298
|
+
// then validate receiver
|
299
|
+
return this.canDiscardChanges() ;
|
300
|
+
},
|
301
|
+
|
302
|
+
_performDiscardChanges: function() {
|
303
|
+
if (!this.get('hasChanges')) return true ;
|
304
|
+
|
305
|
+
// first discard changes for any editors. If not successful, return.
|
306
|
+
// otherwise, clear editors.
|
307
|
+
var ret = true ;
|
308
|
+
if (this._dirtyEditors) {
|
309
|
+
this._clearingChanges = true ;
|
310
|
+
ret = this._dirtyEditors.invokeWhile(true, '_performDiscardChanges') ;
|
311
|
+
this._clearingChanges = false ;
|
312
|
+
if ($ok(ret)) {
|
313
|
+
this._dirtyEditors = null ;
|
314
|
+
} else return ret ;
|
315
|
+
}
|
316
|
+
|
317
|
+
// now discard changes for the receiver.
|
318
|
+
ret = this.performDiscardChanges() ;
|
319
|
+
if ($ok(ret)) this.editorDidClearChanges() ;
|
320
|
+
return ret ;
|
321
|
+
}
|
322
|
+
|
323
|
+
}) ;
|