"use strict";

exports.__esModule = true;
exports.default = 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 _akutils = require("akutils");

var _es6Symbol = require("es6-symbol");

var _es6Symbol2 = _interopRequireDefault(_es6Symbol);

var _invariant = require("invariant");

var _invariant2 = _interopRequireDefault(_invariant);

var _lodash = require("lodash.result");

var _lodash2 = _interopRequireDefault(_lodash);

var _Access = require("./Access");

var _Access2 = _interopRequireDefault(_Access);

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

var _Shader2 = _interopRequireDefault(_Shader);

var _Shadow = require("./Shadow");

var _Shadow2 = _interopRequireDefault(_Shadow);

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

var _ShadowImpl2 = _interopRequireDefault(_ShadowImpl);

var _StateType = require("./StateType");

var _StateType2 = _interopRequireDefault(_StateType);

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"); } }

var _autoShadow = (0, _es6Symbol2.default)('autoShadow');
var _checkpoint = (0, _es6Symbol2.default)('checkpoint');
var _impl = (0, _es6Symbol2.default)('impl');
var _initialState = (0, _es6Symbol2.default)('initialState');
var _mixins = (0, _es6Symbol2.default)('mixins');
var _parent = (0, _es6Symbol2.default)('parent');
var _pid = (0, _es6Symbol2.default)('pid');
var _readonly = (0, _es6Symbol2.default)('readonly');
var _shader = (0, _es6Symbol2.default)('shader');
var _shadowDescriptors = (0, _es6Symbol2.default)('shadowDescriptors');
var _stateType = (0, _es6Symbol2.default)('stateType');
var _store = (0, _es6Symbol2.default)('store');
var _ImplementationClass = (0, _es6Symbol2.default)('ImplementationClass');
var _ShadowClass = (0, _es6Symbol2.default)('ShadowClass');

var stateDeprecatedWarningShown = false;
var rootStateDeprecatedWarningShown = false;

var nextPid = 1;

function isPropertyPrototype(obj) {
	return Property === obj || Property.isPrototypeOf(obj);
}

/**
	Base class for custom f.lux properties. A `Property` has a lifespan from when the state property
	is mapped until it is replaced/deleted. This differs from {@link Shadow} as its lifespan
	that lasts from the time of mapping until the state property is replaced/deleted OR the
	shadow property is invalidated due to a local change or a descendant property change.

	Most shadow state properties can be implemented without writing a `Property` subclass, relying on
	autoshadowing, or defining the state structure using {@link StateType} along with one of the
	built-in property class `createClass()` static methods. The primary reason for writing a
	`Property` subclass is to tie into the f.lux property life-cycle.

	This class provides an experimental check pointing api. Check pointing allows the state to
	be recorded at a point in time and then later reset to that point. This is handy when a form
	may accept changes and then allow the user to cancel the edit session.

	Life cycle methods:
	<ul>
		<li>`propertyWillShadow()` - invoked just before a state property is going to be shadowed using
			this instance. Shadow state is not valid during this method.</li>
		<li>`propertyDidShadow()` - property was shadowed and fully functional.</li>
		<li>`propertyChildInvalidated(childProperty, sourceProperty)` - a child property mutation action
			has occurred and it's value will change in store's next update.</li>
		<li>`propertyDidUpdate()` - state managed by this property has changed.</li>
		<li>`propertyWillUnshadow()` - invoked just before the shadow property is removed from the shadow
			state because the state property being shadowed has been removed from the application state.</li>
	</ul>

	@see {@link ArrayProperty}
	@see {@link CollectionProperty}
	@see {@link IndexedProperty}
	@see {@link MapProperty}
	@see {@link ObjectProperty}
*/

