sproutcore 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +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
|
+
}) ;
|