<!DOCTYPE html> 
<html>
  <head> 
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <link rel="shortcut icon" type="image/x-icon" href="http://sww.sas.com/favicon.ico" />
  <title> SAS Studio Repository </title>
   </head>

<script>
/**
 * This allows a SAS Studio Repository to be loaded from another domain than SAS Studio.
 * An one time id is generated by SAS Studio and then this proxy is called with the that id.
 * If the content loads the content is passed back to the SAS Studio domain using the id and
 * the window.postMessage() protocol. This ensure cross domain communication without having
 * to modify the configuration of the hosting web server or container (this CORS).
 */
/* Resolve all URL parameters passed to the proxy. */
function getUrlVars() {
    var vars = {};
    var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,
            function (m, key, value) {
            vars[key] = value;
        });
    return vars;
}
/* Resolve the current URL for any relative URLs contained in the repository. */
function getBaseUrl(url) {
    var querypos = url.indexOf("?");
    if (querypos > 0)
        url = url.substring(0, querypos);
    var parts = url.split("/");
    var last = parts[parts.length - 1];
    var pos = url.indexOf(last);
    return url.substring(0, pos - 1);
}
/* Global parameters for the proxy. */
var objects = new Object();
var version = 7;
var taskCategories = null;
var snippetCategories = null;
var repositoryDocument = null;
var baseURL = getBaseUrl(window.location.href);
var parms = getUrlVars();
RegExp.escape = function (text) {
    return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}
String.prototype.replaceAll = function (search, replace) {
    return this.replace(new RegExp(RegExp.escape(search), 'g'), replace);
};
/* In addition to be the proxy the loader validates the XML integrity of the repository. */
/* Invalid XML is flagged in the loader page, but is not passed back to SAS Studio.      */
function buildRepository(xmlhttp) {
    try {
        var s = new XMLSerializer();
        parser = new DOMParser();
        addNote("Starting validation of " + xmlhttp.responseURL);
        repositoryDocument = parseXml(xmlhttp.responseText);
        var categories = [];
        /* Check for a top-level repository icon. If the URI is relative resolve it. */
        try {
            var repositoryIcon = repositoryDocument.getElementsByTagName("Icon")[0];
            var iconUri = repositoryIcon.getAttribute("uri");
            if (iconUri) {
                if (iconUri.indexOf(".") == 0)
                    iconUri = baseURL + iconUri.substring(1);
                //console.log(iconUri);
                repositoryIcon.setAttribute("uri", iconUri);
                var img = "<img width=16 height=16 src=\'" + iconUri + "'/>";
                addNote("Repository icon is " + img);
            }
        } catch (e) {
            //          console.log("No repository icon element found.");
        }
        var documentation = null;
        repositoryTags = repositoryDocument.getElementsByTagName("Repository");
        if (repositoryTags.length == 1) {
            documentation = new Array();
            repositoryTag = repositoryTags[0];
            for (i = 0; i < repositoryTag.childNodes.length; i++) {
                if (repositoryTag.childNodes[i].nodeName == "Documentation")
                    documentation.push(repositoryTag.childNodes[i]);
            }
            //          console.log(documentation);
        }
        /**
         * See if the repository itself has documentation.
         */
        if (documentation && documentation.length > 1) {
            for (i = 0; i < documentation.length; i++) {
                docUri = documentation[i].getAttribute("uri");
                var docLabel = documentation[i].getAttribute("label");
                if (!docLabel)
                    docLabel = docUri;
                var docURL = docUri;
                if (docURL.indexOf(".") == 0)
                    docURL = baseURL + docUri.substring(1);
                var docLink = "<a target='_blank' href='" + docURL + "'>" + docLabel + "</a>";
                addNote("A repository documentation link is located at " + docLink);
                documentation[i].setAttribute("uri", docURL);
            }
        }
        /* Validate all task category definitions. */
        var tasks = repositoryDocument.getElementsByTagName("Tasks");
        if (tasks.length == 1)
            taskCategories = tasks[0].getElementsByTagName("Category");
        if (taskCategories) {
            var tasks = window.document.getElementById("tasks");
            var html = "<h4>Task Categories:</h4><ul>";
            for (i = 0; i < taskCategories.length; i++) {
                html += validateCategory("Task", taskCategories[i]);
            }
            html += "</ul>";
            tasks.innerHTML = html;
        }
        /* Validate all snippet category definitions. */
        var snippets = repositoryDocument.getElementsByTagName("Snippets");
        if (snippets.length == 1)
            snippetCategories = snippets[0].getElementsByTagName("Category");
        if (snippetCategories) {
            var snippets = window.document.getElementById("snippets");
            var html = "<h4>Snippet Categories:</h4><ul>";
            for (i = 0; i < snippetCategories.length; i++) {
                html += validateCategory("Snippet", snippetCategories[i]);
            }
            html += "</ul>";
            snippets.innerHTML = html;
        }
        addNote("Task repository validation completed.");
        /* Serialize the XML to make sure resolved URI are updated. */
        processedXML = s.serializeToString(repositoryDocument);
        objects["repository.xml"] = processedXML;
        objects["event"] = "loaded";
        objects["version"] = version;
        objects["status"] = 200;
        /* Pass payload back to SAS Studio. */
        if (parms["target"]) {
            objects.id = parms["id"];
            objects.type = "proxy";
            window.parent.postMessage(objects, parms["target"]);
        }
    } catch (e) {
        addError("Fatal repository condition encountered.");
        addError(e.toString());
    }
}
/**
 * Do all the tedious category validation in one place. For performance reasons we don't validate
 * the category content itself. We leave to be done on the SAS Studio side.
 */
