"use strict";

exports.__esModule = true;
exports.default = exports.DeleteRemoteOp = exports.UpdateRemoteOp = exports.CreateRemoteOp = exports.CascadeRemoteOp = undefined;

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _invariant = require("invariant");

var _invariant2 = _interopRequireDefault(_invariant);

var _eventemitter = require("eventemitter2");

var _akutils = require("akutils");

var _MapProperty = require("./MapProperty");

var _MapProperty2 = _interopRequireDefault(_MapProperty);

var _IsolatedApi = require("./IsolatedApi");

var _IsolatedApi2 = _interopRequireDefault(_IsolatedApi);

var _Property = require("./Property");

var _Property2 = _interopRequireDefault(_Property);

var _ShadowImpl = require("./ShadowImpl");

var _ShadowImpl2 = _interopRequireDefault(_ShadowImpl);

var _Shader = require("./Shader");

var _Shader2 = _interopRequireDefault(_Shader);

var _tick = require("./tick");

var _tick2 = _interopRequireDefault(_tick);

var _TransientsProperty = require("./TransientsProperty");

var _TransientsProperty2 = _interopRequireDefault(_TransientsProperty);

var _debug = require("./debug");

var _debug2 = _interopRequireDefault(_debug);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var debug = (0, _debug2.default)(_debug.StoreKey);

var _Promise = Promise;
var _clearInterval = clearInterval;
var _clearTimeout = clearTimeout;
var _setInterval = setInterval;
var _setTimeout = setTimeout;

var _transients = "__trans__";

var CascadeRemoteOp = exports.CascadeRemoteOp = "cascade";
var CreateRemoteOp = exports.CreateRemoteOp = "create";
var UpdateRemoteOp = exports.UpdateRemoteOp = "update";
var DeleteRemoteOp = exports.DeleteRemoteOp = "delete";

/*
	Futures/Experiments:
		* Isolated objects - objects that can be referenced by properties within the shadow state without
			being explicitly mapped. Motivation: enable a Map interface to front a large number of
			objects without having to remap all properties with each contained object property change.
			Would be useful in efficiently implementing collections.
		* Use Proxy to replace ShadowImpl. Need to test performance first. Would remove many of the
			restrictions on using the shadow state, such as delete, unexpected property assignment.
*/

