blob: 92fa6ee46eed47ac0a0e3857a583ad3fe5802eef [file] [log] [blame]
Kulanthaivel Palanichamy8ccd6722014-02-19 15:48:02 -08001/*
2 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30var cardElements;
31var currentActiveCard = null;
32var defaultActiveCard = null;
33// Forward navigation >= 0, backward navigation == -1, unknown navigation == -2
34var currentNavigationType = -2;
35var blankRegEx = /^\s*$/;
36
37var WMLBrowser = {
38 name: "WMLBrowserContext",
39 variables: new Object(),
40 setVar: function (key, value) {
41 this.variables[key] = value;
42 },
43 getVar: function (key) {
44 var value = this.variables[key];
45 if (value == null)
46 value = '';
47 return value;
48 },
49 newContext: function () {
50 this.variables = new Object();
51 },
52}
53
54function isBlank(str) {
55 return (!str || blankRegEx.test(str));
56}
57
58function isURLHash(str) {
59 return (!isBlank(str) && str.length > 1 && str.indexOf("#") == 0);
60}
61
62function getIdFromURLHash(hash) {
63 return hash.substr(1);
64}
65
66window.onhashchange = function()
67{
68 console.log("window.onhashchange currentNavigationType = " + currentNavigationType);
69 var newHash = window.location.hash;
70 if (!isURLHash(newHash)) {
71 currentActiveCard.style.display = "none";
72 defaultActiveCard.style.display = "initial";
73 currentActiveCard = defaultActiveCard;
74 } else {
75 showCardById(newHash.substr(1));
76 }
77 updateWMLVariables();
78 scheduleTimerTaskIfNeeded(currentActiveCard);
79 handleOnNavigationIntrinsicEvent();
80};
81
82window.onload = function()
83{
84 // Consider all the load/reload on this deck as forward navigation.
85 currentNavigationType = 1;
86 var cardHash = window.location.hash;
87 console.log("window.onload card = " + cardHash);
88 cardElements = document.getElementsByClassName('wml_card');
89 defaultActiveCard = cardElements[0];
90 // All the cards are hidden by default.
91 // Show the active card onload.
92 if (isURLHash(cardHash)) {
93 var cardId = cardHash.substr(1);
94 for(var i=0, l=cardElements.length; i<l; i++) {
95 var card = cardElements[i];
96 if (card.getAttribute("id") == cardId) {
97 currentActiveCard = card;
98 currentActiveCard.style.display = "initial";
99 break;
100 }
101 }
102 }
103 if (!currentActiveCard) {
104 currentActiveCard = defaultActiveCard;
105 currentActiveCard.style.display = "initial";
106 }
107 replaceVariablesInTextContentBySpan();
108 fixTextContentInAnchorTasks();
109 initializeSelectElements();
110 scheduleTimerTaskIfNeeded(currentActiveCard);
111 handleOnNavigationIntrinsicEvent();
112};
113
114function showCardById(cardId, onload)
115{
116 if (currentActiveCard && currentActiveCard.getAttribute("id") == cardId) {
117 // We have nothing to do.
118 return false;
119 }
120 for(var i=0, l=cardElements.length; i<l; i++) {
121 var card = cardElements[i];
122 if (card.getAttribute("id") == cardId) {
123 currentActiveCard.style.display = "none";
124 currentActiveCard = card;
125 currentActiveCard.style.display = "initial";
126 return true;
127 }
128 }
129 return false;
130}
131
132function handleOnNavigationIntrinsicEvent() {
133 var navigationType = currentNavigationType;
134 currentNavigationType = -2;
135
136 if (navigationType >= 0) {
137 executeOnenterforwardTask();
138 } else if (navigationType == -1) {
139 executeOnenterbackwardTask();
140 } else {
141 console.log("WARNING: Cannot determine the navigation event type on this card!");
142 }
143}
144
145////////////////////////// WML Variables ////////////////////////////////////////
146function replaceVariablesInTextContentBySpan()
147{
148 var pattern1 = /(\$\(([_a-z]{1}[_a-z0-9]*)([:]{1}((([e]{1})(scape)?)|(([n]{1})(oesc)?)|(([u]{1})(nesc)?)))?\))/gi;
149 var pattern2 = /(\$([_a-zA-z]{1}[_a-zA-Z0-9]*))/g;
150 var whitespace = /^\s*$/g;
151 var replacer = function () {
152 var name = arguments[2];
153 var escape = "";
154 if (arguments[12])
155 escape = arguments[12];
156 else if (arguments[9])
157 escape = arguments[9];
158 else if (arguments[6])
159 escape = arguments[6];
160 var wml_variable_span = "\<span\ data\-wml\_name\=\"" + name + "\"\ class\=\"wml_variable\" data\-wml\_escape\=\"" + escape + "\"\>\<\/span\>";
161 console.log("replaceVariablesInTextContentBySpan() Found variable " + arguments[0]);
162 return wml_variable_span;
163 };
164
165 var textNodes = [];
166 var treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);
167 while(treeWalker.nextNode()) {
168 textNodes.push(treeWalker.currentNode);
169 }
170
171 for(var i=0, l=textNodes.length; i<l; i++) {
172 var curNode = textNodes[i];
173 var text = curNode.nodeValue;
174 if (!(whitespace.test(text))) {
175 var replaced = false;
176 if (pattern1.test(text)) {
177 text = text.replace(pattern1, replacer);
178 replaced = true;
179 }
180 if (pattern2.test(text)) {
181 text = text.replace(pattern2, replacer);
182 replaced = true;
183 }
184 if (replaced) {
185 var mySpan = document.createElement("span");
186 mySpan.innerHTML = text;
187 curNode.parentNode.replaceChild(mySpan, curNode);
188 }
189 }
190 }
191}
192
193////////////////////////// WML Anchor Tasks ////////////////////////////////////////
194// TODO: Optimize this brutal code
195function fixTextContentInAnchorTasks()
196{
197 var anchorTasks = document.getElementsByClassName('wml_anchor_task');
198 for(var i=0, l=anchorTasks.length; i<l; i++) {
199 var container;
200 var taskParent;
201 var task = anchorTasks[i];
202 if (task.dataset.wml_task_type == 'wml_task_go') {
203 taskParent = task.parentNode;
204 container = taskParent.parentNode;
205 } else {
206 taskParent = task;
207 container = task.parentNode;
208 }
209 container.removeChild(taskParent);
210 var anchorText = container.innerHTML;
211 while (container.firstChild) {
212 container.removeChild(container.firstChild);
213 }
214 container.appendChild(taskParent);
215 console.log("fixTextContentInAnchorTasks: anchorText = " + anchorText);
216 task.innerHTML = anchorText + task.innerHTML;
217 }
218}
219
220////////////////////////// WML Timer ////////////////////////////////////////
221var activeScheduledTimer = null;
222function scheduleTimerTaskIfNeeded(card)
223{
224 var timerElements = card.getElementsByClassName('wml_timer');
225 if (timerElements.length > 0) {
226 var timeout = timerElements[0].dataset.wml_value;
227 activeScheduledTimer = window.setTimeout(executeTimerTask, timeout * 100);
228 console.log("Starting WML timer timeout = " + timeout);
229 }
230}
231
232function cancelScheduledTimerTask()
233{
234 if (activeScheduledTimer != null) {
235 window.clearTimeout(clearTimeout);
236 activeScheduledTimer = null;
237 }
238}
239
240////////////////////////////////// <select> & <option> ////////////////////////
241var multiSelectSeparator = ";";
242var selectElementsMap = new Object();
243var WMLSelectElements = {
244 name: "WMLSelectElementsList",
245 options: new Object(),
246 setSelectedOptions: function (key, options) {
247 this.options[key] = options;
248 },
249 getSelectedOptions: function (key) {
250 return this.options[key];
251 },
252};
253
254function initializeSelectElements()
255{
256 var selectElements = document.getElementsByClassName('wml_select');
257 for(var i=0, l=selectElements.length; i<l; i++) {
258 var select = selectElements[i];
259 var iname = select.dataset.wml_iname;
260 // Preselect options based on 'ivalue'
261 var ivalue = select.dataset.wml_ivalue;
262 if (!isBlank(ivalue)) {
263 var options = select.options;
264 var optionsCount = options.length;
265 var ivalueList = ivalue.split(multiSelectSeparator);
266 for(var j=0, ll=ivalueList.length; j<ll; j++) {
267 var index = parseInt(ivalueList[j], 10);
268 if (index > 0 && index <= optionsCount) {
269 options[index-1].selected = 'selected';
270 }
271 }
272 }
273
274 // Keep a copy of selected options to process 'onpick' events
275 var options = select.selectedOptions;
276 var optionsCount = options.length;
277 var optionsList = [];
278 for(var j=0; j<optionsCount; j++) {
279 optionsList.push(options[j]);
280 }
281 var uniqueId = "wml_select_" + i;
282 select.dataset.wml_unique_id = uniqueId;
283 WMLSelectElements.setSelectedOptions(uniqueId, optionsList);
284 }
285}
286
287function handleSelectOnchangeEvent(select)
288{
289 refreshVariableInSelectElement(select);
290 var options = select.selectedOptions;
291 var optionsCount = options.length;
292 var uniqueId = select.dataset.wml_unique_id;
293 var oldSelectedOptions = WMLSelectElements.getSelectedOptions(uniqueId);
294
295 // Update the copy of selected options before we do anything
296 var optionsList = [];
297 for(var j=0; j<optionsCount; j++) {
298 optionsList.push(options[j]);
299 }
300 WMLSelectElements.setSelectedOptions(uniqueId, optionsList);
301
302 // process 'onpick' events if needed
303 for(var i=0; i<optionsCount; i++) {
304 var option = options[i];
305 var onpick = option.dataset.wml_onpick;
306 if (!isBlank(onpick)) {
307 var selectedNewly = true;
308 for(var j=0; j<oldSelectedOptions.length; j++) {
309 if (option.isSameNode(oldSelectedOptions[j])) {
310 selectedNewly = false;
311 break;
312 }
313 }
314 if (selectedNewly) {
315 internal_executeOnpickTask(option);
316 return;
317 }
318 }
319 }
320}
321
322function handleAOnClick(event, node)
323{
324 var href = node.href;
325 var search = node.search;
326 console.log("handleAOnClick <a> href = " + href);
327 refreshVariablesInControlElements();
328 if (!isBlank(search)) {
329 node.search = substituteVariablesInURL(search);
330 } else {
331 node.href = href.split("?")[0];
332 }
333 event.preventDefault();
334 navigateToURL(node.href);
335 return false;
336}
337
338//////////////////////// Variables /////////////////////////
339function substituteVariablesInURL(url)
340{
341 var pattern = /(((\%24)|(\$))\(([_a-z]{1}[_a-z0-9]*)([:]{1}((([e]{1})(scape)?)|(([n]{1})(oesc)?)|(([u]{1})(nesc)?)))?\))/gi;
342 var replacer = function () {
343 var name = arguments[2];
344 // TODO: Do the URL escaping here
345 console.log("substituteVariablesInURL() found variable : " + arguments[0]);
346 return WMLBrowser.getVar(name);
347 };
348 return url.replace(pattern, replacer);
349}
350
351function substituteVariablesInPostfield(value)
352{
353 var pattern = /(\$\(([_a-z]{1}[_a-z0-9]*)([:]{1}((([e]{1})(scape)?)|(([n]{1})(oesc)?)|(([u]{1})(nesc)?)))?\))/gi;
354 var replacer = function () {
355 var name = arguments[2];
356 // TODO: Do the URL escaping here
357 console.log("substituteVariablesInPostfield() found variable : " + arguments[0]);
358 return WMLBrowser.getVar(name);
359 };
360 return value.replace(pattern, replacer);
361}
362
363function refreshVariableInSelectElement(select)
364{
365 var options = select.selectedOptions;
366 var value = "";
367 var ivalue = "";
368 var optionsCount = options.length;
369 if (optionsCount > 0) {
370 var op = options[0];
371 value = op.value;
372 ivalue = (op.index + 1);
373 for(var i=1; i<optionsCount; i++) {
374 op = options[i];
375 value += multiSelectSeparator + op.value;
376 ivalue += multiSelectSeparator + (op.index + 1);
377 }
378 }
379 var name = select.name;
380 if (!isBlank(name)) {
381 WMLBrowser.setVar(name, value);
382 console.log("refreshVariableInSelectElement name = " + name + ", value = " + value);
383 }
384 var iname = select.dataset.wml_iname;
385 if (!isBlank(iname)) {
386 if (isBlank(ivalue)) {
387 // An index of zero indicates that no option is selected.
388 ivalue = "0";
389 }
390 WMLBrowser.setVar(iname, ivalue);
391 console.log("refreshVariableInSelectElement iname = " + iname + ", ivalue = " + ivalue);
392 }
393}
394
395function refreshVariablesInControlElements()
396{
397 var inputElements = currentActiveCard.getElementsByClassName('wml_input');
398 for(var i=0, l=inputElements.length; i<l; i++) {
399 var input = inputElements[i];
400 WMLBrowser.setVar(input.name, input.value);
401 console.log("refreshVariablesInControlElements <input> name = " + input.name + ", value = " + input.value);
402 }
403
404 var selectElements = currentActiveCard.getElementsByClassName('wml_select');
405 for(var i=0, l=selectElements.length; i<l; i++) {
406 var select = selectElements[i];
407 refreshVariableInSelectElement(selectElements[i]);
408 }
409}
410
411function updateVariableInPostfields(form)
412{
413 var postfields = currentActiveCard.getElementsByClassName('wml_postfield');
414 for(var i=0, l=postfields.length; i<l; i++) {
415 var input = postfields[i];
416 input.value = substituteVariablesInPostfield(input.value);
417 console.log("updateVariableInPostfields <postfield> name = " + input.name + ", value = " + input.value);
418 }
419}
420
421/////////////// Navigation ////////////////////
422function navigateToURL(url)
423{
424 console.log("navigateToURL: url = " + url);
425 cancelScheduledTimerTask();
426 currentNavigationType = 1;
427 window.location = url;
428}
429
430function navigateToCard(card)
431{
432 console.log("navigateToCard: card = " + card);
433 cancelScheduledTimerTask();
434 currentNavigationType = 1;
435 window.location.hash = card;
436}
437
438function navigateBack()
439{
440 console.log("navigateBack: currentState = ");
441 console.log(window.history.state);
442 cancelScheduledTimerTask();
443 currentNavigationType = -1;
444 window.history.back();
445}
446
447////////////// WML Tasks //////////////////////
448//<refresh>
449
450function updateWMLVariables()
451{
452 var wmlVariables = currentActiveCard.getElementsByClassName('wml_variable');
453 for(var i=0, l=wmlVariables.length; i<l; i++) {
454 var varElement = wmlVariables[i];
455 // Handle the variable escaping option here
456 var value = WMLBrowser.getVar(varElement.dataset.wml_name);
457 // TODO: Handle nested variable substitution on 'value'
458 var conversion = varElement.dataset.wml_escape;
459 if (!isBlank(value) && !isBlank(conversion)) {
460 if (conversion == "e" || conversion == "E") {
461 value = encodeURIComponent(value);
462 } else if (conversion == "u" || conversion == "U") {
463 value = decodeURIComponent(value);
464 }
465 }
466 varElement.innerHTML = value;
467 }
468}
469
470function internal_executeRefreshTask(root)
471{
472 console.log("internal_executeRefreshTask");
473 var setvarElements = root.getElementsByClassName('wml_setvar');
474 for(var i=0, l=setvarElements.length; i<l; i++) {
475 var setvar = setvarElements[i];
476 WMLBrowser.setVar(setvar.dataset.wml_name, setvar.dataset.wml_value);
477 console.log("<setvar> " + setvar.dataset.wml_name + " = " + setvar.dataset.wml_value);
478 }
479 updateWMLVariables();
480}
481
482function executeRefreshTask(event, node)
483{
484 event.preventDefault();
485 internal_executeRefreshTask(node.parentNode);
486 return false;
487}
488
489// <prev>
490function internal_executePrevTask(node)
491{
492 console.log("internal_executePrevTask");
493 internal_executeRefreshTask(node.parentNode);
494 navigateBack();
495}
496
497function executePrevTask(event, node)
498{
499 event.preventDefault();
500 internal_executePrevTask(node);
501 return false;
502}
503// <option onpick="...">
504function internal_executeOnpickTask(option)
505{
506 var href = option.dataset.wml_onpick;
507 console.log("internal_executeOnpickTask href = " + href);
508 //internal_executeRefreshTask(option.parentNode);
509 //refreshVariablesInControlElements();
510 if (isURLHash(href)) {
511 navigateToCard(href);
512 return true;
513 }
514 navigateToURL(href);
515 return true;
516}
517
518//<go>
519function internal_executeGoTask(form)
520{
521 var href = form.dataset.wml_href;
522 console.log("internal_executeGoTask href = " + href);
523 internal_executeRefreshTask(form.parentNode);
524 refreshVariablesInControlElements();
525 if (isURLHash(href)) {
526 navigateToCard(href);
527 return true;
528 }
529 // Substitute variables in <postfield> 'value' attributes before form submission.
530 updateVariableInPostfields();
531 form.submit();
532 return false;
533}
534
535function executeGoTask(event, form)
536{
537 event.preventDefault();
538 internal_executeGoTask(form);
539 return false;
540}
541
542//<onevent>
543function executeTimerTask()
544{
545 console.log("executeTimerTask()");
546 activeScheduledTimer = null;
547 // Handle <card ontimer="..."> event first
548 var ontimer = currentActiveCard.dataset.wml_ontimer;
549 if (!isBlank(ontimer))
550 {
551 navigateToURL(ontimer);
552 return;
553 }
554 // Handle <onevent type="timer">... here
555 var tasks = currentActiveCard.getElementsByClassName('wml_onevent_ontimer');
556 if (tasks.length > 0) {
557 var onevent = tasks[0];
558 if (onevent.dataset.wml_task_type == 'wml_task_go')
559 return internal_executeGoTask(onevent);
560 else if (onevent.dataset.wml_task_type == 'wml_task_refresh')
561 return internal_executeRefreshTask(onevent);
562 else if (onevent.dataset.wml_task_type == 'wml_task_prev')
563 return internal_executePrevTask(onevent);
564 else if (onevent.dataset.wml_task_type == 'wml_task_noop')
565 return;
566 }
567 // Handle <template timer="..."> event at the end
568 var templates = currentActiveCard.getElementsByClassName('wml_template');
569 if (templates.length > 0) {
570 var ontimer = templates[0].dataset.wml_ontimer;
571 if (!isBlank(ontimer))
572 {
573 navigateToURL(ontimer);
574 return;
575 }
576 }
577}
578
579function executeOnenterforwardTask()
580{
581 console.log("executeOnenterforwardTask()");
582 // Handle <card onenterforward="..."> event first
583 var onef = currentActiveCard.dataset.wml_onenterforward;
584 if (!isBlank(onef))
585 {
586 navigateToURL(onef);
587 return;
588 }
589 // Handle <onevent type="onenterforward">... here
590 var tasks = currentActiveCard.getElementsByClassName('wml_onevent_onenterforward');
591 if (tasks.length > 0) {
592 var onevent = tasks[0];
593 if (onevent.dataset.wml_task_type == 'wml_task_go')
594 return internal_executeGoTask(onevent);
595 else if (onevent.dataset.wml_task_type == 'wml_task_refresh')
596 return internal_executeRefreshTask(onevent);
597 else if (onevent.dataset.wml_task_type == 'wml_task_prev')
598 return internal_executePrevTask(onevent);
599 else if (onevent.dataset.wml_task_type == 'wml_task_noop')
600 return;
601 }
602 // Handle <template onenterforward="..."> event at the end
603 var templates = currentActiveCard.getElementsByClassName('wml_template');
604 if (templates.length > 0) {
605 var onef = templates[0].dataset.wml_onenterforward;
606 if (!isBlank(onef))
607 {
608 navigateToURL(onef);
609 return;
610 }
611 }
612}
613
614function executeOnenterbackwardTask()
615{
616 console.log("executeOnenterbackwardTask()");
617 // Handle <card onenterforward="..."> event first
618 var oneb = currentActiveCard.dataset.wml_onenterbackward;
619 if (!isBlank(oneb))
620 {
621 navigateToURL(oneb);
622 return;
623 }
624 // Handle <onevent type="onenterbackward">... here
625 var tasks = currentActiveCard.getElementsByClassName('wml_onevent_onenterbackward');
626 if (tasks.length > 0) {
627 var onevent = tasks[0];
628 if (onevent.dataset.wml_task_type == 'wml_task_go')
629 return internal_executeGoTask(onevent);
630 else if (onevent.dataset.wml_task_type == 'wml_task_refresh')
631 return internal_executeRefreshTask(onevent);
632 else if (onevent.dataset.wml_task_type == 'wml_task_prev')
633 return internal_executePrevTask(onevent);
634 else if (onevent.dataset.wml_task_type == 'wml_task_noop')
635 return;
636 }
637 // Handle <template onenterbackward="..."> event at the end
638 var templates = currentActiveCard.getElementsByClassName('wml_template');
639 if (templates.length > 0) {
640 var oneb = templates[0].dataset.wml_onenterbackward;
641 if (!isBlank(oneb))
642 {
643 navigateToURL(oneb);
644 return;
645 }
646 }
647}
648
649