Add assets for WML to HTML transformation using XSLT.

Change-Id: I8777f2409217e92b19a33d1afc32ce9ca0b7608e
diff --git a/assets/wml/swe_wml.js b/assets/wml/swe_wml.js
new file mode 100755
index 0000000..92fa6ee
--- /dev/null
+++ b/assets/wml/swe_wml.js
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+var cardElements;
+var currentActiveCard = null;
+var defaultActiveCard = null;
+// Forward navigation >= 0, backward navigation == -1, unknown navigation == -2
+var currentNavigationType = -2;
+var blankRegEx = /^\s*$/;
+
+var WMLBrowser = {
+    name: "WMLBrowserContext",
+    variables: new Object(),
+    setVar: function (key, value) {
+        this.variables[key] = value;
+    },
+    getVar: function (key) {
+        var value = this.variables[key];
+        if (value == null)
+            value = '';
+        return value;
+    },
+    newContext: function () {
+        this.variables = new Object();
+    },
+}
+
+function isBlank(str) {
+    return (!str || blankRegEx.test(str));
+}
+
+function isURLHash(str) {
+    return (!isBlank(str) && str.length > 1 && str.indexOf("#") == 0);
+}
+
+function getIdFromURLHash(hash) {
+    return hash.substr(1);
+}
+
+window.onhashchange = function()
+{
+    console.log("window.onhashchange currentNavigationType = " + currentNavigationType);
+    var newHash = window.location.hash;
+    if (!isURLHash(newHash)) {
+        currentActiveCard.style.display = "none";
+        defaultActiveCard.style.display = "initial";
+        currentActiveCard = defaultActiveCard;
+    } else {
+        showCardById(newHash.substr(1));
+    }
+    updateWMLVariables();
+    scheduleTimerTaskIfNeeded(currentActiveCard);
+    handleOnNavigationIntrinsicEvent();
+};
+
+window.onload = function()
+{
+    // Consider all the load/reload on this deck as forward navigation.
+    currentNavigationType = 1;
+    var cardHash = window.location.hash;
+    console.log("window.onload card = " + cardHash);
+    cardElements = document.getElementsByClassName('wml_card');
+    defaultActiveCard = cardElements[0];
+    // All the cards are hidden by default.
+    // Show the active card onload.
+    if (isURLHash(cardHash)) {
+        var cardId = cardHash.substr(1);
+        for(var i=0, l=cardElements.length; i<l; i++) {
+            var card = cardElements[i];
+            if (card.getAttribute("id") == cardId) {
+                currentActiveCard = card;
+                currentActiveCard.style.display = "initial";
+                break;
+            }
+        }
+    }
+    if (!currentActiveCard) {
+        currentActiveCard = defaultActiveCard;
+        currentActiveCard.style.display = "initial";
+    }
+    replaceVariablesInTextContentBySpan();
+    fixTextContentInAnchorTasks();
+    initializeSelectElements();
+    scheduleTimerTaskIfNeeded(currentActiveCard);
+    handleOnNavigationIntrinsicEvent();
+};
+
+function showCardById(cardId, onload)
+{
+    if (currentActiveCard && currentActiveCard.getAttribute("id") == cardId) {
+        // We have nothing to do.
+        return false;
+    }
+    for(var i=0, l=cardElements.length; i<l; i++) {
+        var card = cardElements[i];
+        if (card.getAttribute("id") == cardId) {
+            currentActiveCard.style.display = "none";
+            currentActiveCard = card;
+            currentActiveCard.style.display = "initial";
+            return true;
+        }
+    }
+    return false;
+}
+
+function handleOnNavigationIntrinsicEvent() {
+    var navigationType = currentNavigationType;
+    currentNavigationType = -2;
+
+    if (navigationType >= 0) {
+        executeOnenterforwardTask();
+    } else if (navigationType == -1) {
+        executeOnenterbackwardTask();
+    } else {
+        console.log("WARNING: Cannot determine the navigation event type on this card!");
+    }
+}
+
+////////////////////////// WML Variables ////////////////////////////////////////
+function replaceVariablesInTextContentBySpan()
+{
+    var pattern1 = /(\$\(([_a-z]{1}[_a-z0-9]*)([:]{1}((([e]{1})(scape)?)|(([n]{1})(oesc)?)|(([u]{1})(nesc)?)))?\))/gi;
+    var pattern2 = /(\$([_a-zA-z]{1}[_a-zA-Z0-9]*))/g;
+    var whitespace = /^\s*$/g;
+    var replacer = function () {
+        var name = arguments[2];
+        var escape = "";
+        if (arguments[12])
+            escape = arguments[12];
+        else if (arguments[9])
+            escape = arguments[9];
+        else if (arguments[6])
+            escape = arguments[6];
+        var wml_variable_span = "\<span\ data\-wml\_name\=\"" + name + "\"\ class\=\"wml_variable\" data\-wml\_escape\=\"" + escape + "\"\>\<\/span\>";
+        console.log("replaceVariablesInTextContentBySpan() Found variable " + arguments[0]);
+        return wml_variable_span;
+    };
+
+    var textNodes = [];
+    var treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);
+    while(treeWalker.nextNode()) {
+        textNodes.push(treeWalker.currentNode);
+    }
+
+    for(var i=0, l=textNodes.length; i<l; i++) {
+        var curNode = textNodes[i];
+        var text = curNode.nodeValue;
+        if (!(whitespace.test(text))) {
+            var replaced = false;
+            if (pattern1.test(text)) {
+                text = text.replace(pattern1, replacer);
+                replaced = true;
+            }
+            if (pattern2.test(text)) {
+                text = text.replace(pattern2, replacer);
+                replaced = true;
+            }
+            if (replaced) {
+                var mySpan = document.createElement("span");
+                mySpan.innerHTML = text;
+                curNode.parentNode.replaceChild(mySpan, curNode);
+            }
+        }
+    }
+}
+
+////////////////////////// WML Anchor Tasks ////////////////////////////////////////
+// TODO: Optimize this brutal code
+function fixTextContentInAnchorTasks()
+{
+    var anchorTasks = document.getElementsByClassName('wml_anchor_task');
+    for(var i=0, l=anchorTasks.length; i<l; i++) {
+        var container;
+        var taskParent;
+        var task = anchorTasks[i];
+        if (task.dataset.wml_task_type == 'wml_task_go') {
+            taskParent = task.parentNode;
+            container = taskParent.parentNode;
+        } else {
+            taskParent = task;
+            container = task.parentNode;
+        }
+        container.removeChild(taskParent);
+        var anchorText = container.innerHTML;
+        while (container.firstChild) {
+            container.removeChild(container.firstChild);
+        }
+        container.appendChild(taskParent);
+        console.log("fixTextContentInAnchorTasks: anchorText = " + anchorText);
+        task.innerHTML = anchorText + task.innerHTML;
+    }
+}
+
+////////////////////////// WML Timer ////////////////////////////////////////
+var activeScheduledTimer = null;
+function scheduleTimerTaskIfNeeded(card)
+{
+    var timerElements = card.getElementsByClassName('wml_timer');
+    if (timerElements.length > 0) {
+        var timeout = timerElements[0].dataset.wml_value;
+        activeScheduledTimer = window.setTimeout(executeTimerTask, timeout * 100);
+        console.log("Starting WML timer timeout = " + timeout);
+    }
+}
+
+function cancelScheduledTimerTask()
+{
+    if (activeScheduledTimer != null) {
+        window.clearTimeout(clearTimeout);
+        activeScheduledTimer = null;
+    }
+}
+
+////////////////////////////////// <select> & <option> ////////////////////////
+var multiSelectSeparator = ";";
+var selectElementsMap = new Object();
+var WMLSelectElements = {
+    name: "WMLSelectElementsList",
+    options: new Object(),
+    setSelectedOptions: function (key, options) {
+        this.options[key] = options;
+    },
+    getSelectedOptions: function (key) {
+        return this.options[key];
+    },
+};
+
+function initializeSelectElements()
+{
+    var selectElements = document.getElementsByClassName('wml_select');
+    for(var i=0, l=selectElements.length; i<l; i++) {
+        var select = selectElements[i];
+        var iname = select.dataset.wml_iname;
+        // Preselect options based on 'ivalue'
+        var ivalue = select.dataset.wml_ivalue;
+        if (!isBlank(ivalue)) {
+            var options = select.options;
+            var optionsCount = options.length;
+            var ivalueList = ivalue.split(multiSelectSeparator);
+            for(var j=0, ll=ivalueList.length; j<ll; j++) {
+                var index = parseInt(ivalueList[j], 10);
+                if (index > 0 && index <= optionsCount) {
+                    options[index-1].selected = 'selected';
+                }
+            }
+        }
+
+        // Keep a copy of selected options to process 'onpick' events
+        var options = select.selectedOptions;
+        var optionsCount = options.length;
+        var optionsList = [];
+        for(var j=0; j<optionsCount; j++) {
+            optionsList.push(options[j]);
+        }
+        var uniqueId = "wml_select_" + i;
+        select.dataset.wml_unique_id = uniqueId;
+        WMLSelectElements.setSelectedOptions(uniqueId, optionsList);
+    }
+}
+
+function handleSelectOnchangeEvent(select)
+{
+    refreshVariableInSelectElement(select);
+    var options = select.selectedOptions;
+    var optionsCount = options.length;
+    var uniqueId = select.dataset.wml_unique_id;
+    var oldSelectedOptions = WMLSelectElements.getSelectedOptions(uniqueId);
+
+    // Update the copy of selected options before we do anything
+    var optionsList = [];
+    for(var j=0; j<optionsCount; j++) {
+        optionsList.push(options[j]);
+    }
+    WMLSelectElements.setSelectedOptions(uniqueId, optionsList);
+
+    // process 'onpick' events if needed
+    for(var i=0; i<optionsCount; i++) {
+        var option = options[i];
+        var onpick = option.dataset.wml_onpick;
+        if (!isBlank(onpick)) {
+            var selectedNewly = true;
+            for(var j=0; j<oldSelectedOptions.length; j++) {
+                if (option.isSameNode(oldSelectedOptions[j])) {
+                    selectedNewly = false;
+                    break;
+                }
+            }
+            if (selectedNewly) {
+                internal_executeOnpickTask(option);
+                return;
+            }
+        }
+    }
+}
+
+function handleAOnClick(event, node)
+{
+    var href = node.href;
+    var search = node.search;
+    console.log("handleAOnClick <a> href = " + href);
+    refreshVariablesInControlElements();
+    if (!isBlank(search)) {
+        node.search = substituteVariablesInURL(search);
+    } else {
+        node.href = href.split("?")[0];
+    }
+    event.preventDefault();
+    navigateToURL(node.href);
+    return false;
+}
+
+//////////////////////// Variables /////////////////////////
+function substituteVariablesInURL(url)
+{
+    var pattern = /(((\%24)|(\$))\(([_a-z]{1}[_a-z0-9]*)([:]{1}((([e]{1})(scape)?)|(([n]{1})(oesc)?)|(([u]{1})(nesc)?)))?\))/gi;
+    var replacer = function () {
+        var name = arguments[2];
+        // TODO: Do the URL escaping here
+        console.log("substituteVariablesInURL() found variable : " + arguments[0]);
+        return WMLBrowser.getVar(name);
+    };
+    return url.replace(pattern, replacer);
+}
+
+function substituteVariablesInPostfield(value)
+{
+    var pattern = /(\$\(([_a-z]{1}[_a-z0-9]*)([:]{1}((([e]{1})(scape)?)|(([n]{1})(oesc)?)|(([u]{1})(nesc)?)))?\))/gi;
+    var replacer = function () {
+        var name = arguments[2];
+        // TODO: Do the URL escaping here
+        console.log("substituteVariablesInPostfield() found variable : " + arguments[0]);
+        return WMLBrowser.getVar(name);
+    };
+    return value.replace(pattern, replacer);
+}
+
+function refreshVariableInSelectElement(select)
+{
+    var options = select.selectedOptions;
+    var value = "";
+    var ivalue = "";
+    var optionsCount = options.length;
+    if (optionsCount > 0) {
+        var op = options[0];
+        value = op.value;
+        ivalue = (op.index + 1);
+        for(var i=1; i<optionsCount; i++) {
+            op = options[i];
+            value += multiSelectSeparator + op.value;
+            ivalue += multiSelectSeparator + (op.index + 1);
+        }
+    }
+    var name = select.name;
+    if (!isBlank(name)) {
+        WMLBrowser.setVar(name, value);
+        console.log("refreshVariableInSelectElement name = " + name + ", value = " + value);
+    }
+    var iname = select.dataset.wml_iname;
+    if (!isBlank(iname)) {
+        if (isBlank(ivalue)) {
+            // An index of zero indicates that no option is selected.
+            ivalue = "0";
+        }
+        WMLBrowser.setVar(iname, ivalue);
+        console.log("refreshVariableInSelectElement iname = " + iname + ", ivalue = " + ivalue);
+    }
+}
+
+function refreshVariablesInControlElements()
+{
+    var inputElements = currentActiveCard.getElementsByClassName('wml_input');
+    for(var i=0, l=inputElements.length; i<l; i++) {
+        var input = inputElements[i];
+        WMLBrowser.setVar(input.name, input.value);
+        console.log("refreshVariablesInControlElements <input> name = " + input.name + ", value = " + input.value);
+    }
+
+    var selectElements = currentActiveCard.getElementsByClassName('wml_select');
+    for(var i=0, l=selectElements.length; i<l; i++) {
+        var select = selectElements[i];
+        refreshVariableInSelectElement(selectElements[i]);
+    }
+}
+
+function updateVariableInPostfields(form)
+{
+    var postfields = currentActiveCard.getElementsByClassName('wml_postfield');
+    for(var i=0, l=postfields.length; i<l; i++) {
+        var input = postfields[i];
+        input.value = substituteVariablesInPostfield(input.value);
+        console.log("updateVariableInPostfields <postfield> name = " + input.name + ", value = " + input.value);
+    }
+}
+
+/////////////// Navigation ////////////////////
+function navigateToURL(url)
+{
+    console.log("navigateToURL: url = " + url);
+    cancelScheduledTimerTask();
+    currentNavigationType = 1;
+    window.location = url;
+}
+
+function navigateToCard(card)
+{
+    console.log("navigateToCard: card = " + card);
+    cancelScheduledTimerTask();
+    currentNavigationType = 1;
+    window.location.hash = card;
+}
+
+function navigateBack()
+{
+    console.log("navigateBack: currentState = ");
+    console.log(window.history.state);
+    cancelScheduledTimerTask();
+    currentNavigationType = -1;
+    window.history.back();
+}
+
+////////////// WML Tasks //////////////////////
+//<refresh>
+
+function updateWMLVariables()
+{
+    var wmlVariables = currentActiveCard.getElementsByClassName('wml_variable');
+    for(var i=0, l=wmlVariables.length; i<l; i++) {
+        var varElement = wmlVariables[i];
+        // Handle the variable escaping option here
+        var value = WMLBrowser.getVar(varElement.dataset.wml_name);
+        // TODO: Handle nested variable substitution on 'value'
+        var conversion = varElement.dataset.wml_escape;
+        if (!isBlank(value) && !isBlank(conversion)) {
+            if (conversion == "e" || conversion == "E") {
+                value = encodeURIComponent(value);
+            } else if (conversion == "u" || conversion == "U") {
+                value = decodeURIComponent(value);
+            }
+        }
+        varElement.innerHTML = value;
+    }
+}
+
+function internal_executeRefreshTask(root)
+{
+    console.log("internal_executeRefreshTask");
+    var setvarElements = root.getElementsByClassName('wml_setvar');
+    for(var i=0, l=setvarElements.length; i<l; i++) {
+        var setvar = setvarElements[i];
+        WMLBrowser.setVar(setvar.dataset.wml_name, setvar.dataset.wml_value);
+        console.log("<setvar> " + setvar.dataset.wml_name + " = " + setvar.dataset.wml_value);
+    }
+    updateWMLVariables();
+}
+
+function executeRefreshTask(event, node)
+{
+    event.preventDefault();
+    internal_executeRefreshTask(node.parentNode);
+    return false;
+}
+
+// <prev>
+function internal_executePrevTask(node)
+{
+    console.log("internal_executePrevTask");
+    internal_executeRefreshTask(node.parentNode);
+    navigateBack();
+}
+
+function executePrevTask(event, node)
+{
+    event.preventDefault();
+    internal_executePrevTask(node);
+    return false;
+}
+// <option onpick="...">
+function internal_executeOnpickTask(option)
+{
+    var href = option.dataset.wml_onpick;
+    console.log("internal_executeOnpickTask href = " + href);
+    //internal_executeRefreshTask(option.parentNode);
+    //refreshVariablesInControlElements();
+    if (isURLHash(href)) {
+        navigateToCard(href);
+        return true;
+    }
+    navigateToURL(href);
+    return true;
+}
+
+//<go>
+function internal_executeGoTask(form)
+{
+    var href = form.dataset.wml_href;
+    console.log("internal_executeGoTask href = " + href);
+    internal_executeRefreshTask(form.parentNode);
+    refreshVariablesInControlElements();
+    if (isURLHash(href)) {
+        navigateToCard(href);
+        return true;
+    }
+    // Substitute variables in <postfield> 'value' attributes before form submission.
+    updateVariableInPostfields();
+    form.submit();
+    return false;
+}
+
+function executeGoTask(event, form)
+{
+    event.preventDefault();
+    internal_executeGoTask(form);
+    return false;
+}
+
+//<onevent>
+function executeTimerTask()
+{
+    console.log("executeTimerTask()");
+    activeScheduledTimer = null;
+    // Handle <card ontimer="..."> event first
+    var ontimer = currentActiveCard.dataset.wml_ontimer;
+    if (!isBlank(ontimer))
+    {
+        navigateToURL(ontimer);
+        return;
+    }
+    // Handle <onevent type="timer">... here
+    var tasks = currentActiveCard.getElementsByClassName('wml_onevent_ontimer');
+    if (tasks.length > 0) {
+        var onevent = tasks[0];
+        if (onevent.dataset.wml_task_type == 'wml_task_go')
+            return internal_executeGoTask(onevent);
+        else if (onevent.dataset.wml_task_type == 'wml_task_refresh')
+            return internal_executeRefreshTask(onevent);
+        else if (onevent.dataset.wml_task_type == 'wml_task_prev')
+            return internal_executePrevTask(onevent);
+        else if (onevent.dataset.wml_task_type == 'wml_task_noop')
+            return;
+    }
+    // Handle <template timer="..."> event at the end
+    var templates = currentActiveCard.getElementsByClassName('wml_template');
+    if (templates.length > 0) {
+        var ontimer = templates[0].dataset.wml_ontimer;
+        if (!isBlank(ontimer))
+        {
+            navigateToURL(ontimer);
+            return;
+        }
+    }
+}
+
+function executeOnenterforwardTask()
+{
+    console.log("executeOnenterforwardTask()");
+    // Handle <card onenterforward="..."> event first
+    var onef = currentActiveCard.dataset.wml_onenterforward;
+    if (!isBlank(onef))
+    {
+        navigateToURL(onef);
+        return;
+    }
+    // Handle <onevent type="onenterforward">... here
+    var tasks = currentActiveCard.getElementsByClassName('wml_onevent_onenterforward');
+    if (tasks.length > 0) {
+        var onevent = tasks[0];
+        if (onevent.dataset.wml_task_type == 'wml_task_go')
+            return internal_executeGoTask(onevent);
+        else if (onevent.dataset.wml_task_type == 'wml_task_refresh')
+            return internal_executeRefreshTask(onevent);
+        else if (onevent.dataset.wml_task_type == 'wml_task_prev')
+            return internal_executePrevTask(onevent);
+        else if (onevent.dataset.wml_task_type == 'wml_task_noop')
+            return;
+    }
+    // Handle <template onenterforward="..."> event at the end
+    var templates = currentActiveCard.getElementsByClassName('wml_template');
+    if (templates.length > 0) {
+        var onef = templates[0].dataset.wml_onenterforward;
+        if (!isBlank(onef))
+        {
+            navigateToURL(onef);
+            return;
+        }
+    }
+}
+
+function executeOnenterbackwardTask()
+{
+    console.log("executeOnenterbackwardTask()");
+    // Handle <card onenterforward="..."> event first
+    var oneb = currentActiveCard.dataset.wml_onenterbackward;
+    if (!isBlank(oneb))
+    {
+        navigateToURL(oneb);
+        return;
+    }
+    // Handle <onevent type="onenterbackward">... here
+    var tasks = currentActiveCard.getElementsByClassName('wml_onevent_onenterbackward');
+    if (tasks.length > 0) {
+        var onevent = tasks[0];
+        if (onevent.dataset.wml_task_type == 'wml_task_go')
+            return internal_executeGoTask(onevent);
+        else if (onevent.dataset.wml_task_type == 'wml_task_refresh')
+            return internal_executeRefreshTask(onevent);
+        else if (onevent.dataset.wml_task_type == 'wml_task_prev')
+            return internal_executePrevTask(onevent);
+        else if (onevent.dataset.wml_task_type == 'wml_task_noop')
+            return;
+    }
+    // Handle <template onenterbackward="..."> event at the end
+    var templates = currentActiveCard.getElementsByClassName('wml_template');
+    if (templates.length > 0) {
+        var oneb = templates[0].dataset.wml_onenterbackward;
+        if (!isBlank(oneb))
+        {
+            navigateToURL(oneb);
+            return;
+        }
+    }
+}
+
+