/*globals define, require*/ /*jslint indent:4, nomen:true*/ /** * @class de_epages.presentation.tray.mainview * @author Copyright 2006-2013, epages GmbH, All Rights Reserved * * The tray's main view for use without a dialog * * ### Example * * $('.ep-tray').uiTray({ * label: 'Add Products', * smartsearch: 'JSONSmartSearchProductAlias', * tabs: { * products: { * id: #Shop.ProductFolder.ID * action: 'JSONSearchProductsToInsert', * additionalContent: '<a href="#">Additional content</a>' * } * } * }); * * @uses jQuery * @uses jQuery.ui.tabs * @uses Backbone * * @return {Object} Mainview */ define('de_epages/presentation/tray/main-view', [ 'jquery', 'backbone', 'util/storage', './tray-collection', './content-view', './selected-view', './tray-view', '$tmpl!./main-view', '$dict!../dictionary', 'jquery/ui/tabs' ], function ($, Backbone, storage, TrayCollection, ContentView, SelectedView, TrayView, template, dict) { 'use strict'; /** * Options to pass to the main view * * @method constructor * @param {Object} options * @param {Boolean} [options.useTray=false] Whether an explicit tray tab should be displayed or not * @param {Object} [options.tabs=undefined] Key-value-pairs of tab name and corresponding view action */ var translationMap = { 'products': 'Products', 'categories': 'Categories', 'shippingMethods': 'ShippingMethods', 'paymentMethods': 'PaymentMethods', 'customers': 'Customers', 'users': 'Users', 'tray': 'Tray' }, viewOptions = { useTray: false, tabs: undefined }; return Backbone.View.extend({ tagname: 'div', template: template, collection: new TrayCollection(), rememberSelection: false, storageData: [], /** * Initialize options and prepare data to render tabs afterwards * * @since 6.17.0 */ initialize: function (options) { this.options = options; var self = this, o = $.extend(true, {}, viewOptions, self.options); // we have do define them here otherwise it will be on the prototype // and we get a problem with different instances of the view self.tabData = []; self.views = {}; self.activeView = {}; self.activeAlias = undefined; self.additionalContent = {}; $.each(o.tabs, function (key, value) { self.tabData.push({ alias: key, id: value.id, name: dict.translate(translationMap[key]) }); // loop through lS objects and try to find at least one saved selection (saved key is defined by searchActions) self.collection.add(storage.localStorage('epObjectFinder.' + key + 'Selection')); }); // Found something? Then check the box! if (self.collection.length > 0) { self.rememberSelection = true; } // <LEGACY> //#JSCOVERAGE_IF false // tray tab - marked for removal // Kill this push to stop the tray from appearing if (o.trayAction) { self.tabData.push({ alias: 'tray', name: dict.translate('Tray') }); } //#JSCOVERAGE_ENDIF // </LEGACY> self.tabData.push({ alias: 'selected', name: dict.translate('SelectedElements') }); self.collection.on('add remove', $.proxy(self.updateSelectedElements, self)); }, /** * Render tabs according to set options * * @since 6.17.0 * @chainable * @return {Object} Backbone.View */ render: function () { var self = this, o = self.options, renderedTemplate = self.template({ data: self.tabData, cid: self.cid, initialNumberOfElements: self.collection.length }), alias = self.tabData[0].alias, id = self.tabData[0].id; self.$el.append(renderedTemplate); $.extend(self, $.tmplItem(renderedTemplate).elements); self.tabNode.tabs({ beforeActivate: function (event, ui) { var tab = ui.newTab.children(':first'), id = tab.data('id'); self.activeAlias = tab.data('alias'); if (self.activeAlias === 'selected') { self.views.selected = { type: 'rootview', instance: new SelectedView({ collection: self.collection }) }; // update flag if someone hits the checkbox self.listenTo(self.views.selected.instance, 'rememberSelectionChanged', function (_rememberSelection) { self.rememberSelection = _rememberSelection; }); self.selected.empty().html(self.views.selected.instance.render().el); if (self.rememberSelection && self.collection.length > 0) { self.views.selected.instance.rememberSelection.prop('checked', true); } } else if (self.activeAlias === 'tray') { // <LEGACY> //#JSCOVERAGE_IF false // tray tab - marked for removal self.views.tray = { type: 'rootview', instance: new TrayView() }; // already rendered the view? if (!self.tray.html()) { self.tray.html(self.views.tray.instance.render().el); } //#JSCOVERAGE_ENDIF // </LEGACY> } else { self.setTabContent(id, self.activeAlias); } } }); self.activeAlias = alias; self.setTabContent(id, alias, 'rootview'); if (o.afterRender) { o.afterRender(this); } return self; }, /** * Create an instance of a subview if none exists and render it into the corresponding tab content area or get a chached one * * @since 6.17.0 * @param {String} alias The alias of the tab which should be rendered * @return {Object} Backbone.View */ setTabContent: function (identifier, context) { var self = this, o = self.options; if (identifier && context && !self.views[identifier]) { // hide scrollbars in tab content self[context].css('overflow', 'hidden'); self.createContent(identifier, context, 'rootview', o.searchString, o.tabs[context].additionalContent); self.collection.each(function (item) { self.$('input[value=' + item.get('ObjectID') + ']').prop('checked', true); }); self[context].html(self.views[identifier].instance.render().el); self.activeView[context] = self.views[identifier]; } return self; }, /** * Sets the content of the specified tab * * @since 6.17.0 * @param {mixed} identifier String or id that identifies the view */ setSubContent: function (_identifier, _context) { var self = this, o = self.options, context = self.activeView[self.activeAlias].instance.options.context; self.activeView[self.activeAlias].instance.$el.detach(); if (!self.views[_identifier]) { // create new view and render it to the DOM self.createContent(_identifier, context, 'subview', o.searchString, self.activeView[self.activeAlias].instance.options.additionalContent); self[context].html(self.views[_identifier].instance.render().el); } else { // take the already rendered view, mount it and clear the search field self[context].html(self.views[_identifier].instance.el); self.views[_identifier].instance.resetSearchField(); } if (self.additionalContent[context]) { $.each(self.additionalContent[context], function (key, val) { var node = self.views[_identifier].instance.$('input[name=' + key + ']'); if (val) { node .prop('checked', true) .addClass('ui-changed'); } else { node .removeAttr('checked') .removeClass('ui-changed'); } }); } self.activeView[context] = self.views[_identifier]; }, /** * Creates a new view with given name and context * * @since 6.17.0 * @param {String} identifier String or id that identifies the view * @param {String} context Context that binds the view to a specific tab */ createContent: function (identifier, context, viewType, filter, additionalContent) { var self = this, o = self.options; self.views[identifier] = { type: viewType, instance: new ContentView({ context: context, collection: self.collection, parentObjectId: viewType === 'subview' ? identifier : undefined, searchAction: o.tabs[context].action, searchOptions: o.tabs[context].searchOptions, searchString: filter, additionalContent: additionalContent, exclude: o.tabs[context].exclude }) }; self.listenTo(self.views[identifier].instance, 'setAdditionalContentOptions', function (additionalContent) { self.additionalContent[self.views[identifier].instance.context] = additionalContent; }); self.listenTo(self.views[identifier].instance, 'changecontent', self.setSubContent); self.listenTo(self.views[identifier].instance, 'search', self.showSearchResults); self.listenTo(self.views[identifier].instance, 'change:requiredHeight', function (h) { self.trigger('change:requiredHeight', h); }); }, /** * Show results of a search in an extra view that cannot be cached * * @since 6.17.0 * @param {String} searchString String by which the results are filtered */ showSearchResults: function (searchString) { this.createContent( 'search', this.activeAlias, 'searchview', searchString, this.options.tabs[this.activeAlias].additionalContent ); this.activeView[this.activeAlias].instance.$el.detach(); this[this.activeAlias].html(this.views.search.instance.render().el); }, /** * Update elements number on the 'selected' tab * * @since 6.17.0 */ updateSelectedElements: function () { if (this.collection.length > 0) { this.numberOfElements.html(' (' + this.collection.length + ')'); } else { this.numberOfElements.html(''); } }, /** * Fetches all currently selected items from the tray grouped by type * * @since 6.17.0 * @return {Array} Array of objects that were selected. Divided into items and corresponding options */ getSelectedElements: function () { var self = this, items = {}; this.collection.each(function (item) { var type = item.get('type'); if (!items[type]) { items[type] = { data: [], options: self.additionalContent[type] || {} }; } items[type].data.push(item.get('ObjectID')); }); // <LEGACY> //#JSCOVERAGE_IF false // tray tab - marked for removal if (self.tray && self.tray.find('.ep-tray-useTray').prop('checked')) { items.insertFromTray = true; } //#JSCOVERAGE_ENDIF // </LEGACY> // time to get our tab's selection data into lS (or remove it) - omitting selected tab itself $.each(self.tabData, function () { if (this.alias !== 'selected') { if (self.rememberSelection) { if (self.collection.length > 0) { storage.localStorage('epObjectFinder.' + this.alias + 'Selection', self.collection.where({ type: this.alias })); } } else { storage.localStorage('epObjectFinder.' + this.alias + 'Selection', null); } } }); return items; } }); });