1 /*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
2 newcap:true, noarg:true, regexp:true, undef:true, trailing:true,
4 /*global enyo:true, XM:true, XT:true, XV:true, _:true, console:true */
10 @class An input control for the Advanced Search feature
11 in which the user specifies one or more search parameters.<br />
12 Represents one search parameter.
13 A component of {@link XV.ParameterWidget}.
16 /** @lends XV.ParameterItem# */{
17 name: "XV.ParameterItem",
18 classes: "xv-parameter-item",
25 isCharacteristic: false
31 onValueChange: "parameterChanged"
34 {name: "input", classes: "xv-parameter-item-input"}
36 defaultKind: "XV.InputWidget",
38 Sets up widget in parameter item.
41 this.inherited(arguments);
43 if (!this.getOperator() && this.defaultKind === "XV.InputWidget") {
44 this.setOperator("MATCHES");
45 } else if (this.$.input instanceof XV.Picker) {
46 this.$.input.setNoneText("_any".loc());
50 Sets the label value of the parameter item to that
51 specified in the kind definition.
53 labelChanged: function () {
54 this.$.input.setLabel(this.label);
57 Returns the search parameter object.
59 getParameter: function () {
61 attr = this.getAttr(),
62 value = this.getValue();
63 if (attr && value !== undefined && value !== null && value !== "") {
66 operator: this.getOperator(),
67 isCharacteristic: this.getIsCharacteristic(),
74 Returns the value of the parameter.
76 getValue: function () {
77 var value = this.$.input.getValue();
78 if (value && this.$.input.valueAttribute) {
79 value = value.get(this.$.input.valueAttribute);
84 This stores the originating parameter item and it's value
85 in an event and bubbles up a parameter change to the
88 parameterChanged: function () {
89 var inEvent = { value: this.getValue, originator: this };
90 this.doParameterChange(inEvent);
91 return true; // stop right here
93 setDisabled: function (disabled) {
94 if (this.$.input.setDisabled) {
95 return this.$.input.setDisabled(disabled);
100 Sets the value of the parameter item.
102 setValue: function (value, options) {
103 this.$.input.setValue(value, options);
109 @name XV.ParameterWidget
110 @class Contains a set of fittable rows to implement the Advanced Search feature.<br />
111 Each row is a {@link XV.ParameterItem} and represents a parameter on which
112 the user can narrow the search results.<br />
113 Derived from <a href="http://enyojs.com/api/#enyo.FittableRows">enyo.FittableRows</a>.
114 @extends enyo.FittableRows
116 enyo.kind(enyo.mixin(/** @lends XV.ParameterWidget# */{
117 name: "XV.ParameterWidget",
118 kind: "FittableRows",
119 classes: "xv-groupbox xv-parameter",
121 onItemSave: "saveItem",
122 onItemChange: "loadItem",
123 onParameterChange: "parameterChanged"
126 onParameterChange: ""
129 characteristicsRole: undefined,
130 showSaveFilter: true,
136 defaultParameters: null,
137 defaultKind: "XV.ParameterItem",
140 Setup function for the parameter widget which adds the
141 item management forms to the top of the kind and the
142 characteristics to the bottom of the kind. Also loads
143 the last filter choice and sets the parameter values for
146 create: function () {
147 var role = this.getCharacteristicsRole(),
148 K = XM.Characteristic,
153 this.inherited(arguments);
154 this.processExtensions();
155 this.isAllSetUp = true;
159 if (XM.characteristics.where(hash).length) {
161 this.createComponent({
162 kind: "onyx.GroupboxHeader",
163 content: "_characteristics".loc()
166 // Process text and list
167 chars = XM.characteristics.filter(function (char) {
168 var type = char.get('characteristicType');
169 return char.get(role) &&
170 char.get('isSearchable') &&
171 (type === K.TEXT || type === K.LIST);
174 _.each(chars, function (char) {
177 name: char.get('name') + "Characteristic",
178 label: char.get('name'),
179 isCharacteristic: true,
180 attr: char.get('name')
182 if (char.get('characteristicType') === K.LIST) {
184 kind: "XV.PickerWidget",
185 idAttribute: "value",
186 nameAttribute: "value",
187 create: function () {
188 this.inherited(arguments);
191 filteredList: function () {
192 return char.get('options').models;
194 getValue: function () {
195 return this.value ? this.value.get('value') : null;
198 hash.defaultKind = kind;
200 that.createComponent(hash);
204 chars = XM.characteristics.filter(function (char) {
205 var type = char.get('characteristicType');
206 return char.get(role) &&
207 char.get('isSearchable') &&
211 _.each(chars, function (char) {
212 that.createComponent({
213 kind: "onyx.GroupboxHeader",
214 content: char.get('name').loc()
218 name: char.get('name') + "FromCharacteristic",
219 label: "_from".loc(),
220 filterLabel: char.get('name') + " " + "_from".loc(),
222 attr: char.get('name'),
223 isCharacteristic: true,
224 defaultKind: "XV.DateWidget"
226 that.createComponent(hash);
229 name: char.get('name') + "ToCharacteristic",
231 filterLabel: char.get('name') + " " + "_to".loc(),
233 attr: char.get('name'),
234 isCharacteristic: true,
235 defaultKind: "XV.DateWidget"
237 that.createComponent(hash);
241 // Sets published defaultFilter with the params object
242 // from the defaultParameters in the parameter widget (if exists)
243 this.setDefaultFilter(_.result(this, 'defaultParameters'));
245 // The "null" value of addBefore will add this
246 // component to the beginning of the array.
247 if (this.getShowSaveFilter()) {
248 compArray.push({kind: "XV.FilterForm", name: "filterForm", addBefore: null});
251 if (this.getShowLayout()) {
252 compArray.push({kind: "XV.LayoutForm", name: "layoutForm", addBefore: null});
255 this.createComponents(compArray, {owner: this});
256 this.populateFromUserPref();
260 Sends list object from the parent kind to the layout form
262 @param {Object} index
264 buildColumnList: function (list) {
265 this.$.layoutForm.addColumns(list);
268 When the default filter is set, populate the
269 params with the values.
271 defaultFilterChanged: function () {
272 // if defaults are null, this does nothing
273 this.populateParameters(this.getDefaultFilter());
276 Returns an array of the parameter search objects.
278 getParameters: function () {
283 for (i = 0; i < this.children.length; i++) {
284 child = this.children[i];
285 param = child && child.showing && child.getParameter ?
286 child.getParameter() : null;
288 // using union instead of push here in case param is an array of params
289 params = _.union(params, param);
295 Retrieves parameter values. By default returns values as human readable
296 strings. Boolean options are:<br />
297 * name - If true returns the parameter item control name, otherwise returns the label.<br />
298 * value - If true returns the control value, other wise returns a human readable string.<br />
299 * deltaDate - If true returns as string for the date difference for date widgets. (i.e. "+5").
300 @param {Object} options
302 getSelectedValues: function (options) {
303 options = options || {};
314 for (componentName in this.$) {
315 if (this.$[componentName] instanceof XV.ParameterItem &&
316 this.$[componentName].showing &&
317 this.$.hasOwnProperty(componentName)) {
318 component = this.$[componentName];
319 value = component.getValue();
320 label = options.name ?
321 component.getName() :
322 component.getFilterLabel() || component.getLabel();
323 control = component.$.input;
324 // Special handling for toggle buttons that work opposite,
325 // meaning only show me when it's "off"
326 if (component.$.input.kind === 'XV.ToggleButtonWidget') {
328 values[label] = value;
329 } else if (value === false) {
330 values[label] = control.getValueToString ? control.getValueToString() : value;
332 } else if (value !== undefined && value !== null && value !== "") {
333 if (options.deltaDate &&
334 component.$.input.kind === 'XV.DateWidget') {
335 today = XT.date.today();
336 date = XT.date.applyTimezoneOffset(control.getValue(), true);
337 days = XT.date.daysBetween(date, today);
338 days = days < 0 ? "" + days : "+" + days;
339 values[label] = days;
340 } else if (options.value) {
341 values[label] = value instanceof XM.Model ? value.id : value;
343 values[label] = control.getValueToString ?
344 control.getValueToString() : value;
351 // implementation is up to subkinds or monkeypatches
352 parameterChanged: function (inSender, inEvent) {
355 Receives the event from the item form with the user-
356 entered values and combines these with the other item values
358 If the item name already exists for this type, it performs an edit
359 instead of a new insert.
361 saveItem: function (inSender, inEvent) {
362 var Klass = XT.getObjectByName("XM.Filter"),
363 model = inEvent.model,
364 options = {}, attrs = {},
369 if (inSender.itemType === "filter") {
370 params = JSON.stringify(this.getSelectedValues({
375 } else if (inSender.itemType === "layout") {
376 params = JSON.stringify(this.$.layoutForm.getColumnValues());
380 // there is not already a model
381 model = new Klass(null, {isNew: true});
383 // set the values from the save event and save
384 model.set("name", inEvent.itemName);
385 model.set("shared", inEvent.isShared);
386 model.set("params", params);
387 model.set("kind", kind);
388 model.set("type", inSender.itemType);
390 options.success = function (model, resp, options) {
391 // dear item form, we're all done here!
392 inEvent.callback(model);
394 if (model.isDirty()) {
395 model.save(null, options);
397 options.success(model);
401 Gets the model from the change
402 event and populates the fields with parameter
403 values from the model.
405 loadItem: function (inSender, inEvent) {
406 var model = inEvent.model,
407 params = model ? model.get("params") : null,
408 type = model ? model.get("type") : "filter";
410 if (type === "filter") {
411 this.setCurrentFilter(model);
412 // clear out the existing filters before populating this one
413 this.clearParameters();
414 // if there are no parameters, take the defaults
415 params = params ? params : this.getDefaultFilter();
416 // populate the parameters with filter or defaults
417 this.populateParameters(params);
418 } else if (type === "layout") {
419 this.setCurrentLayout(model);
420 this.populateLayout(params);
422 this.saveToUserPref();
425 Clear all of the currently loaded parameters and bubble
426 parameter change events.
428 clearParameters: function () {
429 _.each(this.$, function (item) {
430 if (item instanceof XV.ParameterItem && item.showing) {
431 item.$.input.clear({silent: true});
434 if (this._init) { this.doParameterChange(); }
437 Loops through the values in the params object
438 and sets the value on attribute fields in the
439 layout tree. Bubbles up a change event after
440 all of the fields are set.
442 @param {Object|String} params
444 populateLayout: function (params) {
445 params = _.isObject(params) ? params : JSON.parse(params);
446 this.$.layoutForm.loadColumns(params);
449 Loops through the values in the params object
450 and sets the value on the parameter fields and
451 bubbles up the change events.
453 @param {String} params
455 populateParameters: function (params) {
456 params = _.isObject(params) ? params : JSON.parse(params);
457 _.each(params, function (value, key) {
458 var param = this.$[key];
460 param.setValue(value, {silent: true});
464 if (this._init) { this.doParameterChange(); }
467 Reads the last filter value from the user preferences
468 and populates the filter picker with this value.
470 populateFromUserPref: function () {
477 // we have a cache of preferences, so find the last
478 // selected preferences for this kind
479 lastPref = XT.DataSource.getUserPreference(kind);
481 // if there is a last preference for this kind,
482 // and it isn't null, set the pickers.
483 if (lastPref && lastPref !== "null") {
484 lastPref = JSON.parse(lastPref);
485 lastFilter = lastPref.filter || {};
486 lastLayout = lastPref.layout || {};
487 if (this.getShowSaveFilter()) {
488 this.$.filterForm.$.itemPicker.setValue(lastFilter.uuid);
490 if (this.getShowLayout()) {
491 this.$.layoutForm.$.itemPicker.setValue(lastLayout.uuid);
496 Saves the last selected filter and layout to the user
499 saveToUserPref: function () {
504 filterModel = this.getCurrentFilter(),
505 layoutModel = this.getCurrentLayout();
507 // setup the parms object with the filter and layout
508 params.filter = filterModel || {};
509 params.layout = layoutModel || {};
510 payload = JSON.stringify(params);
512 // save the last selected filter and layout to the user preference
513 operation = XT.DataSource.getUserPreference(kind) ? "replace" : "add";
514 XT.DataSource.saveUserPreference(kind, payload, operation);
515 // save this item to the preference cache
516 XT.session.preferences.set(kind, payload);
519 Accepts an array of items to push into the parameter items. Matches item name
520 to component under the assumption that there will be a component by the
521 name of the incoming name (which itself is the name of the model attribute).
522 Fails silently if it cannot find the name.
525 @param {Array|String} [item.name] A string *or an array of strings* with the name or attr of
526 the attribute to be updated. In the case of an array, this function will set
527 any component that matches any of the names, and ignore the rest.
528 @param {Object|String|Number} [item.value] The payload of the setValue to the ParameterItem.
529 @param {Boolean} [item.showing] Set to false to completely hide and disable the parameter.
531 setParameterItemValues: function (items) {
533 setValueOnMatch = function (name, item) {
534 var control = that.$[name] || _.find(that.$, function (ctl) {
535 // look up controls by name or by the model attribute they map to
536 return ctl.attr === name;
539 if (item.showing !== false) {
540 control.setValue(item.value, {silent: true});
542 control.setShowing(item.showing !== false);
546 _.each(items, function (item) {
547 if (typeof item.name === 'string') {
548 // string case. set the component by this name
549 setValueOnMatch(item.name, item);
550 } else if (typeof item.name === 'object') {
551 // array case. loop through item.name and set any matches
552 _.each(item.name, function (subname) {
553 setValueOnMatch(subname, item);
558 }, XV.ExtensionsMixin));
561 Generalized form for supporting saved filters, layouts, and sorts.
562 This component expects a picker for selecting
563 saved items, a save drawer for saving the current item,
564 and a manage drawer for sharing and deleting items.
566 TODO: Move common components here from Filter and Layout forms
568 enyo.kind(/** @lends XV.UserItemForm# */{
569 name: "XV.UserItemForm",
570 classes: "xv-filter-form",
576 onValueChange: "valueChanged",
577 onListChange: "listChanged"
582 Opens the save drawer and switches the drawer icons.
584 activateSave: function (inSender, inEvent) {
585 this.$.saveDrawer.setOpen(!this.$.saveDrawer.open);
586 this.$.saveTopDrawer.changeIcon(this.$.saveDrawer.open);
589 Opens the manage filter drawer and switches the
592 activateManage: function (inSender, inEvent) {
593 this.$.filterList.reset();
594 this.$.manageDrawer.setOpen(!this.$.manageDrawer.open);
595 this.$.manageTopDrawer.changeIcon(this.$.manageDrawer.open);
598 This is called by the save/apply button in the
599 save drawer. It checks for an existing filter and
600 shows the warning popup or continues to the save.
602 checkExisting: function (inSender, inEvent) {
603 var name = this.$.itemName.getValue() ?
604 this.$.itemName.getValue().trim() : "",
605 exists = this.itemExists(name);
607 this.$.existingPopup.show();
609 this.saveItem(inSender, inEvent);
613 There was a change to the list and a model
614 was added or removed. Refresh list and picker.
616 listChanged: function (inSender, inEvent) {
617 inEvent = inEvent || {};
618 this.$.itemPicker.collectionChanged();
619 // a model was deleted
620 if (inEvent.delete && inEvent.model === this.$.itemPicker.getValue()) {
621 this.$.itemPicker.setValue(null);
623 this.$.filterList.fetched();
626 Sets the default items for the picker and the
627 list of items that can be managed.
629 create: function () {
630 this.inherited(arguments);
631 this.$.apply.setDisabled(!this.validate());
634 var filter = function (models, options) {
635 var filtered = _.filter(models, function (model) {
636 var kind = this.parent.parent.kind === model.get("kind"),
637 permission = model.get("shared") ||
638 model.get("createdBy") === XM.currentUser.get("username"),
639 type = this.parent.itemType === model.get("type") ||
640 (this.parent.itemType === "filter" && !model.get("type"));
641 if (kind && permission && type) {
647 this.$.itemPicker.filter = filter;
648 this.$.itemPicker.collectionChanged();
650 // filters the manage list
651 var query = this.$.filterList.getQuery() || {};
653 query.parameters = [];
654 query.parameters = [{
655 attribute: "createdBy",
657 value: XT.session.details.username
662 value: this.parent.kind
667 value: this.itemType,
668 includeNull: this.itemType === "filter" // handles filters added before type was created
670 this.$.filterList.setQuery(query);
673 Looks at the list of items for the current user and determines
674 an item exists by name.
679 itemExists: function (name) {
680 var exists = _.find(this.$.filterList.getValue().models,
682 var value = model.get("name");
683 return value.toLowerCase() === name.toLowerCase();
687 hidePopup: function (inSender, inEvent) {
688 this.$.existingPopup.hide();
691 This is called by the save/apply button in the
692 save drawer. It bubbles up the event to the parameter
693 widget with a callback so it knows when the save was
694 successful. It then closes the drawer and refreshes
697 saveItem: function (inSender, inEvent) {
698 var name = this.$.itemName.getValue() ?
699 this.$.itemName.getValue().trim() : "",
700 shared = this.$.isShared.getValue(),
701 exists = this.itemExists(name),
704 inEvent = {model: exists, itemName: name, isShared: shared,
705 callback: function (model) {
707 that.$.filterList.getValue().add(model);
709 that.activateSave(); // close the save drawer
710 that.$.existingPopup.hide(); // hide the popup if it was showing
711 that.$.itemPicker.setValue(model);
712 that.$.filterList.doListChange();
714 this.doItemSave(inEvent);
717 Checks that all of the form fields have
720 validate: function () {
721 if (this.$.itemName.getValue()) {
727 When an item is selected from the picker, the
728 form fields are populated and a changed
729 event is bubbled up to the parameter widget.
731 valueChanged: function (inSender, inEvent) {
732 this.$.apply.setDisabled(!this.validate());
733 if (inSender.kind === "XV.FilterPicker") {
734 // get values from inEvent for form load
735 var value = inEvent.originator.value,
736 name = value ? value.get("name") : null,
737 shared = value ? value.get("shared") : false;
738 this.$.itemName.setValue(name);
739 this.$.isShared.setValue(shared);
740 inEvent = {model: value};
741 this.doItemChange(inEvent);
748 User Item Form for selecting saved filters, a save drawer for saving
749 the current filter, and a manage drawer for sharing and deleting filters.
752 /** @lends XV.FilterForm# */{
753 name: "XV.FilterForm",
754 kind: "XV.UserItemForm",
757 {kind: "onyx.GroupboxHeader", content: "_filters".loc()},
758 {kind: "XV.FilterPicker", name: "itemPicker", label: "_filter".loc()},
759 {kind: "XV.TopDrawer", name: "saveTopDrawer", ontap: "activateSave", content: "_saveFilter".loc()},
760 {kind: "onyx.Drawer", name: "saveDrawer", open: false, animated: true, components: [
761 {kind: "onyx.GroupboxHeader", content: "_saveFilter".loc()},
762 {kind: "XV.InputWidget", name: "itemName", label: "_filterName".loc()},
763 {kind: "XV.CheckboxWidget", name: "isShared", attr: "isShared", label: "_isShared".loc()},
764 {kind: "FittableColumns", classes: "button-row", components: [
765 {kind: "onyx.Button", name: "apply", disabled: true, content: "_apply".loc(), ontap: "checkExisting"},
766 {kind: "onyx.Button", content: "_cancel".loc(), ontap: "activateSave"}
769 {kind: "XV.TopDrawer", name: "manageTopDrawer", ontap: "activateManage", content: "_manageFilters".loc()},
770 {kind: "onyx.Drawer", name: "manageDrawer", open: false, animated: true, components: [
771 {kind: "onyx.GroupboxHeader", content: "_manageFilters".loc()},
772 {kind: "XV.FilterList", allowFilter: false},
773 {kind: "FittableColumns", classes: "button-row", components: [
774 {kind: "onyx.Button", content: "_done".loc(), ontap: "activateManage"}
777 {kind: "onyx.Popup", name: "existingPopup", centered: true,
778 modal: true, floating: true, scrim: true, components: [
779 {content: "_filterExists".loc()},
780 {content: "_editFilter?".loc()},
782 {kind: "onyx.Button", content: "_edit".loc(), ontap: "saveItem",
783 classes: "onyx-blue xv-popup-button"},
784 {kind: "onyx.Button", content: "_cancel".loc(), ontap: "hidePopup",
785 classes: "xv-popup-button"}
791 User Item Form for selecting saved layouts, a save drawer for saving
792 the current layout, and a manage drawer for sharing and deleting layouts.
794 enyo.kind(/** @lends XV.LayoutForm# */{
795 name: "XV.LayoutForm",
796 kind: "XV.UserItemForm",
804 {kind: "onyx.GroupboxHeader", content: "_layout".loc()},
805 {kind: "XV.FilterPicker", name: "itemPicker", label: "_layout".loc()},
806 {kind: "XV.TopDrawer", name: "layoutTopDrawer", ontap: "activateLayout", content: "_changeLayout".loc()},
807 {kind: "onyx.Drawer", name: "columnsDrawer", open: false, animated: true, components: [
808 {kind: "XV.LayoutTree", name: "layoutTree"}
810 {kind: "XV.TopDrawer", name: "saveTopDrawer", ontap: "activateSave", content: "_saveLayout".loc()},
811 {kind: "onyx.Drawer", name: "saveDrawer", open: false, animated: true, components: [
812 {kind: "onyx.GroupboxHeader", content: "_saveLayout".loc()},
813 {kind: "XV.InputWidget", name: "itemName", label: "_layoutName".loc()},
814 {kind: "XV.CheckboxWidget", name: "isShared", attr: "isShared", label: "_isShared".loc()},
815 {kind: "FittableColumns", classes: "button-row", components: [
816 {kind: "onyx.Button", name: "apply", disabled: true, content: "_apply".loc(), ontap: "checkExisting"},
817 {kind: "onyx.Button", content: "_cancel".loc(), ontap: "activateSave"}
820 {kind: "XV.TopDrawer", name: "manageTopDrawer", ontap: "activateManage", content: "_manageLayouts".loc()},
821 {kind: "onyx.Drawer", name: "manageDrawer", open: false, animated: true, components: [
822 {kind: "onyx.GroupboxHeader", content: "_manageLayouts".loc()},
823 {kind: "XV.FilterList", allowFilter: false},
824 {kind: "FittableColumns", classes: "button-row", components: [
825 {kind: "onyx.Button", content: "_done".loc(), ontap: "activateManage"}
828 {kind: "onyx.Popup", name: "existingPopup", centered: true,
829 modal: true, floating: true, scrim: true, components: [
830 {content: "_layoutExists".loc()},
831 {content: "_editLayout?".loc()},
833 {kind: "onyx.Button", content: "_edit".loc(), ontap: "saveItem",
834 classes: "onyx-blue xv-popup-button"},
835 {kind: "onyx.Button", content: "_cancel".loc(), ontap: "hidePopup",
836 classes: "xv-popup-button"}
840 Opens the change layout drawer and switches the drawer icons.
842 activateLayout: function (inSender, inEvent) {
843 this.$.columnsDrawer.setOpen(!this.$.columnsDrawer.open);
844 this.$.layoutTopDrawer.changeIcon(this.$.columnsDrawer.open);
847 Builds a tree of nodes based on the current list layout. For each current
848 list attribute, a picker is shown with the set of available attributes. If
849 a column is already shown, it is selected, otherwise it will show as "none."
851 @param {Object} currentLayout
853 addColumns: function (currentLayout) {
855 matchSelections = function (sel) { return sel.attr === attributes[i]; },
860 // set the layout tree with the list of possible display attributes
861 this.$.layoutTree.setListAttrs(currentLayout.getDisplayAttributes());
862 // clear out the tree before building it
863 this.$.layoutTree.$.tree.destroyClientControls();
864 this.$.layoutTree.createTree(currentLayout);
865 this.$.columnsDrawer.render();
867 getColumnValues: function () {
869 _.each(this.$.layoutTree.$, function (leaf) {
870 if (leaf.order !== undefined && leaf.order !== null) {
871 params[leaf.order] = leaf.getAttr();
876 loadColumns: function (params) {
877 _.each(this.$.layoutTree.$, function (leaf) {
878 if (leaf.order !== undefined && leaf.order !== null &&
879 leaf.getAttr() !== params[leaf.order]) {
880 leaf.setAttr(params[leaf.order]);
883 value: params[leaf.order]
885 this.doColumnsChange(inEvent);
890 When an attribute is selected from the picker,
891 a change event is bubbled up to the parameter widget.
893 valueChanged: function (inSender, inEvent) {
894 this.inherited(arguments);
895 if (inEvent.originator.kind === "XV.AttributePicker") {
897 order: inEvent.originator.owner.order,
900 this.doColumnsChange(inEvent);
906 Simple FittableColumns that contains the open/close icon
907 and some text. When the ontap event is fired, this control
908 is responsible for switching icons and opening a drawer.
911 /** @lends XV.TopDrawer */{
912 name: "XV.TopDrawer",
913 kind: "FittableColumns",
914 classes: "top-drawer",
919 {tag: "i", classes: "icon-plus-sign-alt", name: "drawerIcon"},
923 Switch icons depending on open state of a drawer
925 @param {Boolean} drawerOpen
927 changeIcon: function (drawerOpen) {
929 this.$.drawerIcon.setClasses("icon-minus-sign-alt");
931 this.$.drawerIcon.setClasses("icon-plus-sign-alt");
934 contentChanged: function () {
935 this.$.label.setContent(this.getContent());
937 create: function () {
938 this.inherited(arguments);
939 this.contentChanged();