# Source: ui/succession.reel/succession.js

    /**
     * @module "montage/ui/succession.reel"
     */
    var Component = require("ui/component").Component;

    /**
     * Subclasses Component for its `domContent` behavior.
     *
     * If passage properties are defined on the Succession, they will override children's.
     * See {@link Succession#_prepareForBuild}.
     *
     * @class Succession
     * @augments Component
     */
    exports.Succession = Component.specialize(/** @lends Succession.prototype */{

        contentBuildInAnimation: {
            value: undefined
        },

        contentBuildOutAnimation: {
            value: undefined
        },

        /**
         * Setting content to a component will add the component to `history`.
         * Setting content to null will clear `history`.
         */
        content: {
            get: function () {
                return this.history.length ? this.history[this.history.length - 1] : undefined;
            },
            set: function (component) {
                if (component) {
                    this.history.push(component);
                } else {
                    this.history.clear();
                }
            }
        },

        _firstComponent: {
            value: undefined
        },

        _history: {
            value: undefined
        },

        /**
         * A stack consisted of {@link Component}s.
         *
         * @property {Array}
         */
        history: {
            get: function () {
                if (!this._history) {
                    this._history = [];
                    this._history.addBeforeRangeChangeListener(this);
                    this._history.addRangeChangeListener(this);
                }
                return this._history;
            },
            set: function (history) {
                history = Array.isArray(history) ? history : [];
                if (this.history !== history) {
                    if (!this.history.length && history.length) {
                        this._firstComponent = history[0];
                    }
                    this.history.splice.apply(this.history, [0, this.history.length].concat(history));
                }
            }
        },

        /**
         * @property {boolean}
         * @default false
         */
        hasTemplate: {
            enumerable: false,
            value: false
        },

        /**
         * Override build-in / out animation; checks for whether properties are undefined,
         * as null is used to disable passage animation.
         *
         * Priority from most important: Succession -> Passage -> Component
         *
         * @private
         * @function
         */
        _prepareForBuild: {
            value: function (content) {
                if (content) {
                    if (this.contentBuildInAnimation) {
                        content.buildInAnimationOverride = this.contentBuildInAnimation;
                    }
                    if (this.contentBuildOutAnimation) {
                        content.buildOutAnimationOverride = this.contentBuildOutAnimation;
                    }
                }
            }
        },

        /**
         * Ensure components generated by instantiating in JavaScript instead of
         * declaring in template serialization has an element.
         *
         * @private
         * @function
         * @param {Component} content
         */
        _updateDomContentWith: {
            value: function (content) {
                if (content) {
                    var element;
                    if (!content.element) {
                        element = document.createElement("div");
                        element.id = content.identifier || "appendDiv";
                        content.element = element;
                    } else {
                        element = content.element;
                    }
                    this.domContent = element;
                    content.needsDraw = true;
                } else {
                    this.domContent = null;
                }
            }
        },

        // =============================================================================================
        // Event Handlers
        // =============================================================================================

        /**
         * Prepare outgoing content; need to prepare before range actually changes because we need to
         * prepare on outgoing content and handleRangeChange happens after outgoing content is gone
         */
        handleRangeWillChange: {
            value: function (plus, minus, index) {
                this._prepareForBuild(this.content);
            }
        },
        /**
         * Sets classes on Succession depending on how history was changed
         * Prepare incoming content
         */
        handleRangeChange: {
            value: function (plus, minus, index) {
                //console.log(this.content && this.content.title);
                //console.log(plus[0] && plus[0].title);
                //console.log(minus[0] && minus[0].title);
                var length = this.history ? this.history.length : 0,
                    isChanged = plus.length || minus.length,
                    isChangeVisible = isChanged && index + plus.length === length,
                    isPush = isChangeVisible && !minus.length && index,
                    isPop = isChangeVisible && !plus.length && length,
                    isReplace = isChangeVisible && !isPush && !isPop && length,
                    isClear = isChangeVisible && !length;
                // Set appropriate classes and update the succession if necessary.
                if (isChangeVisible) {
                    this.classList[isPush ? "add" : "remove"]("montage-Succession--push");
                    this.classList[isPop ? "add" : "remove"]("montage-Succession--pop");
                    this.classList[isReplace ? "add" : "remove"]("montage-Succession--replace");
                    this.classList[isClear ? "add" : "remove"]("montage-Succession--clear");
                    this._prepareForBuild(this.content);
                    this.dispatchBeforeOwnPropertyChange("content", this.content);
                    this._updateDomContentWith(this.content);
                    this.dispatchOwnPropertyChange("content", this.content);
                }
            }
        },

        handleBuildInEnd: {
            value: function (event) {
                this.needsCssClassCleanup = true;
                this.needsDraw = true;
            }
        },

        handleBuildOutEnd: {
            value: function (event) {
                this.needsCssClassCleanup = true;
                this.needsDraw = true;
            }
        },

        // =============================================================================================
        // Life Cycle Hooks
        // =============================================================================================

        /**
         * The first time, extract the argument component (if any) and insert it into the succession
         * without animation. Must wait for argument component's template to be expanded as
         * parent replaces template with component's element.
         *
         * TODO: Component's extractDomArgument("*") does not seem to be working so the
         *       content argument must explicitly be named "content".
         */
        enterDocument: {
            value: function (isFirstTime) {
                var contentElement = isFirstTime && this.extractDomArgument("content"),
                    contentComponent = contentElement && contentElement.component,
                    self = this;
                if (contentComponent) {
                    contentComponent.expandComponent().then(function () {
                        self.history.push(contentComponent);
                    });
                }
                if (isFirstTime) {
                    this.addEventListener("buildInEnd", this);
                    this.addEventListener("buildOutEnd", this);
                }
            }
        },

        draw: {
            value: function () {
                if (this.needsCssClassCleanup) {
                    this.needsCssClassCleanup = false;
                    this.classList.deleteEach([
                        "montage-Succession--push",
                        "montage-Succession--pop",
                        "montage-Succession--replace",
                        "montage-Succession--clear"
                    ]);
                }
            }
        }

    });