var Property = function () {
	/**
 	If a {@link StateType} is not passed to this constructor then one is located using
 	{@link StateType.from} thus ensuring the f.lux shadowing process is defined for this
 	property.
 		@param {StateType} [stateType] - a specialized {@link StateType} instance describing how f.lux should
 		shadow this property.
 */

	function Property(stateType) {
		_classCallCheck(this, Property);

		stateType = stateType || _StateType2.default.from(this);

		(0, _akutils.assert)(function (a) {
			return a.is(stateType, "No 'type' static property found in class hieararchy");
		});

		this[_stateType] = stateType;
	}

	//------------------------------------------------------------------------------------------------------
	// Experimental checkpoint API
	//------------------------------------------------------------------------------------------------------

	/**
 	Copies the current actual state for later reset using {@link Property#resetToCheckpoint}. An existing
 	checkpoint will take precedence over subsequent calls.
 */


	_createClass(Property, [{
		key: "checkpoint",
		value: function checkpoint() {
			var _this = this;

			(0, _akutils.assert)(function (a) {
				return a.not(_this[_checkpoint], "Checkpoint already set: " + _this.dotPath());
			});

			if (!this[_checkpoint]) {
				this[_checkpoint] = { data: this.state() };
			}
		}

		/**
  	Clears an existing checkpoint created using {@link Property#checkpoint}.
  */

	}, {
		key: "clearCheckpoint",
		value: function clearCheckpoint() {
			delete this[_checkpoint];
		}

		/**
  	Gets the checkpoint state previously recorded using {@link Property#checkpoint}.
  		@return the checkpoint data if checkpoint is set.
  */

	}, {
		key: "getCheckpoint",
		value: function getCheckpoint() {
			return this[_checkpoint] && this[_checkpoint].data;
		}

		/**
  	Gets if an existing checkpoint has be created using {@link Property#checkpoint}.
  		@return {boolean} true if a checkpoint has been recorded.
  */

	}, {
		key: "hasCheckpoint",
		value: function hasCheckpoint() {
			return !!this[_checkpoint];
		}

		/**
  	Replaces the current property state with a checkpoint state previously recorded using
  	{@link Property#checkpoint}. The checkpoint is cleared.
  */

	}, {
		key: "resetToCheckpoint",
		value: function resetToCheckpoint() {
			if (this[_impl] && this[_checkpoint]) {
				this[_impl].assign(this[_checkpoint].data);
			}

			this.clearCheckpoint();
		}

		//------------------------------------------------------------------------------------------------------
		// Experimental checkpoint API
		//------------------------------------------------------------------------------------------------------

		/**
  	Clears validation errors from the model.
  */

	}, {
		key: "clearValidationErrors",
		value: function clearValidationErrors() {
			if (this._validationErrors) {
				delete this._validationErrors;
				this.touch();
			}
		}
	}, {
		key: "hasValidationErrors",
		value: function hasValidationErrors() {
			return this._validationErrors && this._validationErrors.length;
		}

		/**
  	Experimental feature for validation errors. Errors is an array with each element having the form:
  	<ul>
  		<li>message: error description</li>
  		<li>path: path to property causing error</li>
  		<li>type: library specific reason for error</li>
  	</ul>
  */

	}, {
		key: "setValidationErrors",
		value: function setValidationErrors(errors) {
			if (errors) {
				if (!Array.isArray(errors)) {
					return console.warn("Validation errors must be an array", errors);
				} else if (!errors.length) {
					return this.clearValidationErrors();
				}

				var changed = !(this._validationErrors && errors.length === this._validationErrors.length && this._validationErrors.every(function (e) {
					return errors.some(function (er) {
						return er.path === e.path;
					});
				}));

				if (changed) {
					this._validationErrors = errors;
					this.touch();
				}
			} else {
				this.clearValidationErrors();
			}
		}

		/**
  	Gets the validation errors or an empty array if non are set.
  */

	}, {
		key: "validationErrors",
		value: function validationErrors() {
			return this._validationErrors ? Object.assign(this._validationErrors) : [];
		}

		/**
  	Gets the actual shadow property exposed to application code.
  		@return {Shadow} shadow state for this property if active.
  */

	}, {
		key: "_",
		value: function _() {
			var impl = this[_impl];

			if (!this[_store]) {
				return undefined;
			}

			return this.isActive() && impl.isMapped() ? impl.shadow() : (0, _lodash2.default)(this[_store].shadow, impl.dotPath());
		}

		/**
  	Anything is possible (almost) with the ShadowImpl reference.
  		@return {ShadowImpl} the implementation instance backing the current {@link Shadow}.
  */

	}, {
		key: "__",
		value: function __() {
			return this[_impl];
		}

		/**
  	Gets the {@link Access} object for obtaining information about a property.
  		@return {Access}
  */

	}, {
		key: "$",
		value: function $() {
			return this[_impl].access();
		}

		/**
  	Use this.$$() in shadow methods to get access to the property. Useful in `Property` subclass
  	`@shadow` methods since the method will be bound to the shadow. Exposing on the `Property`
  	allows for the same code to work when called as a member function using `this` or
  	called through a shadow function.
  		@return {Property} the property instance (`this`)
  */

	}, {
		key: "$$",
		value: function $$() {
			return this;
		}

		/**
  	Gets if autoshadowing is enabled for this property. Unlike the {@link Property#readonly} shadowing
  	attribute, autoshadowing is not hierarchically determined.
  		@return {boolean} true if autoshadowing is enabled
  */

	}, {
		key: "autoShadow",
		value: function autoShadow() {
			return this[_autoShadow] || this[_stateType]._autoShadow;
		}

		/**
  	Gets the path from root property using a dot (`.`) separator. Suitable for using with the lodash `result()`
  	function.
  		@return {string} path with each component joined by a `.`
  		@see https://lodash.com/docs/4.17.4#result
  */

	}, {
		key: "dotPath",
		value: function dotPath() {
			return this[_impl] ? this[_impl].dotPath() : null;
		}

		/**
  	Gets the initial state for a property at the beginning of the property mounting process. This
  	implementation merges the {@link StateType#initialStateWithDefaults}, the initial state set using
  	{@link #setInitialState}, and the state passed in from the existing store state. The store's state property
  	values take precedence. The explicitly set initial state is used only when the `state` parameter is
  	`undefined`.
  		@param state - the store's property state at the time of mounting.
  		@return merged state with the state parameter taking precedence if the initial state is set
  		otherwise returns the state parameter. This base implementation simply returns the state
  		parameter
  */

	}, {
		key: "getInitialState",
		value: function getInitialState(state) {
			var initialState = state !== undefined ? state : this[_initialState] !== undefined ? this[_initialState] : this[_stateType].computeInitialState();

			return this[_stateType].initialStateWithDefaults(initialState);
		}

		/**
  	Gets the {@link ShadowImpl} subclass used for implementing the {@link Shadow} f.lux integration.
  		@ignore
  */

	}, {
		key: "implementationClass",
		value: function implementationClass() {
			return _StateType2.default.implementationClassForProperty(this, this[_ImplementationClass]);
		}

		/**
  	Gets the result from {@link StateType#computeInitialState}.
  */

	}, {
		key: "initialState",
		value: function initialState() {
			return this[_stateType].computeInitialState();
		}

		/**
  	Gets if the property is currently shadowing an actual state property.
  		@return {boolean}
  */

	}, {
		key: "isActive",
		value: function isActive() {
			return this[_impl] && this[_impl].isActive();
		}

		/**
  	Gets if property is an actual isolated property managed by the store. This implementation always
  	returns false. Override to change behavior.
  		@return {boolean}
  */

	}, {
		key: "isIsolated",
		value: function isIsolated() {
			return false;
		}

		/**
  	Gets if the property allows for assignment through the shadow state, ie `todo.desc = "go skiing"`. The
  	readonly attribute is hierarchically determined through the parent property if not explicitly set.
  		@return {boolean} - true if assignment is not allowed
  */

	}, {
		key: "isReadonly",
		value: function isReadonly() {
			var readonly = this[_readonly] !== undefined ? this[_readonly] : this[_stateType]._readonly;

			// use readonly flag if explicitly set otherwise use value from parent
			return readonly === undefined ? this[_parent] && this[_parent].isReadonly() : readonly;
		}

		/**
  	Gets if this is the shadow state root property.
  		@return {boolean} true if this is the root property of the {@link Store} managing the application state.
  */

	}, {
		key: "isRoot",
		value: function isRoot() {
			return !this[_parent];
		}

		/**
  	The property name by which this property is referenced by the {@link Property.parent}.
  		@return {string|number} the name or `undefined` if not active. A `string` for an object child
  		property and a `nuber` for an array element.
  */

	}, {
		key: "name",
		value: function name() {
			return this[_impl] && this[_impl].name();
		}

		/**
  	Gets what the actual state for this property will be after the {@link Store} updates all pending
  	actions.
  		@return the next state or `undefined` if not active.
  */

	}, {
		key: "nextState",
		value: function nextState() {
			return this[_impl] && this[_impl].nextState();
		}

		/** @ignore */

	}, {
		key: "onPropertyWillShadow",
		value: function onPropertyWillShadow() {
			if (this[_mixins]) {
				var mixins = this[_mixins];

				for (var i = 0, mixin; mixin = mixins[i]; i++) {
					if (mixin.propertyWillShadow) {
						mixin.propertyWillShadow();
					}
				}
			}

			this.propertyWillShadow();
		}

		/** @ignore */

	}, {
		key: "onPropertyDidShadow",
		value: function onPropertyDidShadow() {
			if (this[_mixins]) {
				var mixins = this[_mixins];

				for (var i = 0, mixin; mixin = mixins[i]; i++) {
					if (mixin.propertyDidShadow) {
						mixin.propertyDidShadow();
					}
				}
			}

			this.propertyDidShadow();
		}

		/** @ignore */

	}, {
		key: "onChildInvalidated",
		value: function onChildInvalidated(childProperty, sourceProperty) {
			if (this[_mixins]) {
				var mixins = this[_mixins];

				for (var i = 0, mixin; mixin = mixins[i]; i++) {
					if (mixin.propertyChildInvalidated) {
						mixin.ChildInvalidated(childProperty, sourceProperty);
					}
				}
			}

			this.propertyChildInvalidated(childProperty, sourceProperty);
		}

		/** @ignore */

	}, {
		key: "onPropertyDidUpdate",
		value: function onPropertyDidUpdate() {
			if (this[_mixins]) {
				var mixins = this[_mixins];

				for (var i = 0, mixin; mixin = mixins[i]; i++) {
					if (mixin.propertyDidUpdate) {
						mixin.propertyDidUpdate();
					}
				}
			}

			this.propertyDidUpdate();
		}

		/** @ignore */

	}, {
		key: "onPropertyWillUnshadow",
		value: function onPropertyWillUnshadow() {
			if (this[_mixins]) {
				var mixins = this[_mixins];

				for (var i = 0, mixin; mixin = mixins[i]; i++) {
					if (mixin.propertyWillUnshadow) {
						mixin.propertyWillUnshadow();
					}
				}
			}

			this.propertyWillUnshadow();
		}

		/**
  	Gets the parent property.
  		@return {Property} the parent property or `undefined` if this is the shadow state root.
  */

	}, {
		key: "parent",
		value: function parent() {
			return this[_parent];
		}

		/**
  	Gets the {@link Property#name} components from the root property to this property.
  		@return {[]} array where each name component is either a `string` or `number` depending on the
  		each parent component's type.
  */

	}, {
		key: "path",
		value: function path() {
			return this[_impl] ? this[_impl].path() : null;
		}

		/**
  	Gets the parent's shadow property.
  		@return {Shadow}
  */

	}, {
		key: "parentShadow",
		value: function parentShadow() {
			var parentImpl = this[_parent] && this[_parent][_impl];

			return parentImpl && parentImpl.shadow();
		}

		/**
  	Gets the unique f.lux ID for this property.
  		@return {number} the id
  */

	}, {
		key: "pid",
		value: function pid() {
			if (!this[_pid]) {
				this[_pid] = nextPid++;
			}

			return this[_pid];
		}

		/** @ignore */

	}, {
		key: "readonlyExplicit",
		value: function readonlyExplicit() {
			return this[_readonly];
		}

		/**
  	Gets the shadow state root property for the {@link Store} managing this property.
  		@return {Property} the root property
  */

	}, {
		key: "root",
		value: function root() {
			return this[_store].root;
		}

		/**
  	Gets the root shadow state for the {@link Store} managing this property.
  		@return {Shadow}
  */

	}, {
		key: "rootShadow",
		value: function rootShadow() {
			return this[_store]._;
		}

		/**
  	Sets the auto shadow property flag.
  		Note: this method is rarely required as the {@link StateType} will usually configure this attribute.
  		@param {boolean} auto - boolean where true means to auto map subproperties.
  		@returns - reference to this property object.
  */

	}, {
		key: "setAutoshadow",
		value: function setAutoshadow(auto) {
			this[_autoShadow] = !!auto;

			return this;
		}

		/**
  	Invoked everytime the property is shadowed to set the PropertyImpl instance backing this property.
  		@ignore
  */

	}, {
		key: "setImpl",
		value: function setImpl(impl) {
			var isActive = this.isActive();

			this[_impl] = impl;
		}

		/**
  	*/

	}, {
		key: "setImplementationClass",
		value: function setImplementationClass(ImplClass) {
			(0, _akutils.assert)(function (a) {
				var isImplClass = ImplClass === _ShadowImpl2.default || _ShadowImpl2.default.isPrototypeOf(ImplClass);

				a.is(isImplClass, "ImplClass must be a ShadowImpl subclass");
			});

			this[_ImplementationClass] = ImplClass;
		}

		/**
  	Explicitly sets an initial state that will be used if the state tree does not have a value for this
  	property. This value is used by {@link Property#getInitialState} and is merged using
  	{@link StateType#initialStateWithDefaults}.
  		Note: this method is rarely required as the {@link StateType} will usually configure this attribute.
  		@param state - the initial state for the property
  		@return {Property} - reference to this property object.
  */

	}, {
		key: "setInitialState",
		value: function setInitialState(state) {
			this[_initialState] = state;

			return this;
		}

		/**
  	Sets this property's parent {@link Property} instance.
  		@return {Property} reference to this property object.
  	@throws {Error} parent already set.
  		@ignore
  */

	}, {
		key: "setParent",
		value: function setParent(parent) {
			if (this[_parent]) {
				throw new Error("Parent already set");
			}

			this[_parent] = parent;

			return this;
		}

		/**
  	Sets the readonly flag which will prevent an assignment from changing the value. More technically, a 'set'
  	function being set in the implementation's defineProeprty().
  		Note: this method is rarely required as the {@link StateType} will usually configure this attribute.
  */

	}, {
		key: "setReadonly",
		value: function setReadonly(readonly) {
			this[_readonly] = readonly;
		}

		/**
  	Used by PropertyFactoryShader to set the shader used to create this property. External code should
  	not need to utilize this method.
  		@ignore
  */

	}, {
		key: "setShader",
		value: function setShader(shader) {
			var _this2 = this;

			(0, _akutils.assert)(function (a) {
				return a.not(_this2[_shader], "Shader already set for property");
			});

			this[_shader] = shader;
		}

		/**
  	Sets the class to be used for the shadow api
  		@param {Shadow} ShadowClass - the {@link Shadow} class or one of its subclasses
  */

	}, {
		key: "setShadowClass",
		value: function setShadowClass(ShadowClass) {
			(0, _akutils.assert)(function (a) {
				var isShadowClass = ShadowClass === _Shadow2.default || _Shadow2.default.isPrototypeOf(ShadowClass);

				a.is(isShadowClass, "ShadowClass must be a Shadow subclass");
			});

			this[_ShadowClass] = ShadowClass;
		}

		/**
  	Sets the store containing the state represented by this property.
  		@return {Property} - reference to this property object.
  	@throws {Error} - store already set.
  		@ignore
  */

	}, {
		key: "setStore",
		value: function setStore(store) {
			if (this[_store]) {
				throw new Error("Store already set on property");
			}

			this[_store] = store;

			return this;
		}

		/**
  	Gets the {@link Shader} instance for this property.
  */

	}, {
		key: "shader",
		value: function shader(state) {
			if (!this[_shader]) {
				this[_shader] = this[_stateType].shader(this);
			}

			return this[_shader];
		}

		/**
  	Returns the Shadow subclass used to virtualize the state property.
  		Returns - Shadow class
  */

	}, {
		key: "shadowClass",
		value: function shadowClass() {
			return this[_stateType].shadowClassForProperty(this[_ShadowClass]);
		}

		/**
  	Gets the path from root property using a slash (`/`) separator.
  		@return {string} path with each component separated by a `/`
  */

	}, {
		key: "slashPath",
		value: function slashPath() {
			return this[_impl] ? this[_impl].slashPath() : null;
		}

		/**
  	Gets the actual state being shadowed.
  */

	}, {
		key: "state",
		value: function state() {
			return this[_impl] && this[_impl].state();
		}

		/**
  	Gets the StateType used for creating this property.
  */

	}, {
		key: "stateType",
		value: function stateType() {
			return this[_stateType];
		}

		/**
  	Gets the {@Link Store} containing the application state.
  */

	}, {
		key: "store",
		value: function store() {
			if (!this[_store] && this[_parent]) {
				this[_store] = this[_parent].store();
			}

			return this[_store];
		}

		/**
  	Triggers a reshadow of the properties shadow state. This is handy when a calculated state value changes
  	while concrete state values remain unchanged.
  */

	}, {
		key: "touch",
		value: function touch() {
			var reason = arguments.length <= 0 || arguments[0] === undefined ? "Property.touch()" : arguments[0];

			if (this.isActive()) {
				this[_impl].update(function (state) {
					return { name: reason, nextState: state };
				});
			}
		}

		/**
  	Gets this property's {@link StateType.typeName} value.
  */

	}, {
		key: "typeName",
		value: function typeName() {
			return this.constructor.__fluxTypeName__ || this.constructor.name;
		}

		/**
  	Makes changes to the next proeprty state. The callback should be pure (no side affects).
  		The callback has the form:
  		```js
  	function callback(state) : { name, nextState, replace }
  	```
  	where:
  		<ul>
  	<li>`state` - the current state</li>
  	<li>`nextState` - the next state for the property</li>
  	<li>`name` - the name of the action, such as "assign()" (optional)</li>
  	<li>`replace` - should the entire state for this property be replaced with `nextState`. A value of
  		`true` means this property will unshadow and all of it's children will not be able to make
  		future changes to the model. (optional - default=false)</li>
  	</ul>
  		The callback return value is called an *action descriptor* and describes the update behavior.To
  	understand the reasoning behind the replace flag consider the following example:
  		```
  	const model = { a: { b: { c: 1 } } }
  	const oldB = model.a.b
  		model.a.b = "foo"
  	oldB.c = 5
  		model.a.b.c === undefined
  	```js
  		Thus, oldB.c may change oldB'c property 'c' to 5 but model.a.b is still "foo".
  		@param {function} callback - takes the current state and returns an object containing
  		the *action descriptor* (see discussion above). At a minimum, the action descriptor must contain
  		a `nextState` property containing the property's state following the action.
  */

	}, {
		key: "update",
		value: function update(callback) {
			if (this.isActive()) {
				this[_impl].update(callback);
			} else {
				throw new Error("Property must be active to invoke update()");
			}
		}

		//------------------------------------------------------------------------------------------------------
		// Property subclasses may want to override these methods - no need to call super
		//------------------------------------------------------------------------------------------------------

		/**
  	Creates the object to be assigned to the shadow.$ property. Subclasses can override this method
  	to setup a chain of specialized accessors (`$()`). See {@link Access} for details on setting up
  	specialized accessors. This is an advanced feature and rarely required.
  		@return {Access} a property accessor instance.
  */

	}, {
		key: "create$",
		value: function create$(impl) {
			return new _Access2.default(impl);
		}

		/**
  	Gets the object containing or managing this property. All properties have an owner except
  	the root property. Isolated properties are a case where a property is independently managed
  	by the store so it can have an owner but not a parent.
  		@return {Property} alias for {@link Property#parent}
  */

	}, {
		key: "owner",
		value: function owner() {
			return this.parent();
		}

		//------------------------------------------------------------------------------------------------------
		// State lifecycle methods
		//------------------------------------------------------------------------------------------------------

		/**
  	Invoked by the f.lux shadowing process just before a property initially shadows a state property.
  	The property is not active when this method is invoked so state values cannot be accessed. This
  	method is useful for constructor type activities when you do not want to implement a constructor.
  		Subclasses do not need to invoke the parent implementation.
  */

	}, {
		key: "propertyWillShadow",
		value: function propertyWillShadow() {} /* subscribe to websockets */

		/**
  	Invoked by the f.lux shadowing process after a property initially shadows a state property. The
  	property is active and can safely access the shadow state.
  		Subclasses do not need to invoke the parent implementation.
  */

	}, {
		key: "propertyDidShadow",
		value: function propertyDidShadow() {} /* subscribe to websockets */

		/**
  	A child property or one of its descendents wil be changing state. Useful hook when a property needs
  	to perform some bookkeepng for child properties. Utilizing this hook provides a chance to make tracking
  	changes in shadow properties before the store updates its state.
  		Subclasses do not need to invoke the parent implementation.
  		@param {Property} childProperty - the immediate child property through which the update is occurring.
  	@param {Property} sourceProperty - the property generating the property change.
  */

	}, {
		key: "propertyChildInvalidated",
		value: function propertyChildInvalidated(childProperty, sourceProperty) {}

		/**
  	Invoked by the f.lux shadowing process after a property is reshadowed.
  		Subclasses do not need to invoke the parent implementation.
  */

	}, {
		key: "propertyDidUpdate",
		value: function propertyDidUpdate() {} /* post reshadow */

		/**
  	Invoked by the f.lux shadowing process just before a property will be removed from the shadow state.
  		Subclasses do not need to invoke the parent implementation.
  */

	}, {
		key: "propertyWillUnshadow",
		value: function propertyWillUnshadow() {} /* unsubscribe to websockets */

		//------------------------------------------------------------------------------------------------------
		// mixin framework internal methods
		//------------------------------------------------------------------------------------------------------

		/** @ignore */

	}, {
		key: "__addMixin",
		value: function __addMixin(mixin) {
			// initialize only if needed to conserve space since likely not used often
			if (!this[_mixins]) {
				this[_mixins] = [];
			}

			this[_mixins].push(mixin);
		}

		/** @ignore */

	}, {
		key: "__hasMixins",
		value: function __hasMixins() {
			return !!this[_mixins];
		}

		/** @ignore */

	}, {
		key: "__mixins",
		value: function __mixins() {
			return this[_mixins];
		}
	}]);

	return Property;
}();

exports.default = Property;