diff options
Diffstat (limited to 'toolkit/content/tests/chrome/test_notificationbox.xhtml')
-rw-r--r-- | toolkit/content/tests/chrome/test_notificationbox.xhtml | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/toolkit/content/tests/chrome/test_notificationbox.xhtml b/toolkit/content/tests/chrome/test_notificationbox.xhtml new file mode 100644 index 0000000000..8de985175a --- /dev/null +++ b/toolkit/content/tests/chrome/test_notificationbox.xhtml @@ -0,0 +1,731 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- + XUL Widget Test for notificationbox + --> +<window title="Notification Box" width="500" height="600" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <vbox id="nb"/> + <menupopup id="menupopup" onpopupshown="this.hidePopup()" onpopuphidden="checkPopupClosed()"> + <menuitem label="One"/> + </menupopup> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ +const NOTIFICATION_LOCAL_NAME = "notification-message" +SimpleTest.waitForExplicitFinish(); + +var testtag_notificationbox_buttons = [ + { + label: "Button 1", + accesskey: "u", + callback: testtag_notificationbox_buttonpressed, + popup: "menupopup" + } +]; + +var testtag_notificationbox_buttons_nopopup = [ + { + label: "Button 1 No Popup", + accesskey: "u", + callback: testtag_notificationbox_button1pressed, + }, + { + label: "Button 2 No Popup", + accesskey: "u", + callback: testtag_notificationbox_button2pressed, + } +]; + +let testtag_notificationbox_button_l10n = [ + { + "l10n-id": "test-id" + } +]; + +var testtag_notificationbox_links = [ + { + label: "Link 1", + callback: testtag_notificationbox_buttonpressed, + link: "about:mozilla" + }, + { + label: "Button 2", + accesskey: "u", + callback: testtag_notificationbox_buttonpressed, + } +]; + +var testtag_notificationbox_supportpage = [ + { + supportPage: "test1", + }, + { + label: "This is an existing label", + supportPage: "test2", + }, + { + supportPage: "test3", + "l10n-id": "more-specific-id", + }, + { + supportPage: "test4", + label: "legacy label call", + "l10n-id": "modern-fluent-id" + } +]; + +function testtag_notificationbox_buttonpressed(notification, button) +{ + SimpleTest.is(button.localName, "button"); + return false; +} + +let buttonsPressedLog = ""; +function testtag_notificationbox_button1pressed(notification, button) { buttonsPressedLog += "button1"; return true; } +function testtag_notificationbox_button2pressed(notification, button) { buttonsPressedLog += "button2"; return true; } + +function testtag_notificationbox(nb) +{ + testtag_notificationbox_State(nb, "initial", null, 0); + + SimpleTest.is(nb.removeAllNotifications(false), undefined, "initial removeAllNotifications"); + testtag_notificationbox_State(nb, "initial removeAllNotifications", null, 0); + SimpleTest.is(nb.removeAllNotifications(true), undefined, "initial removeAllNotifications immediate"); + testtag_notificationbox_State(nb, "initial removeAllNotifications immediate", null, 0); + + runTimedTests(tests, -1, nb, null); +} + +var notification_last_events = []; +function notification_eventCallback(event) +{ + notification_last_events.push({ actualEvent: event , item: this }); +} + +/** + * For any notifications that have the notification_eventCallback on + * them, we will have recorded instances of those callbacks firing + * and stored them. This checks to see that the expected event types + * are being fired in order, and targeting the right item. + * + * @param {Array<string>} expectedEvents + * The list of event types, in order, that we expect to have been + * fired on the item. + * @param {<xul:notification>} ntf + * The notification we expect the callback to have been fired from. + * @param {string} testName + * The name of the current test, for logging. + */ +function testtag_notification_eventCallback(expectedEvents, ntf, testName) +{ + for (let i = 0; i < expectedEvents; ++i) { + let expected = expectedEvents[i]; + let { actualEvent, item } = notification_last_events[i]; + SimpleTest.is(actualEvent, expected, testName + ": event name"); + SimpleTest.is(item, ntf, testName + ": event item"); + } + notification_last_events = []; +} + +var tests = +[ + { + async test(nb, ntf) { + ntf = await nb.appendNotification("mutable", { + label: "Original", + priority: nb.PRIORITY_INFO_LOW, + }, testtag_notificationbox_buttons); + + ntf.label = "Changed string"; + await ntf.updateComplete; + SimpleTest.is(ntf.messageText.textContent.trim(), "Changed string", "set notification label with string"); + return ntf; + }, + result(nb, ntf) { + nb.removeNotification(ntf); + testtag_notificationbox_State(nb, "set notification label", null, 0); + } + }, + /* + Ensures that buttons created with the "label" parameter have their + label attribute set correctly. + */ + { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_LOW, + }, testtag_notificationbox_buttons); + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append notification"); + const button = ntf.buttonContainer.querySelector("button"); + SimpleTest.is(button.label, "Button 1", "set button label with the 'label' parameter"); + return ntf; + }, + result(nb, ntf) { + nb.removeNotification(ntf); + testtag_notificationbox_State(nb, "set notification label", null, 0); + } + }, + { + /* + Ensures that buttons created with the "l10n-id" parameter have + their "l10n-id" assigned correctly. + */ + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_LOW, + }, testtag_notificationbox_button_l10n); + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append notification"); + const button = ntf.buttonContainer.querySelector("button"); + SimpleTest.is(button.dataset.l10nId, "test-id", "create notification button with correctly assigned l10n id"); + return ntf; + }, + result(nb, ntf) { + nb.removeNotification(ntf); + testtag_notificationbox_State(nb, "set notification label", null, 0); + } + }, + { + async test(nb, ntf) { + // append a new notification + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_LOW, + }, testtag_notificationbox_buttons); + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append notification"); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "append", ntf, 1); + testtag_notification_State(nb, ntf, "append", "Notification", "note", + "happy.png", nb.PRIORITY_INFO_LOW); + + // check the getNotificationWithValue method + var ntf_found = nb.getNotificationWithValue("note"); + SimpleTest.is(ntf, ntf_found, "getNotificationWithValue note"); + + var none_found = nb.getNotificationWithValue("notenone"); + SimpleTest.is(none_found, null, "getNotificationWithValue null"); + return ntf; + } + }, + { + test(nb, ntf) { + // check that notifications can be removed properly + nb.removeNotification(ntf); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "removeNotification", null, 0); + } + }, + { + async test(nb, ntf) { + // append a new notification, but now with an event callback + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_LOW, + eventCallback: notification_eventCallback, + }, testtag_notificationbox_buttons); + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append notification with callback"); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "append with callback", ntf, 1); + return ntf; + } + }, + { + test(nb, ntf) { + nb.removeNotification(ntf); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "removeNotification with callback", + null, 0); + + testtag_notification_eventCallback(["removed"], ntf, "removeNotification()"); + return ntf; + } + }, + { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_MEDIUM, + eventCallback: notification_eventCallback, + }, testtag_notificationbox_buttons); + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append notification with object"); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "append with callback", ntf, 1); + testtag_notificationbox_State(nb, "append using object", ntf, 1); + testtag_notification_State(nb, ntf, "append object", "Notification", "note", + "happy.png", nb.PRIORITY_INFO_MEDIUM); + return ntf; + } + }, + { + test(rb, ntf) { + // Dismissing the notification instead of removing it should + // fire a dismissed "event" on the callback, followed by + // a removed "event". + ntf.dismiss(); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "called dismiss()", null, 0); + testtag_notification_eventCallback(["dismissed", "removed"], ntf, + "dismiss()"); + return ntf; + } + }, + { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_WARNING_LOW, + eventCallback: notification_eventCallback, + }, [{ + label: "Button", + }]); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "append", ntf, 1); + testtag_notification_State(nb, ntf, "append", "Notification", "note", + "happy.png", nb.PRIORITY_WARNING_LOW); + nb.removeNotification(ntf); + + return [1, null]; + } + }, + { + repeat: true, + async test(nb, arr) { + var idx = arr[0]; + var ntf = arr[1]; + switch (idx) { + case 1: + // append a new notification + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_LOW, + }, testtag_notificationbox_buttons); + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append notification"); + + // Test persistence + ntf.persistence++; + + return [idx, ntf]; + case 2: + case 3: + nb.removeTransientNotifications(); + + return [idx, ntf]; + } + return ntf; + }, + result(nb, arr) { + var idx = arr[0]; + var ntf = arr[1]; + switch (idx) { + case 1: + testtag_notificationbox_State(nb, "notification added", ntf, 1); + testtag_notification_State(nb, ntf, "append", "Notification", "note", + "happy.png", nb.PRIORITY_INFO_LOW); + SimpleTest.is(ntf.persistence, 1, "persistence is 1"); + + return [++idx, ntf]; + case 2: + testtag_notificationbox_State(nb, "first removeTransientNotifications", ntf, 1); + testtag_notification_State(nb, ntf, "append", "Notification", "note", + "happy.png", nb.PRIORITY_INFO_LOW); + SimpleTest.is(ntf.persistence, 0, "persistence is now 0"); + + return [++idx, ntf]; + case 3: + testtag_notificationbox_State(nb, "second removeTransientNotifications", null, 0); + + this.repeat = false; + } + return ntf; + } + }, + { + async test(nb, ntf) { + // append another notification + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_MEDIUM, + }, testtag_notificationbox_buttons); + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append notification again"); + return ntf; + }, + result(nb, ntf) { + // check that appending a second notification after removing the first one works + testtag_notificationbox_State(nb, "append again", ntf, 1); + testtag_notification_State(nb, ntf, "append again", "Notification", "note", + "happy.png", nb.PRIORITY_INFO_MEDIUM); + return ntf; + } + }, + { + test(nb, ntf) { + // check the removeCurrentNotification method + nb.removeCurrentNotification(); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "removeCurrentNotification", null, 0); + } + }, + { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_HIGH, + }, testtag_notificationbox_buttons); + return ntf; + }, + result(nb, ntf) { + // test the removeAllNotifications method + testtag_notificationbox_State(nb, "append info_high", ntf, 1); + SimpleTest.is(ntf.priority, nb.PRIORITY_INFO_HIGH, + "notification.priority " + nb.PRIORITY_INFO_HIGH); + SimpleTest.is(nb.removeAllNotifications(false), undefined, "removeAllNotifications"); + } + }, + { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_LOW, + eventCallback: notification_eventCallback, + }, testtag_notificationbox_links); + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append link notification with callback"); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "append link with callback", ntf, 1); + + let buttonContainer = ntf.buttonContainer; + let button = buttonContainer.lastElementChild; + SimpleTest.is(button.localName, "button", "button is a button"); + SimpleTest.ok(!button.href, "button href is not set"); + + let link = ntf.querySelector(".notification-link"); + SimpleTest.is(link.localName, "label", "link is a label"); + SimpleTest.is(link.href, "about:mozilla", "link href is correct"); + + SimpleTest.is(nb.removeAllNotifications(false), undefined, "removeAllNotifications"); + } + }, + { + async test(nb, ntf) { + // append a new notification + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_LOW, + }, testtag_notificationbox_buttons_nopopup); + return ntf; + }, + result(nb, ntf) { + let buttons = nb.currentNotification.buttonContainer.querySelectorAll("* button"); + + buttons[0].focus(); + synthesizeKey(" ", {}); + SimpleTest.is(buttonsPressedLog, "button1", "button 1 with keyboard"); + buttons[1].focus(); + synthesizeKey(" ", {}); + SimpleTest.is(buttonsPressedLog, "button1button2", "button 2 with keyboard"); + + synthesizeMouseAtCenter(buttons[0], {}); + SimpleTest.is(buttonsPressedLog, "button1button2button1", "button 1 with mouse"); + synthesizeMouseAtCenter(buttons[1], {}); + SimpleTest.is(buttonsPressedLog, "button1button2button1button2", "button 2 with mouse"); + + nb.removeAllNotifications(true); + } + }, + { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_LOW, + eventCallback: notification_eventCallback, + }, testtag_notificationbox_supportpage); + await ntf.updateComplete; + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append support page notification"); + return ntf; + }, + result(nb, ntf) { + testtag_notificationbox_State(nb, "append link with callback", ntf, 1); + + let link = ntf.querySelector(".notification-link"); + SimpleTest.is(link.localName, "a", "link 1 is an anchor"); + SimpleTest.is(link.dataset.l10nId, "moz-support-link-text", "link 1 Fluent ID is set"); + SimpleTest.ok(link.href.endsWith("/test1"), "link 1 href is set"); + + link = link.nextElementSibling; + SimpleTest.is(link.localName, "a", "link 2 is an anchor"); + SimpleTest.is(link.dataset.l10nId, "moz-support-link-text", "link 2 Fluent ID is set"); + SimpleTest.ok(!link.value, "label is not assigned to value when using supportPage"); + SimpleTest.ok(link.href.endsWith("/test2"), "link 2 href is set"); + + link = link.nextElementSibling; + SimpleTest.is(link.localName, "a", "link 3 is an anchor"); + SimpleTest.is(link.dataset.l10nId, "more-specific-id", "link 3 Fluent ID is the passed l10n-id"); + SimpleTest.ok(link.href.endsWith("/test3"), "link 3 href is set"); + + link = link.nextElementSibling; + SimpleTest.is(link.localName, "a", "link 4 is an anchor"); + SimpleTest.is(link.dataset.l10nId, "modern-fluent-id", "link 4 Fluent ID is the passed l10n-id"); + SimpleTest.ok(!link.value, "label is not assigned to value when using supportPage"); + SimpleTest.ok(link.href.endsWith("/test4"), "link 4 href is set"); + + SimpleTest.is(nb.removeAllNotifications(false), undefined, "removeAllNotifications"); + } + }, + { + async test(nb, unused) { + // add a number of notifications and check that they are added in order + await nb.appendNotification("4", { label: "Four", priority: nb.PRIORITY_INFO_HIGH }, + testtag_notificationbox_buttons); + await nb.appendNotification("7", { label: "Seven", priority: nb.PRIORITY_WARNING_HIGH }, + testtag_notificationbox_buttons); + await nb.appendNotification("2", { label: "Two", priority: nb.PRIORITY_INFO_LOW }); + await nb.appendNotification("8", { label: "Eight", priority: nb.PRIORITY_CRITICAL_LOW }); + await nb.appendNotification("5", { label: "Five", priority: nb.PRIORITY_WARNING_LOW }); + await nb.appendNotification("6", { label: "Six", priority: nb.PRIORITY_WARNING_HIGH }); + await nb.appendNotification("1", { label: "One", priority: nb.PRIORITY_INFO_LOW }); + await nb.appendNotification("9", { label: "Nine", priority: nb.PRIORITY_CRITICAL_MEDIUM }); + let ntf = await nb.appendNotification("10", { label: "Ten", priority: nb.PRIORITY_CRITICAL_HIGH }); + await nb.appendNotification("3", { label: "Three", priority: nb.PRIORITY_INFO_MEDIUM }); + return ntf; + }, + result(nb, ntf) { + let expectedValue = "3"; + ntf = nb.getNotificationWithValue(expectedValue); + is(nb.currentNotification, ntf, "appendNotification last notification"); + is(nb.currentNotification.getAttribute("value"), expectedValue, "appendNotification order"); + return 1; + } + }, + { + // test closing notifications to make sure that the current notification is still set properly + repeat: true, + test(nb, testidx) { + this.repeat = false; + return undefined; + }, + result(nb, arr) { + let notificationOrder = [4, 7, 2, 8, 5, 6, 1, 9, 10, 3]; + let allNotificationValues = [...nb.stack.children].map(n => n.getAttribute("value")); + is(allNotificationValues.length, notificationOrder.length, "Expected number of notifications"); + for (let i = 0; i < allNotificationValues.length; i++) { + is( + allNotificationValues[i], + notificationOrder[i].toString(), + `Notification ${i} matches` + ); + } + return undefined; + } + }, + { + async test(nb, ntf) { + var exh = false; + try { + await nb.appendNotification("no", { label: "no", priority: -1 }); + } catch (ex) { exh = true; } + SimpleTest.is(exh, true, "appendNotification priority too low"); + + exh = false; + try { + await nb.appendNotification("no", { label: "no", priority: 11 }); + } catch (ex) { exh = true; } + SimpleTest.is(exh, true, "appendNotification priority too high"); + + // check that the other priority types work properly + runTimedTests(appendPriorityTests, -1, nb, nb.PRIORITY_WARNING_LOW); + } + } +]; + +var appendPriorityTests = [ + { + async test(nb, priority) { + let ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority, + }, testtag_notificationbox_buttons); + SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append notification " + priority); + return [ntf, priority]; + }, + result(nb, obj) { + SimpleTest.is(obj[0].priority, obj[1], "notification.priority " + obj[1]); + return obj[1]; + } + }, + { + test(nb, priority) { + nb.removeCurrentNotification(); + return priority; + }, + async result(nb, priority) { + if (priority == nb.PRIORITY_CRITICAL_HIGH) { + let ntf = await nb.appendNotification("note", { + label: "Notification", + image: "happy.png", + priority: nb.PRIORITY_INFO_LOW, + }, testtag_notificationbox_buttons); + setTimeout(checkPopupTest, 50, nb, ntf); + } + else { + runTimedTests(appendPriorityTests, -1, nb, ++priority); + } + } + }, +]; + +function testtag_notificationbox_State(nb, testid, expecteditem, expectedcount) +{ + SimpleTest.is(nb.currentNotification, expecteditem, testid + " currentNotification"); + SimpleTest.is(nb.allNotifications ? nb.allNotifications.length : "no value", + expectedcount, testid + " allNotifications"); +} + +function testtag_notification_State(nb, ntf, testid, label, value, image, priority) +{ + is(ntf.messageText.textContent.trim(), label, testid + " notification label"); + is(ntf.getAttribute("value"), value, testid + " notification value"); + is(ntf.priority, priority, testid + " notification priority"); + + var type; + switch (priority) { + case nb.PRIORITY_INFO_LOW: + case nb.PRIORITY_INFO_MEDIUM: + case nb.PRIORITY_INFO_HIGH: + type = "info"; + break; + case nb.PRIORITY_WARNING_LOW: + case nb.PRIORITY_WARNING_MEDIUM: + case nb.PRIORITY_WARNING_HIGH: + type = "warning"; + break; + case nb.PRIORITY_CRITICAL_LOW: + case nb.PRIORITY_CRITICAL_MEDIUM: + case nb.PRIORITY_CRITICAL_HIGH: + type = "critical"; + break; + } + + is(ntf.getAttribute("type"), type, testid + " notification type"); + + let icons = { + info: "chrome://global/skin/icons/info-filled.svg", + warning: "chrome://global/skin/icons/warning.svg", + critical: "chrome://global/skin/icons/error.svg", + }; + let icon = icons[type]; + is(ntf.messageImage.src, icon, "notification image is set"); +} + +function checkPopupTest(nb, ntf) +{ + if (nb._animating) { + setTimeout(checkPopupTest, 50, nb, ntf); + } else { + var evt = new Event(""); + ntf.dispatchEvent(evt); + evt.target.buttonInfo = testtag_notificationbox_buttons[0]; + ntf.handleEvent(evt); + } +} + +function checkPopupClosed() +{ + SimpleTest.finish(); +} + +/** + * run one or more tests which perform a test operation, wait for a delay, + * then perform a result operation. + * + * tests - array of objects where each object is : + * { + * test: test function, + * result: result function + * repeat: true to repeat the test + * } + * idx - starting index in tests + * element - element to run tests on + * arg - argument to pass between test functions + * + * If, after executing the result part, the repeat property of the test is + * true, then the test is repeated. If the repeat property is not true, + * continue on to the next test. + * + * The test and result functions take two arguments, the element and the arg. + * The test function may return a value which will passed to the result + * function as its arg. The result function may also return a value which + * will be passed to the next repetition or the next test in the array. + */ +async function runTimedTests(tests, idx, element, arg) +{ + if (idx >= 0 && "result" in tests[idx]) + arg = tests[idx].result(element, arg); + + // if not repeating, move on to the next test + if (idx == -1 || !tests[idx].repeat) + idx++; + + if (idx < tests.length) { + let result = await tests[idx].test(element, arg); + setTimeout(runTimedTestsWait, 50, tests, idx, element, result); + } +} + +function runTimedTestsWait(tests, idx, element, arg) +{ + // use this secret property to check if the animation is still running. If it + // is, then the notification hasn't fully opened or closed yet + if (element._animating) + setTimeout(runTimedTestsWait, 50, tests, idx, element, arg); + else + runTimedTests(tests, idx, element, arg); +} + +setTimeout(() => { + testtag_notificationbox(new MozElements.NotificationBox(e => { + document.getElementById("nb").appendChild(e); + })); +}, 0); +]]> +</script> + +</window> |