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.$.list.refresh();
105 @todo Document the create method.
107 create: function () {
108 this.inherited(arguments);
109 var editor = this.getEditor(),
114 this.createComponent({
115 kind: "onyx.GroupboxHeader",
116 content: this.getTitle()
123 arrangerKind: "CollapsingArranger",
125 {kind: editor, name: "editor"},
126 {kind: this.getListRelations(), name: "list",
127 attr: this.getAttr()}
130 control = this.createComponent(panels);
134 this.createComponent({
135 kind: "FittableColumns",
136 name: "navigationButtonPanel",
137 classes: "xv-buttons",
139 {kind: "onyx.Button", name: "newButton", onclick: "newItem",
140 classes: "icon icon-plus"},
141 {kind: "onyx.Button", name: "deleteButton", onclick: "deleteItem",
142 disabled: true, classes: "icon-minus"},
143 {kind: "onyx.Button", name: "prevButton", onclick: "prevItem",
144 disabled: true, classes: "icon-chevron-left"},
145 {kind: "onyx.Button", name: "nextButton", onclick: "nextItem",
146 disabled: true, classes: "icon-chevron-right"},
147 {kind: "onyx.Button", name: "doneButton", onclick: "doneItem",
148 disabled: true, classes: "icon-ok"},
149 {kind: "onyx.Button", name: "expandButton", ontap: "launchWorkspace",
150 classes: "icon-resize-full"}
153 this.$.expandButton.setShowing(_.isString(this.getChildWorkspace()));
157 Marks the model of the selected item to be deleted on save
158 and remove it from its parent collection and the Enyo list
160 deleteItem: function () {
161 var index = this.$.list.getFirstSelected(),
162 model = index ? this.$.list.getModel(index) : null;
163 this.$.list.getSelection().deselect(index, false);
165 this.$.list.lengthChanged();
167 destroy: function () {
169 this.getValue().off("add remove", this.valueChanged, this);
170 this.inherited(arguments);
173 Disables or enables the view
175 disabledChanged: function () {
176 this.$.newButton.setDisabled(this.getDisabled());
177 // complicated logic we need to disable and enable the
178 // done and delete buttons is here:
179 this.selectionChanged();
180 this.$.expandButton.setDisabled(this.getDisabled());
183 Close the edit session and return to read-only summary view
185 doneItem: function () {
186 var index = this.$.list.getFirstSelected(),
187 selection = this.$.list.getSelection();
188 if (this.validate() && index) {
189 selection.deselect(index);
190 if (this.$.list.$.page0 && !this.$.list.$.page0.hasNode()) {
191 XT.log("Warning: page0 doesn't hasNode");
192 } else if (this.$.list.$.page1 && !this.$.list.$.page1.hasNode()) {
193 XT.log("Warning: page1 doesn't hasNode");
195 this.$.list.render();
198 error: function (model, error) {
203 this.doError(inEvent);
205 launchWorkspace: function (inSender, inEvent) {
206 var index = Number(this.$.list.getFirstSelected());
207 this.doChildWorkspace({
208 workspace: this.getChildWorkspace(),
209 collection: this.getValue(),
211 listRelations: this.$.list
216 Add a new model to the collection and bring up a blank editor to fill it in
218 newItem: function () {
219 var collection = this.$.list.getValue(),
220 Klass = collection.model,
221 model = new Klass(null, {isNew: true}),
222 components = this.$.editor.getComponents(),
226 if (this.validate()) {
227 this.$.editor.clear();
228 collection.add(model);
229 if (collection.comparator) { collection.sort(); }
231 // Exclude models marked for deletion
232 length = _.filter(collection.models, function (model) {
233 return !model.isDestroyed();
235 this.$.list.select(length - 1);
237 // Scroll to top and set focus on first available widget
238 scroller = _.find(components, function (c) {
239 return c instanceof enyo.Scroller;
241 if (scroller) { scroller.scrollToTop(); }
242 first = _.find(components, function (c) {
243 return c.attr && !model.isReadOnly(c.attr);
245 if (first && first.focus) {
251 Move to edit the next item in the collection.
253 nextItem: function () {
254 var index = this.$.list.getFirstSelected() - 0;
255 if (this.validate()) {
256 this.$.list.select(index + 1);
260 Move to edit the previous line in the collection.
262 prevItem: function () {
263 var index = this.$.list.getFirstSelected() - 0;
264 if (this.validate()) {
265 this.$.list.select(index - 1);
269 @todo Document the selectionChanged method.
271 selectionChanged: function () {
272 var index = this.$.list.getFirstSelected(),
273 model = index ? this.$.list.getModel(index) : null,
277 this.$.deleteButton.setDisabled(true);
278 this.$.doneButton.setDisabled(!index || this.getDisabled());
280 model.on("invalid", this.error, this); // Error event binding
281 this.$.editor.setValue(model);
283 model.isBusy() && model._prevStatus === K.READY_NEW) {
284 this.$.deleteButton.setDisabled(this.getDisabled());
287 success: function (resp) {
288 if (that.$.deleteButton) { // Sometimes the workspace has been closed
289 that.$.deleteButton.setDisabled(resp || that.getDisabled());
294 if (this.$.panels.getIndex()) { this.$.panels.setIndex(0); }
295 this.$.prevButton.setDisabled(index - 0 === 0);
296 this.$.nextButton.setDisabled(index - 0 === this.$.list.value.length - 1);
298 if (!this.$.panels.getIndex()) { this.$.panels.setIndex(1); }
299 this.$.prevButton.setDisabled(true);
300 this.$.nextButton.setDisabled(true);
304 @todo Document the transitionFinished method.
306 transitionFinished: function (inSender, inEvent) {
307 if (inEvent.originator.name === 'panels') {
308 if (this.$.panels.getIndex() === 1) {
315 Remove current model bindings.
317 unbind: function () {
318 var model = this.$.editor.getValue();
320 model.off("invalid", this.error, this);
324 Returns whether a selected model is validate. If
325 none selected returns `true`. If an error is found
326 an error event is raised.
330 validate: function () {
331 var list = this.$.list,
332 index = list.getFirstSelected() - 0,
333 model = isNaN(index) ? false : list.getModel(index),
334 error = model ? model.validate(model.attributes) : false;
336 this.error(model, error);
342 @todo Document the valueChanged method.
344 valueChanged: function () {
345 var value = this.getValue();
346 // Make sure list refreshes if collection changed
348 value.once("add remove", this.valueChanged, this);
350 this.$.list.setValue(value);