67948ead5f27747dc032e8c0ac7d00568c645e93
[portal/sdk.git] /
1 /*
2  * Copyright (c) 2014 DataTorrent, Inc. ALL Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 'use strict';
18
19 angular.module('ui.dashboard')
20   .factory('DashboardState', ['$log', '$q', function ($log, $q) {
21     function DashboardState(storage, id, hash, widgetDefinitions, stringify) {
22       this.storage = storage;
23       this.id = id;
24       this.hash = hash;
25       this.widgetDefinitions = widgetDefinitions;
26       this.stringify = stringify;
27     }
28
29     DashboardState.prototype = {
30       /**
31        * Takes array of widget instance objects, serializes, 
32        * and saves state.
33        * 
34        * @param  {Array} widgets  scope.widgets from dashboard directive
35        * @return {Boolean}        true on success, false on failure
36        */
37       save: function (widgets) {
38         
39         if (!this.storage) {
40           return true;
41         }
42
43         var serialized = _.map(widgets, function (widget) {
44           return widget.serialize();
45         });
46
47         var item = { widgets: serialized, hash: this.hash };
48
49         if (this.stringify) {
50           item = JSON.stringify(item);
51         }
52
53         this.storage.setItem(this.id, item);
54         return true;
55       },
56
57       /**
58        * Loads dashboard state from the storage object.
59        * Can handle a synchronous response or a promise.
60        * 
61        * @return {Array|Promise} Array of widget definitions or a promise
62        */
63       load: function () {
64
65         if (!this.storage) {
66           return null;
67         }
68
69         var serialized;
70
71         // try loading storage item
72         serialized = this.storage.getItem( this.id );
73
74         if (serialized) {
75           // check for promise
76           if (angular.isObject(serialized) && angular.isFunction(serialized.then)) {
77             return this._handleAsyncLoad(serialized);
78           }
79           // otherwise handle synchronous load
80           return this._handleSyncLoad(serialized);
81         } else {
82           return null;
83         }
84       },
85
86       _handleSyncLoad: function(serialized) {
87
88         var deserialized, result = [];
89
90         if (!serialized) {
91           return null;
92         }
93
94         if (this.stringify) {
95           try { // to deserialize the string
96
97             deserialized = JSON.parse(serialized);
98
99           } catch (e) {
100
101             // bad JSON, log a warning and return
102             $log.warn('Serialized dashboard state was malformed and could not be parsed: ', serialized);
103             return null;
104
105           }
106         }
107         else {
108           deserialized = serialized;
109         }
110
111         // check hash against current hash
112         if (deserialized.hash !== this.hash) {
113
114           $log.info('Serialized dashboard from storage was stale (old hash: ' + deserialized.hash + ', new hash: ' + this.hash + ')');
115           this.storage.removeItem(this.id);
116           return null;
117
118         }
119
120         // Cache widgets
121         var savedWidgetDefs = deserialized.widgets;
122
123         // instantiate widgets from stored data
124         for (var i = 0; i < savedWidgetDefs.length; i++) {
125
126           // deserialized object
127           var savedWidgetDef = savedWidgetDefs[i];
128
129           // widget definition to use
130           var widgetDefinition = this.widgetDefinitions.getByName(savedWidgetDef.name);
131
132           // check for no widget
133           if (!widgetDefinition) {
134             // no widget definition found, remove and return false
135             $log.warn('Widget with name "' + savedWidgetDef.name + '" was not found in given widget definition objects');
136             continue;
137           }
138
139           // check widget-specific storageHash
140           if (widgetDefinition.hasOwnProperty('storageHash') && widgetDefinition.storageHash !== savedWidgetDef.storageHash) {
141             // widget definition was found, but storageHash was stale, removing storage
142             $log.info('Widget Definition Object with name "' + savedWidgetDef.name + '" was found ' +
143               'but the storageHash property on the widget definition is different from that on the ' +
144               'serialized widget loaded from storage. hash from storage: "' + savedWidgetDef.storageHash + '"' +
145               ', hash from WDO: "' + widgetDefinition.storageHash + '"');
146             continue;
147           }
148
149           // push instantiated widget to result array
150           result.push(savedWidgetDef);
151         }
152
153         return result;
154       },
155
156       _handleAsyncLoad: function(promise) {
157         var self = this;
158         var deferred = $q.defer();
159         promise.then(
160           // success
161           function(res) {
162             var result = self._handleSyncLoad(res);
163             if (result) {
164               deferred.resolve(result);
165             } else {
166               deferred.reject(result);
167             }
168           },
169           // failure
170           function(res) {
171             deferred.reject(res);
172           }
173         );
174
175         return deferred.promise;
176       }
177
178     };
179     return DashboardState;
180   }]);