/**
	A f.lux based application uses a single store for managing all state. A `Store` instance is created
	by specifying a root {@link Property} and the initial state. The property must be a `Property` subclass
	and the state a json compatible value of appropriate type for the root `Property`. The store manages
	changes to the state through the shadow state. The shadow state proxies the actual state and each
	shadow state property is immutable and modifications to the shadow state, whether through function calls
	or direct assignment, are reflected asynchronously in the next javascript interpreter 'tick'.

	The `Store` class contains some advanced features useful for implementing complex business logic,
	loggers, and memory efficient master/detail user interfaces. Most of the time, however, your
	application will create a f.lux store and utilize the [f.lux-react](https://github.com/akrumel/f.lux-react)
	module for accessing the store's shadow state properties.

	## Subscribing to store changes

	Entities can subscribe to state changes resulting from actions on the shadow state. Subscription callbacks
	are registered/unregistered using the methods:

	<ul>
		<li>`subscribe(callback)` - adds a callback to the subscriber list</li>
		<li>`unsubscribe(callback)` - removes a callback from the subscriber list</li>
	</ul>

	The `callback` has the form:

	```
	callback(store, shadow, prevShadow)
	```


	## Update and wait

	Shadow state action changes to the actual state occur asynchronously. Usually, this works out
	well since your event and network processing will want to work with a single, consistent state. Certain
	situations occur where your code performs some actions on the shadow state and then needs to perform
	additional calculations/changes on the updated state. The f.lux store exposes several methods
	to make this process easy:

	<ul>
		<li>
			`updateNow(syncExec)` - performs any pending actions synchronously and then invokes the
				`syncExec` callback which does not take any parameters.
		</li>
		<li>
			`waitFor(callback)` - registers a **one-time** no argument callback to be invoked after the
				next state change.
		</li>
		<li>
			`waitThen()` - returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
				that will be resolved following the next state change.
		</li>
	</ul>


	## Transients

	F.lux store's transient objects are used for managing applications data that is used for short periods
	of time. Transients are expecially useful for implementing master/detail user interfaces. The master list
	is typically long lived while the detail records are rapidly inspected and discarded.

	Transient shadow objects are available through the {@link Store#transients`} method. The
	`transients` property is a {@link TransientShadow} exposes a `Map` interface for adding, accessing,
	and removing transient objects.

	The transient shadow properties are *swept* after each state change and non-locked properties are
	removed from the `transients` object relieving your code of the responsibility of proactively
	removing objects when no longer required.

	The normal flow of working with a transient is:

	<ol>
		<li>Create a `Property`</li>
		<li>Pick a transient object ID (`transId`)</li>
		<li>Obtain `Store.transients`</li>
		<li>`var trans = transients.set(transId, property)` - returns a `TransientProperty`</li>
		<li>Do somethings with the transient object: `trans.data()`</li>
		<li>`trans.lock()`</li>
		<li>`trans.unlock(transId</li>
	</ol>

	The `f.lux-react` module contains the `TransientManager` class that is useful for managing
	transient data in a React component.

	<div data-ice="see"><h4>See:</h4>
		<ul>
			<li>
				<a href=https://github.com/akrumel/f.lux-react/blob/master/src/TransientManager.js>
					TransientManager
				</a>
			</li>
			<li>{@link Store#transients}</li>
			<li>{@link TransientsProperty}</li>
			<li>{@link TransientProperty}</li>
		</ul>
	</div>


	## Listener API

	The `Store` class exposes a listener api for monitoring state changes. The f.lux module ships with
	one listener called {@link Logger} that tracks state changes and provides time travel debugging. A
	listener is registered/unregsitered using:

	<ul>
		<li>{@link Store#addListener}</li>
		<li>{@link Store#removeListener}</li>
	</ul>

	Where the `listener` parameter is an object that can implement the following methos:

	<ul>
		<li>`onError(msg, error)` - an error occurred during a `Store` operation</li>
		<li>`onPreStateUpdate(action, impl)` - invoked before a shadow property executes an action</li>
		<li>`onPostStateUpdate(action, impl)` - invoked after a shadow property executes an action</li>
		<li>`onPreUpdate(currState, time)` - invoked before the store's state is updated</li>
		<li>`onPostUpdate(time, currState, prevState)` - invoked after the store's state is updated</li>
	</ul>

	<div data-ice="see"><h4>See:</h4>
		<ul>
			<li>{@link Logger}</li>
		</ul>
	</div>


	## Promise and timers

	Some environments require special handling for timers, such as
	[React Native](https://facebook.github.io/react-native/docs/timers.html). Additionally, developers
	may desire to utilize specific
	[`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
	libraries in order to take advantage of specialized features. The `Store` class provides methods for
	setting the timer functions and `Promise` module to be utilized by f.lux.

	<div data-ice="see"><h4>See:</h4>
		<ul>
			<li>{@link Store.setPromise}</li>
			<li>{@link Store.setTick}</li>
			<li>{@link Store.getPromise}</li>
			<li>{@link Store.clearInterval}</li>
			<li>{@link Store.clearTimeout}</li>
			<li>{@link Store.setInterval}</li>
			<li>{@link Store.setTimeout}</li>
			<li>{@link Store.all}</li>
			<li>{@link Store.promise}</li>
			<li>{@link Store.reject}</li>
			<li>{@link Store.resolve}</li>
		</ul>
	</div>
*/

