@@ -21,6 +21,8 @@ limitations under the License.
21
21
* It generates URI components like: events&runPrefix=train*
22
22
* which TensorBoard uses after like localhost:8000/#events&runPrefix=train*
23
23
* to store state in the URI.
24
+ *
25
+ * It also allows saving the values to localStorage for long-term persistance.
24
26
*/
25
27
module TF . URIStorage {
26
28
type StringDict = { [ key : string ] : string } ;
@@ -50,76 +52,79 @@ module TF.URIStorage {
50
52
export let DISAMBIGUATOR = 'disambiguator' ;
51
53
52
54
/**
53
- * Return a boolean stored in the URI, given a corresponding key .
55
+ * Return a string stored in URI or localStorage .
54
56
* Undefined if not found.
55
57
*/
56
- export function getBoolean ( key : string ) : boolean {
57
- let items = _componentToDict ( _readComponent ( ) ) ;
58
- let item = items [ key ] ;
59
- return item === 'true' ? true : item === 'false' ? false : undefined ;
58
+ export function getString ( key : string , useLocalStorage : boolean ) : string {
59
+ if ( useLocalStorage ) {
60
+ return window . localStorage . getItem ( key ) ;
61
+ } else {
62
+ return _componentToDict ( _readComponent ( ) ) [ key ] ;
63
+ }
60
64
}
61
65
62
66
/**
63
- * Store a boolean in the URI, with a corresponding key .
67
+ * Set a string in URI or localStorage .
64
68
*/
65
- export function setBoolean ( key : string , value : boolean ) {
66
- let items = _componentToDict ( _readComponent ( ) ) ;
67
- items [ key ] = value . toString ( ) ;
68
- _writeComponent ( _dictToComponent ( items ) ) ;
69
+ export function setString (
70
+ key : string , value : string , useLocalStorage : boolean ) {
71
+ if ( useLocalStorage ) {
72
+ window . localStorage . setItem ( key , value ) ;
73
+ } else {
74
+ let items = _componentToDict ( _readComponent ( ) ) ;
75
+ items [ key ] = value ;
76
+ _writeComponent ( _dictToComponent ( items ) ) ;
77
+ }
69
78
}
70
79
71
80
/**
72
- * Return a string stored in the URI, given a corresponding key .
81
+ * Return a boolean stored in stored in URI or localStorage .
73
82
* Undefined if not found.
74
83
*/
75
- export function getString ( key : string ) : string {
76
- let items = _componentToDict ( _readComponent ( ) ) ;
77
- return items [ key ] ;
84
+ export function getBoolean ( key : string , useLocalStorage : boolean ) : boolean {
85
+ let item = getString ( key , useLocalStorage ) ;
86
+ return item === 'true' ? true : item === 'false' ? false : undefined ;
78
87
}
79
88
80
89
/**
81
- * Store a string in the URI, with a corresponding key .
90
+ * Store a boolean in URI or localStorage .
82
91
*/
83
- export function setString ( key : string , value : string ) {
84
- let items = _componentToDict ( _readComponent ( ) ) ;
85
- items [ key ] = value ;
86
- _writeComponent ( _dictToComponent ( items ) ) ;
92
+ export function setBoolean (
93
+ key : string , value : boolean , useLocalStorage = false ) {
94
+ setString ( key , value . toString ( ) , useLocalStorage ) ;
87
95
}
88
96
89
97
/**
90
- * Return a number stored in the URI, given a corresponding key .
98
+ * Return a number stored in stored in URI or localStorage .
91
99
* Undefined if not found.
92
100
*/
93
- export function getNumber ( key : string ) : number {
94
- let items = _componentToDict ( _readComponent ( ) ) ;
95
- return items [ key ] === undefined ? undefined : + items [ key ] ;
101
+ export function getNumber ( key : string , useLocalStorage : boolean ) : number {
102
+ let item = getString ( key , useLocalStorage ) ;
103
+ return item === undefined ? undefined : + item ;
96
104
}
97
105
98
106
/**
99
- * Store a number in the URI, with a corresponding key .
107
+ * Store a number in URI or localStorage .
100
108
*/
101
- export function setNumber ( key : string , value : number ) {
102
- let items = _componentToDict ( _readComponent ( ) ) ;
103
- items [ key ] = '' + value ;
104
- _writeComponent ( _dictToComponent ( items ) ) ;
109
+ export function setNumber (
110
+ key : string , value : number , useLocalStorage : boolean ) {
111
+ setString ( key , '' + value , useLocalStorage ) ;
105
112
}
106
113
107
114
/**
108
- * Return an object stored in the URI, given a corresponding key .
115
+ * Return an object stored in stored in URI or localStorage .
109
116
* Undefined if not found.
110
117
*/
111
- export function getObject ( key : string ) : Object {
112
- let items = _componentToDict ( _readComponent ( ) ) ;
113
- return items [ key ] === undefined ? undefined : JSON . parse ( atob ( items [ key ] ) ) ;
118
+ export function getObject ( key : string , useLocalStorage : boolean ) : { } {
119
+ let item = getString ( key , useLocalStorage ) ;
120
+ return item === undefined ? undefined : JSON . parse ( atob ( item ) ) ;
114
121
}
115
122
116
123
/**
117
- * Store an object in the URI, with a corresponding key .
124
+ * Store an object in URI or localStorage .
118
125
*/
119
- export function setObject ( key : string , value : Object ) {
120
- let items = _componentToDict ( _readComponent ( ) ) ;
121
- items [ key ] = btoa ( JSON . stringify ( value ) ) ;
122
- _writeComponent ( _dictToComponent ( items ) ) ;
126
+ export function setObject ( key : string , value : { } , useLocalStorage : boolean ) {
127
+ setString ( key , btoa ( JSON . stringify ( value ) ) , useLocalStorage ) ;
123
128
}
124
129
125
130
/**
@@ -129,7 +134,7 @@ module TF.URIStorage {
129
134
* same propertyName.
130
135
*/
131
136
export function getURIStorageName (
132
- component : Object , propertyName : string ) : string {
137
+ component : { } , propertyName : string ) : string {
133
138
let d = component [ DISAMBIGUATOR ] ;
134
139
let components = d == null ? [ propertyName ] : [ d , propertyName ] ;
135
140
return components . join ( '.' ) ;
@@ -142,8 +147,10 @@ module TF.URIStorage {
142
147
* (2) Sets up listener that updates Polymer property on hash change.
143
148
*/
144
149
export function getBooleanInitializer (
145
- propertyName : string , defaultVal : boolean ) : Function {
146
- return _getInitializer ( getBoolean , propertyName , defaultVal ) ;
150
+ propertyName : string , defaultVal : boolean ,
151
+ useLocalStorage = false ) : Function {
152
+ return _getInitializer (
153
+ getBoolean , propertyName , defaultVal , useLocalStorage ) ;
147
154
}
148
155
149
156
/**
@@ -153,8 +160,10 @@ module TF.URIStorage {
153
160
* (2) Sets up listener that updates Polymer property on hash change.
154
161
*/
155
162
export function getStringInitializer (
156
- propertyName : string , defaultVal : string ) : Function {
157
- return _getInitializer ( getString , propertyName , defaultVal ) ;
163
+ propertyName : string , defaultVal : string ,
164
+ useLocalStorage = false ) : Function {
165
+ return _getInitializer (
166
+ getString , propertyName , defaultVal , useLocalStorage ) ;
158
167
}
159
168
160
169
/**
@@ -164,8 +173,10 @@ module TF.URIStorage {
164
173
* (2) Sets up listener that updates Polymer property on hash change.
165
174
*/
166
175
export function getNumberInitializer (
167
- propertyName : string , defaultVal : number ) : Function {
168
- return _getInitializer ( getNumber , propertyName , defaultVal ) ;
176
+ propertyName : string , defaultVal : number ,
177
+ useLocalStorage = false ) : Function {
178
+ return _getInitializer (
179
+ getNumber , propertyName , defaultVal , useLocalStorage ) ;
169
180
}
170
181
171
182
/**
@@ -177,42 +188,50 @@ module TF.URIStorage {
177
188
* Generates a deep clone of the defaultVal to avoid mutation issues.
178
189
*/
179
190
export function getObjectInitializer (
180
- propertyName : string , defaultVal : Object ) : Function {
181
- return _getInitializer ( getObject , propertyName , defaultVal ) ;
191
+ propertyName : string , defaultVal : { } , useLocalStorage = false ) : Function {
192
+ return _getInitializer (
193
+ getObject , propertyName , defaultVal , useLocalStorage ) ;
182
194
}
183
195
184
196
/**
185
197
* Return a function that updates URIStorage when a string property changes.
186
198
*/
187
199
export function getBooleanObserver (
188
- propertyName : string , defaultVal : boolean ) : Function {
189
- return _getObserver ( getBoolean , setBoolean , propertyName , defaultVal ) ;
200
+ propertyName : string , defaultVal : boolean ,
201
+ useLocalStorage = false ) : Function {
202
+ return _getObserver (
203
+ getBoolean , setBoolean , propertyName , defaultVal , useLocalStorage ) ;
190
204
}
191
205
192
206
/**
193
207
* Return a function that updates URIStorage when a string property changes.
194
208
*/
195
209
export function getStringObserver (
196
- propertyName : string , defaultVal : string ) : Function {
197
- return _getObserver ( getString , setString , propertyName , defaultVal ) ;
210
+ propertyName : string , defaultVal : string ,
211
+ useLocalStorage = false ) : Function {
212
+ return _getObserver (
213
+ getString , setString , propertyName , defaultVal , useLocalStorage ) ;
198
214
}
199
215
200
216
/**
201
217
* Return a function that updates URIStorage when a number property changes.
202
218
*/
203
219
export function getNumberObserver (
204
- propertyName : string , defaultVal : number ) : Function {
205
- return _getObserver ( getNumber , setNumber , propertyName , defaultVal ) ;
220
+ propertyName : string , defaultVal : number ,
221
+ useLocalStorage = false ) : Function {
222
+ return _getObserver (
223
+ getNumber , setNumber , propertyName , defaultVal , useLocalStorage ) ;
206
224
}
207
225
208
226
/**
209
227
* Return a function that updates URIStorage when an object property changes.
210
228
* Generates a deep clone of the defaultVal to avoid mutation issues.
211
229
*/
212
230
export function getObjectObserver (
213
- propertyName : string , defaultVal : Object ) : Function {
231
+ propertyName : string , defaultVal : { } , useLocalStorage = false ) : Function {
214
232
let clone = _ . cloneDeep ( defaultVal ) ;
215
- return _getObserver ( getObject , setObject , propertyName , clone ) ;
233
+ return _getObserver (
234
+ getObject , setObject , propertyName , clone , useLocalStorage ) ;
216
235
}
217
236
218
237
/**
@@ -292,7 +311,8 @@ module TF.URIStorage {
292
311
* (2) Sets up listener that updates Polymer property on hash change.
293
312
*/
294
313
function _getInitializer < T > (
295
- get : ( name : string ) => T , propertyName : string , defaultVal : T ) : Function {
314
+ get : ( name : string , useLocalStorage : boolean ) => T , propertyName : string ,
315
+ defaultVal : T , useLocalStorage ) : Function {
296
316
return function ( ) {
297
317
let URIStorageName = getURIStorageName ( this , propertyName ) ;
298
318
// setComponentValue will be called every time the hash changes, and is
@@ -301,23 +321,39 @@ module TF.URIStorage {
301
321
// It is important that this function does not re-assign needlessly,
302
322
// to avoid Polymer observer churn.
303
323
let setComponentValue = ( ) => {
304
- let uriValue = get ( URIStorageName ) ;
324
+ let uriValue = get ( URIStorageName , false ) ;
305
325
let currentValue = this [ propertyName ] ;
306
326
// if uriValue is undefined, we will ensure that the property has the
307
327
// default value
308
328
if ( uriValue === undefined ) {
309
- if ( ! _ . isEqual ( currentValue , defaultVal ) ) {
329
+ let valueToSet : T ;
330
+ // if we are using localStorage, we will set the value to the value
331
+ // from localStorage. Then, the corresponding observer will proxy
332
+ // the localStorage value into URI storage.
333
+ // in this way, localStorage takes precedence over the default val
334
+ // but not over the URI value.
335
+ if ( useLocalStorage ) {
336
+ let useLocalStorageValue = get ( URIStorageName , true ) ;
337
+ valueToSet = useLocalStorageValue === undefined ?
338
+ defaultVal :
339
+ useLocalStorageValue ;
340
+ } else {
341
+ valueToSet = defaultVal ;
342
+ }
343
+ if ( ! _ . isEqual ( currentValue , valueToSet ) ) {
310
344
// If we don't have an explicit URI value, then we need to ensure
311
345
// the property value is equal to the default value.
312
346
// We will assign a clone rather than the canonical default, because
313
347
// the component receiving this property may mutate it, and we need
314
348
// to keep a pristine copy of the default.
315
- this [ propertyName ] = _ . clone ( defaultVal ) ;
349
+ this [ propertyName ] = _ . clone ( valueToSet ) ;
316
350
}
317
351
// In this case, we have an explicit URI value, so we will ensure that
318
352
// the component has an equivalent value.
319
- } else if ( ! _ . isEqual ( uriValue , currentValue ) ) {
320
- this [ propertyName ] = uriValue ;
353
+ } else {
354
+ if ( ! _ . isEqual ( uriValue , currentValue ) ) {
355
+ this [ propertyName ] = uriValue ;
356
+ }
321
357
}
322
358
} ;
323
359
// Set the value on the property.
@@ -331,16 +367,22 @@ module TF.URIStorage {
331
367
* Return a function that updates URIStorage when a property changes.
332
368
*/
333
369
function _getObserver < T > (
334
- get : ( name : string ) => T , set : ( name : string , newVal : T ) => void ,
335
- propertyName : string , defaultVal : T ) : Function {
370
+ get : ( name : string , useLocalStorage : boolean ) => T ,
371
+ set : ( name : string , newVal : T , useLocalStorage : boolean ) => void ,
372
+ propertyName : string , defaultVal : T , useLocalStorage : boolean ) : Function {
336
373
return function ( ) {
337
374
let URIStorageName = getURIStorageName ( this , propertyName ) ;
338
375
let newVal = this [ propertyName ] ;
339
- if ( ! _ . isEqual ( newVal , get ( URIStorageName ) ) ) {
376
+ // if this is a localStorage property, we always synchronize the value
377
+ // in localStorage to match the one currently in the URI.
378
+ if ( useLocalStorage ) {
379
+ set ( URIStorageName , newVal , true ) ;
380
+ }
381
+ if ( ! _ . isEqual ( newVal , get ( URIStorageName , false ) ) ) {
340
382
if ( _ . isEqual ( newVal , defaultVal ) ) {
341
- _unset ( URIStorageName ) ;
383
+ _unsetFromURI ( URIStorageName ) ;
342
384
} else {
343
- set ( URIStorageName , newVal ) ;
385
+ set ( URIStorageName , newVal , false ) ;
344
386
}
345
387
}
346
388
} ;
@@ -349,7 +391,7 @@ module TF.URIStorage {
349
391
/**
350
392
* Delete a key from the URI.
351
393
*/
352
- function _unset ( key ) {
394
+ function _unsetFromURI ( key ) {
353
395
let items = _componentToDict ( _readComponent ( ) ) ;
354
396
delete items [ key ] ;
355
397
_writeComponent ( _dictToComponent ( items ) ) ;
0 commit comments