Created
December 11, 2014 14:37
-
-
Save logical-and/521ccdd0958d031ba153 to your computer and use it in GitHub Desktop.
Revisions
-
logical-and created this gist
Dec 11, 2014 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,876 @@ define([ 'jsclass/class', './WidgetService', 'app/common/layout/manager', 'app/common/layout/model', 'app/common/datatable/component.datatable', 'Shared/Base/ConfigureGrid', 'Shared/Filter/filter', 'Shared/Filter/PageFilterSortInfo', 'Shared/Base/LoadMask', 'Shared/Extensions/ScopedWorkspace', 'Shared/Util/Widget/GenericHelpers', './WidgetLoader', './Util', 'nunjucks', 'Shared/Extensions/WindowTitleForDate', 'async' ], function ( Class, WidgetService, Manager, Model, Datatable, ConfigureGrid, Filter, PageFilterSortInfo, LoadMask, ScopedWorkspace, GenericHelpers, WidgetLoader, Util, nunjucks, WindowTitleForDate, Async ) { 'use strict'; var DIR = 'Shared/WidgetFoundation/', $CacheMemory = {}; return new Class( { extend: { EXPORT_TYPE: { DETACH: 'DETACH' }, loader: function(settings) { return new WidgetLoader(this, settings); } }, settings: { // default: { features: {} } - workspace and subworkspace, all tabs, main and detach // // workspace: { default: { default: { features: {} } } } - workspace, all tabs, main and detach // workspace: { default: { main: { features: {} } } } - workspace, all tabs, main // workspace: { reports: { default: { features: {} } } } - workspace, reports tab, main and detach // workspace: { reports: { main: { features: {} } } } - workspace, reports tab, main // workspace: { reports: { detach: { features: {} } } } - workspace, reports tab, detach // // subworkspace: { default: { features: {} } } - subworkspace, main and detach // subworkspace: { main: { features: {} } } - subworkspace, main // subworkspace: { detach: { features: {} } } - subworkspace, detach default: { title: 'Abstract Widget Title', features: { export: true, exportType: ['DETACH'], maximize: true, mininize: true, close: true, resize: true, drag: true }, size: { width: 500, height: 400 }, listeners: { // format: eventName: function() {}, or eventName: [function() {}] // widgetInitialized: [], // widgetPassive: function() { console.info('just become passive'); } // available event types: // eventName - widget foundation event (see this.eventsMap) // widget:eventName (see this.frameworkEventsMap) // service:serviceName }, // Methods, which will be passed to this[methodKey] methods: {}, plugins: { // PluginPath: { parameters: 'here' } }, internal: { workspaceChangeWorkaroundTimeout: 50, paths: { service: 'Service', view: ':text:' + DIR + 'View/AbstractWidget.html' // :text:, :require:, :service: or direct url (without html) }, services: { getState: 'AUM/GetWidgetState', saveState: 'AUM/SetWidgetState', getStateNoId: 'User/GetWidgetStateWithoutId', saveStateNoId: 'User/SetWidgetStateWithoutId' } } } }, // Fetched settings actualSettings: {}, // State originalState: {}, state: {}, service: null, eventListeners: null, memory: null, plugins: null, // Widget initialized: false, id: null, // unique id widgetFrameworkPath: null, // framework internal, ie AssetsUnderManagement/AssetsUnderManagement:Assets Under Management widgetPath: null, // ie AssetsUnderManagement/AssetsUnderManagement widgetWorkspaceTab: null, widgetObject: null, widgetBodyContainerId: null, isWidgetDetached: null, widgetArgs: [], workspaceContext: null, spinner: null, // Mapping featuresMap: {}, // WidgetFoundation events eventsMap: { settingsFetched: 'settingsFetched', widgetInitialized: 'widgetInitialized', viewInitialized: 'viewInitialized', stateLoaded: 'stateLoaded', statePersisting: 'statePersisting', exportDataToDetached: 'exportDataToDetached', detachedReceiveData: 'detachedReceiveData' }, // Framework events frameworkEventsMap: { // Features onExportClicked: Model.WIDGET_EXPORT, onSettingsClicked: 'settings', onVisualizationClicked: Model.WIDGET_VISUALIZATION, onFilterClicked: 'filter', onRefreshClicked: 'refresh', onResized: Model.AFTER_RESIZE, onMaximized: Model.AFTER_MAXIMIZE, onCascaded: Model.AFTER_CASCADE, onDragged: Model.WIDGET_DRAG_COMPLETED, onClose: Model.WIDGET_CLOSE, // Detach onDetachedInitializing: Model.EXPORTED_VIEW_READY, onDetachedDataReceived: Model.EXPORTED_VIEW_DATA_EVENT, // Strange one-instance widget strategy events onWidgetPassivate: Model.VIEW_IN_PASSIVE_MODE, onWidgetActivate: Model.VIEW_IN_ACTIVE_MODE, onScopeChanged: ScopedWorkspace.SCOPE_CHANGE_EVENT, onUserPreferenceChanged: 'USER_PREFERENCES_SAVED' }, // Work like localization for export items exportTypesMap: { DETACH: 'Detach' }, workspacesIdMap: { "Home": 1, "Clients": 2, "Worklist": 3, "Markets": 4, "Resources": 5, "Trading": 6, "Services": 8, "Reports": 9, "Fixed Income": 10, "Back Office": 19, "Admin": 20, // "Home": 21, // "Home": 22, "Accounts": 23, "Documents": 24, // "Markets": 25, "Bill Pay": 26, "Credit Card Activity": 27 // "Trading": 28, }, initialize: function (settings, whom, isDetached, args, widgetModulePath) { var self = this; // Merge inherited values in the right way Util.forEach(['settings', 'featuresMap', 'eventsMap', 'exportTypesMap', 'frameworkEventsMap'], function(i, key) { var merged = {}; Util.forEach(self.__eigen__().lookup(key), function(i, value) { merged = Util.Object.merge(merged, value); }); self[key] = merged; }); // Merge settings this.settings = Util.Object.merge(this.settings, settings); this.widgetFrameworkPath = widgetModulePath; this.widgetPath = widgetModulePath.split(':')[0]; this.isWidgetDetached = !!isDetached; this.widgetArgs = args || []; (function (initSettings, nextStep) { // Define some settings manually if (!self.isWidgetDetached) { self.updateWorkspaceContext(); setTimeout(function() { // don't work for clients tab // self.widgetWorkspaceTab = Manager.getWorkspaces()[Manager.getWorkspace()].name; self.widgetWorkspaceTab = $('#workspaceTabsHolder').find('.workspace.active').text(); initSettings(nextStep); }, self.settings.default.internal.workspaceChangeWorkaroundTimeout); } // Receive settings from original widget else { var dummyWindow = new Model.WindowProperties(whom, self.widgetFrameworkPath); var dummyWidget = Manager.createWidget(dummyWindow); Manager.renderWidget(dummyWidget, function(event, postMessage) { if (self.frameworkEventsMap.onDetachedDataReceived == event) { if (postMessage.state) { self.originalState = Util.Object.clone(postMessage.state); self.state = Util.Object.clone(postMessage.state); } self.widgetWorkspaceTab = postMessage.widgetWorkspaceTab || 'Unknown'; self.workspaceContext = postMessage.workspaceContext || {}; (function(next) { if (!self.initialized) initSettings(next); else next(); })(function() { nextStep(function() { self.fireEvent(self.eventsMap.detachedReceiveData, { postMessage: postMessage }); if (postMessage.state) { self.fireEvent(self.eventsMap.stateLoaded, postMessage.state); } }); }); } }, ''); // Prepare html to make it wonderful $('#' + dummyWidget.getId()).hide(); $('body #centerpane').prepend($('<div id="loading" style="text-align: center;height: 100%;display: table;width: 100%;">' + '<span style="display: table-cell;vertical-align: middle;font-size: 150%;" class="color-white">' + 'Exporting...' + '</span>' + '</div>') ); Manager.subscribeToEvent(self.frameworkEventsMap.onDetachedDataReceived, self.widgetFrameworkPath); Manager.sendToParentWindow(self.widgetFrameworkPath, self.frameworkEventsMap.onDetachedInitializing); } })(function(callback) { // Fetch settings from given hash self.actualSettings = self._fetchActualSettings(self.settings); // Store methods to use it as native Util.forEach(self.actualSettings.methods, function(methodKey, method) { if (self[methodKey]) throw new Error('Cannot override "' + methodKey + '"! Method must have unique name!'); self[methodKey] = method.bind(self); }); self.fireEvent(self.eventsMap.settingsFetched); // Initialize plugins if (!Util.Object.isEmpty(self.actualSettings.plugins)) { self.plugins = []; Async.each(Util.Object.getKeys(self.actualSettings.plugins), function(plugin, cb) { require([plugin], function(Plugin) { self.plugins[plugin] = new Plugin(self, self.actualSettings.plugins[plugin], cb); }); }, callback); } else callback(); }, function (listenersInitialized) { // Go next var windowProperties = new Model.WindowProperties(whom, self.widgetFrameworkPath); // Title if (self.isWorkspace()) windowProperties.setTitle(self.actualSettings.title); else windowProperties.setTitle(self.actualSettings.title + ' for ' + self.workspaceContext.display); // Features var features = []; for (var feature in self.featuresMap) { if (self.featuresMap.hasOwnProperty(feature)) { if (self.actualSettings.features[feature]) features.push(self.featuresMap[feature]); } } if (features.length) windowProperties.showDefaultMenuOptions(features); windowProperties.showDetachOption(self.actualSettings.features.export); // If not detached, then we customize some window properties if (!self.isWidgetDetached) { windowProperties.isMaximizable(self.actualSettings.features.maximize); windowProperties.isMinimizable(self.actualSettings.features.minimize); windowProperties.isClosable(self.actualSettings.features.close); windowProperties.isResizable(self.actualSettings.features.resize); windowProperties.isDraggable(self.actualSettings.features.drag); } // Size if (!self.isWidgetDetached) { windowProperties.setWidth(self.actualSettings.size.width); windowProperties.setHeight(self.actualSettings.size.height); windowProperties.setMinSize(self.actualSettings.size.width, self.actualSettings.size.height); } else { var viewPortDims = viewport(); windowProperties.setWidth(viewPortDims.width); windowProperties.setHeight(viewPortDims.height); } self.widgetObject = Manager.createWidget(windowProperties); // And here we go self.stackInitServices(function() { self.stackInitEventListeners(function() { listenersInitialized && listenersInitialized(); self.stackInitView(function() { if (self.isWidgetDetached) self.getNode().hide(); // Subworkspace title if (self.isSubworkspace()) { self.appendToTitle('for ' + self.workspaceContext.display); } self.showSpinner(); if (self.isWidgetDetached) { $('#loading').remove(); self.getNode().show(); } self.stackInitState(function() { self.hideSpinner(); self.initialized = true; self.fireEvent(self.eventsMap.widgetInitialized); }); }); }); }); }); }, /** * Fetch settings for current workspace/subworkspace from all settings (smart merge) * * @param allSettings * @returns {{}} * @private */ _fetchActualSettings: function(allSettings) { var settings = Util.Object.clone(allSettings), paths = ['default']; if (this.isWorkspace()) { // Defaults paths.push('workspace.default.default'); // With tab var currentTab = this.getCurrentTabName().toLowerCase(); paths.push('workspace.' + currentTab + '.default'); // With widget state (main or detach) paths.push('workspace.default.' + (!this.isDetached() ? 'main' : 'detach')); paths.push('workspace.' + currentTab + '.' + (!this.isDetached() ? 'main' : 'detach')); } else { // Defaults paths.push('subworkspace.default'); // With widget state (main or detach) paths.push('subworkspace.' + (!this.isDetached() ? 'main' : 'detach')); } var fetchedSettings = {}; for (var i = 0; i < paths.length; i++) { fetchedSettings = Util.Object.merge(fetchedSettings, Util.Object.getPath(settings, paths[i], {})); } return fetchedSettings; }, // --- Stack /** * Initialize widget services * @private */ stackInitServices: function(next) { var self = this; require([this._determineServicesPath()], function (Service) { self.service = (new Service).initializeServices(self.getWidgetPath()); // Listen services as events Util.forEach(self.service.servicesConfig, function(serviceName) { self.service.listenService(serviceName, function(data) { if (!self.active) return; self.fireEvent('service:' + serviceName, data); }); }); next(); }); }, /** * Subscribe to events * @private */ stackInitEventListeners: function(next) { var self = this; Util.forEach(this.actualSettings.listeners, function(eventName) { var events = self.actualSettings.listeners[eventName]; if ('function' == typeof events) events = [events]; for (var i = 0; i < events.length; i++) { self.listenEvent(eventName, events[i]); } }); next(); }, stackInitState: function(next) { if (!this.isWidgetDetached) this.loadState(next); else next(); }, // --- Generic getNodeId: function () { return this.widgetObject.getId(); }, getNode: function () { return $('#' + this.getNodeId()); }, getWidgetObject: function() { return this.widgetObject; }, getNodeBodyContainerId: function() { return this.widgetBodyContainerId; }, getNodeBodyContainer: function() { return $('#' + this.widgetBodyContainerId); }, getWidgetFrameworkPath: function() { return this.widgetFrameworkPath; }, getWidgetPath: function () { return this.widgetPath; }, /** * For require.js usage * @param {Boolean} [trailingSlash=true] * @returns {String} */ getWidgetDir: function(trailingSlash) { if (undefined === trailingSlash) trailingSlash = true; return this.widgetPath.replace(/\/[^/]+$/, '') + (trailingSlash ? '/' : ''); }, getSettings: function () { return this.actualSettings; }, getAllSettings: function() { return this.settings; }, isInitialized: function() { return this.initialized; }, // --- Environment isDetached: function() { return this.isWidgetDetached; }, getArgs: function() { return this.widgetArgs; }, getWorkspaceContext: function() { return this.workspaceContext; }, updateWorkspaceContext: function() { if (this.isWidgetDetached) throw new Error('Cannot update workspace context for detached widget'); this.workspaceContext = ScopedWorkspace.getScopeOfCurrentWorkspace(); return this; }, isWorkspace: function() { return this.workspaceContext === ScopedWorkspace.SCOPE_UNDEFINED }, isSubworkspace: function() { return !this.isWorkspace(); }, getCurrentTabName: function() { return this.widgetWorkspaceTab; }, // --- Events listenEvent: function (event, handler) { if (!this.eventListeners) this.eventListeners = {}; var self = this; var eventModificators = { once: function(event, handler) { var extraHandler = function() { // Remove event if (~self.eventListeners[event].indexOf(extraHandler)) { delete self.eventListeners[event][self.eventListeners[event].indexOf(extraHandler)]; } // Call original handler handler.apply(this, arguments); }; return extraHandler; } }; var extraEventMatch = event.match(/:([^:]+)$/); if (extraEventMatch && eventModificators[extraEventMatch[1]]) { // if (!eventModificators[extraEventMatch[1]]) throw new Error('Modificator ' + extraEventMatch[1] + ' is unknown'); // Remove modificator from event event = event.replace(/:([^:]+)$/, ''); handler = eventModificators[extraEventMatch[1]](event, handler); } if (!this.eventListeners[event]) this.eventListeners[event] = []; this.eventListeners[event].push(handler); return this; }, fireEvent: function(event, args) { if (!this.eventListeners) this.eventListeners = {}; // Currently used for widget final instance handlers (settings.listeners) if (this.eventListeners[event]) { for (var i = 0; i < this.eventListeners[event].length; i++) { this.eventListeners[event][i].call(this, args); } } // Internal event methods var methodEventHandler = event.replace(/^[^:]+:/, '') + 'EventHandler'; if (this[methodEventHandler] && 'function' == typeof this[methodEventHandler]) this[methodEventHandler].call(this, args); return this; }, // --- Event handlers onCloseEventHandler: function() { this.saveStateIfChanged(); }, onExportClickedEventHandler: function() { var self = this; require(["Shared/Extensions/Export"], function (_export) { var items = []; Util.forEach(self.actualSettings.features.exportType, function(i, type) { if (self.exportTypesMap[type]) { items.push({ name: self.exportTypesMap[type], callback: self.onExportingEventHandler.bind(self), id: Util.getUniqueId(), type: type }); } }); _export.createExportmenu(self.getNodeId(), self.getNodeBodyContainerId(), items); }); }, onExportingEventHandler: function(item) { var self = this; switch (item.type) { case 'DETACH': Manager.detachWindow(this.widgetFrameworkPath, function(event) { // Export general values if (self.frameworkEventsMap.onDetachedInitializing == event) { var postMessage = { state: self.state, widgetWorkspaceTab: self.widgetWorkspaceTab, workspaceContext: self.workspaceContext }; self.fireEvent(self.eventsMap.exportDataToDetached, { postMessage: postMessage, sendMethod: this.sendMessage.bind(this) }); this.sendMessage(postMessage); } // console.info('export layoutEventHandler: ', arguments, this); }); break; default: console.error('Unknown export type: ' + item.type); } }, onScopeChangedEventHandler: function() { this.updateWorkspaceContext().appendToTitle('for ' + this.workspaceContext.display); }, // --- Services getService: function() { return this.service; }, getServices: function() { return WidgetService; }, callOwnService: function(service, parameters, callback) { this.service.call(this.service.getRealServiceName(service), parameters, callback); return this; }, _determineServicesPath: function () { return this.getWidgetDir() + this.actualSettings.internal.paths.service; }, // --- State getFromState: function (path, def) { return Util.Object.getPath(this.state, path, def); }, setToState: function (path, value) { Util.Object.setPath(this.state, path, value); return this; }, loadState: function (callback) { var requestCallback = function (data) { var state = data || {}; this.state = state; this.originalState = Util.Object.clone(state); this.fireEvent(this.eventsMap.stateLoaded, state); callback(state); }.bind(this); if (this.actualSettings.stateId) { console.warn('stateId is deprecated, but used in "' + this.widgetPath + '" - ' + this.actualSettings.stateId); WidgetService.directRequest(this.actualSettings.internal.services.getState, { id: this.actualSettings.stateId }, 'GET', 'json', function(data) { requestCallback(data && data.Preference ? data.Preference : {}); }); } else { WidgetService.directRequest(this.actualSettings.internal.services.getStateNoId, { widgetPath: this.widgetFrameworkPath, workspaceId: Util.Object.getPath(this.workspacesIdMap, this.getCurrentTabName(), this.workspacesIdMap.Home), isSubworkspace: this.isSubworkspace() }, 'GET', 'json', function(data) { if (data && data.json) data = data.json; requestCallback(data); }, true); } return this; }, isStateChanged: function () { // skip whitespace from comparison return JSON.stringify(this.originalState).replace(/\s+/g, '') != JSON.stringify(this.state).replace(/\s+/g, ''); }, saveState: function (callback) { var self = this; this.fireEvent(this.eventsMap.statePersisting, this.state); var requestCallback = function () { self.originalState = Util.Object.clone(self.state); if (callback) callback(); }.bind(this); if (this.actualSettings.stateId) { console.warn('stateId is deprecated, but used in "' + this.widgetPath + '" - ' + this.actualSettings.stateId); WidgetService.directRequest(this.actualSettings.internal.services.saveState, { id: this.actualSettings.stateId, Preference: this.state }, 'POST', 'json', requestCallback); } else { WidgetService.directRequest(this.actualSettings.internal.services.saveStateNoId, { widgetPath: this.widgetFrameworkPath, workspaceId: Util.Object.getPath(this.workspacesIdMap, this.getCurrentTabName(), this.workspacesIdMap.Home), isSubworkspace: this.isSubworkspace(), json: this.state }, 'POST', 'json', function(data) { if (!data.Response) alert('Error: state wasn`t persisted! { Response: false }'); requestCallback(); }); } return this; }, saveStateIfChanged: function(callback) { if (!this.isStateChanged()) { if (callback) callback(); } else this.saveState(callback); return this; }, // --- View loadView: function (callback) { this.loadHTML(this.actualSettings.internal.paths.view, callback); return this; }, loadHTML: function(loadUrl, callback) { loadUrl = loadUrl.replace(/{WidgetFoundation}\/?/, DIR); // Service loading if (-1 != loadUrl.indexOf(':service:')) { WidgetService.call(loadUrl.replace(':service:'), callback); } // Require else if (-1 != loadUrl.indexOf(':require:')) { require([loadUrl.replace(':require:')], callback); } // Require-text else if (-1 != loadUrl.indexOf(':require-text:')) { require(['Lib/Vendor/RequireJS/text!' + loadUrl.replace(':require-text:', '')], callback); } // Text else if (-1 != loadUrl.indexOf(':text:')) { nunjucks.render(loadUrl.replace(':text:', '') // Relative to widget path .replace(new RegExp('^\./'), this.widgetPath.replace(/[^/]+$/, '') + '/'), function (err, html) { if (err) { console.error(err); } else setTimeout(function() { callback(html); }, 0); }); } // URL request else { WidgetService.directRequest(loadUrl, {}, 'GET', 'html', function (template) { callback(template); }.bind(this)); } return this; }, // --- Spinner getSpinner: function() { if (!this.spinner) { if (this.widgetBodyContainerId) { this.spinner = new LoadMask.LoadMask(this.getNodeBodyContainer().parent().prop('id')); } else { console.warn('Cannot construct spinner for ' + this.widgetPath + '! Node id is unknown yet!'); } } return this.spinner; }, showSpinner: function() { if (!this.spinnerCallCount) this.spinnerCallCount = 0; // Count how much it called to show this.spinnerCallCount++; if (1 == this.spinnerCallCount) { var spinner = this.getSpinner(); if (spinner) spinner.start(); } return this; }, hideSpinner: function() { if (!this.spinnerCallCount) this.spinnerCallCount = 0; // Prevent from going under zero number if (this.spinnerCallCount > 0) this.spinnerCallCount--; if (0 == this.spinnerCallCount) { var spinner = this.getSpinner(); if (spinner) spinner.stop(); } return this; }, // --- Memory cache getFromGlobalCacheOrExecute: function(handler, key, callback) { if ('function' == typeof key) { callback = key; key = undefined; } if (undefined === key) key = Util.md5(handler.toString()); (function(handle) { // Already cached if ($CacheMemory[key]) handle($CacheMemory[key]); // Need to be cached else handler(function(result) { callback($CacheMemory[key] = result); }); })(callback); }, getFromMemory: function(path, def) { if (!this.memory) this.memory = {}; return Util.Object.getPath(this.memory, path, def); }, setToMemory: function(path, value) { if (!this.memory) this.memory = {}; Util.Object.setPath(this.memory, path, value); return this; }, // --- Title & header /** * Set widget title * @param {String} title */ setTitle: function(title) { title = (title + '').trim(); if (title) { Manager.updateWidgetTitle(this.widgetFrameworkPath, title); this.lastWidgetTitle = title; } return this; }, getTitle: function() { return this.lastWidgetTitle; }, /** * Append something to widget title. If empty value passed (or no value), then we restore original title * @param {String} [text=''] * @param {Boolean} [spaceBefore=true] * @returns this */ appendToTitle: function(text, spaceBefore) { if (!spaceBefore) spaceBefore = true; this.setTitle(this.actualSettings.title + (!text ? '' : (!spaceBefore ? text : ' ' + text))); return this; }, setAsOfDate: function(asOfDate) { if (asOfDate) WindowTitleForDate.addDateToTitle(this.getNodeId(), asOfDate, true); else WindowTitleForDate.removeDateFromTitle(this.getNodeId()); this.lastAsOfDate = asOfDate; return this; }, getAsOfDate: function() { return this.lastAsOfDate || ''; } }); }); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,164 @@ define([ 'jsclass/class', './AbstractPlugin', 'Shared/WidgetFoundation/Util', 'async', // Filters preloading 'Shared/WidgetFoundation/CustomFilter/ApplyButton', 'Shared/WidgetFoundation/CustomFilter/Input', 'Shared/WidgetFoundation/CustomFilter/Select' ], function( Class, AbstractPlugin, Util, Async, // Preloaded filters ApplyButtonFilter, InputFilter, SelectFilter) { return new Class(AbstractPlugin, { // Shortcuts preloadedCustomFilters: { ApplyButton: ApplyButtonFilter, Input: InputFilter, Select: SelectFilter }, filters: null, initialize: function(widget, args, callback) { this.callSuper(widget, args); this.filters = []; // Preserve context var self = this; // Async filter loading (some files aren't loaded yet) Async.each(args.filters, function(filter, cb) { // Constructed filter (yeah, class that not from WF-package can be used) if (filter.setCustomFilterPlugin) { filter.setCustomFilterPlugin(self); self.filters.push(filter); cb(); } else { // Filter name only, without any parameters if ('string' == typeof filter) filter = [filter, {}]; // Filter with parameters [filterName, parametersObject] if (2 === filter.length && 'object' == typeof filter) { // Here is args structure, example - ['SomeFilter', { title: 'custom title' }] var filterName = filter[0], filterArgs = filter[1]; var onLoad = function (filterClass) { var filterObject = new filterClass(filterArgs); filterObject.setCustomFilterPlugin(self); self.filters.push(filterObject); setTimeout(cb, 10); }; // File already loaded if (self.preloadedCustomFilters[filterName]) onLoad(self.preloadedCustomFilters[filterName]); // Or load class by require-js lib else require([filterName], onLoad); } else throw new Error('Args type is unknown!'); } }, function() { // Initial values widget.listenEvent(widget.eventsMap.stateLoaded, function() { Util.forEach(self.filters, function(i, filter) { if (filter.isPersistent()) { filter.setValue(widget.getFromState( 'customFilter.' + filter.getName() + self.getContextUID(), filter.getDefaultValue() )).dataInitialized(); } }); }); // HTML widget.listenEvent(widget.eventsMap.viewInitialized, function() { var filtersNode = widget.getNodeBodyContainer().find('.filters'); Util.forEach(self.filters, function(i, filter) { var node = $(filter.getHTML()); filter.nodeCreated(node); filtersNode.append(node); filter.nodeRendered(); }); }); // Affect on data request widget.listenEvent(widget.eventsMap.dataRequestParametersGenerated, function(parameters) { Util.Object.merge(parameters.pager.customFilters, (function() { var filters = {}; Util.forEach(self.filters, function (i, filter) { if (filter.hasValue()) { filters[filter.getName()] = filter.getValue(); } }); return filters; })()); }); callback(); }); }, /** * Store values in widget state * * @returns this */ persistValues: function() { var self = this; Util.forEach(self.filters, function(i, filter) { if (filter.isPersistent()) { self.getWidget().setToState( 'customFilter.' + filter.getName() + self.getContextUID(), filter.getValue() ); } }); this.getWidget().saveStateIfChanged(); return this; }, /** * Reload widget data (only update) * * @returns this */ reloadData: function() { this.getWidget().reloadGridData(); return this; }, /** * Combined logic * * @returns this */ persistAndReloadData: function() { this.persistValues().reloadData(); return this; }, getContextUID: function() { return ''; } }); }); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,16 @@ /** * Widget data model * * @author Sermik 2014, And <[email protected]> */ define(['Shared/WidgetFoundation/WidgetViewModel'], function (Model) { return Model.constructor({ columns: { AccountNumber: { type: Model.FIELD_TYPE.ACCOUNT_NUMBER, title: 'Account' }, RepCode: { type: Model.FIELD_TYPE.REP_ID, title: 'Rep' }, AccountName: { type: Model.FIELD_TYPE.STRING, title: 'Name' }, OpenDate: { type: Model.FIELD_TYPE.DATE }, Restriction: { type: Model.FIELD_TYPE.STRING } } }); }); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,49 @@ /** * Widget itself * * @author Sermik 2014, And <[email protected]> */ define(['Shared/WidgetFoundation/GridWidget', 'Shared/WidgetFoundation/Util'], function (GridWidget, Util) { return GridWidget.loader({ default: { title: 'Restricted Accounts', // Minimum size size: { width: 1000 }, // Widget custom filters filter: { customFilters: [ ['Select', /* input type */ { title: 'Restriction Type:', // Input name (must match with server model) name: 'restrictionType', values: [ ["All", "All"], ["111175", "An Employee of Other Brokerage Firm-Rule 407"], ["111185", "Abandoned Property"], ["111165", "BRR"], ["111144", "CBG Missing Documents"], ["111134", "CP Baker - contact margin"], ["111153", "Dawson James"], ["111121", "Delinquent Documents"], ["111122", "Date of Birth/UTMA"], ["111142", "EJ Sterling"], ["111125", "Equifax Mismatch - AML"], ["111133", "FINRA Rule 2111"], ["111141", "4 Points Capital-Gene Murphy"], ["111155", "Fraud Alert"], ["111136", "Lead/Generation Accounts"], ["111124", "John Thomas Missing Documents"], ["111135", "Margin"], ["111143", "Meyers"], ["111145", "New Accounts"], ["111126", "PCG-San Francisco - contact NA"], ["111154", "WRP"] ] }], // Button, which send data to server and persist values 'ApplyButton' ] } } }); });