diff options
Diffstat (limited to 'components/search')
-rw-r--r-- | components/search/content/engineManager.js | 492 | ||||
-rw-r--r-- | components/search/content/engineManager.xul | 93 | ||||
-rw-r--r-- | components/search/content/search.xml | 837 | ||||
-rw-r--r-- | components/search/content/searchbarBindings.css | 13 | ||||
-rw-r--r-- | components/search/jar.mn | 9 | ||||
-rw-r--r-- | components/search/moz.build | 7 |
6 files changed, 0 insertions, 1451 deletions
diff --git a/components/search/content/engineManager.js b/components/search/content/engineManager.js deleted file mode 100644 index 993d48b..0000000 --- a/components/search/content/engineManager.js +++ /dev/null @@ -1,492 +0,0 @@ -/* 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/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/Services.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); - -const Ci = Components.interfaces; -const Cc = Components.classes; - -const ENGINE_FLAVOR = "text/x-moz-search-engine"; - -const BROWSER_SUGGEST_PREF = "browser.search.suggest.enabled"; - -var gEngineView = null; - -var gEngineManagerDialog = { - init: function engineManager_init() { - gEngineView = new EngineView(new EngineStore()); - - var suggestEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF); - document.getElementById("enableSuggest").checked = suggestEnabled; - - var tree = document.getElementById("engineList"); - tree.view = gEngineView; - - Services.obs.addObserver(this, "browser-search-engine-modified", false); - }, - - destroy: function engineManager_destroy() { - // Remove the observer - Services.obs.removeObserver(this, "browser-search-engine-modified"); - }, - - observe: function engineManager_observe(aEngine, aTopic, aVerb) { - if (aTopic == "browser-search-engine-modified") { - aEngine.QueryInterface(Ci.nsISearchEngine); - switch (aVerb) { - case "engine-added": - gEngineView._engineStore.addEngine(aEngine); - gEngineView.rowCountChanged(gEngineView.lastIndex, 1); - break; - case "engine-changed": - gEngineView._engineStore.reloadIcons(); - gEngineView.invalidate(); - break; - case "engine-removed": - case "engine-current": - case "engine-default": - // Not relevant - break; - } - } - }, - - onOK: function engineManager_onOK() { - // Set the preference - var newSuggestEnabled = document.getElementById("enableSuggest").checked; - Services.prefs.setBoolPref(BROWSER_SUGGEST_PREF, newSuggestEnabled); - - // Commit the changes - gEngineView._engineStore.commit(); - }, - - onRestoreDefaults: function engineManager_onRestoreDefaults() { - var num = gEngineView._engineStore.restoreDefaultEngines(); - gEngineView.rowCountChanged(0, num); - gEngineView.invalidate(); - }, - - showRestoreDefaults: function engineManager_showRestoreDefaults(val) { - document.documentElement.getButton("extra2").disabled = !val; - }, - - loadAddEngines: function engineManager_loadAddEngines() { - this.onOK(); - window.opener.BrowserSearch.loadAddEngines(); - window.close(); - }, - - remove: function engineManager_remove() { - gEngineView._engineStore.removeEngine(gEngineView.selectedEngine); - var index = gEngineView.selectedIndex; - gEngineView.rowCountChanged(index, -1); - gEngineView.invalidate(); - gEngineView.selection.select(Math.min(index, gEngineView.lastIndex)); - gEngineView.ensureRowIsVisible(gEngineView.currentIndex); - document.getElementById("engineList").focus(); - }, - - /** - * Moves the selected engine either up or down in the engine list - * @param aDir - * -1 to move the selected engine down, +1 to move it up. - */ - bump: function engineManager_move(aDir) { - var selectedEngine = gEngineView.selectedEngine; - var newIndex = gEngineView.selectedIndex - aDir; - - gEngineView._engineStore.moveEngine(selectedEngine, newIndex); - - gEngineView.invalidate(); - gEngineView.selection.select(newIndex); - gEngineView.ensureRowIsVisible(newIndex); - this.showRestoreDefaults(true); - document.getElementById("engineList").focus(); - }, - - editKeyword: Task.async(function* engineManager_editKeyword() { - var selectedEngine = gEngineView.selectedEngine; - if (!selectedEngine) - return; - - var alias = { value: selectedEngine.alias }; - var strings = document.getElementById("engineManagerBundle"); - var title = strings.getString("editTitle"); - var msg = strings.getFormattedString("editMsg", [selectedEngine.name]); - - while (Services.prompt.prompt(window, title, msg, alias, null, {})) { - var bduplicate = false; - var eduplicate = false; - var dupName = ""; - - if (alias.value != "") { - // Check for duplicates in Places keywords. - bduplicate = !!(yield PlacesUtils.keywords.fetch(alias.value)); - - // Check for duplicates in changes we haven't committed yet - let engines = gEngineView._engineStore.engines; - for each (let engine in engines) { - if (engine.alias == alias.value && - engine.name != selectedEngine.name) { - eduplicate = true; - dupName = engine.name; - break; - } - } - } - - // Notify the user if they have chosen an existing engine/bookmark keyword - if (eduplicate || bduplicate) { - var dtitle = strings.getString("duplicateTitle"); - var bmsg = strings.getString("duplicateBookmarkMsg"); - var emsg = strings.getFormattedString("duplicateEngineMsg", [dupName]); - - Services.prompt.alert(window, dtitle, eduplicate ? emsg : bmsg); - } else { - gEngineView._engineStore.changeEngine(selectedEngine, "alias", - alias.value); - gEngineView.invalidate(); - break; - } - } - }), - - onSelect: function engineManager_onSelect() { - // Buttons only work if an engine is selected and it's not the last engine, - // the latter is true when the selected is first and last at the same time. - var lastSelected = (gEngineView.selectedIndex == gEngineView.lastIndex); - var firstSelected = (gEngineView.selectedIndex == 0); - var noSelection = (gEngineView.selectedIndex == -1); - - document.getElementById("cmd_remove") - .setAttribute("disabled", noSelection || - (firstSelected && lastSelected)); - - document.getElementById("cmd_moveup") - .setAttribute("disabled", noSelection || firstSelected); - - document.getElementById("cmd_movedown") - .setAttribute("disabled", noSelection || lastSelected); - - document.getElementById("cmd_editkeyword") - .setAttribute("disabled", noSelection); - } -}; - -function onDragEngineStart(event) { - var selectedIndex = gEngineView.selectedIndex; - if (selectedIndex >= 0) { - event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString()); - event.dataTransfer.effectAllowed = "move"; - } -} - -// "Operation" objects -function EngineMoveOp(aEngineClone, aNewIndex) { - if (!aEngineClone) - throw new Error("bad args to new EngineMoveOp!"); - this._engine = aEngineClone.originalEngine; - this._newIndex = aNewIndex; -} -EngineMoveOp.prototype = { - _engine: null, - _newIndex: null, - commit: function EMO_commit() { - Services.search.moveEngine(this._engine, this._newIndex); - } -} - -function EngineRemoveOp(aEngineClone) { - if (!aEngineClone) - throw new Error("bad args to new EngineRemoveOp!"); - this._engine = aEngineClone.originalEngine; -} -EngineRemoveOp.prototype = { - _engine: null, - commit: function ERO_commit() { - Services.search.removeEngine(this._engine); - } -} - -function EngineUnhideOp(aEngineClone, aNewIndex) { - if (!aEngineClone) - throw new Error("bad args to new EngineUnhideOp!"); - this._engine = aEngineClone.originalEngine; - this._newIndex = aNewIndex; -} -EngineUnhideOp.prototype = { - _engine: null, - _newIndex: null, - commit: function EUO_commit() { - this._engine.hidden = false; - Services.search.moveEngine(this._engine, this._newIndex); - } -} - -function EngineChangeOp(aEngineClone, aProp, aValue) { - if (!aEngineClone) - throw new Error("bad args to new EngineChangeOp!"); - - this._engine = aEngineClone.originalEngine; - this._prop = aProp; - this._newValue = aValue; -} -EngineChangeOp.prototype = { - _engine: null, - _prop: null, - _newValue: null, - commit: function ECO_commit() { - this._engine[this._prop] = this._newValue; - } -} - -function EngineStore() { - this._engines = Services.search.getVisibleEngines().map(this._cloneEngine); - this._defaultEngines = Services.search.getDefaultEngines().map(this._cloneEngine); - - this._ops = []; - - // check if we need to disable the restore defaults button - var someHidden = this._defaultEngines.some(function (e) e.hidden); - gEngineManagerDialog.showRestoreDefaults(someHidden); -} -EngineStore.prototype = { - _engines: null, - _defaultEngines: null, - _ops: null, - - get engines() { - return this._engines; - }, - set engines(val) { - this._engines = val; - return val; - }, - - _getIndexForEngine: function ES_getIndexForEngine(aEngine) { - return this._engines.indexOf(aEngine); - }, - - _getEngineByName: function ES_getEngineByName(aName) { - for each (var engine in this._engines) - if (engine.name == aName) - return engine; - - return null; - }, - - _cloneEngine: function ES_cloneEngine(aEngine) { - var clonedObj={}; - for (var i in aEngine) - clonedObj[i] = aEngine[i]; - clonedObj.originalEngine = aEngine; - return clonedObj; - }, - - // Callback for Array's some(). A thisObj must be passed to some() - _isSameEngine: function ES_isSameEngine(aEngineClone) { - return aEngineClone.originalEngine == this.originalEngine; - }, - - commit: function ES_commit() { - var currentEngine = this._cloneEngine(Services.search.currentEngine); - for (var i = 0; i < this._ops.length; i++) - this._ops[i].commit(); - - // Restore currentEngine if it is a default engine that is still visible. - // Needed if the user deletes currentEngine and then restores it. - if (this._defaultEngines.some(this._isSameEngine, currentEngine) && - !currentEngine.originalEngine.hidden) - Services.search.currentEngine = currentEngine.originalEngine; - }, - - addEngine: function ES_addEngine(aEngine) { - this._engines.push(this._cloneEngine(aEngine)); - }, - - moveEngine: function ES_moveEngine(aEngine, aNewIndex) { - if (aNewIndex < 0 || aNewIndex > this._engines.length - 1) - throw new Error("ES_moveEngine: invalid aNewIndex!"); - var index = this._getIndexForEngine(aEngine); - if (index == -1) - throw new Error("ES_moveEngine: invalid engine?"); - - if (index == aNewIndex) - return; // nothing to do - - // Move the engine in our internal store - var removedEngine = this._engines.splice(index, 1)[0]; - this._engines.splice(aNewIndex, 0, removedEngine); - - this._ops.push(new EngineMoveOp(aEngine, aNewIndex)); - }, - - removeEngine: function ES_removeEngine(aEngine) { - var index = this._getIndexForEngine(aEngine); - if (index == -1) - throw new Error("invalid engine?"); - - this._engines.splice(index, 1); - this._ops.push(new EngineRemoveOp(aEngine)); - if (this._defaultEngines.some(this._isSameEngine, aEngine)) - gEngineManagerDialog.showRestoreDefaults(true); - }, - - restoreDefaultEngines: function ES_restoreDefaultEngines() { - var added = 0; - - for (var i = 0; i < this._defaultEngines.length; ++i) { - var e = this._defaultEngines[i]; - - // If the engine is already in the list, just move it. - if (this._engines.some(this._isSameEngine, e)) { - this.moveEngine(this._getEngineByName(e.name), i); - } else { - // Otherwise, add it back to our internal store - this._engines.splice(i, 0, e); - this._ops.push(new EngineUnhideOp(e, i)); - added++; - } - } - gEngineManagerDialog.showRestoreDefaults(false); - return added; - }, - - changeEngine: function ES_changeEngine(aEngine, aProp, aNewValue) { - var index = this._getIndexForEngine(aEngine); - if (index == -1) - throw new Error("invalid engine?"); - - this._engines[index][aProp] = aNewValue; - this._ops.push(new EngineChangeOp(aEngine, aProp, aNewValue)); - }, - - reloadIcons: function ES_reloadIcons() { - this._engines.forEach(function (e) { - e.uri = e.originalEngine.uri; - }); - } -} - -function EngineView(aEngineStore) { - this._engineStore = aEngineStore; -} -EngineView.prototype = { - _engineStore: null, - tree: null, - - get lastIndex() { - return this.rowCount - 1; - }, - get selectedIndex() { - var seln = this.selection; - if (seln.getRangeCount() > 0) { - var min = {}; - seln.getRangeAt(0, min, {}); - return min.value; - } - return -1; - }, - get selectedEngine() { - return this._engineStore.engines[this.selectedIndex]; - }, - - // Helpers - rowCountChanged: function (index, count) { - this.tree.rowCountChanged(index, count); - }, - - invalidate: function () { - this.tree.invalidate(); - }, - - ensureRowIsVisible: function (index) { - this.tree.ensureRowIsVisible(index); - }, - - getSourceIndexFromDrag: function (dataTransfer) { - return parseInt(dataTransfer.getData(ENGINE_FLAVOR)); - }, - - // nsITreeView - get rowCount() { - return this._engineStore.engines.length; - }, - - getImageSrc: function(index, column) { - if (column.id == "engineName" && this._engineStore.engines[index].iconURI) - return this._engineStore.engines[index].iconURI.spec; - return ""; - }, - - getCellText: function(index, column) { - if (column.id == "engineName") - return this._engineStore.engines[index].name; - else if (column.id == "engineKeyword") - return this._engineStore.engines[index].alias; - return ""; - }, - - setTree: function(tree) { - this.tree = tree; - }, - - canDrop: function(targetIndex, orientation, dataTransfer) { - var sourceIndex = this.getSourceIndexFromDrag(dataTransfer); - return (sourceIndex != -1 && - sourceIndex != targetIndex && - sourceIndex != targetIndex + orientation); - }, - - drop: function(dropIndex, orientation, dataTransfer) { - var sourceIndex = this.getSourceIndexFromDrag(dataTransfer); - var sourceEngine = this._engineStore.engines[sourceIndex]; - - if (dropIndex > sourceIndex) { - if (orientation == Ci.nsITreeView.DROP_BEFORE) - dropIndex--; - } else { - if (orientation == Ci.nsITreeView.DROP_AFTER) - dropIndex++; - } - - this._engineStore.moveEngine(sourceEngine, dropIndex); - gEngineManagerDialog.showRestoreDefaults(true); - - // Redraw, and adjust selection - this.invalidate(); - this.selection.select(dropIndex); - }, - - selection: null, - getRowProperties: function(index) { return ""; }, - getCellProperties: function(index, column) { return ""; }, - getColumnProperties: function(column) { return ""; }, - isContainer: function(index) { return false; }, - isContainerOpen: function(index) { return false; }, - isContainerEmpty: function(index) { return false; }, - isSeparator: function(index) { return false; }, - isSorted: function(index) { return false; }, - getParentIndex: function(index) { return -1; }, - hasNextSibling: function(parentIndex, index) { return false; }, - getLevel: function(index) { return 0; }, - getProgressMode: function(index, column) { }, - getCellValue: function(index, column) { }, - toggleOpenState: function(index) { }, - cycleHeader: function(column) { }, - selectionChanged: function() { }, - cycleCell: function(row, column) { }, - isEditable: function(index, column) { return false; }, - isSelectable: function(index, column) { return false; }, - setCellValue: function(index, column, value) { }, - setCellText: function(index, column, value) { }, - performAction: function(action) { }, - performActionOnRow: function(action, index) { }, - performActionOnCell: function(action, index, column) { } -}; diff --git a/components/search/content/engineManager.xul b/components/search/content/engineManager.xul deleted file mode 100644 index 1152ef8..0000000 --- a/components/search/content/engineManager.xul +++ /dev/null @@ -1,93 +0,0 @@ -<?xml version="1.0"?> -<!-- 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/. --> - -<?xml-stylesheet href="chrome://global/skin/"?> -<?xml-stylesheet href="chrome://browser/skin/engineManager.css"?> - -<!DOCTYPE dialog SYSTEM "chrome://browser/locale/engineManager.dtd"> - -<dialog id="engineManager" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - buttons="accept,cancel,extra2" - buttonlabelextra2="&restoreDefaults.label;" - buttonaccesskeyextra2="&restoreDefaults.accesskey;" - onload="gEngineManagerDialog.init();" - onunload="gEngineManagerDialog.destroy();" - ondialogaccept="gEngineManagerDialog.onOK();" - ondialogextra2="gEngineManagerDialog.onRestoreDefaults();" - title="&engineManager.title;" - style="&engineManager.style;" - persist="screenX screenY width height" - windowtype="Browser:SearchManager"> - - <script type="application/javascript" - src="chrome://browser/content/search/engineManager.js"/> - - <commandset id="engineManagerCommandSet"> - <command id="cmd_remove" - oncommand="gEngineManagerDialog.remove();" - disabled="true"/> - <command id="cmd_moveup" - oncommand="gEngineManagerDialog.bump(1);" - disabled="true"/> - <command id="cmd_movedown" - oncommand="gEngineManagerDialog.bump(-1);" - disabled="true"/> - <command id="cmd_editkeyword" - oncommand="gEngineManagerDialog.editKeyword().catch(Components.utils.reportError);" - disabled="true"/> - </commandset> - - <keyset id="engineManagerKeyset"> - <key id="delete" keycode="VK_DELETE" command="cmd_remove"/> - </keyset> - - <stringbundleset id="engineManagerBundleset"> - <stringbundle id="engineManagerBundle" src="chrome://browser/locale/engineManager.properties"/> - </stringbundleset> - - <description>&engineManager.intro;</description> - <separator class="thin"/> - <hbox flex="1"> - <tree id="engineList" flex="1" rows="10" hidecolumnpicker="true" - seltype="single" onselect="gEngineManagerDialog.onSelect();"> - <treechildren id="engineChildren" flex="1" - ondragstart="onDragEngineStart(event);"/> - <treecols> - <treecol id="engineName" flex="4" label="&columnLabel.name;"/> - <treecol id="engineKeyword" flex="1" label="&columnLabel.keyword;"/> - </treecols> - </tree> - <vbox> - <spacer flex="1"/> - <button id="edit" - label="&edit.label;" - accesskey="&edit.accesskey;" - command="cmd_editkeyword"/> - <button id="up" - label="&up.label;" - accesskey="&up.accesskey;" - command="cmd_moveup"/> - <button id="down" - label="&dn.label;" - accesskey="&dn.accesskey;" - command="cmd_movedown"/> - <spacer flex="1"/> - <button id="remove" - label="&remove.label;" - accesskey="&remove.accesskey;" - command="cmd_remove"/> - </vbox> - </hbox> - <hbox> - <checkbox id="enableSuggest" - label="&enableSuggest.label;" - accesskey="&enableSuggest.accesskey;"/> - </hbox> - <hbox> - <label id="addEngines" class="text-link" value="&addEngine.label;" - onclick="if (event.button == 0) { gEngineManagerDialog.loadAddEngines(); }"/> - </hbox> -</dialog> diff --git a/components/search/content/search.xml b/components/search/content/search.xml deleted file mode 100644 index 0c33b15..0000000 --- a/components/search/content/search.xml +++ /dev/null @@ -1,837 +0,0 @@ -<?xml version="1.0"?> -# -*- Mode: HTML -*- -# 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/. - -<!DOCTYPE bindings [ -<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd" > -%searchBarDTD; -<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd"> -%browserDTD; -]> - -<bindings id="SearchBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="searchbar"> - <resources> - <stylesheet src="chrome://browser/content/search/searchbarBindings.css"/> - <stylesheet src="chrome://browser/skin/searchbar.css"/> - </resources> - <content> - <xul:stringbundle src="chrome://browser/locale/search.properties" - anonid="searchbar-stringbundle"/> - - <xul:textbox class="searchbar-textbox" - anonid="searchbar-textbox" - type="autocomplete" - flex="1" - autocompletepopup="PopupAutoComplete" - autocompletesearch="search-autocomplete" - autocompletesearchparam="searchbar-history" - timeout="250" - maxrows="10" - completeselectedindex="true" - showcommentcolumn="true" - tabscrolling="true" - xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines"> - <xul:box> - <xul:button class="searchbar-engine-button" - type="menu" - anonid="searchbar-engine-button"> - <xul:image class="searchbar-engine-image" xbl:inherits="src"/> - <xul:image class="searchbar-dropmarker-image"/> - <xul:menupopup class="searchbar-popup" - anonid="searchbar-popup"> - <xul:menuseparator/> - <xul:menuitem class="open-engine-manager" - anonid="open-engine-manager" - label="&cmd_engineManager.label;" - oncommand="openManager(event);"/> - </xul:menupopup> - </xul:button> - </xul:box> - <xul:hbox class="search-go-container"> - <xul:image class="search-go-button" - anonid="search-go-button" - onclick="handleSearchCommand(event);" - tooltiptext="&searchEndCap.label;"/> - </xul:hbox> - </xul:textbox> - </content> - - <implementation implements="nsIObserver"> - <constructor><![CDATA[ - if (this.parentNode.parentNode.localName == "toolbarpaletteitem") - return; - // Make sure we rebuild the popup in onpopupshowing - this._needToBuildPopup = true; - - var os = - Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - os.addObserver(this, "browser-search-engine-modified", false); - - this._initialized = true; - - this.searchService.init((function search_init_cb(aStatus) { - // Bail out if the binding has been destroyed - if (!this._initialized) - return; - - if (Components.isSuccessCode(aStatus)) { - // Refresh the display (updating icon, etc) - this.updateDisplay(); - } else { - Components.utils.reportError("Cannot initialize search service, bailing out: " + aStatus); - } - }).bind(this)); - ]]></constructor> - - <destructor><![CDATA[ - if (this._initialized) { - this._initialized = false; - - var os = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - os.removeObserver(this, "browser-search-engine-modified"); - } - - // Make sure to break the cycle from _textbox to us. Otherwise we leak - // the world. But make sure it's actually pointing to us. - if (this._textbox.mController.input == this) - this._textbox.mController.input = null; - ]]></destructor> - - <field name="_stringBundle">document.getAnonymousElementByAttribute(this, - "anonid", "searchbar-stringbundle");</field> - <field name="_textbox">document.getAnonymousElementByAttribute(this, - "anonid", "searchbar-textbox");</field> - <field name="_popup">document.getAnonymousElementByAttribute(this, - "anonid", "searchbar-popup");</field> - <field name="_ss">null</field> - <field name="_engines">null</field> - <field name="FormHistory" readonly="true"> - (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory; - </field> - - <property name="engines" readonly="true"> - <getter><![CDATA[ - if (!this._engines) - this._engines = this.searchService.getVisibleEngines(); - return this._engines; - ]]></getter> - </property> - - <field name="searchButton">document.getAnonymousElementByAttribute(this, - "anonid", "searchbar-engine-button");</field> - - <property name="currentEngine"> - <setter><![CDATA[ - let ss = this.searchService; - ss.defaultEngine = ss.currentEngine = val; - return val; - ]]></setter> - <getter><![CDATA[ - var currentEngine = this.searchService.currentEngine; - // Return a dummy engine if there is no currentEngine - return currentEngine || {name: "", uri: null}; - ]]></getter> - </property> - - <!-- textbox is used by sanitize.js to clear the undo history when - clearing form information. --> - <property name="textbox" readonly="true" - onget="return this._textbox;"/> - - <property name="searchService" readonly="true"> - <getter><![CDATA[ - if (!this._ss) { - const nsIBSS = Components.interfaces.nsIBrowserSearchService; - this._ss = - Components.classes["@mozilla.org/browser/search-service;1"] - .getService(nsIBSS); - } - return this._ss; - ]]></getter> - </property> - - <property name="value" onget="return this._textbox.value;" - onset="return this._textbox.value = val;"/> - - <method name="focus"> - <body><![CDATA[ - this._textbox.focus(); - ]]></body> - </method> - - <method name="select"> - <body><![CDATA[ - this._textbox.select(); - ]]></body> - </method> - - <method name="observe"> - <parameter name="aEngine"/> - <parameter name="aTopic"/> - <parameter name="aVerb"/> - <body><![CDATA[ - if (aTopic == "browser-search-engine-modified") { - switch (aVerb) { - case "engine-removed": - this.offerNewEngine(aEngine); - break; - case "engine-added": - this.hideNewEngine(aEngine); - break; - case "engine-current": - // The current engine was changed. Rebuilding the menu appears to - // confuse its idea of whether it should be open when it's just - // been clicked, so we force it to close now. - this._popup.hidePopup(); - break; - case "engine-changed": - // An engine was removed (or hidden) or added, or an icon was - // changed. Do nothing special. - } - - // Make sure the engine list is refetched next time it's needed - this._engines = null; - - // Rebuild the popup and update the display after any modification. - this.rebuildPopup(); - this.updateDisplay(); - } - ]]></body> - </method> - - <!-- There are two seaprate lists of search engines, whose uses intersect - in this file. The search service (nsIBrowserSearchService and - nsSearchService.js) maintains a list of Engine objects which is used to - populate the searchbox list of available engines and to perform queries. - That list is accessed here via this.SearchService, and it's that sort of - Engine that is passed to this binding's observer as aEngine. - - In addition, browser.js fills two lists of autodetected search engines - (browser.engines and browser.hiddenEngines) as properties of - mCurrentBrowser. Those lists contain unnamed JS objects of the form - { uri:, title:, icon: }, and that's what the searchbar uses to determine - whether to show any "Add <EngineName>" menu items in the drop-down. - - The two types of engines are currently related by their identifying - titles (the Engine object's 'name'), although that may change; see bug - 335102. --> - - <!-- If the engine that was just removed from the searchbox list was - autodetected on this page, move it to each browser's active list so it - will be offered to be added again. --> - <method name="offerNewEngine"> - <parameter name="aEngine"/> - <body><![CDATA[ - var allbrowsers = getBrowser().mPanelContainer.childNodes; - for (var tab = 0; tab < allbrowsers.length; tab++) { - var browser = getBrowser().getBrowserAtIndex(tab); - if (browser.hiddenEngines) { - // XXX This will need to be changed when engines are identified by - // URL rather than title; see bug 335102. - var removeTitle = aEngine.wrappedJSObject.name; - for (var i = 0; i < browser.hiddenEngines.length; i++) { - if (browser.hiddenEngines[i].title == removeTitle) { - if (!browser.engines) - browser.engines = []; - browser.engines.push(browser.hiddenEngines[i]); - browser.hiddenEngines.splice(i, 1); - break; - } - } - } - } - ]]></body> - </method> - - <!-- If the engine that was just added to the searchbox list was - autodetected on this page, move it to each browser's hidden list so it is - no longer offered to be added. --> - <method name="hideNewEngine"> - <parameter name="aEngine"/> - <body><![CDATA[ - var allbrowsers = getBrowser().mPanelContainer.childNodes; - for (var tab = 0; tab < allbrowsers.length; tab++) { - var browser = getBrowser().getBrowserAtIndex(tab); - if (browser.engines) { - // XXX This will need to be changed when engines are identified by - // URL rather than title; see bug 335102. - var removeTitle = aEngine.wrappedJSObject.name; - for (var i = 0; i < browser.engines.length; i++) { - if (browser.engines[i].title == removeTitle) { - if (!browser.hiddenEngines) - browser.hiddenEngines = []; - browser.hiddenEngines.push(browser.engines[i]); - browser.engines.splice(i, 1); - break; - } - } - } - } - ]]></body> - </method> - - <method name="setIcon"> - <parameter name="element"/> - <parameter name="uri"/> - <body><![CDATA[ - element.setAttribute("src", uri); - ]]></body> - </method> - - <method name="updateDisplay"> - <body><![CDATA[ - var uri = this.currentEngine.iconURI; - this.setIcon(this, uri ? uri.spec : ""); - - var name = this.currentEngine.name; - var text = this._stringBundle.getFormattedString("searchtip", [name]); - this._textbox.placeholder = name; - this._textbox.label = text; - this._textbox.tooltipText = text; - ]]></body> - </method> - - <!-- Rebuilds the dynamic portion of the popup menu (i.e., the menu items - for new search engines that can be added to the available list). This - is called each time the popup is shown. - --> - <method name="rebuildPopupDynamic"> - <body><![CDATA[ - // We might not have added the main popup items yet, do that first - // if needed. - if (this._needToBuildPopup) - this.rebuildPopup(); - - var popup = this._popup; - // Clear any addengine menuitems, including addengine-item entries and - // the addengine-separator. Work backward to avoid invalidating the - // indexes as items are removed. - var items = popup.childNodes; - for (var i = items.length - 1; i >= 0; i--) { - if (items[i].classList.contains("addengine-item") || - items[i].classList.contains("addengine-separator")) - popup.removeChild(items[i]); - } - - var addengines = getBrowser().mCurrentBrowser.engines; - if (addengines && addengines.length > 0) { - const kXULNS = - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - - // Find the (first) separator in the remaining menu, or the first item - // if no separators are present. - var insertLocation = popup.firstChild; - while (insertLocation.nextSibling && - insertLocation.localName != "menuseparator") { - insertLocation = insertLocation.nextSibling; - } - if (insertLocation.localName != "menuseparator") - insertLocation = popup.firstChild; - - var separator = document.createElementNS(kXULNS, "menuseparator"); - separator.setAttribute("class", "addengine-separator"); - popup.insertBefore(separator, insertLocation); - - // Insert the "add this engine" items. - for (var i = 0; i < addengines.length; i++) { - var menuitem = document.createElement("menuitem"); - var engineInfo = addengines[i]; - var labelStr = - this._stringBundle.getFormattedString("cmd_addFoundEngine", - [engineInfo.title]); - menuitem = document.createElementNS(kXULNS, "menuitem"); - menuitem.setAttribute("class", "menuitem-iconic addengine-item"); - menuitem.setAttribute("label", labelStr); - menuitem.setAttribute("tooltiptext", engineInfo.uri); - menuitem.setAttribute("uri", engineInfo.uri); - if (engineInfo.icon) - this.setIcon(menuitem, engineInfo.icon); - menuitem.setAttribute("title", engineInfo.title); - popup.insertBefore(menuitem, insertLocation); - } - } - ]]></body> - </method> - - <!-- Rebuilds the list of visible search engines in the menu. Does not remove - or update any dynamic entries (i.e., "Add this engine" items) nor the - Manage Engines item. This is called by the observer when the list of - visible engines, or the currently selected engine, has changed. - --> - <method name="rebuildPopup"> - <body><![CDATA[ - var popup = this._popup; - - // Clear the popup, down to the first separator - while (popup.firstChild && popup.firstChild.localName != "menuseparator") - popup.removeChild(popup.firstChild); - - const kXULNS = - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - - var engines = this.engines; - for (var i = engines.length - 1; i >= 0; --i) { - var menuitem = document.createElementNS(kXULNS, "menuitem"); - var name = engines[i].name; - menuitem.setAttribute("label", name); - menuitem.setAttribute("id", name); - menuitem.setAttribute("class", "menuitem-iconic searchbar-engine-menuitem menuitem-with-favicon"); - // Since this menu is rebuilt by the observer method whenever a new - // engine is selected, the "selected" attribute does not need to be - // explicitly cleared anywhere. - if (engines[i] == this.currentEngine) - menuitem.setAttribute("selected", "true"); - var tooltip = this._stringBundle.getFormattedString("searchtip", [name]); - menuitem.setAttribute("tooltiptext", tooltip); - if (engines[i].iconURI) - this.setIcon(menuitem, engines[i].iconURI.spec); - popup.insertBefore(menuitem, popup.firstChild); - menuitem.engine = engines[i]; - } - - this._needToBuildPopup = false; - ]]></body> - </method> - - <method name="openManager"> - <parameter name="aEvent"/> - <body><![CDATA[ - var wm = - Components.classes["@mozilla.org/appshell/window-mediator;1"] - .getService(Components.interfaces.nsIWindowMediator); - - var window = wm.getMostRecentWindow("Browser:SearchManager"); - if (window) - window.focus() - else { - setTimeout(function () { - openDialog("chrome://browser/content/search/engineManager.xul", - "_blank", "chrome,dialog,modal,centerscreen,resizable"); - }, 0); - } - ]]></body> - </method> - - <method name="selectEngine"> - <parameter name="aEvent"/> - <parameter name="isNextEngine"/> - <body><![CDATA[ - // Find the new index - var newIndex = this.engines.indexOf(this.currentEngine); - newIndex += isNextEngine ? 1 : -1; - - if (newIndex >= 0 && newIndex < this.engines.length) { - this.currentEngine = this.engines[newIndex]; - } - - aEvent.preventDefault(); - aEvent.stopPropagation(); - ]]></body> - </method> - - <method name="handleSearchCommand"> - <parameter name="aEvent"/> - <body><![CDATA[ - var textBox = this._textbox; - var textValue = textBox.value; - - var where = "current"; - if (aEvent && aEvent.originalTarget.getAttribute("anonid") == "search-go-button") { - if (aEvent.button == 2) - return; - where = whereToOpenLink(aEvent, false, true); - } - else { - var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab"); - if ((aEvent && aEvent.altKey) ^ newTabPref) - where = "tab"; - } - - this.doSearch(textValue, where); - ]]></body> - </method> - - <method name="doSearch"> - <parameter name="aData"/> - <parameter name="aWhere"/> - <body><![CDATA[ - var textBox = this._textbox; - - // Save the current value in the form history - if (aData && !PrivateBrowsingUtils.isWindowPrivate(window)) { - this.FormHistory.update( - { op : "bump", - fieldname : textBox.getAttribute("autocompletesearchparam"), - value : aData }, - { handleError : function(aError) { - Components.utils.reportError("Saving search to form history failed: " + aError.message); - }}); - } - - // null parameter below specifies HTML response for search - var submission = this.currentEngine.getSubmission(aData); - openUILinkIn(submission.uri.spec, aWhere, null, submission.postData); - ]]></body> - </method> - </implementation> - - <handlers> - <handler event="command"><![CDATA[ - const target = event.originalTarget; - if (target.engine) { - this.currentEngine = target.engine; - } else if (target.classList.contains("addengine-item")) { - var searchService = - Components.classes["@mozilla.org/browser/search-service;1"] - .getService(Components.interfaces.nsIBrowserSearchService); - // We only detect OpenSearch files - var type = Components.interfaces.nsISearchEngine.DATA_XML; - // Select the installed engine if the installation succeeds - var installCallback = { - onSuccess: engine => this.currentEngine = engine - } - searchService.addEngine(target.getAttribute("uri"), type, - target.getAttribute("src"), false, - installCallback); - } - else - return; - - this.focus(); - this.select(); - ]]></handler> - - <handler event="popupshowing" action="this.rebuildPopupDynamic();"/> - - <handler event="DOMMouseScroll" - phase="capturing" - modifiers="accel" - action="this.selectEngine(event, (event.detail > 0));"/> - - <handler event="focus"> - <![CDATA[ - // Speculatively connect to the current engine's search URI (and - // suggest URI, if different) to reduce request latency - - const SUGGEST_TYPE = "application/x-suggestions+json"; - var engine = this.currentEngine; - var connector = - Services.io.QueryInterface(Components.interfaces.nsISpeculativeConnect); - var searchURI = engine.getSubmission("dummy").uri; - let callbacks = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIWebNavigation) - .QueryInterface(Components.interfaces.nsILoadContext); - connector.speculativeConnect(searchURI, callbacks); - - if (engine.supportsResponseType(SUGGEST_TYPE)) { - var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE).uri; - if (suggestURI.prePath != searchURI.prePath) - connector.speculativeConnect(suggestURI, callbacks); - } - ]]></handler> - </handlers> - </binding> - - <binding id="searchbar-textbox" - extends="chrome://global/content/bindings/autocomplete.xml#autocomplete"> - <implementation implements="nsIObserver"> - <constructor><![CDATA[ - const kXULNS = - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - - if (document.getBindingParent(this).parentNode.parentNode.localName == - "toolbarpaletteitem") - return; - - // Initialize fields - this._stringBundle = document.getBindingParent(this)._stringBundle; - this._prefBranch = - Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - this._suggestEnabled = - this._prefBranch.getBoolPref("browser.search.suggest.enabled"); - this._clickSelectsAll = - this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll"); - - this.setAttribute("clickSelectsAll", this._clickSelectsAll); - - // Add items to context menu and attach controller to handle them - var textBox = document.getAnonymousElementByAttribute(this, - "anonid", "textbox-input-box"); - var cxmenu = document.getAnonymousElementByAttribute(textBox, - "anonid", "input-box-contextmenu"); - var pasteAndSearch; - cxmenu.addEventListener("popupshowing", function() { - if (!pasteAndSearch) - return; - var controller = document.commandDispatcher.getControllerForCommand("cmd_paste"); - var enabled = controller.isCommandEnabled("cmd_paste"); - if (enabled) - pasteAndSearch.removeAttribute("disabled"); - else - pasteAndSearch.setAttribute("disabled", "true"); - }, false); - - var element, label, akey; - - element = document.createElementNS(kXULNS, "menuseparator"); - cxmenu.appendChild(element); - - var insertLocation = cxmenu.firstChild; - while (insertLocation.nextSibling && - insertLocation.getAttribute("cmd") != "cmd_paste") - insertLocation = insertLocation.nextSibling; - if (insertLocation) { - element = document.createElementNS(kXULNS, "menuitem"); - label = this._stringBundle.getString("cmd_pasteAndSearch"); - element.setAttribute("label", label); - element.setAttribute("anonid", "paste-and-search"); - element.setAttribute("oncommand", - "BrowserSearch.searchBar.select(); goDoCommand('cmd_paste'); BrowserSearch.searchBar.handleSearchCommand();"); - cxmenu.insertBefore(element, insertLocation.nextSibling); - pasteAndSearch = element; - } - - element = document.createElementNS(kXULNS, "menuitem"); - label = this._stringBundle.getString("cmd_clearHistory"); - akey = this._stringBundle.getString("cmd_clearHistory_accesskey"); - element.setAttribute("label", label); - element.setAttribute("accesskey", akey); - element.setAttribute("cmd", "cmd_clearhistory"); - cxmenu.appendChild(element); - - element = document.createElementNS(kXULNS, "menuitem"); - label = this._stringBundle.getString("cmd_showSuggestions"); - akey = this._stringBundle.getString("cmd_showSuggestions_accesskey"); - element.setAttribute("anonid", "toggle-suggest-item"); - element.setAttribute("label", label); - element.setAttribute("accesskey", akey); - element.setAttribute("cmd", "cmd_togglesuggest"); - element.setAttribute("type", "checkbox"); - element.setAttribute("checked", this._suggestEnabled); - element.setAttribute("autocheck", "false"); - this._suggestMenuItem = element; - cxmenu.appendChild(element); - - this.controllers.appendController(this.searchbarController); - - // Add observer for suggest preference - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - prefs.addObserver("browser.search.suggest.enabled", this, false); - prefs.addObserver("browser.urlbar.clickSelectsAll", this, false); - ]]></constructor> - - <destructor><![CDATA[ - var prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - prefs.removeObserver("browser.search.suggest.enabled", this); - prefs.removeObserver("browser.urlbar.clickSelectsAll", this); - - // Because XBL and the customize toolbar code interacts poorly, - // there may not be anything to remove here - try { - this.controllers.removeController(this.searchbarController); - } catch (ex) { } - ]]></destructor> - - <field name="_stringBundle"/> - <field name="_prefBranch"/> - <field name="_suggestMenuItem"/> - <field name="_suggestEnabled"/> - <field name="_clickSelectsAll"/> - - <!-- - This overrides the searchParam property in autocomplete.xml. We're - hijacking this property as a vehicle for delivering the privacy - information about the window into the guts of nsSearchSuggestions. - - Note that the setter is the same as the parent. We were not sure whether - we can override just the getter. If that proves to be the case, the setter - can be removed. - --> - <property name="searchParam" - onget="return this.getAttribute('autocompletesearchparam') + - (PrivateBrowsingUtils.isWindowPrivate(window) ? '|private' : '');" - onset="this.setAttribute('autocompletesearchparam', val); return val;"/> - - <!-- - This method overrides the autocomplete binding's openPopup (essentially - duplicating the logic from the autocomplete popup binding's - openAutocompletePopup method), modifying it so that the popup is aligned with - the inner textbox, but sized to not extend beyond the search bar border. - --> - <method name="openPopup"> - <body><![CDATA[ - var popup = this.popup; - if (!popup.mPopupOpen) { - // Initially the panel used for the searchbar (PopupAutoComplete - // in browser.xul) is hidden to avoid impacting startup / new - // window performance. The base binding's openPopup would normally - // call the overriden openAutocompletePopup in urlbarBindings.xml's - // browser-autocomplete-result-popup binding to unhide the popup, - // but since we're overriding openPopup we need to unhide the panel - // ourselves. - popup.hidden = false; - - popup.mInput = this; - popup.view = this.controller.QueryInterface(Components.interfaces.nsITreeView); - popup.invalidate(); - - popup.showCommentColumn = this.showCommentColumn; - popup.showImageColumn = this.showImageColumn; - - document.popupNode = null; - - const isRTL = getComputedStyle(this, "").direction == "rtl"; - - var outerRect = this.getBoundingClientRect(); - var innerRect = this.inputField.getBoundingClientRect(); - if (isRTL) { - var width = innerRect.right - outerRect.left; - } else { - var width = outerRect.right - innerRect.left; - } - popup.setAttribute("width", width > 100 ? width : 100); - - var yOffset = outerRect.bottom - innerRect.bottom; - popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false); - } - ]]></body> - </method> - - <method name="observe"> - <parameter name="aSubject"/> - <parameter name="aTopic"/> - <parameter name="aData"/> - <body><![CDATA[ - if (aTopic == "nsPref:changed") { - switch (aData) { - case "browser.search.suggest.enabled": - this._suggestEnabled = this._prefBranch.getBoolPref(aData); - this._suggestMenuItem.setAttribute("checked", this._suggestEnabled); - break; - case "browser.urlbar.clickSelectsAll": - this._clickSelectsAll = this._prefBranch.getBoolPref(aData); - this.setAttribute("clickSelectsAll", this._clickSelectsAll); - break; - } - } - ]]></body> - </method> - - <method name="openSearch"> - <body> - <![CDATA[ - // Don't open search popup if history popup is open - if (!this.popupOpen) { - document.getBindingParent(this).searchButton.open = true; - return false; - } - return true; - ]]> - </body> - </method> - - <!-- override |onTextEntered| in autocomplete.xml --> - <method name="onTextEntered"> - <parameter name="aEvent"/> - <body><![CDATA[ - var evt = aEvent || this.mEnterEvent; - document.getBindingParent(this).handleSearchCommand(evt); - this.mEnterEvent = null; - ]]></body> - </method> - - <!-- nsIController --> - <field name="searchbarController" readonly="true"><![CDATA[({ - _self: this, - supportsCommand: function(aCommand) { - return aCommand == "cmd_clearhistory" || - aCommand == "cmd_togglesuggest"; - }, - - isCommandEnabled: function(aCommand) { - return true; - }, - - doCommand: function (aCommand) { - switch (aCommand) { - case "cmd_clearhistory": - var param = this._self.getAttribute("autocompletesearchparam"); - - let searchBar = this._self.parentNode; - - BrowserSearch.searchBar.FormHistory.update({ op : "remove", fieldname : param }, null); - this._self.value = ""; - break; - case "cmd_togglesuggest": - // The pref observer will update _suggestEnabled and the menu - // checkmark. - this._self._prefBranch.setBoolPref("browser.search.suggest.enabled", - !this._self._suggestEnabled); - break; - default: - // do nothing with unrecognized command - } - } - })]]></field> - </implementation> - - <handlers> - <handler event="keypress" keycode="VK_UP" modifiers="accel" - phase="capturing" - action="document.getBindingParent(this).selectEngine(event, false);"/> - - <handler event="keypress" keycode="VK_DOWN" modifiers="accel" - phase="capturing" - action="document.getBindingParent(this).selectEngine(event, true);"/> - - <handler event="keypress" keycode="VK_DOWN" modifiers="alt" - phase="capturing" - action="return this.openSearch();"/> - - <handler event="keypress" keycode="VK_UP" modifiers="alt" - phase="capturing" - action="return this.openSearch();"/> - -#ifndef XP_MACOSX - <handler event="keypress" keycode="VK_F4" - phase="capturing" - action="return this.openSearch();"/> -#endif - - <handler event="dragover"> - <![CDATA[ - var types = event.dataTransfer.types; - if (types.contains("text/plain") || types.contains("text/x-moz-text-internal")) - event.preventDefault(); - ]]> - </handler> - - <handler event="drop"> - <![CDATA[ - var dataTransfer = event.dataTransfer; - var data = dataTransfer.getData("text/plain"); - if (!data) - data = dataTransfer.getData("text/x-moz-text-internal"); - if (data) { - event.preventDefault(); - this.value = data; - this.onTextEntered(event); - } - ]]> - </handler> - - </handlers> - </binding> -</bindings> diff --git a/components/search/content/searchbarBindings.css b/components/search/content/searchbarBindings.css deleted file mode 100644 index b20e215..0000000 --- a/components/search/content/searchbarBindings.css +++ /dev/null @@ -1,13 +0,0 @@ -/* 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/. */ - -@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); - -.searchbar-textbox { - -moz-binding: url("chrome://browser/content/search/search.xml#searchbar-textbox"); -} - -.searchbar-engine-button { - -moz-user-focus: none; -} diff --git a/components/search/jar.mn b/components/search/jar.mn deleted file mode 100644 index e6c42f9..0000000 --- a/components/search/jar.mn +++ /dev/null @@ -1,9 +0,0 @@ -# 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/. - -browser.jar: -* content/browser/search/search.xml (content/search.xml) - content/browser/search/searchbarBindings.css (content/searchbarBindings.css) - content/browser/search/engineManager.xul (content/engineManager.xul) - content/browser/search/engineManager.js (content/engineManager.js) diff --git a/components/search/moz.build b/components/search/moz.build deleted file mode 100644 index c97072b..0000000 --- a/components/search/moz.build +++ /dev/null @@ -1,7 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file |