function validateCategory(type, node) {
    var s = new XMLSerializer();
    var label = node.getAttribute("label");
    var location = node.getElementsByTagName("Location");
    var uri = location[0].getAttribute("uri");
    var resolvedURL = uri;
    var validateURL = uri;
    var iconUri = null;
    var catLabel = "";
    if (resolvedURL.indexOf(".") == 0)
        resolvedURL = baseURL + uri.substring(1)
            location[0].setAttribute("uri", resolvedURL);
    var link = "<a target='_blank' href='" + resolvedURL + "'>" + resolvedURL + "</a>";
    var html = "<li>" + label + " - " + link + "</li>";
    addNote("Validating '" + label + "' " + type + " category.");
    catLabel = label;
    /**
     * See if the category itself has documentation.
     */
    var documentation = node.getElementsByTagName("Documentation");
    if (documentation && documentation.length >= 1) {
        for (var i = 0; i < documentation.length; i++) {
            docUri = documentation[i].getAttribute("uri");
            var docLabel = documentation[i].getAttribute("label");
            if (!docLabel)
                docLabel = docUri;
            var docURL = docUri;
            if (docURL.indexOf(".") == 0)
                docURL = baseURL + docUri.substring(1);
            var docLink = "<a target='_blank' href='" + docURL + "'>" + docLabel + "</a>";
            addNote("A category documentation link is located at " + docLink);
            documentation[i].setAttribute("uri", docURL);
        }
    }
    var xhr = new XMLHttpRequest();
    var taskBase = getBaseUrl(resolvedURL);
    xhr.onreadystatechange = function () {
        try {
            if (xhr.readyState == 4 && xhr.status == 200) {
                try {
                    doc = parseXml(xhr.responseText);
                } catch (e) {
                    addError(resolvedURL + " failed to parse as XML.");
                    addError(e);
                    addWarning("Category '" + label + "' will be ignored.");
                    return html;
                }
                var tags = doc.getElementsByTagName(type);
                addNote("'" + label + "' " + type + " category contains " + tags.length + " items.");
                for (j = 0; j < tags.length; j++) {
                    var tag = tags[j];
                    if (tag.getElementsByTagName("Name").length == 0) {
                        addError("Required tag \"<b>Name</b>\" missing from <a href='" + validateURL + "'>" + validateURL + "</a>");
                        addError(type + " after \"<b>" + label + "</b>\" is invalid.");
                        addWarning("Entire category \"<b>" + catLabel + "</b>\" will be discarded.");
                        return html;
                    }
                    if (tag.getElementsByTagName("Template").length == 0) {
                        addError("Required tag \"<b>Template</b>\" missing from <a href='" + validateURL + "'>" + validateURL + "</a>");
                        addError(type + " after \"<b>" + label + "</b>\" is invalid.");
                        addWarning("Entire category \"<b>" + catLabel + "</b>\" will be discarded.");
                        return html;
                    }
                    nameTag = tag.getElementsByTagName("Name")[0];
                    templateTag = tag.getElementsByTagName("Template")[0];
                    label = nameTag.getAttribute("label");
                    templateUri = templateTag.getAttribute("uri");
                    iconUri = null;
                    var icon = tag.getElementsByTagName("Icon");
                    if (icon && icon.length == 1) {
                        iconTag = tag.getElementsByTagName("Icon")[0];
                        iconUri = iconTag.getAttribute("uri");
                        if (iconUri) {
                            if (iconUri.indexOf(".") == 0)
                                iconUri = taskBase + iconUri.substring(1);
                            iconTag.setAttribute("uri", iconUri);
                        }
                    }
                    var documentation = tag.getElementsByTagName("Documentation");
                    if (documentation && documentation.length >= 1) {
                        for (var i = 0; i < documentation.length; i++) {
                            docUri = documentation[i].getAttribute("uri");
                            var docLabel = documentation[i].getAttribute("label");
                            if (!docLabel)
                                docLabel = docUri;
                            var docURL = docUri;
                            if (docURL.indexOf(".") == 0)
                                docURL = taskBase + docUri.substring(1);
                            var docLink = "<a target='_blank' href='" + docURL + "'>" + docLabel + "</a>";
                            addNote("A '" + label + "' documentation link is located at " + docLink);
                            documentation[i].setAttribute("uri", docURL);
                        }
                    }
                    if (templateUri.indexOf(".") == 0)
                        templateUri = taskBase + templateUri.substring(1);
                    templateTag.setAttribute("uri", templateUri);
                    var note = "'" + label + "' contents is located at <a target='_blank' + href='" + templateUri + "'>" + templateUri + "</a>";
                    addNote(note);
                    if (iconUri) {
                        var img = "<img width=16 height=16 src=\'" + iconUri + "'/>";
                        addNote(type + " icon for " + label + " is " + img);
                    }
                }
                processedXML = s.serializeToString(doc);
                objects[resolvedURL] = processedXML;
            } else if (xhr.readyState == 4 && xhr.status == 404) {
                addWarning(resolvedURL + " not found.");
                xhr.abort();
            }
        } catch (e) {
            console.log(e);
        }
    }
    validateURL = resolvedURL;
    xhr.open("GET", resolvedURL, false);
    try {
        xhr.send();
    } catch (e) {
        addWarning(e);
    }
    return html;
}
/**
 * Format a validation note.
 */
