1 /*jshint bitwise:false, indent:2, curly:true, eqeqeq:true, immed:true,
2 latedef:true, newcap:true, noarg:true, regexp:true, undef:true,
3 trailing:true, white:true, strict: false*/
4 /*global XM:true, XV:true, XT:true, _:true, enyo:true*/
8 var _events = "change readOnlyChange statusChange refreshView";
10 XV.RelationsEditorMixin = enyo.mixin({
18 onValueChange: "controlValueChanged"
20 bind: function (action) {
22 this.value[action](_events, this.attributesChanged, this);
23 this.value[action]("notify", this.notify, this);
24 if (this.value.meta) {
25 this.value.meta[action]("change", this.attributesChanged, this);
29 destroy: function () {
32 this.inherited(arguments);
34 setValue: function (value) {
38 this.attributesChanged(value);
39 if (this.valueChanged) { this.valueChanged(value); }
45 @name XV.RelationsEditor
46 @class Use to define the editor for {@link XV.ListRelationsEditorBox}.
49 var editor = enyo.mixin({
50 name: "XV.RelationsEditor",
52 }, XV.RelationsEditorMixin);
56 @name XV.ListRelationsEditorBox
57 @class Provides a container in which its components
58 are a vertically stacked group of horizontal rows.<br />
59 Made up of a header, panels, and a row of navigation buttons.<br />
60 Must include a component called `list`.
61 List must be of subkind {@link XV.ListRelations}.
62 The `value` must be set to a collection of `XM.Model`.
65 enyo.kind(/** @lends XV.ListRelationsEditorBox# */{
66 name: "XV.ListRelationsEditorBox",
68 classes: "panel xv-relations-editor-box",
86 onSelect: "selectionChanged",
87 onDeselect: "selectionChanged",
88 onTransitionFinish: "transitionFinished",
89 onValueChange: "controlValueChanged"
92 @todo Document the attrChanged method.
94 attrChanged: function () {
95 this.$.list.setAttr(this.attr);
98 @todo Document the controlValueChanged method.
100 controlValueChanged: function () {
101 // this is getting called before the list is created
102 //this.$.list.refresh();
106 @todo Document the create method.
108 create: function () {
109 this.inherited(arguments);
110 var editor = this.getEditor(),
115 this.createComponent({
116 kind: "onyx.GroupboxHeader",
117 content: this.getTitle()
124 arrangerKind: "CollapsingArranger",
126 {kind: editor, name: "editor"},
127 {kind: this.getListRelations(), name: "list",
128 attr: this.getAttr()}
131 control = this.createComponent(panels);
135 this.createComponent({
136 kind: "FittableColumns",
137 name: "navigationButtonPanel",
138 classes: "xv-buttons",
140 {kind: "onyx.Button", name: "newButton", onclick: "newItem",
141 classes: "icon icon-plus"},
142 {kind: "onyx.Button", name: "deleteButton", onclick: "deleteItem",
143 disabled: true, classes: "icon-minus"},
144 {kind: "onyx.Button", name: "prevButton", onclick: "prevItem",
145 disabled: true, classes: "icon-chevron-left"},
146 {kind: "onyx.Button", name: "nextButton", onclick: "nextItem",
147 disabled: true, classes: "icon-chevron-right"},
148 {kind: "onyx.Button", name: "doneButton", onclick: "doneItem",
149 disabled: true, classes: "icon-ok"},
150 {kind: "onyx.Button", name: "expandButton", ontap: "launchWorkspace",
151 classes: "icon-resize-full"}
154 this.$.expandButton.setShowing(_.isString(this.getChildWorkspace()));
158 Marks the model of the selected item to be deleted on save
159 and remove it from its parent collection and the Enyo list
161 deleteItem: function () {
162 var index = this.$.list.getFirstSelected(),
163 model = index ? this.$.list.getModel(index) : null;
164 this.$.list.getSelection().deselect(index, false);
166 this.$.list.lengthChanged();
168 destroy: function () {
170 this.getValue().off("add remove", this.valueChanged, this);
171 this.inherited(arguments);
174 Disables or enables the view
176 disabledChanged: function () {
177 this.$.newButton.setDisabled(this.getDisabled());
178 // complicated logic we need to disable and enable the
179 // done and delete buttons is here:
180 this.selectionChanged();
181 this.$.expandButton.setDisabled(this.getDisabled());
184 Close the edit session and return to read-only summary view
186 doneItem: function () {
187 var index = this.$.list.getFirstSelected(),
188 selection = this.$.list.getSelection();
189 if (this.validate() && index) {
190 selection.deselect(index);
191 if (this.$.list.$.page0 && !this.$.list.$.page0.hasNode()) {
192 XT.log("Warning: page0 doesn't hasNode");
193 } else if (this.$.list.$.page1 && !this.$.list.$.page1.hasNode()) {
194 XT.log("Warning: page1 doesn't hasNode");
196 this.$.list.render();
199 error: function (model, error) {
204 this.doError(inEvent);
206 launchWorkspace: function (inSender, inEvent) {
207 var index = Number(this.$.list.getFirstSelected());
208 this.doChildWorkspace({
209 workspace: this.getChildWorkspace(),
210 collection: this.getValue(),
212 listRelations: this.$.list
217 Add a new model to the collection and bring up a blank editor to fill it in
219 newItem: function () {
220 var collection = this.$.list.getValue(),
221 Klass = collection.model,
222 model = new Klass(null, {isNew: true}),
223 components = this.$.editor.getComponents(),
227 if (this.validate()) {
228 this.$.editor.clear();
229 collection.add(model);
230 if (collection.comparator) { collection.sort(); }
232 // Exclude models marked for deletion
233 length = _.filter(collection.models, function (model) {
234 return !model.isDestroyed();
236 this.$.list.select(length - 1);
238 // Scroll to top and set focus on first available widget
239 scroller = _.find(components, function (c) {
240 return c instanceof enyo.Scroller;
242 if (scroller) { scroller.scrollToTop(); }
243 first = _.find(components, function (c) {
244 return c.attr && !model.isReadOnly(c.attr);
246 if (first && first.focus) {
252 Move to edit the next item in the collection.
254 nextItem: function () {
255 var index = this.$.list.getFirstSelected() - 0;
256 if (this.validate()) {
257 this.$.list.select(index + 1);
261 Move to edit the previous line in the collection.
263 prevItem: function () {
264 var index = this.$.list.getFirstSelected() - 0;
265 if (this.validate()) {
266 this.$.list.select(index - 1);
270 @todo Document the selectionChanged method.
272 selectionChanged: function () {
273 var index = this.$.list.getFirstSelected(),
274 model = index ? this.$.list.getModel(index) : null,
278 this.$.deleteButton.setDisabled(true);
279 this.$.doneButton.setDisabled(!index || this.getDisabled());
281 model.on("invalid", this.error, this); // Error event binding
282 this.$.editor.setValue(model);
284 model.isBusy() && model._prevStatus === K.READY_NEW) {
285 this.$.deleteButton.setDisabled(this.getDisabled());
288 success: function (resp) {
289 if (that.$.deleteButton) { // Sometimes the workspace has been closed
290 that.$.deleteButton.setDisabled(resp || that.getDisabled());
295 if (this.$.panels.getIndex()) { this.$.panels.setIndex(0); }
296 this.$.prevButton.setDisabled(index - 0 === 0);
297 this.$.nextButton.setDisabled(index - 0 === this.$.list.value.length - 1);
299 if (!this.$.panels.getIndex()) { this.$.panels.setIndex(1); }
300 this.$.prevButton.setDisabled(true);
301 this.$.nextButton.setDisabled(true);
305 @todo Document the transitionFinished method.
307 transitionFinished: function (inSender, inEvent) {
308 if (inEvent.originator.name === 'panels') {
309 if (this.$.panels.getIndex() === 1) {
316 Remove current model bindings.
318 unbind: function () {
319 var model = this.$.editor.getValue();
321 model.off("invalid", this.error, this);
325 Returns whether a selected model is validate. If
326 none selected returns `true`. If an error is found
327 an error event is raised.
331 validate: function () {
332 var list = this.$.list,
333 index = list.getFirstSelected() - 0,
334 model = isNaN(index) ? false : list.getModel(index),
335 error = model ? model.validate(model.attributes) : false;
337 this.error(model, error);
343 @todo Document the valueChanged method.
345 valueChanged: function () {
346 var value = this.getValue();
347 // Make sure list refreshes if collection changed
349 value.once("add remove", this.valueChanged, this);
351 this.$.list.setValue(value);