mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
572 lines
22 KiB
HTML
572 lines
22 KiB
HTML
<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
|
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
|
|
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
|
|
%brandDTD;
|
|
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
|
|
%globalDTD;
|
|
<!ENTITY % aboutDTD SYSTEM "chrome://browser/locale/aboutAddons.dtd" >
|
|
%aboutDTD;
|
|
]>
|
|
|
|
<!-- ***** BEGIN LICENSE BLOCK *****
|
|
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
-
|
|
- The contents of this file are subject to the Mozilla Public License Version
|
|
- 1.1 (the "License"); you may not use this file except in compliance with
|
|
- the License. You may obtain a copy of the License at
|
|
- http://www.mozilla.org/MPL/
|
|
-
|
|
- Software distributed under the License is distributed on an "AS IS" basis,
|
|
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
- for the specific language governing rights and limitations under the
|
|
- License.
|
|
-
|
|
- The Original Code is Mozilla Communicator client code, released
|
|
- March 31, 1998.
|
|
-
|
|
- The Initial Developer of the Original Code is
|
|
- Netscape Communications Corporation.
|
|
- Portions created by the Initial Developer are Copyright (C) 1998-1999
|
|
- the Initial Developer. All Rights Reserved.
|
|
-
|
|
- Contributor(s):
|
|
- Mark Finkle <mfinkle@mozilla.com>
|
|
-
|
|
- Alternatively, the contents of this file may be used under the terms of
|
|
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
- in which case the provisions of the GPL or the LGPL are applicable instead
|
|
- of those above. If you wish to allow use of your version of this file only
|
|
- under the terms of either the GPL or the LGPL, and not to allow others to
|
|
- use your version of this file under the terms of the MPL, indicate your
|
|
- decision by deleting the provisions above and replace them with the notice
|
|
- and other provisions required by the GPL or the LGPL. If you do not delete
|
|
- the provisions above, a recipient may use your version of this file under
|
|
- the terms of any one of the MPL, the GPL or the LGPL.
|
|
-
|
|
- ***** END LICENSE BLOCK ***** -->
|
|
|
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
<head>
|
|
<title>&aboutAddons.title;</title>
|
|
<meta name="viewport" content="width=480; initial-scale=.6667; user-scalable=0" />
|
|
<link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
|
|
<link rel="stylesheet" href="chrome://browser/skin/aboutAddons.css" type="text/css"/>
|
|
<style>
|
|
.hide-on-enable,
|
|
.show-on-error,
|
|
.show-on-uninstall,
|
|
.show-on-install,
|
|
.show-on-restart,
|
|
div[isDisabled="true"] .hide-on-disable {
|
|
display: none;
|
|
}
|
|
|
|
div[error] .show-on-error,
|
|
div[opType="needs-restart"] .show-on-restart,
|
|
div[opType="needs-uninstall"] .show-on-uninstall,
|
|
div[opType="needs-install"] .show-on-install,
|
|
div[opType="needs-enable"] .show-on-enable,
|
|
div[opType="needs-disable"] .show-on-disable,
|
|
div[isDisabled="true"] .show-on-disable {
|
|
display: -moz-box;
|
|
}
|
|
|
|
div[opType="needs-restart"] .hide-on-restart,
|
|
div[opType="needs-uninstall"] .hide-on-uninstall,
|
|
div[isDisabled="true"][opType="needs-uninstall"],
|
|
div[opType="needs-install"] .hide-on-install,
|
|
div[opType="needs-enable"] .hide-on-enable,
|
|
div[opType="needs-disable"] .hide-on-disable {
|
|
display: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body dir="&locale.dir;" onload="init();" onunload="uninit();">
|
|
<div id="addons-header">
|
|
<div>
|
|
<div>&aboutAddons.header2;</div>
|
|
<img src="chrome://browser/skin/images/addons-amo-hdpi.png" pref="extensions.getAddons.browseAddons" onclick="openLink(this);"/>
|
|
</div>
|
|
</div>
|
|
<div id="addons-list" style="display: none;">
|
|
</div>
|
|
|
|
<div id="addons-details" style="display: none">
|
|
<div class="addon-item">
|
|
<img class="favicon"/>
|
|
<div class="inner">
|
|
<div class="details">
|
|
<div class="title"></div><div class="version"></div><div class="tag"></div>
|
|
</div>
|
|
<div class="description-full"></div>
|
|
</div>
|
|
<div class="buttons">
|
|
<button id="enable-btn" class="show-on-disable hide-on-enable hide-on-uninstall" onclick="Addons.enable();">&addonAction.enable;</button>
|
|
<button id="disable-btn" class="show-on-enable hide-on-disable hide-on-uninstall" onclick="Addons.disable();">&addonAction.disable;</button>
|
|
<button id="uninstall-btn" class="hide-on-uninstall" onclick="Addons.uninstall();">&addonAction.uninstall;</button>
|
|
<button id="cancel-btn" class="show-on-uninstall" onclick="Addons.cancelUninstall();">&addonAction.cancel;</button>
|
|
</div>
|
|
<div class="options-header">&aboutAddons.options;</div>
|
|
<div class="options-box"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="application/javascript;version=1.8"><![CDATA[
|
|
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm")
|
|
Cu.import("resource://gre/modules/AddonManager.jsm");
|
|
|
|
let gStringBundle = Services.strings.createBundle("chrome://browser/locale/aboutAddons.properties");
|
|
|
|
let gChromeWin = null;
|
|
function getChromeWin() {
|
|
if (!gChromeWin) {
|
|
gChromeWin = window
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
|
.rootTreeItem
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindow)
|
|
.QueryInterface(Ci.nsIDOMChromeWindow);
|
|
}
|
|
return gChromeWin;
|
|
}
|
|
|
|
function init() {
|
|
window.addEventListener("popstate", onPopState, false);
|
|
|
|
AddonManager.addInstallListener(Addons);
|
|
Addons.getAddons();
|
|
}
|
|
|
|
function uninit() {
|
|
AddonManager.removeInstallListener(Addons);
|
|
}
|
|
|
|
function openLink(aElement) {
|
|
try {
|
|
let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
|
|
let url = formatter.formatURLPref(aElement.getAttribute("pref"));
|
|
let BrowserApp = getChromeWin().BrowserApp;
|
|
BrowserApp.addTab(url, { selected: true, parentId: BrowserApp.selectedTab.id });
|
|
} catch (ex) {}
|
|
}
|
|
|
|
function onPopState(aEvent) {
|
|
// Called when back/forward is used to change the state of the page
|
|
if (aEvent.state) {
|
|
// Show the detail page for an addon
|
|
Addons.showDetails(Addons._getElementForAddon(aEvent.state.id));
|
|
} else {
|
|
// Clear any previous detail addon
|
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
|
detailItem.addon = null;
|
|
|
|
// Hide the detail page and show the list
|
|
let details = document.querySelector("#addons-details");
|
|
details.style.display = "none";
|
|
let list = document.querySelector("#addons-list");
|
|
list.style.display = "block";
|
|
}
|
|
}
|
|
|
|
var Addons = {
|
|
_createItem: function _createItem(aAddon) {
|
|
let outer = document.createElement("div");
|
|
outer.setAttribute("addonID", aAddon.id);
|
|
outer.className = "addon-item";
|
|
outer.setAttribute("role", "button");
|
|
outer.addEventListener("click", function() {
|
|
this.showDetails(outer);
|
|
history.pushState({ id: aAddon.id }, document.title);
|
|
}.bind(this), true);
|
|
|
|
let img = document.createElement("img");
|
|
img.className = "favicon";
|
|
img.setAttribute("src", aAddon.iconURL);
|
|
outer.appendChild(img);
|
|
|
|
let inner = document.createElement("div");
|
|
inner.className = "inner";
|
|
|
|
let details = document.createElement("div");
|
|
details.className = "details";
|
|
inner.appendChild(details);
|
|
|
|
let titlePart = document.createElement("div");
|
|
titlePart.textContent = aAddon.name;
|
|
titlePart.className = "title";
|
|
details.appendChild(titlePart);
|
|
|
|
let versionPart = document.createElement("div");
|
|
versionPart.textContent = aAddon.version;
|
|
versionPart.className = "version";
|
|
details.appendChild(versionPart);
|
|
|
|
let tagPart = document.createElement("div");
|
|
tagPart.textContent = gStringBundle.GetStringFromName("addonType." + aAddon.type);
|
|
tagPart.className = "tag";
|
|
details.appendChild(tagPart);
|
|
|
|
if ("description" in aAddon) {
|
|
let descPart = document.createElement("div");
|
|
descPart.textContent = aAddon.description;
|
|
descPart.className = "description";
|
|
inner.appendChild(descPart);
|
|
}
|
|
|
|
outer.appendChild(inner);
|
|
return outer;
|
|
},
|
|
|
|
_createItemForAddon: function _createItemForAddon(aAddon) {
|
|
let appManaged = (aAddon.scope == AddonManager.SCOPE_APPLICATION);
|
|
let opType = this._getOpTypeForOperations(aAddon.pendingOperations);
|
|
let updateable = (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE) > 0;
|
|
let uninstallable = (aAddon.permissions & AddonManager.PERM_CAN_UNINSTALL) > 0;
|
|
|
|
let blocked = "";
|
|
switch(aAddon.blocklistState) {
|
|
case Ci.nsIBlocklistService.STATE_BLOCKED:
|
|
blocked = "blocked";
|
|
break;
|
|
case Ci.nsIBlocklistService.STATE_SOFTBLOCKED:
|
|
blocked = "softBlocked";
|
|
break;
|
|
case Ci.nsIBlocklistService.STATE_OUTDATED:
|
|
blocked = "outdated";
|
|
break;
|
|
}
|
|
|
|
let item = this._createItem(aAddon);
|
|
item.setAttribute("isDisabled", !aAddon.isActive);
|
|
item.setAttribute("opType", opType);
|
|
item.setAttribute("updateable", updateable);
|
|
if (blocked)
|
|
item.setAttribute("blockedStatus", blocked);
|
|
item.setAttribute("optionsURL", aAddon.optionsURL || "");
|
|
item.addon = aAddon;
|
|
|
|
return item;
|
|
},
|
|
|
|
_getElementForAddon: function(aKey) {
|
|
let list = document.getElementById("addons-list");
|
|
let element = list.querySelector("div[addonID='" + aKey + "']");
|
|
return element;
|
|
},
|
|
|
|
getAddons: function getAddons() {
|
|
// Clear all content before filling the addons
|
|
let list = document.getElementById("addons-list");
|
|
list.innerHTML = "";
|
|
|
|
let self = this;
|
|
AddonManager.getAddonsByTypes(["extension", "theme", "locale"], function(aAddons) {
|
|
for (let i=0; i<aAddons.length; i++) {
|
|
let item = self._createItemForAddon(aAddons[i]);
|
|
list.appendChild(item);
|
|
}
|
|
|
|
list.style.display = "block";
|
|
document.getElementById("addons-header").setAttribute("showlist", "true");
|
|
|
|
// Load the search engines
|
|
let defaults = Services.search.getDefaultEngines({ }).map(function (e) e.name);
|
|
function isDefault(aEngine)
|
|
defaults.indexOf(aEngine.name) != -1
|
|
|
|
let defaultDescription = gStringBundle.GetStringFromName("addonsSearchEngine.description");
|
|
|
|
let engines = Services.search.getEngines({ });
|
|
for (let e = 0; e < engines.length; e++) {
|
|
let engine = engines[e];
|
|
let addon = {};
|
|
addon.id = engine.name;
|
|
addon.type = "search";
|
|
addon.name = engine.name;
|
|
addon.version = "";
|
|
addon.description = engine.description || defaultDescription;
|
|
addon.iconURL = engine.iconURI ? engine.iconURI.spec : "";
|
|
addon.appDisabled = false;
|
|
addon.scope = isDefault(engine) ? AddonManager.SCOPE_APPLICATION : AddonManager.SCOPE_PROFILE;
|
|
addon.engine = engine;
|
|
|
|
let item = self._createItem(addon);
|
|
item.setAttribute("isDisabled", engine.hidden);
|
|
item.setAttribute("updateable", "false");
|
|
item.setAttribute("opType", "");
|
|
item.addon = addon;
|
|
list.appendChild(item);
|
|
}
|
|
});
|
|
},
|
|
|
|
_getOpTypeForOperations: function _getOpTypeForOperations(aOperations) {
|
|
if (aOperations & AddonManager.PENDING_UNINSTALL)
|
|
return "needs-uninstall";
|
|
if (aOperations & AddonManager.PENDING_ENABLE)
|
|
return "needs-enable";
|
|
if (aOperations & AddonManager.PENDING_DISABLE)
|
|
return "needs-disable";
|
|
return "";
|
|
},
|
|
|
|
showDetails: function showDetails(aListItem) {
|
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
|
detailItem.setAttribute("isDisabled", aListItem.getAttribute("isDisabled"));
|
|
detailItem.setAttribute("opType", aListItem.getAttribute("opType"));
|
|
detailItem.setAttribute("optionsURL", aListItem.getAttribute("optionsURL"));
|
|
detailItem.addon = aListItem.addon;
|
|
|
|
let addon = detailItem.addon;
|
|
document.querySelector("#addons-details > .addon-item .favicon").setAttribute("src", addon.iconURL);
|
|
document.querySelector("#addons-details > .addon-item .title").textContent = addon.name;
|
|
document.querySelector("#addons-details > .addon-item .version").textContent = addon.version;
|
|
document.querySelector("#addons-details > .addon-item .tag").textContent = gStringBundle.GetStringFromName("addonType." + addon.type);
|
|
document.querySelector("#addons-details > .addon-item .description-full").textContent = addon.description;
|
|
|
|
let enableBtn = document.getElementById("uninstall-btn");
|
|
if (addon.appDisabled)
|
|
enableBtn.setAttribute("disabled", "true");
|
|
else
|
|
enableBtn.removeAttribute("disabled");
|
|
|
|
let uninstallBtn = document.getElementById("uninstall-btn");
|
|
if (addon.scope == AddonManager.SCOPE_APPLICATION)
|
|
uninstallBtn.setAttribute("disabled", "true");
|
|
else
|
|
uninstallBtn.removeAttribute("disabled");
|
|
|
|
let box = document.querySelector("#addons-details > .addon-item .options-box");
|
|
box.innerHTML = "";
|
|
|
|
// Retrieve the extensions preferences
|
|
try {
|
|
let optionsURL = aListItem.getAttribute("optionsURL");
|
|
let xhr = new XMLHttpRequest();
|
|
xhr.open("GET", optionsURL, false);
|
|
xhr.send();
|
|
if (xhr.responseXML) {
|
|
let currentNode;
|
|
let nodeIterator = xhr.responseXML.createNodeIterator(xhr.responseXML, NodeFilter.SHOW_TEXT, null, false);
|
|
while (currentNode = nodeIterator.nextNode()) {
|
|
let trimmed = currentNode.nodeValue.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
|
|
if (!trimmed.length)
|
|
currentNode.parentNode.removeChild(currentNode);
|
|
}
|
|
|
|
// Only allow <setting> for now
|
|
let prefs = xhr.responseXML.querySelectorAll(":root > setting");
|
|
for (let i = 0; i < prefs.length; i++)
|
|
box.appendChild(prefs.item(i));
|
|
/*
|
|
// Send an event so add-ons can prepopulate any non-preference based
|
|
// settings
|
|
let event = document.createEvent("Events");
|
|
event.initEvent("AddonOptionsLoad", true, false);
|
|
this.dispatchEvent(event);
|
|
|
|
// Also send a notification to match the behavior of desktop Firefox
|
|
let id = this.id.substring(17); // length of |urn:mozilla:item:|
|
|
Services.obs.notifyObservers(document, "addon-options-displayed", id);
|
|
*/
|
|
}
|
|
} catch (e) {
|
|
Cu.reportError(e)
|
|
}
|
|
|
|
let list = document.querySelector("#addons-list");
|
|
list.style.display = "none";
|
|
let details = document.querySelector("#addons-details");
|
|
details.style.display = "block";
|
|
},
|
|
|
|
enable: function enable() {
|
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
|
if (!detailItem.addon)
|
|
return;
|
|
|
|
let opType;
|
|
let isDisabled;
|
|
if (detailItem.addon.type == "search") {
|
|
isDisabled = false;
|
|
detailItem.addon.engine.hidden = false;
|
|
opType = "needs-enable";
|
|
} else if (detailItem.addon.type == "theme") {
|
|
// We can have only one theme enabled, so disable the current one if any
|
|
let theme = null;
|
|
let list = document.getElementById("addons-list");
|
|
let item = list.firstElementChild;
|
|
while (item) {
|
|
if (item.addon && (item.addon.type == "theme") && (item.addon.isActive)) {
|
|
theme = item;
|
|
break;
|
|
}
|
|
item = item.nextSibling;
|
|
}
|
|
if (theme)
|
|
this.disable(theme);
|
|
|
|
detailItem.addon.userDisabled = false;
|
|
isDisabled = false;
|
|
} else {
|
|
detailItem.addon.userDisabled = false;
|
|
isDisabled = false;
|
|
opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
|
|
|
if (detailItem.addon.pendingOperations & AddonManager.PENDING_ENABLE) {
|
|
this.showRestart();
|
|
} else {
|
|
if (detailItem.getAttribute("opType") == "needs-disable")
|
|
this.hideRestart();
|
|
}
|
|
}
|
|
|
|
detailItem.setAttribute("opType", opType);
|
|
detailItem.setAttribute("isDisabled", isDisabled);
|
|
|
|
// Sync to the list item
|
|
let listItem = this._getElementForAddon(detailItem.addon.id);
|
|
listItem.setAttribute("isDisabled", detailItem.getAttribute("isDisabled"));
|
|
listItem.setAttribute("opType", detailItem.getAttribute("opType"));
|
|
},
|
|
|
|
disable: function disable() {
|
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
|
if (!detailItem.addon)
|
|
return;
|
|
|
|
let opType;
|
|
let isDisabled;
|
|
if (detailItem.addon.type == "search") {
|
|
isDisabled = true;
|
|
detailItem.addon.engine.hidden = true;
|
|
opType = "needs-disable";
|
|
} else if (detailItem.addon.type == "theme") {
|
|
detailItem.addon.userDisabled = true;
|
|
isDisabled = true;
|
|
} else if (detailItem.addon.type == "locale") {
|
|
detailItem.addon.userDisabled = true;
|
|
isDisabled = true;
|
|
} else {
|
|
detailItem.addon.userDisabled = true;
|
|
opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
|
isDisabled = !detailItem.addon.isActive;
|
|
|
|
if (detailItem.addon.pendingOperations & AddonManager.PENDING_DISABLE) {
|
|
this.showRestart();
|
|
} else {
|
|
if (detailItem.getAttribute("opType") == "needs-enable")
|
|
this.hideRestart();
|
|
}
|
|
}
|
|
|
|
detailItem.setAttribute("opType", opType);
|
|
detailItem.setAttribute("isDisabled", isDisabled);
|
|
|
|
// Sync to the list item
|
|
let listItem = this._getElementForAddon(detailItem.addon.id);
|
|
listItem.setAttribute("isDisabled", detailItem.getAttribute("isDisabled"));
|
|
listItem.setAttribute("opType", detailItem.getAttribute("opType"));
|
|
},
|
|
|
|
uninstall: function uninstall() {
|
|
let list = document.getElementById("addons-list");
|
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
|
if (!detailItem.addon)
|
|
return;
|
|
|
|
let listItem = this._getElementForAddon(detailItem.addon.id);
|
|
|
|
if (detailItem.addon.type == "search") {
|
|
// Make sure the engine isn't hidden before removing it, to make sure it's
|
|
// visible if the user later re-adds it (works around bug 341833)
|
|
detailItem.addon.engine.hidden = false;
|
|
Services.search.removeEngine(detailItem.addon.engine);
|
|
// the search-engine-modified observer in browser.js will take care of
|
|
// updating the list
|
|
} else {
|
|
detailItem.addon.uninstall();
|
|
let opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
|
|
|
if (detailItem.addon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
|
|
this.showRestart();
|
|
|
|
// A disabled addon doesn't need a restart so it has no pending ops and
|
|
// can't be cancelled
|
|
if (!detailItem.addon.isActive && opType == "")
|
|
opType = "needs-uninstall";
|
|
|
|
detailItem.setAttribute("opType", opType);
|
|
listItem.setAttribute("opType", opType);
|
|
} else {
|
|
list.removeChild(listItem);
|
|
history.back();
|
|
}
|
|
}
|
|
},
|
|
|
|
cancelUninstall: function ev_cancelUninstall() {
|
|
let detailItem = document.querySelector("#addons-details > .addon-item");
|
|
if (!detailItem.addon)
|
|
return;
|
|
|
|
detailItem.addon.cancelUninstall();
|
|
this.hideRestart();
|
|
|
|
let opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations);
|
|
detailItem.setAttribute("opType", opType);
|
|
|
|
let listItem = this._getElementForAddon(detailItem.addon.id);
|
|
listItem.setAttribute("opType", opType);
|
|
},
|
|
|
|
showRestart: function showRestart(aMode) {
|
|
// TODO (bug 704406)
|
|
},
|
|
|
|
hideRestart: function hideRestart(aMode) {
|
|
// TODO (bug 704406)
|
|
},
|
|
|
|
onInstallEnded: function(aInstall, aAddon) {
|
|
let needsRestart = false;
|
|
if (aInstall.existingAddon && (aInstall.existingAddon.pendingOperations & AddonManager.PENDING_UPGRADE))
|
|
needsRestart = true;
|
|
else if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL)
|
|
needsRestart = true;
|
|
|
|
let list = document.getElementById("addons-list");
|
|
let element = this._getElementForAddon(aAddon.id);
|
|
if (!element) {
|
|
element = this._createItemForAddon(aAddon);
|
|
list.insertBefore(element, list.firstElementChild);
|
|
}
|
|
|
|
if (needsRestart)
|
|
element.setAttribute("opType", "needs-restart");
|
|
},
|
|
|
|
onInstallFailed: function(aInstall) {
|
|
},
|
|
|
|
onDownloadProgress: function xpidm_onDownloadProgress(aInstall) {
|
|
},
|
|
|
|
onDownloadFailed: function(aInstall) {
|
|
},
|
|
|
|
onDownloadCancelled: function(aInstall) {
|
|
}
|
|
}
|
|
]]></script>
|
|
</body>
|
|
</html>
|