function addNote(note) {
    message = window.document.getElementById("message");
    var html = "<div style='color:blue'>NOTE: " + note + "</div>";
    message.innerHTML += html;
}
/**
 * Format a validation warning.
 */
function addWarning(warning) {
    message = window.document.getElementById("message");
    var html = "<div style='color:green'>WARNING: " + warning + "</div>";
    message.innerHTML += html;
}
/*
 * Format a validation error.
 */
function addError(error) {
    message = window.document.getElementById("message");
    var html = "<div style='color:red'>ERROR: " + error + "</div>";
    message.innerHTML += html;
}
/*
 * Utility function for parsing some XML.
 */
function parseXml(xmlString) {
    var parser = new DOMParser();
    var dom = parser.parseFromString(xmlString, 'text/xml');
    errors = dom.getElementsByTagName("parsererror");
    if (errors.length > 0)
        throw new Error(errors[0].innerHTML);
    return dom;
}
/*
 * The proxy sends and receives. The receive sends back an "echo".
 * Receive sends back and "echo".
 */
function receiveData(event) {
    event.data.type = "echo";
    event.data.domain = window.domain;
    event.source.postMessage(event.data, event.origin);
}
/*
 * Proxy main equivalent.
 */
function onLoad() {
    try {
        if (window.addEventListener)
            window.addEventListener("message", receiveData, false);
        else
            if (window.attachEvent)
                window.attachEvent("onmessage", receiveData);
        var root = false;
        var resourceRoot = baseURL;
        var div = window.document.getElementById("url");
        div.innerHTML = "<li>" + baseURL + "</li>";
        /**
         * target is the generated ID for proxy loading - SAS Studio generates this.
         */
        if (parms["target"]) {
            /**
             * Send back an event that the proxy was loaded and started processing.
             */
            var object = new Object();
            object.event = "onloadstart";
            object.status = 200;
            object.type = "proxy";
            object.version = version;
            object.id = parms["id"];
            //console.log(object);
            window.parent.postMessage(object, parms["target"]);
            if (parms["noload"]) return;
        }
        /**
         * If no uri is passed to the proxy, then the repository itself is loaded.
         * If a uri is passed we are loading repository content.
         */
        xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function () {
            /* Successful load. */
            try {
                if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                    if (root) {
                        buildRepository(xmlhttp);
                    } else {
                        var object = new Object();
                        object.event = "loaded";
                        object.status = 200;
                        object.type = "proxy";
						object.version = version;
                        object.id = parms["id"];
                        //console.log(xmlhttp.responseText);
                        //console.log(object);
                        //console.log(baseURL);
                        var content = xmlhttp.responseText;
                        try {
                            content = content.replaceAll("{baseURL}", resourceRoot);
                        } catch (e) {
                            console.log("Error substituting {baseURL}.");
                        }
                        object.content = content;
                        window.parent.postMessage(object, parms["target"]);
                    }
                }
                /* Not so successful. */
                else if (xmlhttp.readyState == 4 && xmlhttp.status == 404) {
                    //console.log( xmlhttp.responseURL + " (" + xmlhttp.statusText + ")" );
                    var object = new Object();
                    object.event = "error";
					object.version = version;
                    object.status = xmlhttp.status;
                    object.message = xmlhttp.statusText;
                    object.url = xmlhttp.responseURL;
                    object.id = parms["id"];
                    object.type = "proxy";
                    console.log(object);
                    xmlhttp.abort();
                    window.parent.postMessage(object, parms["target"]);
                }
            } catch (e) {
                console.log(e);
            }
        }
        /* No URI - load repository itself. */
        /* A URI specified - load content repository. */
        uri = parms["uri"];
        if (!uri) {
            root = true;
            uri = "./repository.xml";
        } else {
            resourceRoot = getBaseUrl(uri);
            if (parms["target"])
                uri += "?id=" + parms["id"];
            //console.log(uri);
        }
        xmlhttp.open("GET", uri);
        xmlhttp.send();
    } catch (e) {
        console.log(e);
    }
}
 
</script>
   <body onload="try { onLoad(); } catch (e) { console.log(e); }">
   <h2>SAS Studio 3.5 Repository</h2>
   <p>
   Proxy loader and repository validation page.
   <p>
   <h4>Access URL:</h4>
   <p>
   <ul>
   <div id="url"></div>
   </ul>
   <p>
   <div id="tasks"></div>
   <p>
   <div id="snippets"></div>
   <p>
   <pre>
   <div id="message"></div>
   </pre>
   </body>
  </html>