(function (VSS)()

in Extensions/Optimizely/Src/UIContribution/lib/VSS.SDK.js [671:1400]


(function (VSS) {
    // W A R N I N G: if VssSDKVersion changes, the VSS WEB SDK demand resolver needs to be updated with the new version
    VSS.VssSDKVersion = 2.0;
    VSS.VssSDKRestVersion = "4.0";
    var bodyElement;
    var webContext;
    var hostPageContext;
    var extensionContext;
    var initialConfiguration;
    var initialContribution;
    var initOptions;
    var loaderConfigured = false;
    var usingPlatformScripts;
    var usingPlatformStyles;
    var isReady = false;
    var readyCallbacks;
    var parentChannel = XDM.XDMChannelManager.get().addChannel(window.parent);
    var shimmedLocalStorage;
    var hostReadyForShimUpdates = false;
    var Storage = (function () {
        var changeCallback;
        function invokeChangeCallback() {
            if (changeCallback) {
                changeCallback.call(this);
            }
        }
        function Storage(changeCallback) {
        }
        Object.defineProperties(Storage.prototype, {
            getItem: {
                get: function () {
                    return function (key) {
                        var item = this["" + key];
                        return typeof item === "undefined" ? null : item;
                    };
                }
            },
            setItem: {
                get: function () {
                    return function (key, value) {
                        key = "" + key;
                        var existingValue = this[key];
                        var newValue = "" + value;
                        if (existingValue !== newValue) {
                            this[key] = newValue;
                            invokeChangeCallback();
                        }
                    };
                }
            },
            removeItem: {
                get: function () {
                    return function (key) {
                        key = "" + key;
                        if (typeof this[key] !== "undefined") {
                            delete this[key];
                            invokeChangeCallback();
                        }
                    };
                }
            },
            clear: {
                get: function () {
                    return function () {
                        var keys = Object.keys(this);
                        if (keys.length > 0) {
                            for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
                                var key = keys_1[_i];
                                delete this[key];
                            }
                            invokeChangeCallback();
                        }
                    };
                }
            },
            key: {
                get: function () {
                    return function (index) {
                        return Object.keys(this)[index];
                    };
                }
            },
            length: {
                get: function () {
                    return Object.keys(this).length;
                }
            }
        });
        return Storage;
    }());
    function shimSandboxedProperties() {
        var updateSettingsTimeout;
        function updateShimmedStorageCallback() {
            // Talk to the host frame on a 50 ms delay in order to batch storage/cookie updates
            if (!updateSettingsTimeout) {
                updateSettingsTimeout = setTimeout(function () {
                    updateSettingsTimeout = 0;
                    updateHostSandboxedStorage();
                }, 50);
            }
        }
        // Override document.cookie if it is not available
        var hasCookieSupport = false;
        try {
            hasCookieSupport = typeof document.cookie === "string";
        }
        catch (ex) {
        }
        if (!hasCookieSupport) {
            Object.defineProperty(Document.prototype, "cookie", {
                get: function () {
                    return "";
                },
                set: function (value) {
                }
            });
        }
        // Override browser storage
        var hasLocalStorage = false;
        try {
            hasLocalStorage = !!window.localStorage;
        }
        catch (ex) {
        }
        if (!hasLocalStorage) {
            delete window.localStorage;
            shimmedLocalStorage = new Storage(updateShimmedStorageCallback);
            Object.defineProperty(window, "localStorage", { value: shimmedLocalStorage });
            delete window.sessionStorage;
            Object.defineProperty(window, "sessionStorage", { value: new Storage() });
        }
    }
    if (!window["__vssNoSandboxShim"]) {
        try {
            shimSandboxedProperties();
        }
        catch (ex) {
            if (window.console && window.console.warn) {
                window.console.warn("Failed to shim support for sandboxed properties: " + ex.message + ". Set \"window.__vssNoSandboxShim = true\" in order to bypass the shim of sandboxed properties.");
            }
        }
    }
    /**
    * Service Ids for core services (to be used in VSS.getService)
    */
    var ServiceIds;
    (function (ServiceIds) {
        /**
        * Service for showing dialogs in the host frame
        * Use: <IHostDialogService>
        */
        ServiceIds.Dialog = "ms.vss-web.dialog-service";
        /**
        * Service for interacting with the host frame's navigation (getting/updating the address/hash, reloading the page, etc.)
        * Use: <IHostNavigationService>
        */
        ServiceIds.Navigation = "ms.vss-web.navigation-service";
        /**
        * Service for interacting with extension data (setting/setting documents and collections)
        * Use: <IExtensionDataService>
        */
        ServiceIds.ExtensionData = "ms.vss-web.data-service";
    })(ServiceIds = VSS.ServiceIds || (VSS.ServiceIds = {}));
    /**
     * Initiates the handshake with the host window.
     *
     * @param options Initialization options for the extension.
     */
    function init(options) {
        initOptions = options || {};
        usingPlatformScripts = initOptions.usePlatformScripts;
        usingPlatformStyles = initOptions.usePlatformStyles;
        // Run this after current execution path is complete - allows objects to get initialized
        window.setTimeout(function () {
            var appHandshakeData = {
                notifyLoadSucceeded: !initOptions.explicitNotifyLoaded,
                extensionReusedCallback: initOptions.extensionReusedCallback,
                vssSDKVersion: VSS.VssSDKVersion
            };
            parentChannel.invokeRemoteMethod("initialHandshake", "VSS.HostControl", [appHandshakeData]).then(function (handshakeData) {
                hostPageContext = handshakeData.pageContext;
                webContext = hostPageContext.webContext;
                initialConfiguration = handshakeData.initialConfig || {};
                initialContribution = handshakeData.contribution;
                extensionContext = handshakeData.extensionContext;
                if (handshakeData.sandboxedStorage) {
                    var updateNeeded = false;
                    if (shimmedLocalStorage) {
                        if (handshakeData.sandboxedStorage.localStorage) {
                            // Merge host data in with any values already set.
                            var newData = handshakeData.sandboxedStorage.localStorage;
                            // Check for any properties written prior to the initial handshake
                            for (var _i = 0, _a = Object.keys(shimmedLocalStorage); _i < _a.length; _i++) {
                                var key = _a[_i];
                                var value = shimmedLocalStorage.getItem(key);
                                if (value !== newData[key]) {
                                    newData[key] = value;
                                    updateNeeded = true;
                                }
                            }
                            // Update the stored values
                            for (var _b = 0, _c = Object.keys(newData); _b < _c.length; _b++) {
                                var key = _c[_b];
                                shimmedLocalStorage.setItem(key, newData[key]);
                            }
                        }
                        else if (shimmedLocalStorage.length > 0) {
                            updateNeeded = true;
                        }
                    }
                    hostReadyForShimUpdates = true;
                    if (updateNeeded) {
                        // Talk to host frame to issue update
                        updateHostSandboxedStorage();
                    }
                }
                if (usingPlatformScripts || usingPlatformStyles) {
                    setupAmdLoader();
                }
                else {
                    triggerReady();
                }
            });
        }, 0);
    }
    VSS.init = init;
    function updateHostSandboxedStorage() {
        var storage = {
            localStorage: JSON.stringify(shimmedLocalStorage || {})
        };
        parentChannel.invokeRemoteMethod("updateSandboxedStorage", "VSS.HostControl", [storage]);
    }
    /**
     * Ensures that the AMD loader from the host is configured and fetches a script (AMD) module
     * (and its dependencies). If no callback is supplied, this will still perform an asynchronous
     * fetch of the module (unlike AMD require which returns synchronously). This method has no return value.
     *
     * Usage:
     *
     * VSS.require(["VSS/Controls", "VSS/Controls/Grids"], function(Controls, Grids) {
     *    ...
     * });
     *
     * @param modules A single module path (string) or array of paths (string[])
     * @param callback Method called once the modules have been loaded.
     */
    function require(modules, callback) {
        var modulesArray;
        if (typeof modules === "string") {
            modulesArray = [modules];
        }
        else {
            modulesArray = modules;
        }
        if (!callback) {
            // Generate an empty callback for require
            callback = function () { };
        }
        if (loaderConfigured) {
            // Loader already configured, just issue require
            issueVssRequire(modulesArray, callback);
        }
        else {
            if (!initOptions) {
                init({ usePlatformScripts: true });
            }
            else if (!usingPlatformScripts) {
                usingPlatformScripts = true;
                if (isReady) {
                    // We are in the ready state, but previously not using the loader, so set it up now
                    // which will re-trigger ready
                    isReady = false;
                    setupAmdLoader();
                }
            }
            ready(function () {
                issueVssRequire(modulesArray, callback);
            });
        }
    }
    VSS.require = require;
    function issueVssRequire(modules, callback) {
        if (hostPageContext.diagnostics.bundlingEnabled) {
            window.require(["VSS/Bundling"], function (VSS_Bundling) {
                VSS_Bundling.requireModules(modules).spread(function () {
                    callback.apply(this, arguments);
                });
            });
        }
        else {
            window.require(modules, callback);
        }
    }
    /**
    * Register a callback that gets called once the initial setup/handshake has completed.
    * If the initial setup is already completed, the callback is invoked at the end of the current call stack.
    */
    function ready(callback) {
        if (isReady) {
            window.setTimeout(callback, 0);
        }
        else {
            if (!readyCallbacks) {
                readyCallbacks = [];
            }
            readyCallbacks.push(callback);
        }
    }
    VSS.ready = ready;
    /**
    * Notifies the host that the extension successfully loaded (stop showing the loading indicator)
    */
    function notifyLoadSucceeded() {
        parentChannel.invokeRemoteMethod("notifyLoadSucceeded", "VSS.HostControl");
    }
    VSS.notifyLoadSucceeded = notifyLoadSucceeded;
    /**
    * Notifies the host that the extension failed to load
    */
    function notifyLoadFailed(e) {
        parentChannel.invokeRemoteMethod("notifyLoadFailed", "VSS.HostControl", [e]);
    }
    VSS.notifyLoadFailed = notifyLoadFailed;
    /**
    * Get the web context from the parent host
    */
    function getWebContext() {
        return webContext;
    }
    VSS.getWebContext = getWebContext;
    /**
    * Get the configuration data passed in the initial handshake from the parent frame
    */
    function getConfiguration() {
        return initialConfiguration;
    }
    VSS.getConfiguration = getConfiguration;
    /**
    * Get the context about the extension that owns the content that is being hosted
    */
    function getExtensionContext() {
        return extensionContext;
    }
    VSS.getExtensionContext = getExtensionContext;
    /**
    * Gets the information about the contribution that first caused this extension to load.
    */
    function getContribution() {
        return initialContribution;
    }
    VSS.getContribution = getContribution;
    /**
    * Get a contributed service from the parent host.
    *
    * @param contributionId Full Id of the service contribution to get the instance of
    * @param context Optional context information to use when obtaining the service instance
    */
    function getService(contributionId, context) {
        return getServiceContribution(contributionId).then(function (serviceContribution) {
            if (!context) {
                context = {};
            }
            if (!context["webContext"]) {
                context["webContext"] = getWebContext();
            }
            if (!context["extensionContext"]) {
                context["extensionContext"] = getExtensionContext();
            }
            return serviceContribution.getInstance(serviceContribution.id, context);
        });
    }
    VSS.getService = getService;
    /**
    * Get the contribution with the given contribution id. The returned contribution has a method to get a registered object within that contribution.
    *
    * @param contributionId Id of the contribution to get
    */
    function getServiceContribution(contributionId) {
        var deferred = XDM.createDeferred();
        VSS.ready(function () {
            parentChannel.invokeRemoteMethod("getServiceContribution", "vss.hostManagement", [contributionId]).then(function (contribution) {
                var serviceContribution = contribution;
                serviceContribution.getInstance = function (objectId, context) {
                    return getBackgroundContributionInstance(contribution, objectId, context);
                };
                deferred.resolve(serviceContribution);
            }, deferred.reject);
        });
        return deferred.promise;
    }
    VSS.getServiceContribution = getServiceContribution;
    /**
    * Get contributions that target a given contribution id. The returned contributions have a method to get a registered object within that contribution.
    *
    * @param targetContributionId Contributions that target the contribution with this id will be returned
    */
    function getServiceContributions(targetContributionId) {
        var deferred = XDM.createDeferred();
        VSS.ready(function () {
            parentChannel.invokeRemoteMethod("getContributionsForTarget", "vss.hostManagement", [targetContributionId]).then(function (contributions) {
                var serviceContributions = [];
                contributions.forEach(function (contribution) {
                    var serviceContribution = contribution;
                    serviceContribution.getInstance = function (objectId, context) {
                        return getBackgroundContributionInstance(contribution, objectId, context);
                    };
                    serviceContributions.push(serviceContribution);
                });
                deferred.resolve(serviceContributions);
            }, deferred.reject);
        });
        return deferred.promise;
    }
    VSS.getServiceContributions = getServiceContributions;
    /**
    * Create an instance of a registered object within the given contribution in the host's frame
    *
    * @param contribution The contribution to get an object from
    * @param objectId Optional id of the registered object (the contribution's id property is used by default)
    * @param contextData Optional context to use when getting the object.
    */
    function getBackgroundContributionInstance(contribution, objectId, contextData) {
        var deferred = XDM.createDeferred();
        VSS.ready(function () {
            parentChannel.invokeRemoteMethod("getBackgroundContributionInstance", "vss.hostManagement", [contribution, objectId, contextData]).then(deferred.resolve, deferred.reject);
        });
        return deferred.promise;
    }
    /**
    * Register an object (instance or factory method) that this extension exposes to the host frame.
    *
    * @param instanceId unique id of the registered object
    * @param instance Either: (1) an object instance, or (2) a function that takes optional context data and returns an object instance.
    */
    function register(instanceId, instance) {
        parentChannel.getObjectRegistry().register(instanceId, instance);
    }
    VSS.register = register;
    /**
    * Removes an object that this extension exposed to the host frame.
    *
    * @param instanceId unique id of the registered object
    */
    function unregister(instanceId) {
        parentChannel.getObjectRegistry().unregister(instanceId);
    }
    VSS.unregister = unregister;
    /**
    * Get an instance of an object registered with the given id
    *
    * @param instanceId unique id of the registered object
    * @param contextData Optional context data to pass to the contructor of an object factory method
    */
    function getRegisteredObject(instanceId, contextData) {
        return parentChannel.getObjectRegistry().getInstance(instanceId, contextData);
    }
    VSS.getRegisteredObject = getRegisteredObject;
    /**
    * Fetch an access token which will allow calls to be made to other VSTS services
    */
    function getAccessToken() {
        return parentChannel.invokeRemoteMethod("getAccessToken", "VSS.HostControl");
    }
    VSS.getAccessToken = getAccessToken;
    /**
    * Fetch an token which can be used to identify the current user
    */
    function getAppToken() {
        return parentChannel.invokeRemoteMethod("getAppToken", "VSS.HostControl");
    }
    VSS.getAppToken = getAppToken;
    /**
    * Requests the parent window to resize the container for this extension based on the current extension size.
    *
    * @param width Optional width, defaults to scrollWidth
    * @param height Optional height, defaults to scrollHeight
    */
    function resize(width, height) {
        if (!bodyElement) {
            bodyElement = document.getElementsByTagName("body").item(0);
        }
        var newWidth = typeof width === "number" ? width : bodyElement.scrollWidth;
        var newHeight = typeof height === "number" ? height : bodyElement.scrollHeight;
        parentChannel.invokeRemoteMethod("resize", "VSS.HostControl", [newWidth, newHeight]);
    }
    VSS.resize = resize;
    function setupAmdLoader() {
        var hostRootUri = getRootUri(hostPageContext.webContext);
        // Place context so that VSS scripts pick it up correctly
        window.__vssPageContext = hostPageContext;
        // MS Ajax config needs to exist before loading MS Ajax library
        window.__cultureInfo = hostPageContext.microsoftAjaxConfig.cultureInfo;
        // Append CSS first
        if (usingPlatformStyles !== false) {
            if (hostPageContext.coreReferences.stylesheets) {
                hostPageContext.coreReferences.stylesheets.forEach(function (stylesheet) {
                    if (stylesheet.isCoreStylesheet) {
                        var cssLink = document.createElement("link");
                        cssLink.href = getAbsoluteUrl(stylesheet.url, hostRootUri);
                        cssLink.rel = "stylesheet";
                        safeAppendToDom(cssLink, "head");
                    }
                });
            }
        }
        if (!usingPlatformScripts) {
            // Just wanted to load CSS, no scripts. Can exit here.
            loaderConfigured = true;
            triggerReady();
            return;
        }
        var scripts = [];
        var anyCoreScriptLoaded = false;
        // Add scripts and loader configuration
        if (hostPageContext.coreReferences.scripts) {
            hostPageContext.coreReferences.scripts.forEach(function (script) {
                if (script.isCoreModule) {
                    var alreadyLoaded = false;
                    var global = window;
                    if (script.identifier === "JQuery") {
                        alreadyLoaded = !!global.jQuery;
                    }
                    else if (script.identifier === "JQueryUI") {
                        alreadyLoaded = !!(global.jQuery && global.jQuery.ui && global.jQuery.ui.version);
                    }
                    else if (script.identifier === "AMDLoader") {
                        alreadyLoaded = typeof global.define === "function" && !!global.define.amd;
                    }
                    if (!alreadyLoaded) {
                        scripts.push({ source: getAbsoluteUrl(script.url, hostRootUri) });
                    }
                    else {
                        anyCoreScriptLoaded = true;
                    }
                }
            });
            if (hostPageContext.coreReferences.coreScriptsBundle && !anyCoreScriptLoaded) {
                // If core scripts bundle exists and no core scripts already loaded by extension,
                // we are free to add core bundle. otherwise, load core scripts individually.
                scripts = [{ source: getAbsoluteUrl(hostPageContext.coreReferences.coreScriptsBundle.url, hostRootUri) }];
            }
            if (hostPageContext.coreReferences.extensionCoreReferences) {
                scripts.push({ source: getAbsoluteUrl(hostPageContext.coreReferences.extensionCoreReferences.url, hostRootUri) });
            }
        }
        // Define a new config for extension loader
        var newConfig = {
            baseUrl: extensionContext.baseUri,
            contributionPaths: null,
            paths: {},
            shim: {}
        };
        // See whether any configuration specified initially. If yes, copy them to new config
        if (initOptions.moduleLoaderConfig) {
            if (initOptions.moduleLoaderConfig.baseUrl) {
                newConfig.baseUrl = initOptions.moduleLoaderConfig.baseUrl;
            }
            // Copy paths
            extendLoaderPaths(initOptions.moduleLoaderConfig, newConfig);
            // Copy shim
            extendLoaderShim(initOptions.moduleLoaderConfig, newConfig);
        }
        // Use some of the host config to support VSSF and TFS platform as well as some 3rd party libraries
        if (hostPageContext.moduleLoaderConfig) {
            // Copy host shim
            extendLoaderShim(hostPageContext.moduleLoaderConfig, newConfig);
            // Add contribution paths to new config
            var contributionPaths = hostPageContext.moduleLoaderConfig.contributionPaths;
            if (contributionPaths) {
                for (var p in contributionPaths) {
                    if (contributionPaths.hasOwnProperty(p) && !newConfig.paths[p]) {
                        // Add the contribution path
                        var contributionPathValue = contributionPaths[p].value;
                        if (!contributionPathValue.match("^https?://")) {
                            newConfig.paths[p] = hostRootUri + contributionPathValue;
                        }
                        else {
                            newConfig.paths[p] = contributionPathValue;
                        }
                        // Look for other path mappings that fall under the contribution path (e.g. "bundles")
                        var configPaths = hostPageContext.moduleLoaderConfig.paths;
                        if (configPaths) {
                            var contributionRoot = p + "/";
                            var rootScriptPath = combinePaths(hostRootUri, hostPageContext.moduleLoaderConfig.baseUrl);
                            for (var pathKey in configPaths) {
                                if (startsWith(pathKey, contributionRoot)) {
                                    var pathValue = configPaths[pathKey];
                                    if (!pathValue.match("^https?://")) {
                                        if (pathValue[0] === "/") {
                                            pathValue = combinePaths(hostRootUri, pathValue);
                                        }
                                        else {
                                            pathValue = combinePaths(rootScriptPath, pathValue);
                                        }
                                    }
                                    newConfig.paths[pathKey] = pathValue;
                                }
                            }
                        }
                    }
                }
            }
        }
        // requireJS public api doesn't support reading the current config, so save it off for use by our internal host control.
        window.__vssModuleLoaderConfig = newConfig;
        scripts.push({ content: "require.config(" + JSON.stringify(newConfig) + ");" });
        addScriptElements(scripts, 0, function () {
            loaderConfigured = true;
            triggerReady();
        });
    }
    function startsWith(rootString, startSubstring) {
        if (rootString && rootString.length >= startSubstring.length) {
            return rootString.substr(0, startSubstring.length).localeCompare(startSubstring) === 0;
        }
        return false;
    }
    function combinePaths(path1, path2) {
        var result = path1 || "";
        if (result[result.length - 1] !== "/") {
            result += "/";
        }
        if (path2) {
            if (path2[0] === "/") {
                result += path2.substr(1);
            }
            else {
                result += path2;
            }
        }
        return result;
    }
    function extendLoaderPaths(source, target, pathTranslator) {
        if (source.paths) {
            if (!target.paths) {
                target.paths = {};
            }
            for (var key in source.paths) {
                if (source.paths.hasOwnProperty(key)) {
                    var value = source.paths[key];
                    if (pathTranslator) {
                        value = pathTranslator(key, source.paths[key]);
                    }
                    if (value) {
                        target.paths[key] = value;
                    }
                }
            }
        }
    }
    function extendLoaderShim(source, target) {
        if (source.shim) {
            if (!target.shim) {
                target.shim = {};
            }
            for (var key in source.shim) {
                if (source.shim.hasOwnProperty(key)) {
                    target.shim[key] = source.shim[key];
                }
            }
        }
    }
    function getRootUri(webContext) {
        var hostContext = (webContext.account || webContext.host);
        var rootUri = hostContext.uri;
        var relativeUri = hostContext.relativeUri;
        if (rootUri && relativeUri) {
            // Ensure both relative and root paths end with a trailing slash before trimming the relative path.
            if (rootUri[rootUri.length - 1] !== "/") {
                rootUri += "/";
            }
            if (relativeUri[relativeUri.length - 1] !== "/") {
                relativeUri += "/";
            }
            rootUri = rootUri.substr(0, rootUri.length - relativeUri.length);
        }
        return rootUri;
    }
    function addScriptElements(scripts, index, callback) {
        var _this = this;
        if (index >= scripts.length) {
            callback.call(this);
            return;
        }
        var scriptTag = document.createElement("script");
        scriptTag.type = "text/javascript";
        if (scripts[index].source) {
            var scriptSource = scripts[index].source;
            scriptTag.src = scriptSource;
            scriptTag.addEventListener("load", function () {
                addScriptElements.call(_this, scripts, index + 1, callback);
            });
            scriptTag.addEventListener("error", function (e) {
                notifyLoadFailed("Failed to load script: " + scriptSource);
            });
            safeAppendToDom(scriptTag, "head");
        }
        else if (scripts[index].content) {
            scriptTag.textContent = scripts[index].content;
            safeAppendToDom(scriptTag, "head");
            addScriptElements.call(this, scripts, index + 1, callback);
        }
    }
    function safeAppendToDom(element, section) {
        var parent = document.getElementsByTagName(section)[0];
        if (!parent) {
            parent = document.createElement(section);
            document.appendChild(parent);
        }
        parent.appendChild(element);
    }
    function getAbsoluteUrl(url, baseUrl) {
        var lcUrl = (url || "").toLowerCase();
        if (lcUrl.substr(0, 2) !== "//" && lcUrl.substr(0, 5) !== "http:" && lcUrl.substr(0, 6) !== "https:") {
            url = baseUrl + (lcUrl[0] === "/" ? "" : "/") + url;
        }
        return url;
    }
    function triggerReady() {
        var _this = this;
        isReady = true;
        if (readyCallbacks) {
            var savedReadyCallbacks = readyCallbacks;
            readyCallbacks = null;
            savedReadyCallbacks.forEach(function (callback) {
                callback.call(_this);
            });
        }
    }
})(VSS || (VSS = {}));