// Generated by CoffeeScript 1.10.0
(function() {
  var JSONStorage, KEY_FOR_EMPTY_STRING, LocalStorage, MetaKey, QUOTA_EXCEEDED_ERR, StorageEvent, _emptyDirectory, _escapeKey, _rm, createMap, events, fs, path, writeSync,
    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    hasProp = {}.hasOwnProperty;

  path = require('path');

  fs = require('fs');

  events = require('events');

  writeSync = require('write-file-atomic').sync;

  KEY_FOR_EMPTY_STRING = '---.EMPTY_STRING.---';

  _emptyDirectory = function(target) {
    var i, len, p, ref, results;
    ref = fs.readdirSync(target);
    results = [];
    for (i = 0, len = ref.length; i < len; i++) {
      p = ref[i];
      results.push(_rm(path.join(target, p)));
    }
    return results;
  };

  _rm = function(target) {
    if (fs.statSync(target).isDirectory()) {
      _emptyDirectory(target);
      return fs.rmdirSync(target);
    } else {
      return fs.unlinkSync(target);
    }
  };

  _escapeKey = function(key) {
    var newKey;
    if (key === '') {
      newKey = KEY_FOR_EMPTY_STRING;
    } else {
      newKey = key.toString();
    }
    return newKey;
  };

  QUOTA_EXCEEDED_ERR = (function(superClass) {
    extend(QUOTA_EXCEEDED_ERR, superClass);

    function QUOTA_EXCEEDED_ERR(message) {
      this.message = message != null ? message : 'Unknown error.';
      if (Error.captureStackTrace != null) {
        Error.captureStackTrace(this, this.constructor);
      }
      this.name = this.constructor.name;
    }

    QUOTA_EXCEEDED_ERR.prototype.toString = function() {
      return this.name + ": " + this.message;
    };

    return QUOTA_EXCEEDED_ERR;

  })(Error);

  StorageEvent = (function() {
    function StorageEvent(key1, oldValue1, newValue1, url, storageArea) {
      this.key = key1;
      this.oldValue = oldValue1;
      this.newValue = newValue1;
      this.url = url;
      this.storageArea = storageArea != null ? storageArea : 'localStorage';
    }

    return StorageEvent;

  })();

  MetaKey = (function() {
    function MetaKey(key1, index1) {
      this.key = key1;
      this.index = index1;
      if (!(this instanceof MetaKey)) {
        return new MetaKey(this.key, this.index);
      }
    }

    return MetaKey;

  })();

  createMap = function() {
    var Map;
    Map = function() {};
    Map.prototype = Object.create(null);
    return new Map();
  };

  LocalStorage = (function(superClass) {
    var instanceMap;

    extend(LocalStorage, superClass);

    instanceMap = {};

    function LocalStorage(_location, quota) {
      this._location = _location;
      this.quota = quota != null ? quota : 5 * 1024 * 1024;
      if (!(this instanceof LocalStorage)) {
        return new LocalStorage(this._location, this.quota);
      }
      this._location = path.resolve(this._location);
      if (instanceMap[this._location] != null) {
        return instanceMap[this._location];
      }
      this.length = 0;
      this._bytesInUse = 0;
      this._keys = [];
      this._metaKeyMap = createMap();
      this._eventUrl = "pid:" + process.pid;
      this._init();
      this._QUOTA_EXCEEDED_ERR = QUOTA_EXCEEDED_ERR;
      instanceMap[this._location] = this;
      return instanceMap[this._location];
    }

    LocalStorage.prototype._init = function() {
      var _MetaKey, _decodedKey, _keys, e, error, error1, i, index, k, len, stat;
      try {
        stat = fs.statSync(this._location);
        if ((stat != null) && !stat.isDirectory()) {
          throw new Error("A file exists at the location '" + this._location + "' when trying to create/open localStorage");
        }
        this._bytesInUse = 0;
        this.length = 0;
        _keys = fs.readdirSync(this._location);
        for (index = i = 0, len = _keys.length; i < len; index = ++i) {
          k = _keys[index];
          _decodedKey = decodeURIComponent(k);
          this._keys.push(_decodedKey);
          _MetaKey = new MetaKey(k, index);
          this._metaKeyMap[_decodedKey] = _MetaKey;
          stat = this._getStat(k);
          if ((stat != null ? stat.size : void 0) != null) {
            _MetaKey.size = stat.size;
            this._bytesInUse += stat.size;
          }
        }
        this.length = _keys.length;
      } catch (error) {
        e = error;
        if (e.code !== "ENOENT") {
          throw e;
        }
        try {
          fs.mkdirSync(this._location);
        } catch (error1) {
          e = error1;
          if (e.code !== "EEXIST") {
            throw e;
          }
        }
      }
    };

    LocalStorage.prototype.setItem = function(key, value) {
      var encodedKey, evnt, existsBeforeSet, filename, hasListeners, metaKey, oldLength, oldValue, valueString, valueStringLength;
      hasListeners = events.EventEmitter.listenerCount(this, 'storage');
      oldValue = null;
      if (hasListeners) {
        oldValue = this.getItem(key);
      }
      key = _escapeKey(key);
      encodedKey = encodeURIComponent(key);
      filename = path.join(this._location, encodedKey);
      valueString = value.toString();
      valueStringLength = valueString.length;
      metaKey = this._metaKeyMap[key];
      existsBeforeSet = !!metaKey;
      if (existsBeforeSet) {
        oldLength = metaKey.size;
      } else {
        oldLength = 0;
      }
      if (this._bytesInUse - oldLength + valueStringLength > this.quota) {
        throw new QUOTA_EXCEEDED_ERR();
      }
      writeSync(filename, valueString, 'utf8');
      if (!existsBeforeSet) {
        metaKey = new MetaKey(encodedKey, (this._keys.push(key)) - 1);
        metaKey.size = valueStringLength;
        this._metaKeyMap[key] = metaKey;
        this.length += 1;
        this._bytesInUse += valueStringLength;
      }
      if (hasListeners) {
        evnt = new StorageEvent(key, oldValue, value, this._eventUrl);
        return this.emit('storage', evnt);
      }
    };

    LocalStorage.prototype.getItem = function(key) {
      var filename, metaKey;
      key = _escapeKey(key);
      metaKey = this._metaKeyMap[key];
      if (!!metaKey) {
        filename = path.join(this._location, metaKey.key);
        return fs.readFileSync(filename, 'utf8');
      } else {
        return null;
      }
    };

    LocalStorage.prototype._getStat = function(key) {
      var error, filename;
      key = _escapeKey(key);
      filename = path.join(this._location, encodeURIComponent(key));
      try {
        return fs.statSync(filename);
      } catch (error) {
        return null;
      }
    };

    LocalStorage.prototype.removeItem = function(key) {
      var evnt, filename, hasListeners, k, meta, metaKey, oldValue, ref, v;
      key = _escapeKey(key);
      metaKey = this._metaKeyMap[key];
      if (!!metaKey) {
        hasListeners = events.EventEmitter.listenerCount(this, 'storage');
        oldValue = null;
        if (hasListeners) {
          oldValue = this.getItem(key);
        }
        delete this._metaKeyMap[key];
        this.length -= 1;
        this._bytesInUse -= metaKey.size;
        filename = path.join(this._location, metaKey.key);
        this._keys.splice(metaKey.index, 1);
        ref = this._metaKeyMap;
        for (k in ref) {
          v = ref[k];
          meta = this._metaKeyMap[k];
          if (meta.index > metaKey.index) {
            meta.index -= 1;
          }
        }
        _rm(filename);
        if (hasListeners) {
          evnt = new StorageEvent(key, oldValue, null, this._eventUrl);
          return this.emit('storage', evnt);
        }
      }
    };

    LocalStorage.prototype.key = function(n) {
      return this._keys[n];
    };

    LocalStorage.prototype.clear = function() {
      var evnt;
      _emptyDirectory(this._location);
      this._metaKeyMap = createMap();
      this._keys = [];
      this.length = 0;
      this._bytesInUse = 0;
      if (events.EventEmitter.listenerCount(this, 'storage')) {
        evnt = new StorageEvent(null, null, null, this._eventUrl);
        return this.emit('storage', evnt);
      }
    };

    LocalStorage.prototype._getBytesInUse = function() {
      return this._bytesInUse;
    };

    LocalStorage.prototype._deleteLocation = function() {
      delete instanceMap[this._location];
      _rm(this._location);
      this._metaKeyMap = {};
      this._keys = [];
      this.length = 0;
      return this._bytesInUse = 0;
    };

    return LocalStorage;

  })(events.EventEmitter);

  JSONStorage = (function(superClass) {
    extend(JSONStorage, superClass);

    function JSONStorage() {
      return JSONStorage.__super__.constructor.apply(this, arguments);
    }

    JSONStorage.prototype.setItem = function(key, value) {
      var newValue;
      newValue = JSON.stringify(value);
      return JSONStorage.__super__.setItem.call(this, key, newValue);
    };

    JSONStorage.prototype.getItem = function(key) {
      return JSON.parse(JSONStorage.__super__.getItem.call(this, key));
    };

    return JSONStorage;

  })(LocalStorage);

  exports.LocalStorage = LocalStorage;

  exports.JSONStorage = JSONStorage;

  exports.QUOTA_EXCEEDED_ERR = QUOTA_EXCEEDED_ERR;

}).call(this);
