diff --git a/viewer/js/config/app.js b/viewer/js/config/app.js index dc36e5e1c..15bbaca98 100644 --- a/viewer/js/config/app.js +++ b/viewer/js/config/app.js @@ -49,15 +49,16 @@ //_MyCustomMixin ) { - var controller = new (declare([ - _ControllerBase, - _ConfigMixin, + var App = declare([ _LayoutMixin, _MapMixin, _WidgetsMixin, - _WebMapMixin - ]))(); - controller.startup(); + _WebMapMixin, + _ConfigMixin, + _ControllerBase + ]); + var app = new App(); + app.startup(); }); })(); diff --git a/viewer/js/config/viewer.js b/viewer/js/config/viewer.js index 1ea172b9f..d1f45deeb 100644 --- a/viewer/js/config/viewer.js +++ b/viewer/js/config/viewer.js @@ -78,7 +78,7 @@ define([ sliderStyle: 'small' }, - //webMapId: 'ef9c7fbda731474d98647bebb4b33c20', // High Cost Mortgage + webMapId: '880a967872114b44a63c207df10e0a75', // High Cost Mortgage // webMapOptions: {}, // panes: { @@ -273,6 +273,16 @@ define([ }], // set include:true to load. For titlePane type set position the the desired order in the sidebar widgets: { + // a widget that loads and runs before the app + // loader: { + // type: 'loading', + // path: 'dijit/_WidgetBase', + // options: { + // constructor: function () { + // alert('Preload!'); + // } + // } + // }, growler: { include: true, id: 'growler', diff --git a/viewer/js/viewer/_ConfigMixin.js b/viewer/js/viewer/_ConfigMixin.js index afbe4abe4..10a7929fe 100644 --- a/viewer/js/viewer/_ConfigMixin.js +++ b/viewer/js/viewer/_ConfigMixin.js @@ -9,12 +9,41 @@ define([ ) { return declare(null, { - startup: function () { - this.inherited(arguments); - this.initConfigAsync().then( - lang.hitch(this, 'initConfigSuccess'), - lang.hitch(this, 'initConfigError') - ); + loadConfig: function (wait) { + + // this will be used to make any inherited methods 'wait' + var waitDeferred; + + if (wait) { + waitDeferred = new Deferred(); + + // if we need to wait for a previous deferred + // wait for it, + wait.then(lang.hitch(this, function () { + + // load the config + this.initConfigAsync().then(lang.hitch(this, function () { + + // do some stuff + this.initConfigSuccess(arguments); + + // resolve + waitDeferred.resolve(); + }), + lang.hitch(this, 'initConfigError') + ); + + })); + } else { + + waitDeferred = this.initConfigAsync(); + waitDeferred.then( + lang.hitch(this, 'initConfigSuccess'), + lang.hitch(this, 'initConfigError') + ); + } + // call any inherited methods or return a deferred + return this.inherited(arguments, [waitDeferred]) || waitDeferred; }, initConfigAsync: function () { @@ -46,8 +75,6 @@ define([ current: config.defaultMapClickMode, defaultMode: config.defaultMapClickMode }; - - this.configDeferred.resolve(config); }, initConfigError: function (err) { diff --git a/viewer/js/viewer/_ControllerBase.js b/viewer/js/viewer/_ControllerBase.js index c524fb828..f9727882f 100644 --- a/viewer/js/viewer/_ControllerBase.js +++ b/viewer/js/viewer/_ControllerBase.js @@ -4,38 +4,74 @@ define([ 'dojo/_base/lang', 'dojo/Deferred' ], function ( - declare, - lang, - Deferred + declare, lang, Deferred ) { return declare(null, { - init: function () { + /** + * A method run before anything else, can be inherited by mixins to + * load and process the config sync or async + * @return {undefined | Deferred} If the operation is async it should return + * a deferred, otherwise it should return the value of `this.inherited(arguments)` + */ + loadConfig: function () { + return this.inherited(arguments); + }, + /** + * A method run after the config is loaded but before startup is called + * on mixins + * @return {undefined | Deferred} If the operation is async it should return + * a deferred, otherwise it should return the value of `this.inherited(arguments)` + */ + preStartup: function () { + return this.inherited(arguments); + }, + /** + * executes an array of asynchronous methods synchronously + * @param {Array} methods The array of functions to execute + * @param {Deferred} deferred A deferred created inside the method and resolved once all methods are complete + * @return {Deferred} A deferred resolved once all methods are executed + */ + executeSync: function (methods, deferred) { + deferred = deferred || new Deferred(); + + // if our list is empty, resolve the deferred and quit + if (!methods || !methods.length) { + deferred.resolve(); + return deferred; + } - // create a set of deferreds that can be resolved by mixins - // other mixins can also create deferreds in their relevent constructors - this.configDeferred = new Deferred(); + // execute and remove the method from the list + var result = lang.hitch(this, methods.splice(0, 1)[0])(); - this.inherited(arguments); - }, + // execute our next function once this one completes + if (result) { + result.then(lang.hitch(this, 'executeSync', methods, deferred)); + } else { + this.executeSync(methods, deferred); + } + return deferred; + }, startup: function () { - this.init(); - this.mapDeferred.then(function () { - console.log(' map deferred'); - }); - this.configDeferred.then(function () { - console.log(' config deferred'); - }); - this.layoutDeferred.then(function () { - console.log(' layout deferred'); - }); - this.inherited(arguments); + + var inherited = this.getInherited(arguments); + this.executeSync([ + this.loadConfig, + this.preStartup + ]).then(lang.hitch(this, function () { + console.log(this); + + // start up the mixin chain + inherited.apply(this); + })); + + }, //centralized error handler handleError: function (options) { if (this.config.isDebug) { - if (typeof (console) === 'object') { + if (typeof(console) === 'object') { for (var option in options) { if (options.hasOwnProperty(option)) { console.log(option, options[option]); diff --git a/viewer/js/viewer/_LayoutMixin.js b/viewer/js/viewer/_LayoutMixin.js index f4de2c880..0b1657a16 100644 --- a/viewer/js/viewer/_LayoutMixin.js +++ b/viewer/js/viewer/_LayoutMixin.js @@ -65,16 +65,12 @@ define([ } }, collapseButtons: {}, - init: function () { - this.inherited(arguments); + preStartup: function () { this.layoutDeferred = new Deferred(); - }, - startup: function () { - this.inherited(arguments); - promiseAll([this.configDeferred]).then(lang.hitch(this, 'initLayout')); + return this.inherited(arguments); }, - initLayout: function () { + startup: function () { this.config.layout = this.config.layout || {}; this.addTopics(); @@ -84,6 +80,7 @@ define([ // resolve the layout deferred this.layoutDeferred.resolve(); + this.inherited(arguments); }, // add topics for subscribing and publishing @@ -186,7 +183,7 @@ define([ panes[key] = lang.mixin(this.defaultPanes[key], panes[key]); } } - // where to place the buttons + // where to place the buttons // either the center map pane or the outer pane? this.collapseButtonsPane = this.config.collapseButtonsPane || 'outer'; @@ -266,7 +263,10 @@ define([ } if (!suppressEvent) { - topic.publish('viewer/onTogglePane', {pane: id, show: show}); + topic.publish('viewer/onTogglePane', { + pane: id, + show: show + }); } } } diff --git a/viewer/js/viewer/_MapMixin.js b/viewer/js/viewer/_MapMixin.js index 492bfd877..1c0eeab15 100644 --- a/viewer/js/viewer/_MapMixin.js +++ b/viewer/js/viewer/_MapMixin.js @@ -25,14 +25,14 @@ define([ return declare(null, { - init: function () { - this.inherited(arguments); + preStartup: function () { this.mapDeferred = new Deferred(); + return this.inherited(arguments); }, startup: function () { this.inherited(arguments); - promiseAll([this.configDeferred, this.layoutDeferred]).then(lang.hitch(this, 'initMapAsync')); + this.layoutDeferred.then(lang.hitch(this, 'initMapAsync')); }, initMapAsync: function () { @@ -42,6 +42,7 @@ define([ this._createMap(returnWarnings).then( lang.hitch(this, '_createMapResult', returnDeferred, returnWarnings) ); + returnDeferred.then(lang.hitch(this, 'initMapComplete')); return returnDeferred; }, @@ -49,17 +50,8 @@ define([ var mapDeferred = new Deferred(), container = dom.byId(this.config.layout.map) || 'mapCenter'; - if (this.config.webMapId) { - if (this._initWebMap) { - // mapDeferred = this._initWebMap(this.config.webMapId, container, this.config.webMapOptions); - } else { - returnWarnings.push('The "_WebMapMixin" Controller Mixin is required to use a webmap'); - mapDeferred.resolve(returnWarnings); - } - } else { - this.map = new Map(container, this.config.mapOptions); - mapDeferred.resolve(returnWarnings); - } + this.map = new Map(container, this.config.mapOptions); + mapDeferred.resolve(returnWarnings); return mapDeferred; }, @@ -206,8 +198,6 @@ define([ } if (this.map) { - // in _WidgetsMixin - // this.createWidgets(['map', 'layer']); this.map.on('resize', function (evt) { var pnt = evt.target.extent.getCenter(); @@ -216,11 +206,7 @@ define([ }, 100); }); - // in _LayoutsMixin - // this.createPanes(); - - // in _WidgetsMixin - // this.createWidgets(); + // resolve the map deferred this.mapDeferred.resolve(this.map); } diff --git a/viewer/js/viewer/_WebMapMixin.js b/viewer/js/viewer/_WebMapMixin.js index a637c4094..9bd9ad373 100644 --- a/viewer/js/viewer/_WebMapMixin.js +++ b/viewer/js/viewer/_WebMapMixin.js @@ -3,6 +3,7 @@ define([ 'dojo/_base/lang', 'dojo/_base/array', 'dojo/promise/all', + 'dojo/dom', 'esri/arcgis/utils', 'esri/units', @@ -14,6 +15,7 @@ define([ lang, array, promiseAll, + dom, arcgisUtils, units, @@ -23,7 +25,7 @@ define([ return declare(null, { startup: function () { this.inherited(arguments); - promiseAll([this.configDeferred, this.mapDeferred]).then(lang.hitch(this, '_initWebMap')); + this.mapDeferred.then(lang.hitch(this, '_initWebMap')); }, _initWebMap: function () { diff --git a/viewer/js/viewer/_WidgetsMixin.js b/viewer/js/viewer/_WidgetsMixin.js index 0dacdc1da..75c6fdfd1 100644 --- a/viewer/js/viewer/_WidgetsMixin.js +++ b/viewer/js/viewer/_WidgetsMixin.js @@ -2,6 +2,8 @@ define([ 'dojo/_base/declare', 'dojo/_base/array', 'dojo/_base/lang', + 'dojo/promise/all', + 'dojo/Deferred', 'put-selector', @@ -15,6 +17,8 @@ define([ declare, array, lang, + promiseAll, + Deferred, put, @@ -34,20 +38,36 @@ define([ widgets: {}, widgetTypes: ['titlePane', 'contentPane', 'floating', 'domNode', 'invisible', 'map', 'layer', 'layout', 'loading'], - init: function () { - this.inherited(arguments); - this.configDeferred.then(lang.hitch(this, 'createWidgets', ['loading'])); + preStartup: function (wait) { + + var waitDeferred; + if (wait) { + waitDeferred = new Deferred(); + + wait.then(lang.hitch(this, function () { + // load loading widgets + promiseAll(this.createWidgets(['loading'])).then(waitDeferred.resolve); + })); + } else { + var deferreds = this.createWidgets(['loading']); + if (deferreds.length) { + waitDeferred = promiseAll(deferreds); + } + } + + return this.inherited(arguments) || waitDeferred; }, startup: function () { this.inherited(arguments); - this.configDeferred.then(lang.hitch(this, function () { - if (this.mapDeferred) { - this.mapDeferred.then(lang.hitch(this, 'createWidgets', ['map', 'layer'])); - } - if (this.layoutDeferred) { - this.layoutDeferred.then(lang.hitch(this, 'createWidgets', ['titlePane', 'contentPane', 'floating', 'domNode', 'invisible', 'layout'])); - } - })); + if (this.mapDeferred) { + this.mapDeferred.then(lang.hitch(this, 'createWidgets', ['map', 'layer'])); + } + if (this.layoutDeferred) { + promiseAll([this.mapDeferred, this.layoutDeferred]) + .then(lang.hitch(this, + 'createWidgets', ['titlePane', 'contentPane', 'floating', 'domNode', 'invisible', 'layout'] + )); + } }, createWidgets: function (widgetTypes) { @@ -97,10 +117,14 @@ define([ paneWidgets.sort(function (a, b) { return a.position - b.position; }); - + var deferreds = []; array.forEach(paneWidgets, function (paneWidget, i) { - this.widgetLoader(paneWidget, i); + var def = this.widgetLoader(paneWidget, i); + if (def) { + deferreds.push(def); + } }, this); + return deferreds; }, widgetLoader: function (widgetConfig, position) { @@ -137,11 +161,19 @@ define([ } // 2 ways to use require to accommodate widgets that may have an optional separate configuration file + var deferred = new Deferred(); if (typeof(widgetConfig.options) === 'string') { - require([widgetConfig.options, widgetConfig.path], lang.hitch(this, 'createWidget', widgetConfig)); + require([widgetConfig.options, widgetConfig.path], lang.hitch(this, function (options, WidgetClass) { + deferred.resolve(); + this.createWidget(widgetConfig, options, WidgetClass); + })); } else { - require([widgetConfig.path], lang.hitch(this, 'createWidget', widgetConfig, widgetConfig.options)); + require([widgetConfig.path], lang.hitch(this, function (WidgetClass) { + deferred.resolve(); + this.createWidget(widgetConfig, widgetConfig.options, WidgetClass); + })); } + return deferred; }, createWidget: function (widgetConfig, options, WidgetClass) {