"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var sp_component_base_1 = require("@microsoft/sp-component-base");
var sp_diagnostics_1 = require("@microsoft/sp-diagnostics");
var PlaceholderManager_1 = tslib_1.__importDefault(require("./PlaceholderManager"));
var PlaceholderName_1 = tslib_1.__importDefault(require("./PlaceholderName"));
/**
 * Allows third-party components to discover and use SharePoint placeholders.  Placeholders
 * enable third-party components to render custom content into designated regions on the page.
 *
 * @remarks
 * Placeholders are designated regions on the page, identified by one of the predefined
 * {@link PlaceholderName} constants.  Different applications and pages might support different
 * placeholder names.  Placeholders can appear and disappear as the user interacts with the page,
 * for example via in-place navigation.  Third-party components should not assume that a given placeholder
 * will always be available.
 *
 * Use {@link PlaceholderProvider.tryCreateContent} to test whether a placeholder is
 * available and add content to it.   Use the {@link PlaceholderProvider.changedEvent} event
 * to discover when new placeholders appear.
 *
 * @public
 */
var PlaceholderProvider = /** @class */ (function () {
    /**
     * @internal
     */
    function PlaceholderProvider(serviceScope, applicationCustomizerContextParameters, componentTag) {
        this._placeholderContents = [];
        this._isDisposed = false;
        this._sequence = applicationCustomizerContextParameters.sequence;
        var qosMonitor = new sp_diagnostics_1._QosMonitor('PlaceholderProvider');
        var preAllocatedApplicationCustomizerTopHeight = applicationCustomizerContextParameters.preAllocatedApplicationCustomizerTopHeight, preAllocatedApplicationCustomizerBottomHeight = applicationCustomizerContextParameters.preAllocatedApplicationCustomizerBottomHeight;
        this._componentTag = componentTag ? componentTag : '';
        /*
         * We're not using the whenFinished callback, as we know that at this point, the servicescope
         * in use is already finished in the ApplicationCustomizerContext.
         */
        this._placeholderManager = serviceScope.consume(PlaceholderManager_1.default.serviceKey);
        if (preAllocatedApplicationCustomizerTopHeight > 0) {
            this._preAllocatedTopPlaceholderContent = this._preAllocateContent(PlaceholderName_1.default.Top, preAllocatedApplicationCustomizerTopHeight, qosMonitor);
        }
        if (preAllocatedApplicationCustomizerBottomHeight > 0) {
            this._preAllocatedBottomPlaceholderContent = this._preAllocateContent(PlaceholderName_1.default.Bottom, preAllocatedApplicationCustomizerBottomHeight, qosMonitor);
        }
        qosMonitor.writeSuccess({ name: name });
    }
    PlaceholderProvider.prototype.dispose = function () {
        if (!this._isDisposed) {
            this._placeholderContents.forEach(function (content) { return content.dispose(); });
            delete this._placeholderContents;
            delete this._placeholderManager;
        }
        this._isDisposed = true;
    };
    Object.defineProperty(PlaceholderProvider.prototype, "isDisposed", {
        get: function () {
            return this._isDisposed;
        },
        enumerable: false,
        configurable: true
    });
    /**
     * Tests whether the page contains the specified placeholder.  If so, a new PlaceholderContent
     * object is created, which allows the caller to render custom content inside the placeholder.
     *
     * @param name - The requested placeholder
     * @param options - Additional options, for example to detect when the placeholder is disposed.
     * @returns A new PlaceholderContent object, or undefined if the requested placeholder does not exist.
     *
     * @remarks
     *
     * The host application makes no guarantees about the availability of a given placeholder.
     * In situations where an expected placeholder is not available, the third-party extension
     * must handle it gracefully, e.g. by not rendering anything, or by choosing an alternative
     * placeholder.
     */
    PlaceholderProvider.prototype.tryCreateContent = function (name, options) {
        sp_diagnostics_1._TraceLogger.logVerbose(PlaceholderProvider._logSource, "Creating placeholder content in placeholder \"" + name + "\"");
        var qosMonitor = new sp_diagnostics_1._QosMonitor('Placeholder.tryCreateContent');
        var content;
        if (name === PlaceholderName_1.default.Top && this._preAllocatedTopPlaceholderContent) {
            content = this._preAllocatedTopPlaceholderContent;
            this._preAllocatedTopPlaceholderContent = undefined;
        }
        else if (name === PlaceholderName_1.default.Bottom && this._preAllocatedBottomPlaceholderContent) {
            content = this._preAllocatedBottomPlaceholderContent;
            this._preAllocatedBottomPlaceholderContent = undefined;
        }
        if (content) {
            content._setOptions(options);
            this._postContentCreation(content, qosMonitor);
            content.domElement.innerHTML = '';
            return content;
        }
        return this._tryCreateContent(qosMonitor, name, options);
    };
    PlaceholderProvider.prototype._preAllocateContent = function (placeholderName, height, qosMonitor) {
        // There is a chance that someone is looking for placeholders before the PlaceholderProvider.changedEvent has fired.
        // This is a bug, and we should warn the user of this anti-pattern. The easiest thing to do is make sure that
        // the .changedEvent callback isn't empty.
        if (this._placeholderManager.changedEvent._listenerCount() === 0) {
            // This is a bad state to be in.  We should log this out:
            console.log('tryCreateContent was called, but PlaceholderProvider.changedEvent has no handlers. ' +
                'This is likely to cause an error in the future.  You should probably be calling ' +
                'tryCreateContent in the PlaceholderProvider.changedEvent callback.');
        }
        // Check if placeholders are enabled first.
        if (!this._placeholderManager.isEnabled || !this._placeholderManager.placeholders.has(placeholderName)) {
            var error = new Error("No placeholder found with the name '" + name + "'");
            qosMonitor.writeExpectedFailure('PlaceholderNotFound', error, { name: name });
            return undefined;
        }
        var placeholder = this._placeholderManager.placeholders.get(placeholderName);
        var preAllocatedContent = placeholder.createPlaceholderContent(this._sequence);
        // This should never really happen. But this gives an explicit failure to help debug issues quickly.
        if (!placeholder) {
            var error = new Error('Placeholder dictionary has only the key but not the value for the placeholder');
            qosMonitor.writeUnexpectedFailure('NoValueInPlaceholdersMap', error, { name: name });
            return undefined;
        }
        try {
            preAllocatedContent.domElement.style.height = height + 'px';
            var shimmer = sp_component_base_1._ShimmerFactory.createShimmer(preAllocatedContent.domElement.clientWidth, height, PlaceholderName_1.default + this._componentTag, '', 0);
            preAllocatedContent.domElement.appendChild(shimmer);
            qosMonitor.writeSuccess({ name: name });
        }
        catch (error) {
            qosMonitor.writeUnexpectedFailure('CreateContentFailed', error, { name: name });
        }
        return preAllocatedContent;
    };
    Object.defineProperty(PlaceholderProvider.prototype, "placeholderNames", {
        /**
         * Returns the names of the currently available placeholders.
         */
        get: function () {
            if (!this._placeholderManager.isEnabled) {
                return [];
            }
            var array = [];
            this._placeholderManager.placeholders.forEach(function (value, key) { return array.push(key); });
            return array;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(PlaceholderProvider.prototype, "changedEvent", {
        /**
         * This event is raised when the list of currently available placeholders is changed.
         *
         * @remarks
         * The application can change its list of available placeholders at any time. This means that the existing
         * placeholders may get disposed or new placeholders may be added. Use this event to discover new
         * placeholders when they appear.
         *
         * @eventproperty
         */
        get: function () {
            return this._placeholderManager.changedEvent;
        },
        enumerable: false,
        configurable: true
    });
    PlaceholderProvider.prototype._tryCreateContent = function (qosMonitor, name, options) {
        var placeholder = this._placeholderManager.placeholders.get(name);
        var content;
        try {
            content = placeholder.createPlaceholderContent(this._sequence, options);
        }
        catch (error) {
            qosMonitor.writeUnexpectedFailure('CreateContentFailed', error, { name: name });
            return undefined;
        }
        this._postContentCreation(content, qosMonitor);
        return content;
    };
    PlaceholderProvider.prototype._postContentCreation = function (content, qosMonitor) {
        if (content) {
            this._placeholderContents.push(content);
            qosMonitor.writeSuccess({ name: name });
        }
        else {
            var error = new Error('Placeholder could not create new content');
            qosMonitor.writeUnexpectedFailure('CreateContentUndefined', error, { name: name });
        }
    };
    PlaceholderProvider._logSource = sp_diagnostics_1._LogSource.create('PlaceholderProvider');
    return PlaceholderProvider;
}());
exports.default = PlaceholderProvider;
//# sourceMappingURL=PlaceholderProvider.js.map