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         '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