1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 /** 6 * @fileoverview Implementation of ScreenContext class: key-value storage for 7 * values that are shared between C++ and JS sides. 8 */ 9 cr.define('login', function() { 10 'use strict'; 11 12 function require(condition, message) { 13 if (!condition) { 14 throw Error(message); 15 } 16 } 17 18 function checkKeyIsValid(key) { 19 var keyType = typeof key; 20 require(keyType === 'string', 'Invalid type of key: "' + keyType + '".'); 21 } 22 23 function checkValueIsValid(value) { 24 var valueType = typeof value; 25 require(['string', 'boolean', 'number'].indexOf(valueType) != -1, 26 'Invalid type of value: "' + valueType + '".'); 27 } 28 29 function ScreenContext() { 30 this.storage_ = {}; 31 this.changes_ = {}; 32 this.observers_ = {}; 33 } 34 35 ScreenContext.prototype = { 36 /** 37 * Returns stored value for |key| or |defaultValue| if key not found in 38 * storage. Throws Error if key not found and |defaultValue| omitted. 39 */ 40 get: function(key, defaultValue) { 41 checkKeyIsValid(key); 42 if (this.hasKey(key)) { 43 return this.storage_[key]; 44 } else if (typeof defaultValue !== 'undefined') { 45 return defaultValue; 46 } else { 47 throw Error('Key "' + key + '" not found.'); 48 } 49 }, 50 51 /** 52 * Sets |value| for |key|. Returns true if call changes state of context, 53 * false otherwise. 54 */ 55 set: function(key, value) { 56 checkKeyIsValid(key); 57 checkValueIsValid(value); 58 if (this.hasKey(key) && this.storage_[key] === value) 59 return false; 60 this.changes_[key] = value; 61 this.storage_[key] = value; 62 return true; 63 }, 64 65 hasKey: function(key) { 66 checkKeyIsValid(key); 67 return this.storage_.hasOwnProperty(key); 68 }, 69 70 hasChanges: function() { 71 return Object.keys(this.changes_).length > 0; 72 }, 73 74 /** 75 * Applies |changes| to context. Returns Array of changed keys' names. 76 */ 77 applyChanges: function(changes) { 78 require(!this.hasChanges(), 'Context has changes.'); 79 var oldValues = {}; 80 for (var key in changes) { 81 checkKeyIsValid(key); 82 checkValueIsValid(changes[key]); 83 oldValues[key] = this.storage_[key]; 84 this.storage_[key] = changes[key]; 85 } 86 var observers = this.cloneObservers_(); 87 for (var key in changes) { 88 if (observers.hasOwnProperty(key)) { 89 var keyObservers = observers[key]; 90 for (var observerIndex in keyObservers) 91 keyObservers[observerIndex](changes[key], oldValues[key], key); 92 } 93 } 94 return Object.keys(changes); 95 }, 96 97 /** 98 * Returns changes made on context since previous call. 99 */ 100 getChangesAndReset: function() { 101 var result = this.changes_; 102 this.changes_ = {}; 103 return result; 104 }, 105 106 addObserver: function(key, observer) { 107 if (!this.observers_.hasOwnProperty(key)) 108 this.observers_[key] = []; 109 if (this.observers_[key].indexOf(observer) !== -1) { 110 console.warn('Observer already registered.'); 111 return; 112 } 113 this.observers_[key].push(observer); 114 }, 115 116 removeObserver: function(observer) { 117 for (var key in this.observers_) { 118 var observerIndex = this.observers_[key].indexOf(observer); 119 if (observerIndex != -1) 120 this.observers_[key].splice(observerIndex, 1); 121 } 122 }, 123 124 /** 125 * Creates deep copy of observers lists. 126 * @private 127 */ 128 cloneObservers_: function() { 129 var result = {}; 130 for (var key in this.observers_) 131 result[key] = this.observers_[key].slice(); 132 return result; 133 } 134 }; 135 136 return { 137 ScreenContext: ScreenContext 138 }; 139 }); 140