Home | History | Annotate | Download | only in login
      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              Array.isArray(value)),
     27             'Invalid type of value: "' + valueType + '".');
     28   }
     29 
     30   function ScreenContext() {
     31     this.storage_ = {};
     32     this.changes_ = {};
     33     this.observers_ = {};
     34   }
     35 
     36   ScreenContext.prototype = {
     37     /**
     38      * Returns stored value for |key| or |defaultValue| if key not found in
     39      * storage. Throws Error if key not found and |defaultValue| omitted.
     40      */
     41     get: function(key, defaultValue) {
     42       checkKeyIsValid(key);
     43       if (this.hasKey(key)) {
     44         return this.storage_[key];
     45       } else if (typeof defaultValue !== 'undefined') {
     46         return defaultValue;
     47       } else {
     48         throw Error('Key "' + key + '" not found.');
     49       }
     50     },
     51 
     52     /**
     53      * Sets |value| for |key|. Returns true if call changes state of context,
     54      * false otherwise.
     55      */
     56     set: function(key, value) {
     57       checkKeyIsValid(key);
     58       checkValueIsValid(value);
     59       if (this.hasKey(key) && this.storage_[key] === value)
     60         return false;
     61       this.changes_[key] = value;
     62       this.storage_[key] = value;
     63       return true;
     64     },
     65 
     66     hasKey: function(key) {
     67       checkKeyIsValid(key);
     68       return this.storage_.hasOwnProperty(key);
     69     },
     70 
     71     hasChanges: function() {
     72       return Object.keys(this.changes_).length > 0;
     73     },
     74 
     75     /**
     76      * Applies |changes| to context. Returns Array of changed keys' names.
     77      */
     78     applyChanges: function(changes) {
     79       require(!this.hasChanges(), 'Context has changes.');
     80       var oldValues = {};
     81       for (var key in changes) {
     82         checkKeyIsValid(key);
     83         checkValueIsValid(changes[key]);
     84         oldValues[key] = this.storage_[key];
     85         this.storage_[key] = changes[key];
     86       }
     87       var observers = this.cloneObservers_();
     88       for (var key in changes) {
     89         if (observers.hasOwnProperty(key)) {
     90           var keyObservers = observers[key];
     91           for (var observerIndex in keyObservers)
     92             keyObservers[observerIndex](changes[key], oldValues[key], key);
     93         }
     94       }
     95       return Object.keys(changes);
     96     },
     97 
     98     /**
     99      * Returns changes made on context since previous call.
    100      */
    101     getChangesAndReset: function() {
    102       var result = this.changes_;
    103       this.changes_ = {};
    104       return result;
    105     },
    106 
    107     addObserver: function(key, observer) {
    108       if (!this.observers_.hasOwnProperty(key))
    109         this.observers_[key] = [];
    110       if (this.observers_[key].indexOf(observer) !== -1) {
    111         console.warn('Observer already registered.');
    112         return;
    113       }
    114       this.observers_[key].push(observer);
    115     },
    116 
    117     removeObserver: function(observer) {
    118       for (var key in this.observers_) {
    119         var observerIndex = this.observers_[key].indexOf(observer);
    120         if (observerIndex != -1)
    121           this.observers_[key].splice(observerIndex, 1);
    122       }
    123     },
    124 
    125     /**
    126      * Creates deep copy of observers lists.
    127      * @private
    128      */
    129     cloneObservers_: function() {
    130       var result = {};
    131       for (var key in this.observers_)
    132         result[key] = this.observers_[key].slice();
    133       return result;
    134     }
    135   };
    136 
    137   return {
    138     ScreenContext: ScreenContext
    139   };
    140 });
    141