From f673d8c325eb6dec31a1cddd0cfc2f70b13f2484 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Tue, 31 May 2016 14:17:03 -0700 Subject: [PATCH] Bug 1268447 - Convert webconsole to use new key shortcut module. r=bgrins --- .../framework/test/browser_keybindings_02.js | 10 +- .../framework/test/browser_toolbox_zoom.js | 4 +- devtools/client/framework/toolbox.js | 97 +------------- .../locales/en-US/webconsole.properties | 13 ++ devtools/client/preferences/devtools.js | 3 - devtools/client/shared/moz.build | 1 + devtools/client/shared/zoom-keys.js | 88 ++++++++++++ devtools/client/webconsole/hudservice.js | 3 - devtools/client/webconsole/test/browser.ini | 1 - .../browser_console_keyboard_accessibility.js | 12 +- .../browser_webconsole_change_font_size.js | 39 ------ devtools/client/webconsole/webconsole.js | 126 +++++------------- devtools/client/webconsole/webconsole.xul | 21 --- 13 files changed, 155 insertions(+), 263 deletions(-) create mode 100644 devtools/client/shared/zoom-keys.js delete mode 100644 devtools/client/webconsole/test/browser_webconsole_change_font_size.js diff --git a/devtools/client/framework/test/browser_keybindings_02.js b/devtools/client/framework/test/browser_keybindings_02.js index a843aa426af3..dc30a11d5e64 100644 --- a/devtools/client/framework/test/browser_keybindings_02.js +++ b/devtools/client/framework/test/browser_keybindings_02.js @@ -13,6 +13,10 @@ var {Toolbox} = require("devtools/client/framework/toolbox"); var strings = Services.strings.createBundle( "chrome://devtools/locale/toolbox.properties"); +function getZoomValue() { + return parseFloat(Services.prefs.getCharPref("devtools.toolbox.zoomValue")); +} + add_task(function* () { info("Create a test tab and open the toolbox"); let tab = yield addTab(URL); @@ -41,9 +45,9 @@ function zoomWithKey(toolbox, key) { return; } info("Zooming with key: " + key); - let currentZoom = toolbox.zoomValue; - synthesizeKeyShortcut(shortcut); - isnot(toolbox.zoomValue, currentZoom, "The zoom level was changed in the toolbox"); + let currentZoom = getZoomValue(); + synthesizeKeyShortcut(shortcut, toolbox.win); + isnot(getZoomValue(), currentZoom, "The zoom level was changed in the toolbox"); } function* checkKeyBindings(toolbox) { diff --git a/devtools/client/framework/test/browser_toolbox_zoom.js b/devtools/client/framework/test/browser_toolbox_zoom.js index 943419686c7f..aa4f89cc55b9 100644 --- a/devtools/client/framework/test/browser_toolbox_zoom.js +++ b/devtools/client/framework/test/browser_toolbox_zoom.js @@ -40,7 +40,9 @@ function testZoomLevel(type, times, expected) { let zoom = getCurrentZoom(toolbox); is(zoom.toFixed(2), expected, "zoom level correct after zoom " + type); - is(toolbox.zoomValue.toFixed(2), expected, + let savedZoom = parseFloat(Services.prefs.getCharPref( + "devtools.toolbox.zoomValue")); + is(savedZoom.toFixed(2), expected, "saved zoom level is correct after zoom " + type); } diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js index 5c687776fcdd..1e3e53ba2ffb 100644 --- a/devtools/client/framework/toolbox.js +++ b/devtools/client/framework/toolbox.js @@ -7,11 +7,8 @@ "use strict"; const MAX_ORDINAL = 99; -const ZOOM_PREF = "devtools.toolbox.zoomValue"; const SPLITCONSOLE_ENABLED_PREF = "devtools.toolbox.splitconsoleEnabled"; const SPLITCONSOLE_HEIGHT_PREF = "devtools.toolbox.splitconsoleHeight"; -const MIN_ZOOM = 0.5; -const MAX_ZOOM = 2; const OS_HISTOGRAM = "DEVTOOLS_OS_ENUMERATED_PER_USER"; const OS_IS_64_BITS = "DEVTOOLS_OS_IS_64_BITS_PER_USER"; const SCREENSIZE_HISTOGRAM = "DEVTOOLS_SCREEN_RESOLUTION_ENUMERATED_PER_USER"; @@ -73,6 +70,8 @@ loader.lazyRequireGetter(this, "getPreferenceFront", "devtools/server/actors/preference", true); loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts", true); +loader.lazyRequireGetter(this, "ZoomKeys", + "devtools/client/shared/zoom-keys"); loader.lazyGetter(this, "osString", () => { return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS; @@ -303,13 +302,6 @@ Toolbox.prototype = { return this.frame.contentDocument; }, - /** - * Get current zoom level of toolbox - */ - get zoomValue() { - return parseFloat(Services.prefs.getCharPref(ZOOM_PREF)); - }, - /** * Get the toolbox highlighter front. Note that it may not always have been * initialized first. Use `initInspector()` if needed. @@ -440,8 +432,7 @@ Toolbox.prototype = { this._addHostListeners(shortcuts); this._registerOverlays(); if (!this._hostOptions || this._hostOptions.zoom === true) { - this._addZoomKeys(shortcuts); - this._loadInitialZoom(); + ZoomKeys.register(this.win); } this._setToolbarKeyboardNavigation(); @@ -659,88 +650,6 @@ Toolbox.prototype = { } }, - /** - * Wire up the listeners for the zoom keys. - */ - _addZoomKeys: function (shortcuts) { - shortcuts.on(toolboxStrings("toolbox.zoomIn.key"), - this.zoomIn.bind(this)); - let zoomIn2 = toolboxStrings("toolbox.zoomIn2.key"); - if (zoomIn2) { - shortcuts.on(zoomIn2, this.zoomIn.bind(this)); - } - let zoomIn3 = toolboxStrings("toolbox.zoomIn2.key"); - if (zoomIn3) { - shortcuts.on(zoomIn3, this.zoomIn.bind(this)); - } - - shortcuts.on(toolboxStrings("toolbox.zoomOut.key"), - this.zoomOut.bind(this)); - let zoomOut2 = toolboxStrings("toolbox.zoomOut2.key"); - if (zoomOut2) { - shortcuts.on(zoomOut2, this.zoomOut.bind(this)); - } - - shortcuts.on(toolboxStrings("toolbox.zoomReset.key"), - this.zoomReset.bind(this)); - let zoomReset2 = toolboxStrings("toolbox.zoomReset2.key"); - if (zoomReset2) { - shortcuts.on(zoomReset2, this.zoomReset.bind(this)); - } - }, - - /** - * Set zoom on toolbox to whatever the last setting was. - */ - _loadInitialZoom: function () { - this.setZoom(this.zoomValue); - }, - - /** - * Increase zoom level of toolbox window - make things bigger. - */ - zoomIn: function (name, event) { - this.setZoom(this.zoomValue + 0.1); - event.preventDefault(); - }, - - /** - * Decrease zoom level of toolbox window - make things smaller. - */ - zoomOut: function (name, event) { - this.setZoom(this.zoomValue - 0.1); - event.preventDefault(); - }, - - /** - * Reset zoom level of the toolbox window. - */ - zoomReset: function (name, event) { - this.setZoom(1); - event.preventDefault(); - }, - - /** - * Set zoom level of the toolbox window. - * - * @param {number} zoomValue - * Zoom level e.g. 1.2 - */ - setZoom: function (zoomValue) { - // cap zoom value - zoomValue = Math.max(zoomValue, MIN_ZOOM); - zoomValue = Math.min(zoomValue, MAX_ZOOM); - - let docShell = this.win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell); - let contViewer = docShell.contentViewer; - - contViewer.fullZoom = zoomValue; - - Services.prefs.setCharPref(ZOOM_PREF, zoomValue); - }, - /** * Adds the keys and commands to the Toolbox Window in window mode. */ diff --git a/devtools/client/locales/en-US/webconsole.properties b/devtools/client/locales/en-US/webconsole.properties index a000979a73b9..15a0930337e2 100644 --- a/devtools/client/locales/en-US/webconsole.properties +++ b/devtools/client/locales/en-US/webconsole.properties @@ -258,3 +258,16 @@ severity.error=Error severity.warn=Warning severity.info=Info severity.log=Log + +# LOCALIZATION NOTE (webconsole.find.key) +# Key shortcut used to focus the search box on upper right of the console +webconsole.find.key=CmdOrCtrl+F + +# LOCALIZATION NOTE (webconsole.close.key) +# Key shortcut used to close the Browser console (doesn't work in regular web console) +webconsole.close.key=CmdOrCtrl+W + +# LOCALIZATION NOTE (webconsole.clear.key*) +# Key shortcut used to clear the console output +webconsole.clear.key=Ctrl+Shift+L +webconsole.clear.keyOSX=Ctrl+L diff --git a/devtools/client/preferences/devtools.js b/devtools/client/preferences/devtools.js index 5144dad1f6b3..a711f91d549d 100644 --- a/devtools/client/preferences/devtools.js +++ b/devtools/client/preferences/devtools.js @@ -276,9 +276,6 @@ pref("devtools.browserconsole.filter.serverwarn", false); pref("devtools.browserconsole.filter.serverinfo", false); pref("devtools.browserconsole.filter.serverlog", false); -// Text size in the Web Console. Use 0 for the system default size. -pref("devtools.webconsole.fontSize", 0); - // Max number of inputs to store in web console history. pref("devtools.webconsole.inputHistoryCount", 50); diff --git a/devtools/client/shared/moz.build b/devtools/client/shared/moz.build index fc30d5b622c5..3ee00e1fe6f1 100644 --- a/devtools/client/shared/moz.build +++ b/devtools/client/shared/moz.build @@ -53,4 +53,5 @@ DevToolsModules( 'undo.js', 'view-source.js', 'webgl-utils.js', + 'zoom-keys.js', ) diff --git a/devtools/client/shared/zoom-keys.js b/devtools/client/shared/zoom-keys.js new file mode 100644 index 000000000000..289840027139 --- /dev/null +++ b/devtools/client/shared/zoom-keys.js @@ -0,0 +1,88 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Ci } = require("chrome"); +const Services = require("Services"); +const { KeyShortcuts } = require("devtools/client/shared/key-shortcuts"); + +const ZOOM_PREF = "devtools.toolbox.zoomValue"; +const MIN_ZOOM = 0.5; +const MAX_ZOOM = 2; + +const properties = "chrome://devtools/locale/toolbox.properties"; +const bundle = Services.strings.createBundle(properties); +function l10n(key) { + return bundle.GetStringFromName(key); +} + +/** + * Register generic keys to control zoom level of the given document. + * Used by both the toolboxes and the browser console. + * + * @param {DOMWindow} The window on which we should listent to key strokes and + * modify the zoom factor. + */ +exports.register = function (window) { + let shortcuts = new KeyShortcuts({ + window + }); + let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell); + let contViewer = docShell.contentViewer; + let zoomValue = parseFloat(Services.prefs.getCharPref(ZOOM_PREF)); + let zoomIn = function (name, event) { + setZoom(zoomValue + 0.1); + event.preventDefault(); + }; + + let zoomOut = function (name, event) { + setZoom(zoomValue - 0.1); + event.preventDefault(); + }; + + let zoomReset = function (name, event) { + setZoom(1); + event.preventDefault(); + }; + + let setZoom = function (newValue) { + // cap zoom value + zoomValue = Math.max(newValue, MIN_ZOOM); + zoomValue = Math.min(zoomValue, MAX_ZOOM); + + contViewer.fullZoom = zoomValue; + + Services.prefs.setCharPref(ZOOM_PREF, zoomValue); + }; + + // Set zoom to whatever the last setting was. + setZoom(zoomValue); + + shortcuts.on(l10n("toolbox.zoomIn.key"), zoomIn); + let zoomIn2 = l10n("toolbox.zoomIn2.key"); + if (zoomIn2) { + shortcuts.on(zoomIn2, zoomIn); + } + let zoomIn3 = l10n("toolbox.zoomIn2.key"); + if (zoomIn3) { + shortcuts.on(zoomIn3, zoomIn); + } + + shortcuts.on(l10n("toolbox.zoomOut.key"), + zoomOut); + let zoomOut2 = l10n("toolbox.zoomOut2.key"); + if (zoomOut2) { + shortcuts.on(zoomOut2, zoomOut); + } + + shortcuts.on(l10n("toolbox.zoomReset.key"), + zoomReset); + let zoomReset2 = l10n("toolbox.zoomReset2.key"); + if (zoomReset2) { + shortcuts.on(zoomReset2, zoomReset); + } +}; diff --git a/devtools/client/webconsole/hudservice.js b/devtools/client/webconsole/hudservice.js index b2da96a011c2..7b2c12e38804 100644 --- a/devtools/client/webconsole/hudservice.js +++ b/devtools/client/webconsole/hudservice.js @@ -669,9 +669,6 @@ BrowserConsole.prototype = extend(WebConsole.prototype, { }; window.addEventListener("unload", onClose); - // Make sure Ctrl-W closes the Browser Console window. - window.document.getElementById("cmd_close").removeAttribute("disabled"); - this._telemetry.toolOpened("browserconsole"); // Create an onFocus handler just to display the dev edition promo. diff --git a/devtools/client/webconsole/test/browser.ini b/devtools/client/webconsole/test/browser.ini index fdc2aff44ad3..5b6e111854c7 100644 --- a/devtools/client/webconsole/test/browser.ini +++ b/devtools/client/webconsole/test/browser.ini @@ -300,7 +300,6 @@ skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s [browser_webconsole_show_subresource_security_errors.js] skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243987 [browser_webconsole_cached_autocomplete.js] -[browser_webconsole_change_font_size.js] [browser_webconsole_chrome.js] [browser_webconsole_clickable_urls.js] [browser_webconsole_closure_inspection.js] diff --git a/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js b/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js index e2a068a3ebf3..c64e45f5d7e1 100644 --- a/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js +++ b/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js @@ -53,9 +53,13 @@ add_task(function* () { info("try ctrl-l to clear output"); executeSoon(() => { - let selector = "key[command=consoleCmd_clearOutput]:not([disabled])"; - let clearKey = hud.ui.window.document.querySelector(selector); - synthesizeKeyFromKeyTag(clearKey); + let clearShortcut; + if (Services.appinfo.OS === "Darwin") { + clearShortcut = WCUL10n.getStr("webconsole.clear.keyOSX"); + } else { + clearShortcut = WCUL10n.getStr("webconsole.clear.key"); + } + synthesizeKeyShortcut(clearShortcut); }); yield hud.jsterm.once("messages-cleared"); @@ -64,7 +68,7 @@ add_task(function* () { "jsterm input is focused"); info("try ctrl-f to focus filter"); - EventUtils.synthesizeKey("F", { accelKey: true }); + synthesizeKeyShortcut(WCUL10n.getStr("webconsole.find.key")); ok(!hud.jsterm.inputNode.getAttribute("focused"), "jsterm input is not focused"); is(hud.ui.filterBox.getAttribute("focused"), "true", diff --git a/devtools/client/webconsole/test/browser_webconsole_change_font_size.js b/devtools/client/webconsole/test/browser_webconsole_change_font_size.js deleted file mode 100644 index 3fae47795fe3..000000000000 --- a/devtools/client/webconsole/test/browser_webconsole_change_font_size.js +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const TEST_URI = "http://example.com/"; - -add_task(function* () { - yield loadTab(TEST_URI); - Services.prefs.setIntPref("devtools.webconsole.fontSize", 10); - let hud = yield HUDService.toggleBrowserConsole(); - - let inputNode = hud.jsterm.inputNode; - let outputNode = hud.jsterm.outputNode; - outputNode.focus(); - - EventUtils.synthesizeKey("-", { accelKey: true }, hud.iframeWindow); - is(inputNode.style.fontSize, "10px", - "input font stays at same size with ctrl+-"); - is(outputNode.style.fontSize, inputNode.style.fontSize, - "output font stays at same size with ctrl+-"); - - EventUtils.synthesizeKey("=", { accelKey: true }, hud.iframeWindow); - is(inputNode.style.fontSize, "11px", "input font increased with ctrl+="); - is(outputNode.style.fontSize, inputNode.style.fontSize, - "output font stays at same size with ctrl+="); - - EventUtils.synthesizeKey("-", { accelKey: true }, hud.iframeWindow); - is(inputNode.style.fontSize, "10px", "font decreased with ctrl+-"); - is(outputNode.style.fontSize, inputNode.style.fontSize, - "output font stays at same size with ctrl+-"); - - EventUtils.synthesizeKey("0", { accelKey: true }, hud.iframeWindow); - is(inputNode.style.fontSize, "", "font reset with ctrl+0"); - is(outputNode.style.fontSize, inputNode.style.fontSize, - "output font stays at same size with ctrl+0"); -}); diff --git a/devtools/client/webconsole/webconsole.js b/devtools/client/webconsole/webconsole.js index 68eb08ad310e..34b6d36c6186 100644 --- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -36,6 +36,8 @@ loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/wi loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm"); loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true); loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm"); +loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts", true); +loader.lazyRequireGetter(this, "ZoomKeys", "devtools/client/shared/zoom-keys"); const STRINGS_URI = "chrome://devtools/locale/webconsole.properties"; var l10n = new WebConsoleUtils.L10n(STRINGS_URI); @@ -521,12 +523,6 @@ WebConsoleFrame.prototype = { let doc = this.document; - if (system.constants.platform === "macosx") { - doc.querySelector("#key_clearOSX").removeAttribute("disabled"); - } else { - doc.querySelector("#key_clear").removeAttribute("disabled"); - } - this.filterBox = doc.querySelector(".hud-filter-box"); this.outputNode = doc.getElementById("output-container"); this.outputWrapper = doc.getElementById("output-wrapper"); @@ -537,25 +533,6 @@ WebConsoleFrame.prototype = { this._setFilterTextBoxEvents(); this._initFilterButtons(); - let fontSize = this.owner._browserConsole ? - Services.prefs.getIntPref("devtools.webconsole.fontSize") : - 0; - - if (fontSize != 0) { - fontSize = Math.max(MIN_FONT_SIZE, fontSize); - - this.outputNode.style.fontSize = fontSize + "px"; - this.completeNode.style.fontSize = fontSize + "px"; - this.inputNode.style.fontSize = fontSize + "px"; - } - - if (this.owner._browserConsole) { - for (let id of ["Enlarge", "Reduce", "Reset"]) { - this.document.getElementById("cmd_fullZoom" + id) - .removeAttribute("disabled"); - } - } - // Update the character width and height needed for the popup offset // calculations. this._updateCharSize(); @@ -627,6 +604,8 @@ WebConsoleFrame.prototype = { newValue: Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP), }); + this._initShortcuts(); + // focus input node this.jsterm.focus(); }, @@ -671,6 +650,34 @@ WebConsoleFrame.prototype = { } }, + _initShortcuts: function() { + var shortcuts = new KeyShortcuts({ + window: this.window + }); + + shortcuts.on(l10n.getStr("webconsole.find.key"), + (name, event) => { + this.filterBox.focus(); + event.preventDefault(); + }); + + let clearShortcut; + if (system.constants.platform === "macosx") { + clearShortcut = l10n.getStr("webconsole.clear.keyOSX"); + } else { + clearShortcut = l10n.getStr("webconsole.clear.key"); + } + shortcuts.on(clearShortcut, + () => this.jsterm.clearOutput(true)); + + if (this.owner._browserConsole) { + shortcuts.on(l10n.getStr("webconsole.close.key"), + this.window.close.bind(this.window)); + + ZoomKeys.register(this.window); + } + }, + /** * Attach / detach reflow listeners depending on the checked status * of the `CSS > Log` menuitem. @@ -798,50 +805,6 @@ WebConsoleFrame.prototype = { } }, - /** - * Increase, decrease or reset the font size. - * - * @param string size - * The size of the font change. Accepted values are "+" and "-". - * An unmatched size assumes a font reset. - */ - changeFontSize: function (size) { - let fontSize = this.window - .getComputedStyle(this.outputNode, null) - .getPropertyValue("font-size").replace("px", ""); - - if (this.outputNode.style.fontSize) { - fontSize = this.outputNode.style.fontSize.replace("px", ""); - } - - if (size == "+" || size == "-") { - fontSize = parseInt(fontSize, 10); - - if (size == "+") { - fontSize += 1; - } else { - fontSize -= 1; - } - - if (fontSize < MIN_FONT_SIZE) { - fontSize = MIN_FONT_SIZE; - } - - Services.prefs.setIntPref("devtools.webconsole.fontSize", fontSize); - fontSize = fontSize + "px"; - - this.completeNode.style.fontSize = fontSize; - this.inputNode.style.fontSize = fontSize; - this.outputNode.style.fontSize = fontSize; - } else { - this.completeNode.style.fontSize = ""; - this.inputNode.style.fontSize = ""; - this.outputNode.style.fontSize = ""; - Services.prefs.clearUserPref("devtools.webconsole.fontSize"); - } - this._updateCharSize(); - }, - /** * Calculates the width and height of a single character of the input box. * This will be used in opening the popup at the correct offset. @@ -3049,15 +3012,8 @@ CommandController.prototype = { return this.owner._contextMenuHandler.lastClickedMessage && !this.owner.output.getSelectedMessages(1)[0]; } - case "consoleCmd_clearOutput": case "cmd_selectAll": - case "cmd_find": return true; - case "cmd_fontSizeEnlarge": - case "cmd_fontSizeReduce": - case "cmd_fontSizeReset": - case "cmd_close": - return this.owner.owner._browserConsole; } return false; }, @@ -3070,30 +3026,12 @@ CommandController.prototype = { case "consoleCmd_copyURL": this.copyURL(); break; - case "consoleCmd_clearOutput": - this.owner.jsterm.clearOutput(true); - break; case "cmd_copy": this.copyLastClicked(); break; - case "cmd_find": - this.owner.filterBox.focus(); - break; case "cmd_selectAll": this.selectAll(); break; - case "cmd_fontSizeEnlarge": - this.owner.changeFontSize("+"); - break; - case "cmd_fontSizeReduce": - this.owner.changeFontSize("-"); - break; - case "cmd_fontSizeReset": - this.owner.changeFontSize(""); - break; - case "cmd_close": - this.owner.window.close(); - break; } } }; diff --git a/devtools/client/webconsole/webconsole.xul b/devtools/client/webconsole/webconsole.xul index 9d64bf6a9c59..38344eb19325 100644 --- a/devtools/client/webconsole/webconsole.xul +++ b/devtools/client/webconsole/webconsole.xul @@ -47,29 +47,8 @@ function goUpdateConsoleCommands() { oncommand="goDoCommand('consoleCmd_openURL');"/> - - - - - - - - - - - - - - - - - - -