var Store = function (_Emitter) {
	_inherits(Store, _Emitter);

	/**
 	Creates a new f.lux store for managing the application state.
 		@param {Property} root - the top-level `Property` used to shadow the application state
 	@param {Object|Array} [state=root.initialState()] - the initial application state
 	@param {boolean} [useTransients=false] - `true` to enable transient state
 */

	function Store(root, state) {
		var useTransients = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
		var offline = arguments.length <= 3 || arguments[3] === undefined ? null : arguments[3];

		_classCallCheck(this, Store);

		var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Store).call(this, {
			wildcard: true
		}));

		(0, _invariant2.default)(root instanceof _Property2.default || Array.isArray(root) || (0, _akutils.isObject)(root), "Store root must be one of: Property subclass, object, or array");

		// ensure root
		if (!(root instanceof _Property2.default)) {
			state = root;

			root = Array.isArray() ? new _MapProperty2.default() : new _MapProperty2.default();
		} else if (state === undefined) {
			state = root.initialState();
		}

		_this._isolated = new _IsolatedApi2.default(_this);
		_this._listeners = [];
		_this._remoteListeners = {};
		_this._subscribers = [];
		_this._useTransients = useTransients;
		_this._offlineStore = offline;

		// dispatcher queues for pending actions and waitFor() requests
		_this._updateAction = null;
		_this._waitFor = [];

		if (root) {
			_this.setRootProperty(root, state);
		}
		return _this;
	}

	_createClass(Store, [{
		key: "addRemoteListener",
		value: function addRemoteListener(type, listener) {
			var list = this._remoteListeners[type];

			if (!list) {
				list = [];
				this._remoteListeners[type] = list;
			}

			list.push(listener);
		}
	}, {
		key: "removeRemoteListener",
		value: function removeRemoteListener(type, listener) {
			var list = this._remoteListeners[type];

			if (list) {
				list = list.filter(function (l) {
					return l !== listener;
				});

				if (list.length) {
					this._remoteListeners[type] = list;
				} else {
					delete this._remoteListeners[type];
				}
			}
		}
	}, {
		key: "onRemoteUpdate",
		value: function onRemoteUpdate(type, op, key, values) {
			var _this2 = this;

			this.wait().then(function () {
				var list = _this2._remoteListeners[type];

				list && list.forEach(function (l) {
					try {
						l.onRemoteUpdate(type, op, key, values);
					} catch (ex) {
						console.log("Remote update exception", ex);
					}
				});
			});
		}

		/**
  	Gets the {@link OfflineDataStore} used by properties to work offline.
  		@return {OfflineDataStore}
  		@experimantal
  */

	}, {
		key: "offlineStore",
		value: function offlineStore() {
			return this._offlineStore;
		}

		/**
  	Gets the {@link OfflineDataStore} used by properties to work offline.
  		@param {OfflineDataStore} - the shared offline storage instance
  		@experimantal
  */

	}, {
		key: "setOfflineStore",
		value: function setOfflineStore(offline) {
			this._offlineStore = offline;
		}

		/**
  	Adds a callback to be invoked after each application state change. The `callback` has the form:
  	```
  	callback(store, shadow, prevShadow)
  	```
  		@param {function(store: Store, shadow: Shadow, prevShadow: Shadow)} callback - called after each
  		state update
  */

	}, {
		key: "subscribe",
		value: function subscribe(callback) {
			(0, _akutils.assert)(function (a) {
				return a.is(typeof callback === 'function', "Callback must be a function");
			});

			if (typeof callback === 'function') {
				this._subscribers.push(callback);
			}
		}

		/**
  	Removes a callback to be invoked after each state change.
  */

	}, {
		key: "unsubscribe",
		value: function unsubscribe(callback) {
			this._subscribers = this._subscribers.filter(function (s) {
				return s != callback;
			});
		}

		//******************************************************************************************************************
		//  Properties and methods dealing with state
		//******************************************************************************************************************

		/**
  	Alias for top-level shadow property.
  		@see Store#shadow
  */

	}, {
		key: "changeState",


		/**
  	Set a new value for the application state. This will totally replace the current state and trigger
  	a reshadowing. The root `Property` instance will be reused.
  		This method is used by {@link Logger} to implement time travel debugging.
  		@param {Object|Array} state - the new application state
  	@param {boolean} [newRoot=false] - `true` if the state object represents an entirely new state.
  		`false` implies the root object is the same but child properties have been updated.
  	@param {number} [time=tick()] - the f.lux time for the change.
  */
		value: function changeState(state) {
			var newRoot = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
			var time = arguments.length <= 2 || arguments[2] === undefined ? (0, _tick2.default)() : arguments[2];

			this._updateTime = time;

			// all pending callbacks are obsolete now
			this._clearCallbacks();

			// invoke will shadow/update lifecycle methods
			if (this._rootImpl && this._state) {
				this._rootImpl.willShadow(newRoot);
			}

			this._isolated.reset();

			// proxy/shadow the state
			this._rootImpl = this._root.shader(state).shadowProperty(time, "/", state);

			// get the final state after merging any property initial states
			this._state = this._rootImpl.state();

			// invoke did shadow/update lifecycle methods
			this._rootImpl.didShadow(time, newRoot);

			this._notifySubscribers();
		}

		/**
  	Gets the shadow state property based on dotted path (`value.$().dotPath()`).
  		@param {string} path - dotted path from the root to the desired property.
  		@return {Shadow} the shadow state property if property exists.
  */

	}, {
		key: "findByDotPath",
		value: function findByDotPath(path) {
			return this.findByPath(path.split("."));
		}

		/**
  	Gets the shadow state property based on array of property keys.
  		@param {Array} path - array of property keys from the root to the desired property.
  		@return {Shadow} the shadow state property if property exists.
  */

	}, {
		key: "findByPath",
		value: function findByPath(path) {
			var rootImpl = this._root.__();
			var impl = rootImpl.findByPath(path);

			return impl && impl._();
		}

		/**
  	Gets the Property based on array of property keys.
  		@param {Array} path - array of property keys from the root to the desired property.
  		@return {Property} the Property if exists.
  */

	}, {
		key: "findPropertyByPath",
		value: function findPropertyByPath(path) {
			var rootImpl = this._root.__();
			var impl = rootImpl.findByPath(path);

			return impl && impl.property();
		}
	}, {
		key: "isolated",
		value: function isolated() {
			return this._isolated;
		}

		/**
  	Changes the top-level `Property` and application state.
  		@param {Property} root - the top-level `Property` used to shadow the application state
  	@param {Object|Array} [state=root.initialState()] - the initial application state
  */

	}, {
		key: "setRootProperty",
		value: function setRootProperty(root) {
			var _this3 = this;

			var state = arguments.length <= 1 || arguments[1] === undefined ? root.initialState() : arguments[1];

			if (!(root instanceof _Property2.default)) {
				throw new Error("Root property must be a Property instance");
			}

			var currRoot = this._root;
			var currRootActive = currRoot && currRoot.isActive();

			// inform property it is being replaced
			if (currRoot && root !== currRoot && currRootActive) {
				currRoot.__().obsoleteTree(function (impl) {
					// set the root property and set it's store to this object
					root.setStore(_this3);
					_this3._root = root;

					_this3.changeState(state, true);
				});
			} else {
				// set the root property and set it's store to this object
				if (root !== currRoot) {
					root.setStore(this);
					this._root = root;
				}

				this.changeState(state, root !== currRoot);
				this._setupTransients();
			}
		}

		//******************************************************************************************************************
		//  Listeners methods
		//******************************************************************************************************************

		/** @ignore */

	}, {
		key: "onError",
		value: function onError(msg, error) {
			var listeners = this._listeners;

			for (var i = 0, l; l = listeners[i]; i++) {
				if (l.onError) {
					l.onError(this, msg, error);
				}
			}
		}

		/** @ignore */

	}, {
		key: "onPreStateUpdate",
		value: function onPreStateUpdate(action, impl) {
			var listeners = this._listeners;

			for (var i = 0, l; l = listeners[i]; i++) {
				if (l.onPreStateUpdate) {
					l.onPreStateUpdate(this, action, impl);
				}
			}
		}

		/** @ignore */

	}, {
		key: "onPostStateUpdate",
		value: function onPostStateUpdate(action, impl) {
			var listeners = this._listeners;

			for (var i = 0, l; l = listeners[i]; i++) {
				if (l.onPostStateUpdate) {
					l.onPostStateUpdate(this, action, impl);
				}
			}
		}

		/** @ignore */

	}, {
		key: "onPreUpdate",
		value: function onPreUpdate(currState, time) {
			var listeners = this._listeners;

			for (var i = 0, l; l = listeners[i]; i++) {
				if (l.onPreUpdate) {
					l.onPreUpdate(this, currState, time);
				}
			}
		}

		/** @ignore */

	}, {
		key: "onPostUpdate",
		value: function onPostUpdate(time, currState, prevState) {
			if (this._listeners.length === 0) {
				return;
			}

			var listeners = this._listeners;
			var isoState = this._isolated.serialize();

			for (var i = 0, l; l = listeners[i]; i++) {
				if (l.onPostUpdate) {
					l.onPostUpdate(this, time, currState, prevState, isoState);
				}
			}
		}

		/**
  	Adds a listener object to monitor state changes. A `listener` is an object that can
  	implement the following methos:
  		<ul>
  		<li>`onError(msg, error)` - an error occurred during a `Store` operation</li>
  		<li>`onPreStateUpdate(action, impl)` - invoked before a shadow property executes an action</li>
  		<li>`onPostStateUpdate(action, impl)` - invoked after a shadow property executes an action</li>
  		<li>`onPreUpdate(currState, time)` - invoked before the store's state is updated</li>
  		<li>`onPostUpdate(time, currState, prevState)` - invoked after the store's state is updated</li>
  	</ul>
  		@param {Object} listener - object with methods to be invoked on state changes.
  		@see Logger
  */

	}, {
		key: "addListener",
		value: function addListener(listener) {
			this._listeners.push(listener);
		}

		/**
  	Removes a listener object from monitoring state changes.
  */

	}, {
		key: "removeListener",
		value: function removeListener(listener) {
			var idx;

			while ((idx = this._listeners.indexOf(listener)) != -1) {
				this._listeners.splice(idx, 1);
			}
		}

		//******************************************************************************************************************
		//  Store state update related methods
		//******************************************************************************************************************

		/**
  	Schedules an action to update the application state. This method is utilized by root {@link ShadowImpl} to
  	trigger the reshadow process and custom properties, listeners, and application logic should never need to
  	explicitly invoke it.
  		@param {function(time: number)} action - the callback will normally trigger a reshadow.
  		@throws {Error} f.lux store already scheduled to perform an update.
  */

	}, {
		key: "dispatchUpdate",
		value: function dispatchUpdate(action) {
			if (this._updateAction) {
				throw new Error("Pending action already waiting for dispatch");
			}

			this._updateAction = action;

			this.schedule();
		}

		/**
  	Gets if an update action has been registered with the store.
  		@return {boolean}
  */

	}, {
		key: "isScheduled",
		value: function isScheduled() {
			return !!this._updateAction;
		}
	}, {
		key: "isPaused",
		value: function isPaused() {
			return !!this.pauseId;
		}
	}, {
		key: "pause",
		value: function pause() {
			var _this4 = this;

			if (this.pauseId) {
				clearTimeout(this.pauseId);
			}

			this.pauseId = setTimeout(function () {
				_this4.pauseId = null;
				_this4._exec();
			}, 1000);
		}
	}, {
		key: "resume",
		value: function resume() {
			if (this.pauseId) {
				clearTimeout(this.pauseId);
				this.pauseId = null;

				this._exec();
			}
		}

		/**
  	Schedules pending tasks to execute. This method should not need to be called by code external to the class.
  */

	}, {
		key: "schedule",
		value: function schedule() {
			var _this5 = this;

			// Nothing to do if paused or no registered pendingActions
			if (this.pauseId || !this._updateAction && !this._waitFor.length) {
				return;
			}

			_setTimeout(function () {
				return _this5._exec();
			});
		}

		/**
  	Executes all pending updates and waitFor() requests synchronously (immediately) and then invokes
  	an optional callback once the shadow state is updated.
  		This method is useful when custom properties change the shadow state and need these changes
  	to be reflected before performing additional processing.
  		@param {function()} syncExec - a callback to be invoked after all pending changes have been
  		reflected in the shadow state.
  */

	}, {
		key: "updateNow",
		value: function updateNow(syncExec) {
			this._exec();

			if (syncExec && syncExec()) {
				this._exec();
			}
		}

		/**
  	Registers a **one-time** no argument callback to be invoked after the next state change.
  		@param {function()} callback - a callback to be invoked after all pending changes have been
  		reflected in the shadow state.
  */

	}, {
		key: "waitFor",
		value: function waitFor(callback) {
			(0, _akutils.assert)(function (a) {
				return a.is(typeof callback === 'function', "Callback must be a function");
			});

			if (typeof callback === 'function') {
				this._waitFor.push(callback);

				this.schedule();
			}
		}

		/**
  	Returns a `Promise` that is resolved following the next state change.
  		@return {Promise}
  */

	}, {
		key: "wait",
		value: function wait() {
			var _this6 = this;

			return new Promise(function (resolve, reject) {
				_this6.waitFor(resolve);
			});
		}

		/**
  	Returns a `Promise` that is resolved following the next state change.
  		@return {Promise}
  		@deprecated - use {@link #wait}
  */

	}, {
		key: "waitThen",
		value: function waitThen() {
			var _this7 = this;

			return new Promise(function (resolve, reject) {
				_this7.waitFor(resolve);
			});
		}

		//******************************************************************************************************************
		//  Promise and timer setup methods
		//******************************************************************************************************************

	}, {
		key: "_clearCallbacks",


		/** @ignore */
		value: function _clearCallbacks() {
			// clear callback data structures
			this._updateAction = null;
			this._waitFor = [];
		}

		/** @ignore */

	}, {
		key: "_exec",
		value: function _exec() {
			var updateAction = this._updateAction;
			var waitFor = this._waitFor;
			var prevShadow = this.shadow;

			// Reset the pending actions
			this._updateAction = null;
			this._waitFor = [];

			if (updateAction) {
				if (this._updating) {
					debugger;
					throw new Error("Already updating state - use store.waitFor()");
				}

				try {
					this._updating = true;

					var currState = this._state;
					var time = this._updateTime = (0, _tick2.default)();

					// inform middleware going to perform an update
					this.onPreUpdate(currState, time);

					this._rootImpl.willShadow(false);

					// perform the update action
					var impl = updateAction(time);

					if (!(impl instanceof _ShadowImpl2.default)) {
						this._onError("Property update action did not return a ShadowImpl type");
					} else if (impl.property() != this._root) {
						this._onError("Property update action cannot replace the root state");
					} else {
						this._rootImpl = impl;
						this._state = impl.state();

						// Have each property invoke did shadow/update lifecycle methods now that store is coherent with new stat
						this._rootImpl.didShadow(time, false);
					}

					// inform middleware update completed
					this.onPostUpdate(time, this._state, currState);
				} catch (error) {
					this._onError("State dispatch UPDATE action exception: " + error, error);
				} finally {
					this._updating = false;
				}
			}

			this._notifyWaitFors(waitFor);
			this._notifySubscribers(prevShadow);
			this._sweepTransients();
		}

		/** @ignore */

	}, {
		key: "_notifySubscribers",
		value: function _notifySubscribers(prevShadow) {
			var _this8 = this;

			if (this.subscriberTimerId) {
				clearTimeout(this.subscriberTimerId);
			}

			var worker = function worker() {
				var shadow = _this8.shadow;

				delete _this8.subscriberTimerId;

				// Iterate each subscriber
				for (var i = 0, subscriber; subscriber = _this8._subscribers[i]; i++) {
					try {
						subscriber(_this8, shadow, prevShadow);
					} catch (error) {
						_this8._onError("Store subscriber notification caused an exception: " + error, error);
					}
				}
			};

			if (prevShadow) {
				this.subscriberTimerId = setTimeout(worker);
			} else {
				worker();
			}
		}

		/** @ignore */

	}, {
		key: "_notifyWaitFors",
		value: function _notifyWaitFors(waitFor) {
			var _this9 = this;

			var worker = function worker() {
				// Iterate each waitFor request
				for (var i = 0, waiting; waiting = waitFor[i]; i++) {
					try {
						waiting();
					} catch (error) {
						_this9._onError("Store dispatch waitFor() request exception: " + error, error);
					}
				}
			};

			setTimeout(worker);
		}

		/** @ignore */

	}, {
		key: "_onError",
		value: function _onError(msg, error) {
			debug(function (d) {
				return d("Store error: " + msg, error);
			});

			if (process.env.NODE_ENV !== 'production') {
				console.warn("f.lux Store Error", error.stack || error);
			}

			this.onError(msg, error);
		}

		/** @ignore */

	}, {
		key: "_setupTransients",
		value: function _setupTransients() {
			var root = this._root;
			var rootShader = root.shader();
			var keyedApi = root._keyed ? root._keyed : root;

			if (keyedApi && this._useTransients && !rootShader.has(_transients)) {
				keyedApi.addProperty(_transients, new _TransientsProperty2.default());
			}
		}

		/** @ignore */

	}, {
		key: "_sweepTransients",
		value: function _sweepTransients() {
			var trans = this.shadow[_transients];

			if (trans) {
				setTimeout(function () {
					return trans.sweep();
				});
			}
		}
	}, {
		key: "_",
		get: function get() {
			return this._root._();
		}

		/**
  	Alias for rootImpl property.
  		@see Store#rootImpl
  */

	}, {
		key: "__",
		get: function get() {
			return this.rootImpl;
		}

		/**
  	Gets the top-level `Property` used for shadowing the application state.
  		@return {Property}
  		@see Store#state
  */

	}, {
		key: "root",
		get: function get() {
			return this._root;
		}

		/**
  	Gets the {@link ShadowImpl} backing the {@link Store#shadow}.
  		@return {ShadowImpl}
  */

	}, {
		key: "rootImpl",
		get: function get() {
			return this._rootImpl || this._root.__();
		}

		/**
  	Gets the current f.lux shadow state.
  		@return {Shadow}
  */

	}, {
		key: "shadow",
		get: function get() {
			return this._root._();
		}

		/**
  	Gets the current, actual application state.
  		@return {Object|Array}
  */

	}, {
		key: "state",
		get: function get() {
			return this._state;
		}

		/**
  	Gets the `transients` objects
  		@return {TransientsShadow}
  */

	}, {
		key: "transients",
		get: function get() {
			return this.shadow[_transients];
		}

		/**
  	@ignore
  */

	}, {
		key: "updateTime",
		get: function get() {
			return this._updateTime;
		}
	}], [{
		key: "getPromise",
		value: function getPromise() {
			return _Promise;
		}
	}, {
		key: "setPromise",
		value: function setPromise(promise) {
			_Promise = promise;
		}
	}, {
		key: "setTick",
		value: function setTick(ticker) {
			_clearInterval = ticker.clearInterval;
			_clearTimeout = ticker.clearTimeout;
			_setInterval = ticker.setInterval;
			_setTimeout = ticker.setTimeout;
		}

		//******************************************************************************************************************
		//  Promise and timer convenience methods
		//******************************************************************************************************************

	}, {
		key: "clearInterval",
		value: function clearInterval(intervalId) {
			return _clearInterval(intervalId);
		}
	}, {
		key: "clearTimeout",
		value: function clearTimeout(timerId) {
			return _clearTimeout(timerId);
		}
	}, {
		key: "setInterval",
		value: function setInterval(callback, time) {
			for (var _len = arguments.length, params = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
				params[_key - 2] = arguments[_key];
			}

			return _setInterval.apply(undefined, [callback, time].concat(params));
		}
	}, {
		key: "setTimeout",
		value: function setTimeout(callback, time) {
			for (var _len2 = arguments.length, params = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
				params[_key2 - 2] = arguments[_key2];
			}

			return _setTimeout.apply(undefined, [callback, time].concat(params));
		}
	}, {
		key: "all",
		value: function all(promises) {
			return _Promise.all(promises);
		}
	}, {
		key: "promise",
		value: function promise(callback) {
			return new _Promise(callback);
		}
	}, {
		key: "reject",
		value: function reject(error) {
			return _Promise.reject(error);
		}
	}, {
		key: "resolve",
		value: function resolve() {
			var _Promise$resolve;

			for (var _len3 = arguments.length, params = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
				params[_key3] = arguments[_key3];
			}

			return (_Promise$resolve = _Promise.resolve).call.apply(_Promise$resolve, [_Promise].concat(params));
		}
	}]);

	return Store;
}(_eventemitter.EventEmitter2);

// Mix in `Emitter`


exports.default = Store;
(0, _eventemitter.EventEmitter2)(Store.prototype);