Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | const Cc = Components.classes; |
michael@0 | 7 | const Ci = Components.interfaces; |
michael@0 | 8 | |
michael@0 | 9 | var gSanitizePromptDialog = { |
michael@0 | 10 | |
michael@0 | 11 | get bundleBrowser() |
michael@0 | 12 | { |
michael@0 | 13 | if (!this._bundleBrowser) |
michael@0 | 14 | this._bundleBrowser = document.getElementById("bundleBrowser"); |
michael@0 | 15 | return this._bundleBrowser; |
michael@0 | 16 | }, |
michael@0 | 17 | |
michael@0 | 18 | get selectedTimespan() |
michael@0 | 19 | { |
michael@0 | 20 | var durList = document.getElementById("sanitizeDurationChoice"); |
michael@0 | 21 | return parseInt(durList.value); |
michael@0 | 22 | }, |
michael@0 | 23 | |
michael@0 | 24 | get sanitizePreferences() |
michael@0 | 25 | { |
michael@0 | 26 | if (!this._sanitizePreferences) { |
michael@0 | 27 | this._sanitizePreferences = |
michael@0 | 28 | document.getElementById("sanitizePreferences"); |
michael@0 | 29 | } |
michael@0 | 30 | return this._sanitizePreferences; |
michael@0 | 31 | }, |
michael@0 | 32 | |
michael@0 | 33 | get warningBox() |
michael@0 | 34 | { |
michael@0 | 35 | return document.getElementById("sanitizeEverythingWarningBox"); |
michael@0 | 36 | }, |
michael@0 | 37 | |
michael@0 | 38 | init: function () |
michael@0 | 39 | { |
michael@0 | 40 | // This is used by selectByTimespan() to determine if the window has loaded. |
michael@0 | 41 | this._inited = true; |
michael@0 | 42 | |
michael@0 | 43 | var s = new Sanitizer(); |
michael@0 | 44 | s.prefDomain = "privacy.cpd."; |
michael@0 | 45 | |
michael@0 | 46 | let sanitizeItemList = document.querySelectorAll("#itemList > [preference]"); |
michael@0 | 47 | for (let i = 0; i < sanitizeItemList.length; i++) { |
michael@0 | 48 | let prefItem = sanitizeItemList[i]; |
michael@0 | 49 | let name = s.getNameFromPreference(prefItem.getAttribute("preference")); |
michael@0 | 50 | s.canClearItem(name, function canClearCallback(aItem, aCanClear, aPrefItem) { |
michael@0 | 51 | if (!aCanClear) { |
michael@0 | 52 | aPrefItem.preference = null; |
michael@0 | 53 | aPrefItem.checked = false; |
michael@0 | 54 | aPrefItem.disabled = true; |
michael@0 | 55 | } |
michael@0 | 56 | }, prefItem); |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | document.documentElement.getButton("accept").label = |
michael@0 | 60 | this.bundleBrowser.getString("sanitizeButtonOK"); |
michael@0 | 61 | |
michael@0 | 62 | if (this.selectedTimespan === Sanitizer.TIMESPAN_EVERYTHING) { |
michael@0 | 63 | this.prepareWarning(); |
michael@0 | 64 | this.warningBox.hidden = false; |
michael@0 | 65 | document.title = |
michael@0 | 66 | this.bundleBrowser.getString("sanitizeDialog2.everything.title"); |
michael@0 | 67 | } |
michael@0 | 68 | else |
michael@0 | 69 | this.warningBox.hidden = true; |
michael@0 | 70 | }, |
michael@0 | 71 | |
michael@0 | 72 | selectByTimespan: function () |
michael@0 | 73 | { |
michael@0 | 74 | // This method is the onselect handler for the duration dropdown. As a |
michael@0 | 75 | // result it's called a couple of times before onload calls init(). |
michael@0 | 76 | if (!this._inited) |
michael@0 | 77 | return; |
michael@0 | 78 | |
michael@0 | 79 | var warningBox = this.warningBox; |
michael@0 | 80 | |
michael@0 | 81 | // If clearing everything |
michael@0 | 82 | if (this.selectedTimespan === Sanitizer.TIMESPAN_EVERYTHING) { |
michael@0 | 83 | this.prepareWarning(); |
michael@0 | 84 | if (warningBox.hidden) { |
michael@0 | 85 | warningBox.hidden = false; |
michael@0 | 86 | window.resizeBy(0, warningBox.boxObject.height); |
michael@0 | 87 | } |
michael@0 | 88 | window.document.title = |
michael@0 | 89 | this.bundleBrowser.getString("sanitizeDialog2.everything.title"); |
michael@0 | 90 | return; |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | // If clearing a specific time range |
michael@0 | 94 | if (!warningBox.hidden) { |
michael@0 | 95 | window.resizeBy(0, -warningBox.boxObject.height); |
michael@0 | 96 | warningBox.hidden = true; |
michael@0 | 97 | } |
michael@0 | 98 | window.document.title = |
michael@0 | 99 | window.document.documentElement.getAttribute("noneverythingtitle"); |
michael@0 | 100 | }, |
michael@0 | 101 | |
michael@0 | 102 | sanitize: function () |
michael@0 | 103 | { |
michael@0 | 104 | // Update pref values before handing off to the sanitizer (bug 453440) |
michael@0 | 105 | this.updatePrefs(); |
michael@0 | 106 | var s = new Sanitizer(); |
michael@0 | 107 | s.prefDomain = "privacy.cpd."; |
michael@0 | 108 | |
michael@0 | 109 | s.range = Sanitizer.getClearRange(this.selectedTimespan); |
michael@0 | 110 | s.ignoreTimespan = !s.range; |
michael@0 | 111 | |
michael@0 | 112 | // As the sanitize is async, we disable the buttons, update the label on |
michael@0 | 113 | // the 'accept' button to indicate things are happening and return false - |
michael@0 | 114 | // once the async operation completes (either with or without errors) |
michael@0 | 115 | // we close the window. |
michael@0 | 116 | let docElt = document.documentElement; |
michael@0 | 117 | let acceptButton = docElt.getButton("accept"); |
michael@0 | 118 | acceptButton.disabled = true; |
michael@0 | 119 | acceptButton.setAttribute("label", |
michael@0 | 120 | this.bundleBrowser.getString("sanitizeButtonClearing")); |
michael@0 | 121 | docElt.getButton("cancel").disabled = true; |
michael@0 | 122 | try { |
michael@0 | 123 | s.sanitize().then(null, Components.utils.reportError) |
michael@0 | 124 | .then(() => window.close()) |
michael@0 | 125 | .then(null, Components.utils.reportError); |
michael@0 | 126 | } catch (er) { |
michael@0 | 127 | Components.utils.reportError("Exception during sanitize: " + er); |
michael@0 | 128 | return true; // We *do* want to close immediately on error. |
michael@0 | 129 | } |
michael@0 | 130 | return false; |
michael@0 | 131 | }, |
michael@0 | 132 | |
michael@0 | 133 | /** |
michael@0 | 134 | * If the panel that displays a warning when the duration is "Everything" is |
michael@0 | 135 | * not set up, sets it up. Otherwise does nothing. |
michael@0 | 136 | * |
michael@0 | 137 | * @param aDontShowItemList Whether only the warning message should be updated. |
michael@0 | 138 | * True means the item list visibility status should not |
michael@0 | 139 | * be changed. |
michael@0 | 140 | */ |
michael@0 | 141 | prepareWarning: function (aDontShowItemList) { |
michael@0 | 142 | // If the date and time-aware locale warning string is ever used again, |
michael@0 | 143 | // initialize it here. Currently we use the no-visits warning string, |
michael@0 | 144 | // which does not include date and time. See bug 480169 comment 48. |
michael@0 | 145 | |
michael@0 | 146 | var warningStringID; |
michael@0 | 147 | if (this.hasNonSelectedItems()) { |
michael@0 | 148 | warningStringID = "sanitizeSelectedWarning"; |
michael@0 | 149 | if (!aDontShowItemList) |
michael@0 | 150 | this.showItemList(); |
michael@0 | 151 | } |
michael@0 | 152 | else { |
michael@0 | 153 | warningStringID = "sanitizeEverythingWarning2"; |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | var warningDesc = document.getElementById("sanitizeEverythingWarning"); |
michael@0 | 157 | warningDesc.textContent = |
michael@0 | 158 | this.bundleBrowser.getString(warningStringID); |
michael@0 | 159 | }, |
michael@0 | 160 | |
michael@0 | 161 | /** |
michael@0 | 162 | * Called when the value of a preference element is synced from the actual |
michael@0 | 163 | * pref. Enables or disables the OK button appropriately. |
michael@0 | 164 | */ |
michael@0 | 165 | onReadGeneric: function () |
michael@0 | 166 | { |
michael@0 | 167 | var found = false; |
michael@0 | 168 | |
michael@0 | 169 | // Find any other pref that's checked and enabled. |
michael@0 | 170 | var i = 0; |
michael@0 | 171 | while (!found && i < this.sanitizePreferences.childNodes.length) { |
michael@0 | 172 | var preference = this.sanitizePreferences.childNodes[i]; |
michael@0 | 173 | |
michael@0 | 174 | found = !!preference.value && |
michael@0 | 175 | !preference.disabled; |
michael@0 | 176 | i++; |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | try { |
michael@0 | 180 | document.documentElement.getButton("accept").disabled = !found; |
michael@0 | 181 | } |
michael@0 | 182 | catch (e) { } |
michael@0 | 183 | |
michael@0 | 184 | // Update the warning prompt if needed |
michael@0 | 185 | this.prepareWarning(true); |
michael@0 | 186 | |
michael@0 | 187 | return undefined; |
michael@0 | 188 | }, |
michael@0 | 189 | |
michael@0 | 190 | /** |
michael@0 | 191 | * Sanitizer.prototype.sanitize() requires the prefs to be up-to-date. |
michael@0 | 192 | * Because the type of this prefwindow is "child" -- and that's needed because |
michael@0 | 193 | * without it the dialog has no OK and Cancel buttons -- the prefs are not |
michael@0 | 194 | * updated on dialogaccept on platforms that don't support instant-apply |
michael@0 | 195 | * (i.e., Windows). We must therefore manually set the prefs from their |
michael@0 | 196 | * corresponding preference elements. |
michael@0 | 197 | */ |
michael@0 | 198 | updatePrefs : function () |
michael@0 | 199 | { |
michael@0 | 200 | var tsPref = document.getElementById("privacy.sanitize.timeSpan"); |
michael@0 | 201 | Sanitizer.prefs.setIntPref("timeSpan", this.selectedTimespan); |
michael@0 | 202 | |
michael@0 | 203 | // Keep the pref for the download history in sync with the history pref. |
michael@0 | 204 | document.getElementById("privacy.cpd.downloads").value = |
michael@0 | 205 | document.getElementById("privacy.cpd.history").value; |
michael@0 | 206 | |
michael@0 | 207 | // Now manually set the prefs from their corresponding preference |
michael@0 | 208 | // elements. |
michael@0 | 209 | var prefs = this.sanitizePreferences.rootBranch; |
michael@0 | 210 | for (let i = 0; i < this.sanitizePreferences.childNodes.length; ++i) { |
michael@0 | 211 | var p = this.sanitizePreferences.childNodes[i]; |
michael@0 | 212 | prefs.setBoolPref(p.name, p.value); |
michael@0 | 213 | } |
michael@0 | 214 | }, |
michael@0 | 215 | |
michael@0 | 216 | /** |
michael@0 | 217 | * Check if all of the history items have been selected like the default status. |
michael@0 | 218 | */ |
michael@0 | 219 | hasNonSelectedItems: function () { |
michael@0 | 220 | let checkboxes = document.querySelectorAll("#itemList > [preference]"); |
michael@0 | 221 | for (let i = 0; i < checkboxes.length; ++i) { |
michael@0 | 222 | let pref = document.getElementById(checkboxes[i].getAttribute("preference")); |
michael@0 | 223 | if (!pref.value) |
michael@0 | 224 | return true; |
michael@0 | 225 | } |
michael@0 | 226 | return false; |
michael@0 | 227 | }, |
michael@0 | 228 | |
michael@0 | 229 | /** |
michael@0 | 230 | * Show the history items list. |
michael@0 | 231 | */ |
michael@0 | 232 | showItemList: function () { |
michael@0 | 233 | var itemList = document.getElementById("itemList"); |
michael@0 | 234 | var expanderButton = document.getElementById("detailsExpander"); |
michael@0 | 235 | |
michael@0 | 236 | if (itemList.collapsed) { |
michael@0 | 237 | expanderButton.className = "expander-up"; |
michael@0 | 238 | itemList.setAttribute("collapsed", "false"); |
michael@0 | 239 | if (document.documentElement.boxObject.height) |
michael@0 | 240 | window.resizeBy(0, itemList.boxObject.height); |
michael@0 | 241 | } |
michael@0 | 242 | }, |
michael@0 | 243 | |
michael@0 | 244 | /** |
michael@0 | 245 | * Hide the history items list. |
michael@0 | 246 | */ |
michael@0 | 247 | hideItemList: function () { |
michael@0 | 248 | var itemList = document.getElementById("itemList"); |
michael@0 | 249 | var expanderButton = document.getElementById("detailsExpander"); |
michael@0 | 250 | |
michael@0 | 251 | if (!itemList.collapsed) { |
michael@0 | 252 | expanderButton.className = "expander-down"; |
michael@0 | 253 | window.resizeBy(0, -itemList.boxObject.height); |
michael@0 | 254 | itemList.setAttribute("collapsed", "true"); |
michael@0 | 255 | } |
michael@0 | 256 | }, |
michael@0 | 257 | |
michael@0 | 258 | /** |
michael@0 | 259 | * Called by the item list expander button to toggle the list's visibility. |
michael@0 | 260 | */ |
michael@0 | 261 | toggleItemList: function () |
michael@0 | 262 | { |
michael@0 | 263 | var itemList = document.getElementById("itemList"); |
michael@0 | 264 | |
michael@0 | 265 | if (itemList.collapsed) |
michael@0 | 266 | this.showItemList(); |
michael@0 | 267 | else |
michael@0 | 268 | this.hideItemList(); |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | #ifdef CRH_DIALOG_TREE_VIEW |
michael@0 | 272 | // A duration value; used in the same context as Sanitizer.TIMESPAN_HOUR, |
michael@0 | 273 | // Sanitizer.TIMESPAN_2HOURS, et al. This should match the value attribute |
michael@0 | 274 | // of the sanitizeDurationCustom menuitem. |
michael@0 | 275 | get TIMESPAN_CUSTOM() |
michael@0 | 276 | { |
michael@0 | 277 | return -1; |
michael@0 | 278 | }, |
michael@0 | 279 | |
michael@0 | 280 | get placesTree() |
michael@0 | 281 | { |
michael@0 | 282 | if (!this._placesTree) |
michael@0 | 283 | this._placesTree = document.getElementById("placesTree"); |
michael@0 | 284 | return this._placesTree; |
michael@0 | 285 | }, |
michael@0 | 286 | |
michael@0 | 287 | init: function () |
michael@0 | 288 | { |
michael@0 | 289 | // This is used by selectByTimespan() to determine if the window has loaded. |
michael@0 | 290 | this._inited = true; |
michael@0 | 291 | |
michael@0 | 292 | var s = new Sanitizer(); |
michael@0 | 293 | s.prefDomain = "privacy.cpd."; |
michael@0 | 294 | |
michael@0 | 295 | let sanitizeItemList = document.querySelectorAll("#itemList > [preference]"); |
michael@0 | 296 | for (let i = 0; i < sanitizeItemList.length; i++) { |
michael@0 | 297 | let prefItem = sanitizeItemList[i]; |
michael@0 | 298 | let name = s.getNameFromPreference(prefItem.getAttribute("preference")); |
michael@0 | 299 | s.canClearItem(name, function canClearCallback(aCanClear) { |
michael@0 | 300 | if (!aCanClear) { |
michael@0 | 301 | prefItem.preference = null; |
michael@0 | 302 | prefItem.checked = false; |
michael@0 | 303 | prefItem.disabled = true; |
michael@0 | 304 | } |
michael@0 | 305 | }); |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | document.documentElement.getButton("accept").label = |
michael@0 | 309 | this.bundleBrowser.getString("sanitizeButtonOK"); |
michael@0 | 310 | |
michael@0 | 311 | this.selectByTimespan(); |
michael@0 | 312 | }, |
michael@0 | 313 | |
michael@0 | 314 | /** |
michael@0 | 315 | * Sets up the hashes this.durationValsToRows, which maps duration values |
michael@0 | 316 | * to rows in the tree, this.durationRowsToVals, which maps rows in |
michael@0 | 317 | * the tree to duration values, and this.durationStartTimes, which maps |
michael@0 | 318 | * duration values to their corresponding start times. |
michael@0 | 319 | */ |
michael@0 | 320 | initDurationDropdown: function () |
michael@0 | 321 | { |
michael@0 | 322 | // First, calculate the start times for each duration. |
michael@0 | 323 | this.durationStartTimes = {}; |
michael@0 | 324 | var durVals = []; |
michael@0 | 325 | var durPopup = document.getElementById("sanitizeDurationPopup"); |
michael@0 | 326 | var durMenuitems = durPopup.childNodes; |
michael@0 | 327 | for (let i = 0; i < durMenuitems.length; i++) { |
michael@0 | 328 | let durMenuitem = durMenuitems[i]; |
michael@0 | 329 | let durVal = parseInt(durMenuitem.value); |
michael@0 | 330 | if (durMenuitem.localName === "menuitem" && |
michael@0 | 331 | durVal !== Sanitizer.TIMESPAN_EVERYTHING && |
michael@0 | 332 | durVal !== this.TIMESPAN_CUSTOM) { |
michael@0 | 333 | durVals.push(durVal); |
michael@0 | 334 | let durTimes = Sanitizer.getClearRange(durVal); |
michael@0 | 335 | this.durationStartTimes[durVal] = durTimes[0]; |
michael@0 | 336 | } |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | // Sort the duration values ascending. Because one tree index can map to |
michael@0 | 340 | // more than one duration, this ensures that this.durationRowsToVals maps |
michael@0 | 341 | // a row index to the largest duration possible in the code below. |
michael@0 | 342 | durVals.sort(); |
michael@0 | 343 | |
michael@0 | 344 | // Now calculate the rows in the tree of the durations' start times. For |
michael@0 | 345 | // each duration, we are looking for the node in the tree whose time is the |
michael@0 | 346 | // smallest time greater than or equal to the duration's start time. |
michael@0 | 347 | this.durationRowsToVals = {}; |
michael@0 | 348 | this.durationValsToRows = {}; |
michael@0 | 349 | var view = this.placesTree.view; |
michael@0 | 350 | // For all rows in the tree except the grippy row... |
michael@0 | 351 | for (let i = 0; i < view.rowCount - 1; i++) { |
michael@0 | 352 | let unfoundDurVals = []; |
michael@0 | 353 | let nodeTime = view.QueryInterface(Ci.nsINavHistoryResultTreeViewer). |
michael@0 | 354 | nodeForTreeIndex(i).time; |
michael@0 | 355 | // For all durations whose rows have not yet been found in the tree, see |
michael@0 | 356 | // if index i is their index. An index may map to more than one duration, |
michael@0 | 357 | // in which case the final duration (the largest) wins. |
michael@0 | 358 | for (let j = 0; j < durVals.length; j++) { |
michael@0 | 359 | let durVal = durVals[j]; |
michael@0 | 360 | let durStartTime = this.durationStartTimes[durVal]; |
michael@0 | 361 | if (nodeTime < durStartTime) { |
michael@0 | 362 | this.durationValsToRows[durVal] = i - 1; |
michael@0 | 363 | this.durationRowsToVals[i - 1] = durVal; |
michael@0 | 364 | } |
michael@0 | 365 | else |
michael@0 | 366 | unfoundDurVals.push(durVal); |
michael@0 | 367 | } |
michael@0 | 368 | durVals = unfoundDurVals; |
michael@0 | 369 | } |
michael@0 | 370 | |
michael@0 | 371 | // If any durations were not found above, then every node in the tree has a |
michael@0 | 372 | // time greater than or equal to the duration. In other words, those |
michael@0 | 373 | // durations include the entire tree (except the grippy row). |
michael@0 | 374 | for (let i = 0; i < durVals.length; i++) { |
michael@0 | 375 | let durVal = durVals[i]; |
michael@0 | 376 | this.durationValsToRows[durVal] = view.rowCount - 2; |
michael@0 | 377 | this.durationRowsToVals[view.rowCount - 2] = durVal; |
michael@0 | 378 | } |
michael@0 | 379 | }, |
michael@0 | 380 | |
michael@0 | 381 | /** |
michael@0 | 382 | * If the Places tree is not set up, sets it up. Otherwise does nothing. |
michael@0 | 383 | */ |
michael@0 | 384 | ensurePlacesTreeIsInited: function () |
michael@0 | 385 | { |
michael@0 | 386 | if (this._placesTreeIsInited) |
michael@0 | 387 | return; |
michael@0 | 388 | |
michael@0 | 389 | this._placesTreeIsInited = true; |
michael@0 | 390 | |
michael@0 | 391 | // Either "Last Four Hours" or "Today" will have the most history. If |
michael@0 | 392 | // it's been more than 4 hours since today began, "Today" will. Otherwise |
michael@0 | 393 | // "Last Four Hours" will. |
michael@0 | 394 | var times = Sanitizer.getClearRange(Sanitizer.TIMESPAN_TODAY); |
michael@0 | 395 | |
michael@0 | 396 | // If it's been less than 4 hours since today began, use the past 4 hours. |
michael@0 | 397 | if (times[1] - times[0] < 14400000000) { // 4*60*60*1000000 |
michael@0 | 398 | times = Sanitizer.getClearRange(Sanitizer.TIMESPAN_4HOURS); |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | var histServ = Cc["@mozilla.org/browser/nav-history-service;1"]. |
michael@0 | 402 | getService(Ci.nsINavHistoryService); |
michael@0 | 403 | var query = histServ.getNewQuery(); |
michael@0 | 404 | query.beginTimeReference = query.TIME_RELATIVE_EPOCH; |
michael@0 | 405 | query.beginTime = times[0]; |
michael@0 | 406 | query.endTimeReference = query.TIME_RELATIVE_EPOCH; |
michael@0 | 407 | query.endTime = times[1]; |
michael@0 | 408 | var opts = histServ.getNewQueryOptions(); |
michael@0 | 409 | opts.sortingMode = opts.SORT_BY_DATE_DESCENDING; |
michael@0 | 410 | opts.queryType = opts.QUERY_TYPE_HISTORY; |
michael@0 | 411 | var result = histServ.executeQuery(query, opts); |
michael@0 | 412 | |
michael@0 | 413 | var view = gContiguousSelectionTreeHelper.setTree(this.placesTree, |
michael@0 | 414 | new PlacesTreeView()); |
michael@0 | 415 | result.addObserver(view, false); |
michael@0 | 416 | this.initDurationDropdown(); |
michael@0 | 417 | }, |
michael@0 | 418 | |
michael@0 | 419 | /** |
michael@0 | 420 | * Called on select of the duration dropdown and when grippyMoved() sets a |
michael@0 | 421 | * duration based on the location of the grippy row. Selects all the nodes in |
michael@0 | 422 | * the tree that are contained in the selected duration. If clearing |
michael@0 | 423 | * everything, the warning panel is shown instead. |
michael@0 | 424 | */ |
michael@0 | 425 | selectByTimespan: function () |
michael@0 | 426 | { |
michael@0 | 427 | // This method is the onselect handler for the duration dropdown. As a |
michael@0 | 428 | // result it's called a couple of times before onload calls init(). |
michael@0 | 429 | if (!this._inited) |
michael@0 | 430 | return; |
michael@0 | 431 | |
michael@0 | 432 | var durDeck = document.getElementById("durationDeck"); |
michael@0 | 433 | var durList = document.getElementById("sanitizeDurationChoice"); |
michael@0 | 434 | var durVal = parseInt(durList.value); |
michael@0 | 435 | var durCustom = document.getElementById("sanitizeDurationCustom"); |
michael@0 | 436 | |
michael@0 | 437 | // If grippy row is not at a duration boundary, show the custom menuitem; |
michael@0 | 438 | // otherwise, hide it. Since the user cannot specify a custom duration by |
michael@0 | 439 | // using the dropdown, this conditional is true only when this method is |
michael@0 | 440 | // called onselect from grippyMoved(), so no selection need be made. |
michael@0 | 441 | if (durVal === this.TIMESPAN_CUSTOM) { |
michael@0 | 442 | durCustom.hidden = false; |
michael@0 | 443 | return; |
michael@0 | 444 | } |
michael@0 | 445 | durCustom.hidden = true; |
michael@0 | 446 | |
michael@0 | 447 | // If clearing everything, show the warning and change the dialog's title. |
michael@0 | 448 | if (durVal === Sanitizer.TIMESPAN_EVERYTHING) { |
michael@0 | 449 | this.prepareWarning(); |
michael@0 | 450 | durDeck.selectedIndex = 1; |
michael@0 | 451 | window.document.title = |
michael@0 | 452 | this.bundleBrowser.getString("sanitizeDialog2.everything.title"); |
michael@0 | 453 | document.documentElement.getButton("accept").disabled = false; |
michael@0 | 454 | return; |
michael@0 | 455 | } |
michael@0 | 456 | |
michael@0 | 457 | // Otherwise -- if clearing a specific time range -- select that time range |
michael@0 | 458 | // in the tree. |
michael@0 | 459 | this.ensurePlacesTreeIsInited(); |
michael@0 | 460 | durDeck.selectedIndex = 0; |
michael@0 | 461 | window.document.title = |
michael@0 | 462 | window.document.documentElement.getAttribute("noneverythingtitle"); |
michael@0 | 463 | var durRow = this.durationValsToRows[durVal]; |
michael@0 | 464 | gContiguousSelectionTreeHelper.rangedSelect(durRow); |
michael@0 | 465 | gContiguousSelectionTreeHelper.scrollToGrippy(); |
michael@0 | 466 | |
michael@0 | 467 | // If duration is empty (there are no selected rows), disable the dialog's |
michael@0 | 468 | // OK button. |
michael@0 | 469 | document.documentElement.getButton("accept").disabled = durRow < 0; |
michael@0 | 470 | }, |
michael@0 | 471 | |
michael@0 | 472 | sanitize: function () |
michael@0 | 473 | { |
michael@0 | 474 | // Update pref values before handing off to the sanitizer (bug 453440) |
michael@0 | 475 | this.updatePrefs(); |
michael@0 | 476 | var s = new Sanitizer(); |
michael@0 | 477 | s.prefDomain = "privacy.cpd."; |
michael@0 | 478 | |
michael@0 | 479 | var durList = document.getElementById("sanitizeDurationChoice"); |
michael@0 | 480 | var durValue = parseInt(durList.value); |
michael@0 | 481 | s.ignoreTimespan = durValue === Sanitizer.TIMESPAN_EVERYTHING; |
michael@0 | 482 | |
michael@0 | 483 | // Set the sanitizer's time range if we're not clearing everything. |
michael@0 | 484 | if (!s.ignoreTimespan) { |
michael@0 | 485 | // If user selected a custom timespan, use that. |
michael@0 | 486 | if (durValue === this.TIMESPAN_CUSTOM) { |
michael@0 | 487 | var view = this.placesTree.view; |
michael@0 | 488 | var now = Date.now() * 1000; |
michael@0 | 489 | // We disable the dialog's OK button if there's no selection, but we'll |
michael@0 | 490 | // handle that case just in... case. |
michael@0 | 491 | if (view.selection.getRangeCount() === 0) |
michael@0 | 492 | s.range = [now, now]; |
michael@0 | 493 | else { |
michael@0 | 494 | var startIndexRef = {}; |
michael@0 | 495 | // Tree sorted by visit date DEscending, so start time time comes last. |
michael@0 | 496 | view.selection.getRangeAt(0, {}, startIndexRef); |
michael@0 | 497 | view.QueryInterface(Ci.nsINavHistoryResultTreeViewer); |
michael@0 | 498 | var startNode = view.nodeForTreeIndex(startIndexRef.value); |
michael@0 | 499 | s.range = [startNode.time, now]; |
michael@0 | 500 | } |
michael@0 | 501 | } |
michael@0 | 502 | // Otherwise use the predetermined range. |
michael@0 | 503 | else |
michael@0 | 504 | s.range = [this.durationStartTimes[durValue], Date.now() * 1000]; |
michael@0 | 505 | } |
michael@0 | 506 | |
michael@0 | 507 | try { |
michael@0 | 508 | s.sanitize(); |
michael@0 | 509 | } catch (er) { |
michael@0 | 510 | Components.utils.reportError("Exception during sanitize: " + er); |
michael@0 | 511 | } |
michael@0 | 512 | return true; |
michael@0 | 513 | }, |
michael@0 | 514 | |
michael@0 | 515 | /** |
michael@0 | 516 | * In order to mark the custom Places tree view and its nsINavHistoryResult |
michael@0 | 517 | * for garbage collection, we need to break the reference cycle between the |
michael@0 | 518 | * two. |
michael@0 | 519 | */ |
michael@0 | 520 | unload: function () |
michael@0 | 521 | { |
michael@0 | 522 | let result = this.placesTree.getResult(); |
michael@0 | 523 | result.removeObserver(this.placesTree.view); |
michael@0 | 524 | this.placesTree.view = null; |
michael@0 | 525 | }, |
michael@0 | 526 | |
michael@0 | 527 | /** |
michael@0 | 528 | * Called when the user moves the grippy by dragging it, clicking in the tree, |
michael@0 | 529 | * or on keypress. Updates the duration dropdown so that it displays the |
michael@0 | 530 | * appropriate specific or custom duration. |
michael@0 | 531 | * |
michael@0 | 532 | * @param aEventName |
michael@0 | 533 | * The name of the event whose handler called this method, e.g., |
michael@0 | 534 | * "ondragstart", "onkeypress", etc. |
michael@0 | 535 | * @param aEvent |
michael@0 | 536 | * The event captured in the event handler. |
michael@0 | 537 | */ |
michael@0 | 538 | grippyMoved: function (aEventName, aEvent) |
michael@0 | 539 | { |
michael@0 | 540 | gContiguousSelectionTreeHelper[aEventName](aEvent); |
michael@0 | 541 | var lastSelRow = gContiguousSelectionTreeHelper.getGrippyRow() - 1; |
michael@0 | 542 | var durList = document.getElementById("sanitizeDurationChoice"); |
michael@0 | 543 | var durValue = parseInt(durList.value); |
michael@0 | 544 | |
michael@0 | 545 | // Multiple durations can map to the same row. Don't update the dropdown |
michael@0 | 546 | // if the current duration is valid for lastSelRow. |
michael@0 | 547 | if ((durValue !== this.TIMESPAN_CUSTOM || |
michael@0 | 548 | lastSelRow in this.durationRowsToVals) && |
michael@0 | 549 | (durValue === this.TIMESPAN_CUSTOM || |
michael@0 | 550 | this.durationValsToRows[durValue] !== lastSelRow)) { |
michael@0 | 551 | // Setting durList.value causes its onselect handler to fire, which calls |
michael@0 | 552 | // selectByTimespan(). |
michael@0 | 553 | if (lastSelRow in this.durationRowsToVals) |
michael@0 | 554 | durList.value = this.durationRowsToVals[lastSelRow]; |
michael@0 | 555 | else |
michael@0 | 556 | durList.value = this.TIMESPAN_CUSTOM; |
michael@0 | 557 | } |
michael@0 | 558 | |
michael@0 | 559 | // If there are no selected rows, disable the dialog's OK button. |
michael@0 | 560 | document.documentElement.getButton("accept").disabled = lastSelRow < 0; |
michael@0 | 561 | } |
michael@0 | 562 | #endif |
michael@0 | 563 | |
michael@0 | 564 | }; |
michael@0 | 565 | |
michael@0 | 566 | |
michael@0 | 567 | #ifdef CRH_DIALOG_TREE_VIEW |
michael@0 | 568 | /** |
michael@0 | 569 | * A helper for handling contiguous selection in the tree. |
michael@0 | 570 | */ |
michael@0 | 571 | var gContiguousSelectionTreeHelper = { |
michael@0 | 572 | |
michael@0 | 573 | /** |
michael@0 | 574 | * Gets the tree associated with this helper. |
michael@0 | 575 | */ |
michael@0 | 576 | get tree() |
michael@0 | 577 | { |
michael@0 | 578 | return this._tree; |
michael@0 | 579 | }, |
michael@0 | 580 | |
michael@0 | 581 | /** |
michael@0 | 582 | * Sets the tree that this module handles. The tree is assigned a new view |
michael@0 | 583 | * that is equipped to handle contiguous selection. You can pass in an |
michael@0 | 584 | * object that will be used as the prototype of the new view. Otherwise |
michael@0 | 585 | * the tree's current view is used as the prototype. |
michael@0 | 586 | * |
michael@0 | 587 | * @param aTreeElement |
michael@0 | 588 | * The tree element |
michael@0 | 589 | * @param aProtoTreeView |
michael@0 | 590 | * If defined, this will be used as the prototype of the tree's new |
michael@0 | 591 | * view |
michael@0 | 592 | * @return The new view |
michael@0 | 593 | */ |
michael@0 | 594 | setTree: function CSTH_setTree(aTreeElement, aProtoTreeView) |
michael@0 | 595 | { |
michael@0 | 596 | this._tree = aTreeElement; |
michael@0 | 597 | var newView = this._makeTreeView(aProtoTreeView || aTreeElement.view); |
michael@0 | 598 | aTreeElement.view = newView; |
michael@0 | 599 | return newView; |
michael@0 | 600 | }, |
michael@0 | 601 | |
michael@0 | 602 | /** |
michael@0 | 603 | * The index of the row that the grippy occupies. Note that the index of the |
michael@0 | 604 | * last selected row is getGrippyRow() - 1. If getGrippyRow() is 0, then |
michael@0 | 605 | * no selection exists. |
michael@0 | 606 | * |
michael@0 | 607 | * @return The row index of the grippy |
michael@0 | 608 | */ |
michael@0 | 609 | getGrippyRow: function CSTH_getGrippyRow() |
michael@0 | 610 | { |
michael@0 | 611 | var sel = this.tree.view.selection; |
michael@0 | 612 | var rangeCount = sel.getRangeCount(); |
michael@0 | 613 | if (rangeCount === 0) |
michael@0 | 614 | return 0; |
michael@0 | 615 | if (rangeCount !== 1) { |
michael@0 | 616 | throw "contiguous selection tree helper: getGrippyRow called with " + |
michael@0 | 617 | "multiple selection ranges"; |
michael@0 | 618 | } |
michael@0 | 619 | var max = {}; |
michael@0 | 620 | sel.getRangeAt(0, {}, max); |
michael@0 | 621 | return max.value + 1; |
michael@0 | 622 | }, |
michael@0 | 623 | |
michael@0 | 624 | /** |
michael@0 | 625 | * Helper function for the dragover event. Your dragover listener should |
michael@0 | 626 | * call this. It updates the selection in the tree under the mouse. |
michael@0 | 627 | * |
michael@0 | 628 | * @param aEvent |
michael@0 | 629 | * The observed dragover event |
michael@0 | 630 | */ |
michael@0 | 631 | ondragover: function CSTH_ondragover(aEvent) |
michael@0 | 632 | { |
michael@0 | 633 | // Without this when dragging on Windows the mouse cursor is a "no" sign. |
michael@0 | 634 | // This makes it a drop symbol. |
michael@0 | 635 | var ds = Cc["@mozilla.org/widget/dragservice;1"]. |
michael@0 | 636 | getService(Ci.nsIDragService). |
michael@0 | 637 | getCurrentSession(); |
michael@0 | 638 | ds.canDrop = true; |
michael@0 | 639 | ds.dragAction = 0; |
michael@0 | 640 | |
michael@0 | 641 | var tbo = this.tree.treeBoxObject; |
michael@0 | 642 | aEvent.QueryInterface(Ci.nsIDOMMouseEvent); |
michael@0 | 643 | var hoverRow = tbo.getRowAt(aEvent.clientX, aEvent.clientY); |
michael@0 | 644 | |
michael@0 | 645 | if (hoverRow < 0) |
michael@0 | 646 | return; |
michael@0 | 647 | |
michael@0 | 648 | this.rangedSelect(hoverRow - 1); |
michael@0 | 649 | }, |
michael@0 | 650 | |
michael@0 | 651 | /** |
michael@0 | 652 | * Helper function for the dragstart event. Your dragstart listener should |
michael@0 | 653 | * call this. It starts a drag session. |
michael@0 | 654 | * |
michael@0 | 655 | * @param aEvent |
michael@0 | 656 | * The observed dragstart event |
michael@0 | 657 | */ |
michael@0 | 658 | ondragstart: function CSTH_ondragstart(aEvent) |
michael@0 | 659 | { |
michael@0 | 660 | var tbo = this.tree.treeBoxObject; |
michael@0 | 661 | var clickedRow = tbo.getRowAt(aEvent.clientX, aEvent.clientY); |
michael@0 | 662 | |
michael@0 | 663 | if (clickedRow !== this.getGrippyRow()) |
michael@0 | 664 | return; |
michael@0 | 665 | |
michael@0 | 666 | // This part is a hack. What we really want is a grab and slide, not |
michael@0 | 667 | // drag and drop. Start a move drag session with dummy data and a |
michael@0 | 668 | // dummy region. Set the region's coordinates to (Infinity, Infinity) |
michael@0 | 669 | // so it's drawn offscreen and its size to (1, 1). |
michael@0 | 670 | var arr = Cc["@mozilla.org/supports-array;1"]. |
michael@0 | 671 | createInstance(Ci.nsISupportsArray); |
michael@0 | 672 | var trans = Cc["@mozilla.org/widget/transferable;1"]. |
michael@0 | 673 | createInstance(Ci.nsITransferable); |
michael@0 | 674 | trans.init(null); |
michael@0 | 675 | trans.setTransferData('dummy-flavor', null, 0); |
michael@0 | 676 | arr.AppendElement(trans); |
michael@0 | 677 | var reg = Cc["@mozilla.org/gfx/region;1"]. |
michael@0 | 678 | createInstance(Ci.nsIScriptableRegion); |
michael@0 | 679 | reg.setToRect(Infinity, Infinity, 1, 1); |
michael@0 | 680 | var ds = Cc["@mozilla.org/widget/dragservice;1"]. |
michael@0 | 681 | getService(Ci.nsIDragService); |
michael@0 | 682 | ds.invokeDragSession(aEvent.target, arr, reg, ds.DRAGDROP_ACTION_MOVE); |
michael@0 | 683 | }, |
michael@0 | 684 | |
michael@0 | 685 | /** |
michael@0 | 686 | * Helper function for the keypress event. Your keypress listener should |
michael@0 | 687 | * call this. Users can use Up, Down, Page Up/Down, Home, and End to move |
michael@0 | 688 | * the bottom of the selection window. |
michael@0 | 689 | * |
michael@0 | 690 | * @param aEvent |
michael@0 | 691 | * The observed keypress event |
michael@0 | 692 | */ |
michael@0 | 693 | onkeypress: function CSTH_onkeypress(aEvent) |
michael@0 | 694 | { |
michael@0 | 695 | var grippyRow = this.getGrippyRow(); |
michael@0 | 696 | var tbo = this.tree.treeBoxObject; |
michael@0 | 697 | var rangeEnd; |
michael@0 | 698 | switch (aEvent.keyCode) { |
michael@0 | 699 | case aEvent.DOM_VK_HOME: |
michael@0 | 700 | rangeEnd = 0; |
michael@0 | 701 | break; |
michael@0 | 702 | case aEvent.DOM_VK_PAGE_UP: |
michael@0 | 703 | rangeEnd = grippyRow - tbo.getPageLength(); |
michael@0 | 704 | break; |
michael@0 | 705 | case aEvent.DOM_VK_UP: |
michael@0 | 706 | rangeEnd = grippyRow - 2; |
michael@0 | 707 | break; |
michael@0 | 708 | case aEvent.DOM_VK_DOWN: |
michael@0 | 709 | rangeEnd = grippyRow; |
michael@0 | 710 | break; |
michael@0 | 711 | case aEvent.DOM_VK_PAGE_DOWN: |
michael@0 | 712 | rangeEnd = grippyRow + tbo.getPageLength(); |
michael@0 | 713 | break; |
michael@0 | 714 | case aEvent.DOM_VK_END: |
michael@0 | 715 | rangeEnd = this.tree.view.rowCount - 2; |
michael@0 | 716 | break; |
michael@0 | 717 | default: |
michael@0 | 718 | return; |
michael@0 | 719 | break; |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | aEvent.stopPropagation(); |
michael@0 | 723 | |
michael@0 | 724 | // First, clip rangeEnd. this.rangedSelect() doesn't clip the range if we |
michael@0 | 725 | // select past the ends of the tree. |
michael@0 | 726 | if (rangeEnd < 0) |
michael@0 | 727 | rangeEnd = -1; |
michael@0 | 728 | else if (this.tree.view.rowCount - 2 < rangeEnd) |
michael@0 | 729 | rangeEnd = this.tree.view.rowCount - 2; |
michael@0 | 730 | |
michael@0 | 731 | // Next, (de)select. |
michael@0 | 732 | this.rangedSelect(rangeEnd); |
michael@0 | 733 | |
michael@0 | 734 | // Finally, scroll the tree. We always want one row above and below the |
michael@0 | 735 | // grippy row to be visible if possible. |
michael@0 | 736 | if (rangeEnd < grippyRow) // moved up |
michael@0 | 737 | tbo.ensureRowIsVisible(rangeEnd < 0 ? 0 : rangeEnd); |
michael@0 | 738 | else { // moved down |
michael@0 | 739 | if (rangeEnd + 2 < this.tree.view.rowCount) |
michael@0 | 740 | tbo.ensureRowIsVisible(rangeEnd + 2); |
michael@0 | 741 | else if (rangeEnd + 1 < this.tree.view.rowCount) |
michael@0 | 742 | tbo.ensureRowIsVisible(rangeEnd + 1); |
michael@0 | 743 | } |
michael@0 | 744 | }, |
michael@0 | 745 | |
michael@0 | 746 | /** |
michael@0 | 747 | * Helper function for the mousedown event. Your mousedown listener should |
michael@0 | 748 | * call this. Users can click on individual rows to make the selection |
michael@0 | 749 | * jump to them immediately. |
michael@0 | 750 | * |
michael@0 | 751 | * @param aEvent |
michael@0 | 752 | * The observed mousedown event |
michael@0 | 753 | */ |
michael@0 | 754 | onmousedown: function CSTH_onmousedown(aEvent) |
michael@0 | 755 | { |
michael@0 | 756 | var tbo = this.tree.treeBoxObject; |
michael@0 | 757 | var clickedRow = tbo.getRowAt(aEvent.clientX, aEvent.clientY); |
michael@0 | 758 | |
michael@0 | 759 | if (clickedRow < 0 || clickedRow >= this.tree.view.rowCount) |
michael@0 | 760 | return; |
michael@0 | 761 | |
michael@0 | 762 | if (clickedRow < this.getGrippyRow()) |
michael@0 | 763 | this.rangedSelect(clickedRow); |
michael@0 | 764 | else if (clickedRow > this.getGrippyRow()) |
michael@0 | 765 | this.rangedSelect(clickedRow - 1); |
michael@0 | 766 | }, |
michael@0 | 767 | |
michael@0 | 768 | /** |
michael@0 | 769 | * Selects range [0, aEndRow] in the tree. The grippy row will then be at |
michael@0 | 770 | * index aEndRow + 1. aEndRow may be -1, in which case the selection is |
michael@0 | 771 | * cleared and the grippy row will be at index 0. |
michael@0 | 772 | * |
michael@0 | 773 | * @param aEndRow |
michael@0 | 774 | * The range [0, aEndRow] will be selected. |
michael@0 | 775 | */ |
michael@0 | 776 | rangedSelect: function CSTH_rangedSelect(aEndRow) |
michael@0 | 777 | { |
michael@0 | 778 | var tbo = this.tree.treeBoxObject; |
michael@0 | 779 | if (aEndRow < 0) |
michael@0 | 780 | this.tree.view.selection.clearSelection(); |
michael@0 | 781 | else |
michael@0 | 782 | this.tree.view.selection.rangedSelect(0, aEndRow, false); |
michael@0 | 783 | tbo.invalidateRange(tbo.getFirstVisibleRow(), tbo.getLastVisibleRow()); |
michael@0 | 784 | }, |
michael@0 | 785 | |
michael@0 | 786 | /** |
michael@0 | 787 | * Scrolls the tree so that the grippy row is in the center of the view. |
michael@0 | 788 | */ |
michael@0 | 789 | scrollToGrippy: function CSTH_scrollToGrippy() |
michael@0 | 790 | { |
michael@0 | 791 | var rowCount = this.tree.view.rowCount; |
michael@0 | 792 | var tbo = this.tree.treeBoxObject; |
michael@0 | 793 | var pageLen = tbo.getPageLength() || |
michael@0 | 794 | parseInt(this.tree.getAttribute("rows")) || |
michael@0 | 795 | 10; |
michael@0 | 796 | |
michael@0 | 797 | // All rows fit on a single page. |
michael@0 | 798 | if (rowCount <= pageLen) |
michael@0 | 799 | return; |
michael@0 | 800 | |
michael@0 | 801 | var scrollToRow = this.getGrippyRow() - Math.ceil(pageLen / 2.0); |
michael@0 | 802 | |
michael@0 | 803 | // Grippy row is in first half of first page. |
michael@0 | 804 | if (scrollToRow < 0) |
michael@0 | 805 | scrollToRow = 0; |
michael@0 | 806 | |
michael@0 | 807 | // Grippy row is in last half of last page. |
michael@0 | 808 | else if (rowCount < scrollToRow + pageLen) |
michael@0 | 809 | scrollToRow = rowCount - pageLen; |
michael@0 | 810 | |
michael@0 | 811 | tbo.scrollToRow(scrollToRow); |
michael@0 | 812 | }, |
michael@0 | 813 | |
michael@0 | 814 | /** |
michael@0 | 815 | * Creates a new tree view suitable for contiguous selection. If |
michael@0 | 816 | * aProtoTreeView is specified, it's used as the new view's prototype. |
michael@0 | 817 | * Otherwise the tree's current view is used as the prototype. |
michael@0 | 818 | * |
michael@0 | 819 | * @param aProtoTreeView |
michael@0 | 820 | * Used as the new view's prototype if specified |
michael@0 | 821 | */ |
michael@0 | 822 | _makeTreeView: function CSTH__makeTreeView(aProtoTreeView) |
michael@0 | 823 | { |
michael@0 | 824 | var view = aProtoTreeView; |
michael@0 | 825 | var that = this; |
michael@0 | 826 | |
michael@0 | 827 | //XXXadw: When Alex gets the grippy icon done, this may or may not change, |
michael@0 | 828 | // depending on how we style it. |
michael@0 | 829 | view.isSeparator = function CSTH_View_isSeparator(aRow) |
michael@0 | 830 | { |
michael@0 | 831 | return aRow === that.getGrippyRow(); |
michael@0 | 832 | }; |
michael@0 | 833 | |
michael@0 | 834 | // rowCount includes the grippy row. |
michael@0 | 835 | view.__defineGetter__("_rowCount", view.__lookupGetter__("rowCount")); |
michael@0 | 836 | view.__defineGetter__("rowCount", |
michael@0 | 837 | function CSTH_View_rowCount() |
michael@0 | 838 | { |
michael@0 | 839 | return this._rowCount + 1; |
michael@0 | 840 | }); |
michael@0 | 841 | |
michael@0 | 842 | // This has to do with visual feedback in the view itself, e.g., drawing |
michael@0 | 843 | // a small line underneath the dropzone. Not what we want. |
michael@0 | 844 | view.canDrop = function CSTH_View_canDrop() { return false; }; |
michael@0 | 845 | |
michael@0 | 846 | // No clicking headers to sort the tree or sort feedback on columns. |
michael@0 | 847 | view.cycleHeader = function CSTH_View_cycleHeader() {}; |
michael@0 | 848 | view.sortingChanged = function CSTH_View_sortingChanged() {}; |
michael@0 | 849 | |
michael@0 | 850 | // Override a bunch of methods to account for the grippy row. |
michael@0 | 851 | |
michael@0 | 852 | view._getCellProperties = view.getCellProperties; |
michael@0 | 853 | view.getCellProperties = |
michael@0 | 854 | function CSTH_View_getCellProperties(aRow, aCol) |
michael@0 | 855 | { |
michael@0 | 856 | var grippyRow = that.getGrippyRow(); |
michael@0 | 857 | if (aRow === grippyRow) |
michael@0 | 858 | return "grippyRow"; |
michael@0 | 859 | if (aRow < grippyRow) |
michael@0 | 860 | return this._getCellProperties(aRow, aCol); |
michael@0 | 861 | |
michael@0 | 862 | return this._getCellProperties(aRow - 1, aCol); |
michael@0 | 863 | }; |
michael@0 | 864 | |
michael@0 | 865 | view._getRowProperties = view.getRowProperties; |
michael@0 | 866 | view.getRowProperties = |
michael@0 | 867 | function CSTH_View_getRowProperties(aRow) |
michael@0 | 868 | { |
michael@0 | 869 | var grippyRow = that.getGrippyRow(); |
michael@0 | 870 | if (aRow === grippyRow) |
michael@0 | 871 | return "grippyRow"; |
michael@0 | 872 | |
michael@0 | 873 | if (aRow < grippyRow) |
michael@0 | 874 | return this._getRowProperties(aRow); |
michael@0 | 875 | |
michael@0 | 876 | return this._getRowProperties(aRow - 1); |
michael@0 | 877 | }; |
michael@0 | 878 | |
michael@0 | 879 | view._getCellText = view.getCellText; |
michael@0 | 880 | view.getCellText = |
michael@0 | 881 | function CSTH_View_getCellText(aRow, aCol) |
michael@0 | 882 | { |
michael@0 | 883 | var grippyRow = that.getGrippyRow(); |
michael@0 | 884 | if (aRow === grippyRow) |
michael@0 | 885 | return ""; |
michael@0 | 886 | aRow = aRow < grippyRow ? aRow : aRow - 1; |
michael@0 | 887 | return this._getCellText(aRow, aCol); |
michael@0 | 888 | }; |
michael@0 | 889 | |
michael@0 | 890 | view._getImageSrc = view.getImageSrc; |
michael@0 | 891 | view.getImageSrc = |
michael@0 | 892 | function CSTH_View_getImageSrc(aRow, aCol) |
michael@0 | 893 | { |
michael@0 | 894 | var grippyRow = that.getGrippyRow(); |
michael@0 | 895 | if (aRow === grippyRow) |
michael@0 | 896 | return ""; |
michael@0 | 897 | aRow = aRow < grippyRow ? aRow : aRow - 1; |
michael@0 | 898 | return this._getImageSrc(aRow, aCol); |
michael@0 | 899 | }; |
michael@0 | 900 | |
michael@0 | 901 | view.isContainer = function CSTH_View_isContainer(aRow) { return false; }; |
michael@0 | 902 | view.getParentIndex = function CSTH_View_getParentIndex(aRow) { return -1; }; |
michael@0 | 903 | view.getLevel = function CSTH_View_getLevel(aRow) { return 0; }; |
michael@0 | 904 | view.hasNextSibling = function CSTH_View_hasNextSibling(aRow, aAfterIndex) |
michael@0 | 905 | { |
michael@0 | 906 | return aRow < this.rowCount - 1; |
michael@0 | 907 | }; |
michael@0 | 908 | |
michael@0 | 909 | return view; |
michael@0 | 910 | } |
michael@0 | 911 | }; |
michael@0 | 912 | #endif |