mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
531 lines
15 KiB
JavaScript
531 lines
15 KiB
JavaScript
/* ***** 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.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 2001
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Joe Hewitt <hewitt@netscape.com> (original author)
|
|
*
|
|
* 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 ***** */
|
|
|
|
/***************************************************************
|
|
* inSearchModule -----------------------------------------------
|
|
* Encapsulates an ISML module and exposes it to inSearchService.
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
* REQUIRED IMPORTS:
|
|
* chrome://inspector/content/jsutil/xpcom/XPCU.js
|
|
* chrome://inspector/content/jsutil/rdf/RDFU.js
|
|
* chrome://inspector/content/jsutil/rdf/RDFArray.js
|
|
* chrome://inspector/content/jsutil/xul/inFormManager.js
|
|
* chrome://inspector/content/search/xul/inSearchUtils.js
|
|
****************************************************************/
|
|
|
|
//////////// global variables /////////////////////
|
|
|
|
//////////// global constants ////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//// class inSearchModule
|
|
|
|
function inSearchModule(aBaseURL)
|
|
{
|
|
this.mObservers = [];
|
|
this.mBaseURL = aBaseURL;
|
|
}
|
|
|
|
inSearchModule.prototype =
|
|
{
|
|
mTitle: null,
|
|
mBaseURL: null,
|
|
mImpl: null,
|
|
mDialogElementIds: null,
|
|
mDialogURL: null,
|
|
mContextMenuItems: null,
|
|
mColumns: null,
|
|
mColDelimiter: null,
|
|
mNameSpace: null,
|
|
|
|
mRDFArray: null,
|
|
mResult: null,
|
|
mObservers: null,
|
|
|
|
mStartTime: null,
|
|
mElapsed: 0,
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Properties
|
|
|
|
get searchService() { return this.mSearchService},
|
|
set searchService(aVal) { this.mSearchService = aVal },
|
|
|
|
get title() { return this.mTitle },
|
|
get baseURL() { return this.mBaseURL },
|
|
get defaultIconURL() { return this.mDefaultIconURL },
|
|
|
|
get datasource() { return this.mRDFArray.datasource },
|
|
get resultCount() { return this.mRDFArray.length },
|
|
get progressPercent() { return this.mImpl.progressPercent },
|
|
get progressText() { return this.mImp.progressText },
|
|
get isPastMilestone() { return this.mImpl.isPastMilestone },
|
|
get elapsed() { return this.mElapsed },
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Initialization
|
|
|
|
initFromElement: function(aSearchEl)
|
|
{
|
|
this.parseSearch(aSearchEl);
|
|
|
|
if (this.mImpl.constructor)
|
|
this.mImpl.constructor();
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Parser
|
|
|
|
parseSearch: function(aSearchEl)
|
|
{
|
|
// get the title
|
|
this.mTitle = aSearchEl.getAttribute("title");
|
|
// get the default icon url
|
|
this.mDefaultIconURL = aSearchEl.getAttribute("defaultIcon");
|
|
// get the namespace
|
|
var ns = aSearchEl.getAttribute("namespace")
|
|
this.mNameSpace = ns ? ns : kInspectorNSURI;
|
|
|
|
this.parseDialog(aSearchEl);
|
|
this.parseContextMenu(aSearchEl);
|
|
this.parseColumns(aSearchEl);
|
|
this.parseImplementation(aSearchEl);
|
|
},
|
|
|
|
parseDialog: function(aSearchEl)
|
|
{
|
|
var els = aSearchEl.getElementsByTagNameNS(kISMLNSURI, "dialog");
|
|
if (els.length > 0) {
|
|
var dialogEl = els[0];
|
|
|
|
// get the array of dialog element ids
|
|
var ids = dialogEl.getAttribute("elements");
|
|
if (ids)
|
|
this.mDialogElementIds = ids.split(",");
|
|
|
|
// get the dialog url
|
|
this.setDialogURL(dialogEl.getAttribute("href"));
|
|
// get the dialog parameters
|
|
this.mDialogResizable = (dialogEl.getAttribute("resizable") == "true") ? true : false;
|
|
}
|
|
},
|
|
|
|
parseContextMenu: function(aSearchEl)
|
|
{
|
|
var els = aSearchEl.getElementsByTagNameNS(kISMLNSURI, "contextmenu");
|
|
if (els.length > 0) {
|
|
var kids = els[0].childNodes;
|
|
this.mContextMenu = [];
|
|
for (var i = 0; i < kids.length; ++i) {
|
|
this.mContextMenu[i] = kids[i].cloneNode(true);
|
|
}
|
|
}
|
|
},
|
|
|
|
parseColumns: function(aSearchEl)
|
|
{
|
|
// get the result columns
|
|
var els = aSearchEl.getElementsByTagNameNS(kISMLNSURI, "columns");
|
|
if (els.length > 0) {
|
|
this.mColumns = [];
|
|
var cols = els[0];
|
|
this.mColDelimiter = cols.getAttribute("delimiter");
|
|
|
|
var kids = cols.childNodes;
|
|
var col, data;
|
|
for (var i= 0; i < kids.length; ++i) {
|
|
col = kids[i];
|
|
if (col.nodeType == Node.ELEMENT_NODE) { // ignore non-element nodes
|
|
data = {
|
|
name: col.getAttribute("name"),
|
|
title: col.getAttribute("title"),
|
|
flex: col.getAttribute("flex"),
|
|
className: col.getAttribute("class"),
|
|
copy: col.getAttribute("copy") == "true"
|
|
};
|
|
this.mColumns.push(data);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
parseImplementation: function(aSearchEl)
|
|
{
|
|
this.mImpl = this.getDefaultImplementation();
|
|
|
|
// get the implementation object
|
|
var els = aSearchEl.getElementsByTagNameNS(kISMLNSURI, "implementation");
|
|
if (els.length > 0) {
|
|
var kids = aSearchEl.getElementsByTagNameNS(kISMLNSURI, "*");
|
|
for (var i = 0; i < kids.length; i++) {
|
|
if (kids[i].localName == "property")
|
|
this.parseProperty(kids[i]);
|
|
if (kids[i].localName == "method")
|
|
this.parseMethod(kids[i]);
|
|
}
|
|
}
|
|
},
|
|
|
|
parseProperty: function(aPropEl)
|
|
{
|
|
var name = aPropEl.getAttribute("name");
|
|
var fn = null;
|
|
|
|
// look for a getter
|
|
try {
|
|
fn = this.getCodeTagFunction(aPropEl, "getter", null);
|
|
if (fn)
|
|
this.mImpl.__defineGetter__(name, fn);
|
|
} catch (ex) {
|
|
throw "### SYNTAX ERROR IN ISML GETTER \"" + name + "\" ###\n" + ex;
|
|
}
|
|
|
|
// look for a setter
|
|
try {
|
|
fn = this.getCodeTagFunction(aPropEl, "setter", ["val"]);
|
|
if (fn)
|
|
this.mImpl.__defineSetter__(name, fn);
|
|
} catch (ex) {
|
|
throw "### SYNTAX ERROR IN ISML SETTER \"" + name + "\" ###\n" + ex;
|
|
}
|
|
},
|
|
|
|
parseMethod: function(aMethodEl)
|
|
{
|
|
var name = aMethodEl.getAttribute("name");
|
|
var def = aMethodEl.getAttribute("defaultCommand") == "true";
|
|
|
|
// get all the parameters
|
|
var els = aMethodEl.getElementsByTagNameNS(kISMLNSURI, "parameter");
|
|
var params = [];
|
|
for (var i = 0; i < els.length; i++) {
|
|
params[i] = els[i].getAttribute("name");
|
|
}
|
|
|
|
// get the body javascript and create the function
|
|
try {
|
|
var fn = this.getCodeTagFunction(aMethodEl, "body", params);
|
|
this.mImpl[name] = fn;
|
|
if (def)
|
|
this.mImpl.__DefaultCmd__ = fn;
|
|
} catch (ex) {
|
|
throw "### SYNTAX ERROR IN ISML METHOD \"" + name + "\" ###\n" + ex;
|
|
}
|
|
},
|
|
|
|
getCodeTagFunction: function(aParent, aLocalName, aParams)
|
|
{
|
|
var els = aParent.getElementsByTagNameNS(kISMLNSURI, aLocalName);
|
|
if (els.length) {
|
|
var body = els[0];
|
|
// try to determine where the code is located
|
|
var node = body.childNodes.length > 0 ? body.firstChild : body;
|
|
return this.getJSFunction(aParams, node.nodeValue);
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
getJSFunction: function(aParams, aCode)
|
|
{
|
|
var params = "";
|
|
if (aParams) {
|
|
for (var i = 0; i < aParams.length; i++) {
|
|
params += aParams[i];
|
|
if (i < aParams.length-1) params += ",";
|
|
}
|
|
}
|
|
|
|
var js = "function(" + params + ") " +
|
|
"{" +
|
|
(aCode ? aCode : "") +
|
|
"}";
|
|
|
|
var fn;
|
|
eval("fn = " + js);
|
|
return fn;
|
|
},
|
|
|
|
getDefaultImplementation: function()
|
|
{
|
|
return { module: this };
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Dialog box
|
|
|
|
openDialog: function()
|
|
{
|
|
window.openDialog(this.mDialogURL, "inSearchModule_dialog",
|
|
"chrome,modal,resizable="+this.mDialogResizable, this);
|
|
},
|
|
|
|
processDialog: function(aWindow)
|
|
{
|
|
var map = inFormManager.readWindow(aWindow, this.mDialogElementIds);
|
|
this.implStartSearch(map);
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Searching
|
|
|
|
startSearch: function()
|
|
{
|
|
if (this.mDialogURL) {
|
|
this.openDialog();
|
|
} else
|
|
this.implStartSearch(null);
|
|
},
|
|
|
|
implStartSearch: function(aMap)
|
|
{
|
|
this.mStartTime = new Date();
|
|
this.mElapsed = 0;
|
|
|
|
this.notifySearchStart();
|
|
this.initDataSource();
|
|
this.prepareForResult();
|
|
this.mImpl.searchStart(aMap);
|
|
},
|
|
|
|
stopSearch: function()
|
|
{
|
|
this.searchEnd();
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Result Returns
|
|
|
|
setResultProperty: function(aAttr, aValue)
|
|
{
|
|
this.mResult[aAttr] = aValue;
|
|
},
|
|
|
|
searchResultReady: function()
|
|
{
|
|
this.mRDFArray.add(this.mResult);
|
|
this.notifySearchResult();
|
|
this.prepareForResult();
|
|
},
|
|
|
|
searchError: function(aMsg)
|
|
{
|
|
},
|
|
|
|
searchEnd: function()
|
|
{
|
|
this.mElapsed = new Date() - this.mStartTime;
|
|
this.notifySearchEnd();
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Columns
|
|
|
|
get columnCount() { return this.mColumns.length },
|
|
|
|
getColumn: function(aIndex) { return this.mColumns[aIndex] },
|
|
getColumnName: function(aIndex) { return this.mColumns[aIndex].name },
|
|
getColumnTitle: function(aIndex) { return this.mColumns[aIndex].title },
|
|
getColumnClassName: function(aIndex) { return this.mColumns[aIndex].className },
|
|
getColumnFlex: function(aIndex) { return this.mColumns[aIndex].flex },
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// RDF Datasource
|
|
|
|
initDataSource: function()
|
|
{
|
|
this.mRDFArray = new RDFArray(this.mNameSpace, "inspector:searchResults", "results");
|
|
this.mRDFArray.initialize();
|
|
},
|
|
|
|
getResultPropertyAt: function(aIndex, aProp)
|
|
{
|
|
return this.mRDFArray.get(aIndex, aProp);
|
|
},
|
|
|
|
getItemText: function(aIndex)
|
|
{
|
|
var cols = this.mColumns;
|
|
var text = [];
|
|
for (var i = 0; i < cols.length; ++i) {
|
|
if (cols[i].copy) {
|
|
text.push(this.getResultPropertyAt(aIndex, cols[i].name));
|
|
}
|
|
}
|
|
return text.join(this.mColDelimiter);
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Context Menu
|
|
|
|
installContextMenu: function(aMenu, aInsertionPoint, aDir)
|
|
{
|
|
if (this.mContextMenu) {
|
|
aMenu._searchModule = this;
|
|
var item;
|
|
this.mMenuItems = [];
|
|
if (this.mContextMenu.length == 0)
|
|
aInsertionPoint.setAttribute("hide", "true");
|
|
for (var i = 0; i < this.mContextMenu.length; ++i) {
|
|
item = this.mContextMenu[i];
|
|
this.mMenuItems.push(item);
|
|
this.installSearchReference(item);
|
|
if (aDir == inSearchService.INSERT_BEFORE)
|
|
aMenu.insertBefore(item, aInsertionPoint);
|
|
else {
|
|
// NOT YET IMPLEMENTED
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
uninstallContextMenu: function(aMenu, aInsertionPoint, aDir)
|
|
{
|
|
if (this.mContextMenu) {
|
|
if (this.mContextMenu.length == 0)
|
|
aInsertionPoint.removeAttribute("hide");
|
|
// remove the menu items
|
|
for (var i = 0; i < this.mContextMenu.length; ++i)
|
|
aMenu.removeChild(this.mMenuItems[i]);
|
|
}
|
|
},
|
|
|
|
installSearchReference: function(aItem)
|
|
{
|
|
if (aItem.nodeType == Node.ELEMENT_NODE) {
|
|
if (aItem.localName == "menuitem") {
|
|
aItem.search = this.mImpl;
|
|
for (var i = 0; i < aItem.childNodes.length; ++i)
|
|
this.installSearchReference(aItem.childNodes[i]);
|
|
}
|
|
}
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Event Notification
|
|
|
|
// NOTE TO SELF - this code could be cut down to nothing if you write a module
|
|
// called "ObserverManager" to do the work for you
|
|
|
|
addSearchObserver: function(aObserver)
|
|
{
|
|
this.mObservers.push(aObserver);
|
|
},
|
|
|
|
removeSearchObserver: function(aObserver)
|
|
{
|
|
var o;
|
|
var obs = this.mObservers;
|
|
for (var i = 0; i < obs.length; i++) {
|
|
o = obs[i];
|
|
if (o == aObserver) {
|
|
obs.splice(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
|
|
notifySearchStart: function()
|
|
{
|
|
var o = this.mObservers;
|
|
for (var i = 0; i < o.length; i++)
|
|
o[i].onSearchStart(this);
|
|
},
|
|
|
|
notifySearchResult: function()
|
|
{
|
|
var o = this.mObservers;
|
|
for (var i = 0; i < o.length; i++)
|
|
o[i].onSearchResult(this);
|
|
},
|
|
|
|
notifySearchEnd: function(aResult)
|
|
{
|
|
var o = this.mObservers;
|
|
for (var i = 0; i < o.length; i++)
|
|
o[i].onSearchEnd(this, aResult);
|
|
},
|
|
|
|
notifySearchError: function(aMsg)
|
|
{
|
|
var o = this.mObservers;
|
|
for (var i = 0; i < o.length; i++)
|
|
o[i].onSearchError(this, aMsg);
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Uncategorized
|
|
|
|
callDefaultCommand: function()
|
|
{
|
|
if (this.mImpl.__DefaultCmd__)
|
|
this.mImpl.__DefaultCmd__();
|
|
},
|
|
|
|
prepareForResult: function()
|
|
{
|
|
this.mResult = { _icon: this.mDefaultIconURL };
|
|
},
|
|
|
|
setDialogURL: function(aURL)
|
|
{
|
|
this.mDialogURL = aURL;
|
|
// This block below doesn't work for now because none of the local file implementations
|
|
// implement SetURL. So, for now, the url in the search file MUST be absolute :(
|
|
/* this.mDialogURL = aURL;
|
|
var baseFile = inSearchUtils.createLocalFile(this.mBaseURL);
|
|
try {
|
|
baseFile.append(aURL);
|
|
} catch (ex) {
|
|
basefile = inSearchUtils.createLocalFile(aURL);
|
|
}
|
|
|
|
var file = XPCU.QI(file, "nsIFile");
|
|
|
|
var ioService = XPCU.getService("@mozilla.org/network/io-service;1", "nsIIOService");
|
|
var fileHandler = XPCU.QI(ioService.getProtocolHandler("file"), "nsIFileProtocolHandler");
|
|
|
|
this.mDialogURL = fileHandler.getURLSpecFromFile(basefile);
|
|
*/
|
|
}
|
|
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//// Event Listeners
|
|
|