1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/**
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * @fileoverview Implementation of ScreenContext class: key-value storage for
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) * values that are shared between C++ and JS sides.
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) */
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)cr.define('login', function() {
106d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  'use strict';
116d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  function require(condition, message) {
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (!condition) {
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      throw Error(message);
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  function checkKeyIsValid(key) {
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    var keyType = typeof key;
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    require(keyType === 'string', 'Invalid type of key: "' + keyType + '".');
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  function checkValueIsValid(value) {
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    var valueType = typeof value;
25116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    require((['string', 'boolean', 'number'].indexOf(valueType) != -1 ||
26116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch             Array.isArray(value)),
27116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            'Invalid type of value: "' + valueType + '".');
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  function ScreenContext() {
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    this.storage_ = {};
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    this.changes_ = {};
336d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    this.observers_ = {};
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ScreenContext.prototype = {
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    /**
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     * Returns stored value for |key| or |defaultValue| if key not found in
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     * storage. Throws Error if key not found and |defaultValue| omitted.
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     */
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    get: function(key, defaultValue) {
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      checkKeyIsValid(key);
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (this.hasKey(key)) {
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return this.storage_[key];
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      } else if (typeof defaultValue !== 'undefined') {
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return defaultValue;
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      } else {
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        throw Error('Key "' + key + '" not found.');
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    },
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    /**
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     * Sets |value| for |key|. Returns true if call changes state of context,
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     * false otherwise.
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     */
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    set: function(key, value) {
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      checkKeyIsValid(key);
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      checkValueIsValid(value);
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (this.hasKey(key) && this.storage_[key] === value)
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return false;
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      this.changes_[key] = value;
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      this.storage_[key] = value;
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return true;
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    },
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    hasKey: function(key) {
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      checkKeyIsValid(key);
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return this.storage_.hasOwnProperty(key);
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    },
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    hasChanges: function() {
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return Object.keys(this.changes_).length > 0;
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    },
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    /**
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     * Applies |changes| to context. Returns Array of changed keys' names.
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     */
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    applyChanges: function(changes) {
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      require(!this.hasChanges(), 'Context has changes.');
806d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      var oldValues = {};
816d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      for (var key in changes) {
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        checkKeyIsValid(key);
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        checkValueIsValid(changes[key]);
846d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        oldValues[key] = this.storage_[key];
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        this.storage_[key] = changes[key];
866d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      }
876d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      var observers = this.cloneObservers_();
886d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      for (var key in changes) {
896d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        if (observers.hasOwnProperty(key)) {
906d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          var keyObservers = observers[key];
916d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          for (var observerIndex in keyObservers)
926d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)            keyObservers[observerIndex](changes[key], oldValues[key], key);
936d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        }
946d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      }
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return Object.keys(changes);
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    },
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    /**
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     * Returns changes made on context since previous call.
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     */
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    getChangesAndReset: function() {
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      var result = this.changes_;
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      this.changes_ = {};
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return result;
1056d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    },
1066d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
1076d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    addObserver: function(key, observer) {
1086d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      if (!this.observers_.hasOwnProperty(key))
1096d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        this.observers_[key] = [];
1106d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      if (this.observers_[key].indexOf(observer) !== -1) {
1116d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        console.warn('Observer already registered.');
1126d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        return;
1136d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      }
1146d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      this.observers_[key].push(observer);
1156d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    },
1166d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
1176d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    removeObserver: function(observer) {
1186d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      for (var key in this.observers_) {
1196d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        var observerIndex = this.observers_[key].indexOf(observer);
1206d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        if (observerIndex != -1)
1216d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          this.observers_[key].splice(observerIndex, 1);
1226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      }
1236d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    },
1246d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
1256d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    /**
1266d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)     * Creates deep copy of observers lists.
1276d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)     * @private
1286d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)     */
1296d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    cloneObservers_: function() {
1306d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      var result = {};
1316d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      for (var key in this.observers_)
1326d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        result[key] = this.observers_[key].slice();
1336d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      return result;
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  };
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return {
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ScreenContext: ScreenContext
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  };
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)});
141