mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-07 20:17:37 +00:00
884 lines
32 KiB
JavaScript
Executable File
884 lines
32 KiB
JavaScript
Executable File
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** 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 Places System.
|
|
*
|
|
* The Initial Developer of the Original Code is Google Inc.
|
|
* Portions created by the Initial Developer are Copyright (C) 2005
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Ben Goodger <beng@google.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 ***** */
|
|
|
|
const PREF_PLACES_GROUPING_GENERIC = "browser.places.grouping.generic";
|
|
const PREF_PLACES_GROUPING_BOOKMARK = "browser.places.grouping.bookmark";
|
|
|
|
// Default Search Queries
|
|
const INDEX_HISTORY = 0;
|
|
const INDEX_BOOKMARKS = 1;
|
|
|
|
var PlacesUIHook = {
|
|
_tabbrowser: null,
|
|
_topWindow: null,
|
|
_placesURI: "chrome://browser/content/places/places.xul",
|
|
_bundle: null,
|
|
|
|
init: function PUIH_init(placesList) {
|
|
try {
|
|
this._topWindow = placesList.browserWindow;
|
|
this._tabbrowser = this._topWindow.getBrowser();
|
|
|
|
// Hook into the tab strip to get notifications about when the Places Page is
|
|
// selected so that the browser UI can be modified.
|
|
var self = this;
|
|
function onTabSelect(event) {
|
|
self.onTabSelect(event);
|
|
}
|
|
this._tabbrowser.mTabContainer.addEventListener("select", onTabSelect, false);
|
|
|
|
this._showPlacesUI();
|
|
}
|
|
catch (e) {
|
|
}
|
|
|
|
this._bundle = document.getElementById("placeBundle");
|
|
|
|
// Stop the browser from handling certain types of events.
|
|
function onDragEvent(event) {
|
|
event.stopPropagation();
|
|
}
|
|
window.addEventListener("draggesture", onDragEvent, false);
|
|
window.addEventListener("dragover", onDragEvent, false);
|
|
window.addEventListener("dragdrop", onDragEvent, false);
|
|
},
|
|
|
|
_commands: ["Browser:SavePage", "Browser:SaveFrame", "Browser:SendLink",
|
|
"cmd_pageSetup", "cmd_print", "cmd_printPreview",
|
|
"cmd_findAgain", "cmd_switchTextDirection", "Browser:Stop",
|
|
"Browser:Reload", "viewTextZoomMenu", "pageStyleMenu",
|
|
"charsetMenu", "View:PageSource", "View:FullScreen",
|
|
"documentDirection-swap", "Browser:AddBookmarkAs",
|
|
"Browser:ShowBookmarks", "Browser:ShowHistory", "View:PageInfo",
|
|
"cmd_toggleTaskbar"],
|
|
|
|
/**
|
|
* Disable commands that are not relevant to the Places page, so that all
|
|
* applicable UI becomes inactive.
|
|
*/
|
|
_disableCommands: function PUIH__disableCommands() {
|
|
for (var i = 0; i < this._commands.length; ++i)
|
|
this._topWindow.document.getElementById(this._commands[i]).
|
|
setAttribute("disabled", true);
|
|
},
|
|
|
|
/**
|
|
* Enable commands that aren't updated automatically by the command updater
|
|
* when we switch away from the Places page.
|
|
*/
|
|
_enableCommands: function PUIH__enableCommands() {
|
|
for (var i = 0; i < this._commands.length; ++i)
|
|
this._topWindow.document.getElementById(this._commands[i]).
|
|
removeAttribute("disabled");
|
|
},
|
|
|
|
uninit: function PUIH_uninit() {
|
|
this._hidePlacesUI();
|
|
},
|
|
|
|
onTabSelect: function PP_onTabSelect(event) {
|
|
var tabURI = this._tabbrowser.selectedBrowser.currentURI.spec;
|
|
var isPlaces = tabURI.substr(0, this._placesURI.length) == this._placesURI;
|
|
isPlaces ? this._showPlacesUI() : this._hidePlacesUI();
|
|
},
|
|
|
|
_topElement: function PUIH__topElement(id) {
|
|
return this._topWindow.document.getElementById(id);
|
|
},
|
|
|
|
_showPlacesUI: function PP__showPlacesUI() {
|
|
this._tabbrowser.setAttribute("places", "true");
|
|
var statusbar = this._topElement("status-bar");
|
|
statusbar.hidden = true;
|
|
this._disableCommands();
|
|
|
|
var findItem = this._topWindow.document.getElementById("menu_find");
|
|
findItem.setAttribute("label", this._bundle.getString("findPlaceLabel"));
|
|
},
|
|
|
|
_hidePlacesUI: function PP__hidePlacesUI() {
|
|
this._tabbrowser.removeAttribute("places");
|
|
|
|
// Approaches that cache the value of the status bar before the Places page
|
|
// is loaded and the status bar hidden are unreliable. This is because the
|
|
// cached state can get confused when tabs are opened and switched to. Thus,
|
|
// always read the user's actual preferred value (held in the checked state
|
|
// of the menuitem, not what state the status bar was in "last" - because
|
|
// "last" may not be a state we want to restore to. See bug 318820.
|
|
var statusbarMenu = this._topWindow.document.getElementById("toggle_taskbar");
|
|
var statusbar = this._topElement("status-bar");
|
|
statusbar.hidden = statusbarMenu.getAttribute("checked") != "true";
|
|
this._enableCommands();
|
|
|
|
var findItem = this._topWindow.document.getElementById("menu_find");
|
|
findItem.setAttribute("label", this._bundle.getString("findPageLabel"));
|
|
},
|
|
};
|
|
|
|
var PlacesPage = {
|
|
_content: null,
|
|
_places: null,
|
|
|
|
// the NavHistory service
|
|
__hist: null,
|
|
get _hist() {
|
|
if (!this.__hist) {
|
|
this.__hist =
|
|
Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
|
|
}
|
|
return this.__hist;
|
|
},
|
|
|
|
init: function PP_init() {
|
|
// Attach the Command Controller to the Places Views.
|
|
this._places = document.getElementById("placesList");
|
|
this._content = document.getElementById("placeContent");
|
|
this._places.controllers.appendController(PlacesController);
|
|
this._content.controllers.appendController(PlacesController);
|
|
|
|
this._places.init(new ViewConfig([TYPE_X_MOZ_PLACE_CONTAINER],
|
|
ViewConfig.GENERIC_DROP_TYPES,
|
|
true, false, 3));
|
|
this._content.init(new ViewConfig(ViewConfig.GENERIC_DROP_TYPES,
|
|
ViewConfig.GENERIC_DROP_TYPES,
|
|
false, false, 0));
|
|
|
|
PlacesController.groupableView = this._content;
|
|
|
|
var GroupingSerializer = {
|
|
serialize: function GS_serialize(raw) {
|
|
return raw.join(",");
|
|
},
|
|
deserialize: function GS_deserialize(str) {
|
|
return str === "" ? [] : str.split(",");
|
|
}
|
|
};
|
|
PlacesController.groupers.generic =
|
|
new PrefHandler(PREF_PLACES_GROUPING_GENERIC,
|
|
[Ci.nsINavHistoryQueryOptions.GROUP_BY_DOMAIN], GroupingSerializer);
|
|
PlacesController.groupers.bookmark =
|
|
new PrefHandler(PREF_PLACES_GROUPING_BOOKMARK,
|
|
[Ci.nsINavHistoryQueryOptions.GROUP_BY_FOLDER], GroupingSerializer);
|
|
|
|
// Hook the browser UI
|
|
PlacesUIHook.init(this._content);
|
|
|
|
// Attach the Places model to the Place View
|
|
// XXXben - move this to an attribute/property on the tree view
|
|
var bms = PlacesController._bms;
|
|
this._places.loadFolder(bms.placesRoot);
|
|
|
|
// Now load the appropriate folder in the Content View, and select the
|
|
// corresponding entry in the Places View. This is a little fragile.
|
|
var params = window.location.search;
|
|
var index = params == "?history" ? INDEX_HISTORY : INDEX_BOOKMARKS;
|
|
this._places.view.selection.select(index);
|
|
|
|
// Set up the search UI.
|
|
PlacesSearchBox.init();
|
|
|
|
// Set up the advanced query builder UI
|
|
PlacesQueryBuilder.init();
|
|
},
|
|
|
|
uninit: function PP_uninit() {
|
|
PlacesUIHook.uninit();
|
|
},
|
|
|
|
/**
|
|
* A range has been selected from the calendar picker. Update the view
|
|
* to show only those results within the selected range.
|
|
*/
|
|
rangeSelected: function PP_rangeSelected() {
|
|
var result = this._content.getResult();
|
|
var queries = result.getQueries({ });
|
|
|
|
var calendar = document.getElementById("historyCalendar");
|
|
var begin = calendar.beginrange.getTime();
|
|
var end = calendar.endrange.getTime();
|
|
if (begin == end) {
|
|
const DAY_MSEC = 86400000;
|
|
end = begin + DAY_MSEC;
|
|
}
|
|
var newQueries = [];
|
|
for (var i = 0; i < queries.length; ++i) {
|
|
var query = queries[i].clone();
|
|
query.beginTime = begin * 1000;
|
|
query.endTime = end * 1000;
|
|
newQueries.push(query);
|
|
}
|
|
|
|
this._content.load(newQueries, result.queryOptions);
|
|
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Fill the header with information about what view is being displayed.
|
|
*/
|
|
_setHeader: function(type, text) {
|
|
var bundle = document.getElementById("placeBundle");
|
|
var key = null;
|
|
var isSearch = false;
|
|
switch(type) {
|
|
case "showing":
|
|
key = "headerTextShowing";
|
|
break;
|
|
case "results":
|
|
isSearch = true;
|
|
key = "headerTextResultsFor";
|
|
break;
|
|
case "advanced":
|
|
isSearch = true;
|
|
key = "headerTextAdvancedSearch";
|
|
break;
|
|
}
|
|
var showingPrefix = document.getElementById("showingPrefix");
|
|
showingPrefix.setAttribute("value", bundle.getString(key));
|
|
|
|
var contentTitle = document.getElementById("contentTitle");
|
|
contentTitle.setAttribute("value", text);
|
|
|
|
var searchModifiers = document.getElementById("searchModifiers");
|
|
searchModifiers.hidden = !isSearch;
|
|
},
|
|
|
|
/**
|
|
* Run a search for the specified text, over the collection specified by
|
|
* the dropdown arrow. The default is all bookmarks and history, but can be
|
|
* localized to the active collection.
|
|
* @param filterString
|
|
* The text to search for.
|
|
*/
|
|
search: function PP_applyFilter(filterString) {
|
|
switch (PlacesSearchBox.filterCollection) {
|
|
case "collection":
|
|
var folder = this._content.getResult().folderId;
|
|
this._content.applyFilter(filterString, true, folder);
|
|
this._setHeader("results", filterString);
|
|
break;
|
|
case "all":
|
|
this._content.filterString = filterString;
|
|
this._setHeader("results", filterString);
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Called when a place folder is selected in the left pane.
|
|
*/
|
|
placeSelected: function PP_placeSelected(event) {
|
|
var node = this._places.selectedNode;
|
|
if (!node || this._places.suppressSelection)
|
|
return;
|
|
if (node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER)
|
|
node.QueryInterface(Ci.nsINavHistoryFolderResultNode);
|
|
else if (node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY)
|
|
node.QueryInterface(Ci.nsINavHistoryQueryResultNode);
|
|
else
|
|
return; // should not get here
|
|
var queries = node.getQueries({});
|
|
var newQueries = [];
|
|
for (var i = 0; i < queries.length; ++i) {
|
|
var query = queries[i].clone();
|
|
newQueries.push(query);
|
|
}
|
|
var newOptions = node.queryOptions.clone();
|
|
|
|
var isBookmark = PlacesController.nodeIsFolder(node);
|
|
if (isBookmark)
|
|
groupings = PlacesController.groupers.bookmark.value;
|
|
|
|
this._content.load(newQueries, newOptions);
|
|
|
|
this._setHeader("showing", node.title);
|
|
},
|
|
|
|
/**
|
|
* Updates the calendar widget to show the range of dates selected in the
|
|
* current result.
|
|
*/
|
|
_updateCalendar: function PP__updateCalendar() {
|
|
// Make sure that by updating the calendar widget we don't fire selection
|
|
// events and cause the UI to infinitely reload.
|
|
var calendar = document.getElementById("historyCalendar");
|
|
calendar.suppressRangeEvents = true;
|
|
|
|
var result = this._content.getResult();
|
|
if (result.root.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY)
|
|
result.root.QueryInterface(Ci.nsINavHistoryQueryResultNode);
|
|
else if (result.root.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER)
|
|
result.root.QueryInterface(Ci.nsINavHistoryFolderResultNode);
|
|
else
|
|
return;
|
|
var queries = result.root.getQueries({ });
|
|
if (!queries.length)
|
|
return;
|
|
|
|
// Query values are OR'ed together, so just use the first.
|
|
// FIXME: if there is more than one query, we probably do NOT want to
|
|
// highlight the date range in the calendar.
|
|
var query = queries[0];
|
|
|
|
const NOW = new Date();
|
|
var begin = Math.floor(query.beginTime / 1000);
|
|
var end = Math.floor(query.endTime / 1000);
|
|
if (query.beginTimeReference == Ci.nsINavHistoryQuery.TIME_RELATIVE_TODAY) {
|
|
if (query.beginTime == 0) {
|
|
var d = new Date();
|
|
d.setFullYear(NOW.getFullYear(), NOW.getMonth(), NOW.getDate());
|
|
d.setHours(0, 0, 0, 0);
|
|
begin += d.getTime();
|
|
}
|
|
else
|
|
begin += NOW.getTime();
|
|
end += NOW.getTime();
|
|
}
|
|
calendar.beginrange = new Date(begin);
|
|
calendar.endrange = new Date(end);
|
|
|
|
// Allow user selection events once again.
|
|
calendar.suppressRangeEvents = false;
|
|
},
|
|
|
|
/**
|
|
* Update the Places UI when the content of the right tree changes.
|
|
*/
|
|
onContentChanged: function PP_onContentChanged() {
|
|
var panelID = "commands_history";
|
|
var filterButtonID = "filterList_history";
|
|
var isBookmarks = this._content.isBookmarks;
|
|
if (isBookmarks) {
|
|
// if (query.annotation == "feed") {
|
|
panelID = "commands_bookmark";
|
|
filterButtonID = "filterList_bookmark";
|
|
}
|
|
var commandBar = document.getElementById("commandBar");
|
|
commandBar.selectedPanel = document.getElementById(panelID);
|
|
|
|
// Hide the Calendar for Bookmark queries.
|
|
document.getElementById("historyCalendar").setAttribute("hidden", isBookmarks);
|
|
|
|
// Update the calendar with the current date range, if applicable.
|
|
if (!isBookmarks)
|
|
this._updateCalendar();
|
|
},
|
|
};
|
|
|
|
/**
|
|
* A set of utilities relating to search within Bookmarks and History.
|
|
*/
|
|
var PlacesSearchBox = {
|
|
/**
|
|
* Gets/sets the active collection from the dropdown menu.
|
|
*/
|
|
get filterCollection() {
|
|
var searchFilter = document.getElementById("searchFilter");
|
|
return searchFilter.getAttribute("collection");
|
|
},
|
|
set filterCollection(collectionName) {
|
|
LOG("SET COLN: " + collectionName);
|
|
var searchFilter = document.getElementById("searchFilter");
|
|
searchFilter.setAttribute("collection", collectionName);
|
|
return collectionName;
|
|
},
|
|
|
|
/**
|
|
* When the field is activated, if the contents are the gray text, clear
|
|
* the field, otherwise select the contents.
|
|
*/
|
|
onFocus: function PS_onFocus() {
|
|
var searchFilter = document.getElementById("searchFilter");
|
|
var placeBundle = document.getElementById("placeBundle");
|
|
|
|
var searchDefault = placeBundle.getString("searchDefault");
|
|
if (searchFilter.value == searchDefault) {
|
|
searchFilter.removeAttribute("empty");
|
|
searchFilter.value = "";
|
|
}
|
|
else
|
|
searchFilter.select();
|
|
},
|
|
|
|
/**
|
|
* When the field is deactivated, reset the gray text if the value is
|
|
* empty or has the gray text value.
|
|
*/
|
|
onBlur: function PS_onBlur() {
|
|
var placeBundle = document.getElementById("placeBundle");
|
|
var searchDefault = placeBundle.getString("searchDefault");
|
|
var searchFilter = document.getElementById("searchFilter");
|
|
|
|
if (searchFilter.value == searchDefault || !searchFilter.value) {
|
|
searchFilter.setAttribute("empty", "true");
|
|
searchFilter.value = searchDefault;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Set up the gray text in the search bar as the Places View loads.
|
|
*/
|
|
init: function PS_init() {
|
|
var placeBundle = document.getElementById("placeBundle");
|
|
var searchDefault = placeBundle.getString("searchDefault");
|
|
var searchFilter = document.getElementById("searchFilter");
|
|
searchFilter.value = searchDefault;
|
|
searchFilter.setAttribute("empty", "true");
|
|
searchFilter.focus();
|
|
},
|
|
|
|
};
|
|
|
|
/**
|
|
* Functions and data for advanced query builder
|
|
*/
|
|
var PlacesQueryBuilder = {
|
|
|
|
_numRows: 1,
|
|
_maxRows: 4,
|
|
|
|
_keywordSearch: {
|
|
advancedSearch_N_Subject: "advancedSearch_N_SubjectKeyword",
|
|
advancedSearch_N_HostMenulist: false,
|
|
advancedSearch_N_KeywordLabel: true,
|
|
advancedSearch_N_UriMenulist: false,
|
|
advancedSearch_N_TimeMenulist: false,
|
|
advancedSearch_N_Textbox: "",
|
|
advancedSearch_N_TimePicker: false,
|
|
advancedSearch_N_TimeMenulist2: false,
|
|
},
|
|
_hostSearch: {
|
|
advancedSearch_N_Subject: "advancedSearch_N_SubjectHost",
|
|
advancedSearch_N_HostMenulist: "advancedSearch_N_HostMenuSelected",
|
|
advancedSearch_N_KeywordLabel: false,
|
|
advancedSearch_N_UriMenulist: false,
|
|
advancedSearch_N_TimeMenulist: false,
|
|
advancedSearch_N_Textbox: "",
|
|
advancedSearch_N_TimePicker: false,
|
|
advancedSearch_N_TimeMenulist2: false,
|
|
},
|
|
_uriSearch: {
|
|
advancedSearch_N_Subject: "advancedSearch_N_SubjectUri",
|
|
advancedSearch_N_HostMenulist: false,
|
|
advancedSearch_N_KeywordLabel: false,
|
|
advancedSearch_N_UriMenulist: "advancedSearch_N_UriMenuSelected",
|
|
advancedSearch_N_TimeMenulist: false,
|
|
advancedSearch_N_Textbox: "http://",
|
|
advancedSearch_N_TimePicker: false,
|
|
advancedSearch_N_TimeMenulist2: false,
|
|
},
|
|
_timeSearch: {
|
|
advancedSearch_N_Subject: "advancedSearch_N_SubjectVisited",
|
|
advancedSearch_N_HostMenulist: false,
|
|
advancedSearch_N_KeywordLabel: false,
|
|
advancedSearch_N_UriMenulist: false,
|
|
advancedSearch_N_TimeMenulist: true,
|
|
advancedSearch_N_Textbox: false,
|
|
advancedSearch_N_TimePicker: "date",
|
|
advancedSearch_N_TimeMenulist2: false,
|
|
},
|
|
_timeInLastSearch: {
|
|
advancedSearch_N_Subject: "advancedSearch_N_SubjectVisited",
|
|
advancedSearch_N_HostMenulist: false,
|
|
advancedSearch_N_KeywordLabel: false,
|
|
advancedSearch_N_UriMenulist: false,
|
|
advancedSearch_N_TimeMenulist: true,
|
|
advancedSearch_N_Textbox: "7",
|
|
advancedSearch_N_TimePicker: false,
|
|
advancedSearch_N_TimeMenulist2: true,
|
|
},
|
|
_nextSearch: null,
|
|
_queryBuilders: null,
|
|
|
|
init: function PQB_init() {
|
|
// Initialize advanced search
|
|
this._nextSearch = {
|
|
"keyword": this._timeSearch,
|
|
"visited": this._hostSearch,
|
|
"host": this._uriSearch,
|
|
"uri": this._keywordSearch,
|
|
};
|
|
|
|
this._queryBuilders = {
|
|
"keyword": this.setKeywordQuery,
|
|
"visited": this.setVisitedQuery,
|
|
"host": this.setHostQuery,
|
|
"uri": this.setUriQuery,
|
|
};
|
|
|
|
this._dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
|
|
getService(Ci.nsIScriptableDateFormat);
|
|
},
|
|
|
|
toggle: function PQB_toggle() {
|
|
var advancedSearch = document.getElementById("advancedSearch");
|
|
if (advancedSearch.collapsed) {
|
|
// Need to expand the advanced search box and initialize it.
|
|
|
|
// Should have one row, containing a keyword search with
|
|
// the keyword from the basic search box pre-filled.
|
|
while (this._numRows > 1)
|
|
this.removeRow();
|
|
this.showSearch(1, this._keywordSearch);
|
|
var searchbox = document.getElementById("searchFilter");
|
|
var keywordbox = document.getElementById("advancedSearch1Textbox");
|
|
keywordbox.value = searchbox.value;
|
|
advancedSearch.collapsed = false;
|
|
|
|
// Update the +/- button and the header.
|
|
var button = document.getElementById("moreCriteria");
|
|
var placeBundle = document.getElementById("placeBundle");
|
|
button.label = placeBundle.getString("lessCriteria.label");
|
|
PlacesPage._setHeader("advanced", "");
|
|
}
|
|
else {
|
|
// Need to collapse the advanced search box.
|
|
advancedSearch.collapsed = true;
|
|
|
|
// Update the +/- button
|
|
var button = document.getElementById("moreCriteria");
|
|
var placeBundle = document.getElementById("placeBundle");
|
|
button.label = placeBundle.getString("moreCriteria.label");
|
|
}
|
|
},
|
|
|
|
setRowId: function PQB_setRowId(element, rowId) {
|
|
if (element.id)
|
|
element.id = element.id.replace("advancedSearch1", "advancedSearch" + rowId);
|
|
if (element.hasAttribute('rowid'))
|
|
element.setAttribute('rowid', rowId);
|
|
for (var i = 0; i < element.childNodes.length; i++) {
|
|
this.setRowId(element.childNodes[i], rowId);
|
|
}
|
|
},
|
|
|
|
updateUIForRowChange: function PQB_updateUIForRowChange() {
|
|
// Titlebar should show "match any/all" iff there are > 1 queries visible.
|
|
var matchUI = document.getElementById("titlebarMatch");
|
|
matchUI.hidden = (this._numRows <= 1);
|
|
PlacesPage._setHeader("advanced", this._numRows <= 1 ? "" : ",");
|
|
|
|
// Disable the + buttons if there are max advanced search rows
|
|
// Disable the - button is there is only one advanced search row
|
|
for (var i = 1; i <= this._numRows; i++) {
|
|
var plus = document.getElementById("advancedSearch" + i + "Plus");
|
|
plus.disabled = (this._numRows >= this._maxRows);
|
|
var minus = document.getElementById("advancedSearch" + i + "Minus");
|
|
minus.disabled = (this._numRows == 1);
|
|
}
|
|
},
|
|
|
|
addRow: function PQB_addRow() {
|
|
if (this._numRows >= this._maxRows)
|
|
return;
|
|
|
|
var gridRows = document.getElementById("advancedSearchRows");
|
|
var newRow = gridRows.firstChild.cloneNode(true);
|
|
|
|
var searchType = this._keywordSearch;
|
|
var lastMenu = document.getElementById("advancedSearch" +
|
|
this._numRows +
|
|
"Subject");
|
|
if (lastMenu && lastMenu.selectedItem) {
|
|
searchType = this._nextSearch[lastMenu.selectedItem.value];
|
|
}
|
|
|
|
this._numRows++;
|
|
this.setRowId(newRow, this._numRows);
|
|
this.showSearch(this._numRows, searchType);
|
|
gridRows.appendChild(newRow);
|
|
this.updateUIForRowChange();
|
|
},
|
|
|
|
removeRow: function PQB_removeRow() {
|
|
if (this._numRows <= 1)
|
|
return;
|
|
|
|
var row = document.getElementById("advancedSearch" + this._numRows + "Row");
|
|
row.parentNode.removeChild(row);
|
|
this._numRows--;
|
|
|
|
this.updateUIForRowChange();
|
|
},
|
|
|
|
onDateTyped: function PQB_onDateTyped(event, row) {
|
|
var textbox = document.getElementById("advancedSearch" + row + "TimePicker");
|
|
var dateString = textbox.value;
|
|
var dateArr = dateString.split("-");
|
|
// The date can be split into a range by the '-' character, i.e.
|
|
// 9/5/05 - 10/2/05. Unfortunately, dates can also be written like
|
|
// 9-5-05 - 10-2-05. Try to parse the date based on how many hyphens
|
|
// there are.
|
|
var d0 = null;
|
|
var d1 = null;
|
|
// If there are an even number of elements in the date array, try to
|
|
// parse it as a range of two dates.
|
|
if ((dateArr.length & 1) == 0) {
|
|
var mid = dateArr.length / 2;
|
|
var dateStr0 = dateArr[0];
|
|
var dateStr1 = dateArr[mid];
|
|
for (var i = 1; i < mid; i++) {
|
|
dateStr0 += "-" + dateArr[i];
|
|
dateStr1 += "-" + dateArr[i + mid];
|
|
}
|
|
d0 = new Date(dateStr0);
|
|
d1 = new Date(dateStr1);
|
|
}
|
|
// If that didn't work, try to parse it as a single date.
|
|
if (d0 == null || d0 == "Invalid Date") {
|
|
d0 = new Date(dateString);
|
|
}
|
|
|
|
if (d0 != null && d0 != "Invalid Date") {
|
|
// Parsing succeeded -- update the calendar.
|
|
var calendar = document.getElementById("advancedSearch" + row + "Calendar");
|
|
if (d0.getFullYear() < 2000)
|
|
d0.setFullYear(2000 + (d0.getFullYear() % 100));
|
|
if (d1 != null && d1 != "Invalid Date") {
|
|
if (d1.getFullYear() < 2000)
|
|
d1.setFullYear(2000 + (d1.getFullYear() % 100));
|
|
calendar.updateSelection(d0, d1);
|
|
}
|
|
else {
|
|
calendar.updateSelection(d0, d0);
|
|
}
|
|
|
|
// And update the search.
|
|
this.doSearch();
|
|
}
|
|
},
|
|
|
|
onCalendarChanged: function PQB_onCalendarChanged(event, row) {
|
|
var calendar = document.getElementById("advancedSearch" + row + "Calendar");
|
|
var begin = calendar.beginrange;
|
|
var end = calendar.endrange;
|
|
|
|
// If the calendar doesn't have a begin/end, don't change the textbox.
|
|
if (begin == null || end == null)
|
|
return true;
|
|
|
|
// If the begin and end are the same day, only fill that into the textbox.
|
|
var textbox = document.getElementById("advancedSearch" + row + "TimePicker");
|
|
var beginDate = begin.getDate();
|
|
var beginMonth = begin.getMonth() + 1;
|
|
var beginYear = begin.getFullYear();
|
|
var endDate = end.getDate();
|
|
var endMonth = end.getMonth() + 1;
|
|
var endYear = end.getFullYear();
|
|
if (beginDate == endDate && beginMonth == endMonth && beginYear == endYear) {
|
|
// Just one date.
|
|
textbox.value = this._dateService.FormatDate("",
|
|
this._dateService.dateFormatShort,
|
|
beginYear,
|
|
beginMonth,
|
|
beginDate);
|
|
}
|
|
else
|
|
{
|
|
// Two dates.
|
|
var beginStr = this._dateService.FormatDate("",
|
|
this._dateService.dateFormatShort,
|
|
beginYear,
|
|
beginMonth,
|
|
beginDate);
|
|
var endStr = this._dateService.FormatDate("",
|
|
this._dateService.dateFormatShort,
|
|
endYear,
|
|
endMonth,
|
|
endDate);
|
|
textbox.value = beginStr + " - " + endStr;
|
|
}
|
|
|
|
// Update the search.
|
|
this.doSearch();
|
|
|
|
return true;
|
|
},
|
|
|
|
handleTimePickerClick: function PQB_handleTimePickerClick(event, row) {
|
|
var popup = document.getElementById("advancedSearch" + row + "DatePopup");
|
|
if (popup.showing)
|
|
popup.hidePopup();
|
|
else {
|
|
var textbox = document.getElementById("advancedSearch" + row + "TimePicker");
|
|
popup.showPopup(textbox, -1, -1, "popup", "bottomleft", "topleft");
|
|
}
|
|
},
|
|
|
|
showSearch: function PQB_showSearch(row, values) {
|
|
for (val in values) {
|
|
var id = val.replace("_N_", row);
|
|
var element = document.getElementById(id);
|
|
if (values[val] || typeof(values[val]) == "string") {
|
|
if (typeof(values[val]) == "string") {
|
|
if (values[val] == "date") {
|
|
// "date" means that the current date should be filled into the
|
|
// textbox, and the calendar for the row updated.
|
|
var d = new Date();
|
|
element.value = this._dateService.FormatDate("",
|
|
this._dateService.dateFormatShort,
|
|
d.getFullYear(),
|
|
d.getMonth() + 1,
|
|
d.getDate());
|
|
var calendar = document.getElementById("advancedSearch" + row + "Calendar");
|
|
calendar.updateSelection(d, d);
|
|
}
|
|
else if (element.nodeName == "textbox") {
|
|
// values[val] is the initial value of the textbox.
|
|
element.value = values[val];
|
|
} else {
|
|
// values[val] is the menuitem which should be selected.
|
|
var itemId = values[val].replace("_N_", row);
|
|
var item = document.getElementById(itemId);
|
|
element.selectedItem = item;
|
|
}
|
|
}
|
|
element.hidden = false;
|
|
}
|
|
else {
|
|
element.hidden = true;
|
|
}
|
|
}
|
|
|
|
this.doSearch();
|
|
},
|
|
|
|
setKeywordQuery: function PQB_setKeywordQuery(query, prefix) {
|
|
query.searchTerms += document.getElementById(prefix + "Textbox").value + " ";
|
|
},
|
|
|
|
setUriQuery: function PQB_setUriQuery(query, prefix) {
|
|
var matchType = document.getElementById(prefix + "UriMenulist").selectedItem.value;
|
|
if (matchType == "startsWith")
|
|
query.uriIsPrefix = true;
|
|
else
|
|
query.uriIsPrefix = false;
|
|
|
|
var ios = Cc["@mozilla.org/network/io-service;1"].
|
|
getService(Ci.nsIIOService);
|
|
var spec = document.getElementById(prefix + "Textbox").value;
|
|
query.uri = ios.newURI(spec, null, null);
|
|
},
|
|
|
|
setHostQuery: function PQB_setHostQuery(query, prefix) {
|
|
if (document.getElementById(prefix + "HostMenulist").selectedItem.value == "is")
|
|
query.domainIsHost = true;
|
|
query.domain = document.getElementById(prefix + "Textbox").value;
|
|
},
|
|
|
|
setVisitedQuery: function PQB_setVisitedQuery(query, prefix) {
|
|
var searchType = document.getElementById(prefix + "TimeMenulist").selectedItem.value;
|
|
const DAY_MSEC = 86400000;
|
|
switch (searchType) {
|
|
case "on":
|
|
var calendar = document.getElementById(prefix + "Calendar");
|
|
var begin = calendar.beginrange.getTime();
|
|
var end = calendar.endrange.getTime();
|
|
if (begin == end) {
|
|
end = begin + DAY_MSEC;
|
|
}
|
|
query.beginTime = begin * 1000;
|
|
query.endTime = end * 1000;
|
|
break;
|
|
case "before":
|
|
var calendar = document.getElementById(prefix + "Calendar");
|
|
var time = calendar.beginrange.getTime();
|
|
query.endTime = time * 1000;
|
|
break;
|
|
case "after":
|
|
var calendar = document.getElementById(prefix + "Calendar");
|
|
var time = calendar.endrange.getTime();
|
|
query.beginTime = time * 1000;
|
|
break;
|
|
case "inLast":
|
|
var textbox = document.getElementById(prefix + "Textbox");
|
|
var amount = parseInt(textbox.value);
|
|
amount = amount * DAY_MSEC;
|
|
var menulist = document.getElementById(prefix + "TimeMenulist2");
|
|
if (menulist.selectedItem.value == "weeks")
|
|
amount = amount * 7;
|
|
else if (menulist.selectedItem.value == "months")
|
|
amount = amount * 30;
|
|
var now = new Date();
|
|
now = now - amount;
|
|
query.beginTime = now * 1000;
|
|
break;
|
|
}
|
|
},
|
|
|
|
doSearch: function PQB_doSearch() {
|
|
// Create the individual queries.
|
|
var queryType = document.getElementById("advancedSearchType").selectedItem.value;
|
|
var queries = [];
|
|
if (queryType == "and")
|
|
queries.push(PlacesPage._hist.getNewQuery());
|
|
var onlyBookmarked = document.getElementById("advancedSearchOnlyBookmarked").checked;
|
|
for (var i = 1; i <= this._numRows; i++) {
|
|
var prefix = "advancedSearch" + i;
|
|
|
|
// If the queries are being AND-ed, put all the rows in one query.
|
|
// If they're being OR-ed, add a separate query for each row.
|
|
var query;
|
|
if (queryType == "and")
|
|
query = queries[0];
|
|
else
|
|
query = PlacesPage._hist.getNewQuery();
|
|
query.onlyBookmarked = onlyBookmarked;
|
|
|
|
var querySubject = document.getElementById(prefix + "Subject").value;
|
|
this._queryBuilders[querySubject](query, prefix);
|
|
|
|
if (queryType == "or")
|
|
queries.push(query);
|
|
}
|
|
|
|
// Set max results
|
|
var result = PlacesPage._content.getResult();
|
|
if (document.getElementById("advancedSearchHasMax").checked) {
|
|
var max = parseInt(document.getElementById("advancedSearchMaxResults").value);
|
|
if (isNaN(max))
|
|
max = 100;
|
|
result.queryOptions.maxResults = max;
|
|
dump("Max results = " + result.queryOptions.maxResults + "(" + max + ")\n");
|
|
}
|
|
// Make sure we're getting uri results, not visits
|
|
result.queryOptions.resultType = result.queryOptions.RESULT_TYPE_URI;
|
|
|
|
PlacesPage._content.load(queries, result.queryOptions);
|
|
},
|
|
};
|