text: /*
   * Copyright (c) 2015 Adobe Systems Incorporated. All rights reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   * DEALINGS IN THE SOFTWARE.
   *
   */
  
  define(function (require, exports, module) {
      "use strict";
  
      var Fluxxor = require("fluxxor"),
          Immutable = require("immutable");
  
      var events = require("../events"),
          log = require("js/util/log");
  
      /**
       * The key at which the preferences index is stored.
       * 
       * @const
       * @type {string} 
       */
      var PREF_INDEX = "com.adobe.photoshop.prefs.index";
  
      /**
       * The prefix with which preference keys are qualified.
       * 
       * @const
       * @type {string} 
       */
      var PREF_PREFIX = "com.adobe.photoshop.prefs.keys.";
  
      /**
       * @private
       * @type {Storage}
       */
      var _storage = window.localStorage;
  
      /**
       * Gets a key name qualified with a unique prefix.
       *
       * @private
       * @param {string} key
       * @return {string}
       */
      var _getQualifiedKey = function (key) {
          return PREF_PREFIX + key;
      };
  
      /**
       * Manages the set of preferences, including persistent loading and storing.
       */
      var PreferencesStore = Fluxxor.createStore({
          /**
           * The preferences map. Values can be any JSON.stringifyable type.
           * 
           * @private
           * @type {Immutable.Map.<string, *>}
           */
          _preferences: null,
  
          /** 
           * Loads saved preferences from local storage and binds flux actions.
           */
          initialize: function () {
              this._loadPreferences();
  
              this.bindActions(
                  events.RESET, this._loadPreferences,
                  events.preferences.SET_PREFERENCE, this._setPreference,
                  events.preferences.SET_PREFERENCES, this._setPreferences,
                  events.preferences.DELETE_PREFERENCE, this._deletePreference,
                  events.preferences.CLEAR_PREFERENCES, this._clearPreferences
              );
          },
  
          /**
           * Get the preferences table.
           *
           * @return {Immutable.Map.<string, *>}
           */
          getState: function () {
              return this._preferences;
          },
  
          /**
          * Directly get the value for the given preference key, or an unsetValue if the key does not exist.
          * 
          * @param {string} key
          * @param {?string} unsetValue
          * @return {?string} 
          */
          get: function (key, unsetValue) {
              return this._preferences.get(key, unsetValue);
          },
  
          /**
           * Load persisted preferences and initialize the preferences table.
           *
           * @private
           */
          _loadPreferences: function () {
              var keysJSON = _storage.getItem(PREF_INDEX);
              if (keysJSON === null) {
                  this._preferences = Immutable.Map();
                  return;
              }
  
              try {
                  var keys = JSON.parse(keysJSON),
                      prefs = keys.reduce(function (map, key) {
                          var qualifiedKey = _getQualifiedKey(key),
                              valueJSON = _storage.getItem(qualifiedKey),
                              value = JSON.parse(valueJSON);
  
                          return map.set(key, value);
                      }.bind(this), new Map());
  
                  this._preferences = Immutable.Map(prefs);
              } catch (e) {
                  log.debug("Failed to load preferences index", e);
                  this._saveIndex(Immutable.Map());
              }
          },
  
          /**
           * Save and new set of preferences and persist its index.
           *
           * @param {Immutable.Map.<string, *>} newPrefs
           */
          _saveIndex: function (newPrefs) {
              var keys = newPrefs.keySeq().toArray(),
                  keysJSON = JSON.stringify(keys);
  
              this._preferences = newPrefs;
              _storage.setItem(PREF_INDEX, keysJSON);
              this.emit("change");
          },
  
          /**
           * Set a single preference.
           *
           * @private
           * @param {{key: string, value: *}} payload
           */
          _setPreference: function (payload) {
              var key = payload.key,
                  value = payload.value;
  
              try {
                  // Skip if the value remains unchanged, to avoid emitting unnecessary change event.
                  if (this._preferences.get(key) === value) {
                      return;
                  }
                  
                  var valueJSON = JSON.stringify(value),
                      qualifiedKey = _getQualifiedKey(key);
  
                  _storage.setItem(qualifiedKey, valueJSON);
                  this._saveIndex(this._preferences.set(key, value));
                  this.emit("change");
              } catch (err) {
                  var message = err instanceof Error ? (err.stack || err.message) : err;
  
                  log.error("Failed to set preference", key, value, message);
              }
          },
  
          /**
           * Set a set of preferences.
           *
           * @private
           * @param {Object.<string, *>} payload
           */
          _setPreferences: function (payload) {
              var prefs = payload.prefs;
  
              try {
                  Object.keys(prefs).forEach(function (key) {
                      var value = prefs[key],
                          valueJSON = JSON.stringify(value),
                          qualifiedKey = _getQualifiedKey(key);
  
                      _storage.setItem(qualifiedKey, valueJSON);
                  });
  
                  this._saveIndex(this._preferences.merge(prefs));
                  this.emit("change");
              } catch (err) {
                  var message = err instanceof Error ? (err.stack || err.message) : err;
  
                  log.error("Failed to set preferences", prefs, message);
              }
          },
  
          /**
           * Delete a single preference.
           *
           * @private
           * @param {{key: string}} payload
           */
          _deletePreference: function (payload) {
              var key = payload.key,
                  qualifiedKey = _getQualifiedKey(key);
  
              _storage.removeItem(qualifiedKey);
              this._saveIndex(this._preferences.delete(key));
              this.emit("change");
          },
  
          /**
           * Clear all preferences.
           *
           * @private
           */
          _clearPreferences: function () {
              this._preferences.keySeq().forEach(function (key) {
                  var qualifiedKey = _getQualifiedKey(key);
                  _storage.removeItem(qualifiedKey);
              }, this);
  
              this._saveIndex(Immutable.Map());
              this.emit("change");
          }
      });
  
      module.exports = PreferencesStore;
  });
  