1 |
/* ***** BEGIN LICENSE BLOCK ***** |
2 |
* Version: MPL 1.1 |
3 |
* |
4 |
* The contents of this file are subject to the Mozilla Public License Version |
5 |
* 1.1 (the "License"); you may not use this file except in compliance with |
6 |
* the License. You may obtain a copy of the License at |
7 |
* http://www.mozilla.org/MPL/ |
8 |
* |
9 |
* Software distributed under the License is distributed on an "AS IS" basis, |
10 |
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
11 |
* for the specific language governing rights and limitations under the |
12 |
* License. |
13 |
* |
14 |
* The Original Code is Adblock for Mozilla. |
15 |
* |
16 |
* The Initial Developer of the Original Code is |
17 |
* Henrik Aasted Sorensen. |
18 |
* Portions created by the Initial Developer are Copyright (C) 2002 |
19 |
* the Initial Developer. All Rights Reserved. |
20 |
* |
21 |
* Contributor(s): |
22 |
* Henrik Aasted Sorensen |
23 |
* Stefan Kinitz |
24 |
* Wladimir Palant |
25 |
* rue |
26 |
* ***** END LICENSE BLOCK ***** */ |
27 |
|
28 |
var sortFlag = 0; // global flag to discern if the user has already been told to "reload settings to activate sort" |
29 |
var popupBeingShownN = new Array(); // global node for button-menus |
30 |
popupBeingShownN[0] = false; // boolean: are we showing a button-menu -- "now" |
31 |
popupBeingShownN[1] = null; // the id of the button-menu showing now -- "currentItem" |
32 |
var restoreindexTimer; // calls restoreListIndex after a short delay when the list is created / updated |
33 |
var menuTimer; // deactivates the button-menu's after 15 seconds |
34 |
var buttonMenusLoaded = false; // global flag to discern if the button-menus have been called -- filter context-menu needs this |
35 |
|
36 |
// Loads preferences into the UI. |
37 |
function loadPrefWindow() { |
38 |
|
39 |
try { |
40 |
var prefObj = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService); |
41 |
var Branch = prefObj.getBranch("adblock."); |
42 |
var Enabled = !Branch.prefHasUserValue("enabled") || Branch.getBoolPref("enabled"); // default:true |
43 |
var LinkCheck = Branch.prefHasUserValue("linkcheck") && Branch.getBoolPref("linkcheck"); // default:false |
44 |
var PageBlock = Branch.prefHasUserValue("pageblock") && Branch.getBoolPref("pageblock"); // default:false |
45 |
var Hide = Branch.prefHasUserValue("hide") && Branch.getBoolPref("hide"); // default:false; |
46 |
var FastCollapse = !Branch.prefHasUserValue("fastcollapse") || Branch.getBoolPref("fastcollapse"); // default:true |
47 |
var Patterns = Branch.prefHasUserValue("patterns") ? Branch.getCharPref("patterns").split(" ") : null; |
48 |
var ListSort = Branch.prefHasUserValue("listsort") && Branch.getBoolPref("listsort"); // default:false |
49 |
var FrameObjects = !Branch.prefHasUserValue("frameobjects") || Branch.getBoolPref("frameobjects"); // default:true |
50 |
|
51 |
} catch(e) {} |
52 |
|
53 |
// load patterns into list |
54 |
var overwriteCurrentList = true; // always overwrite the current list |
55 |
if (Patterns != null && Patterns != "") |
56 |
fillList(Patterns, overwriteCurrentList, ListSort); |
57 |
//else document.getElementById("newfilter").focus(); // UNNEEDED: reSelectListItem does this -- empty-list returns focus to the textbox |
58 |
|
59 |
var enablebox = document.getElementById("enabled"); |
60 |
enablebox.checked = Enabled; |
61 |
|
62 |
var radiohide = document.getElementById("radio-hide"); |
63 |
var radioremove = document.getElementById("radio-remove"); |
64 |
radiohide.disabled = !enablebox.checked; |
65 |
radioremove.disabled = !enablebox.checked; |
66 |
|
67 |
if (Hide) radiohide.setAttribute("selected", true); |
68 |
else radioremove.setAttribute("selected", true); |
69 |
|
70 |
/* |
71 |
var collapsebox = document.getElementById("fastcollapse"); |
72 |
collapsebox.checked = FastCollapse; |
73 |
(document.getElementById('radio-remove').selected) ? |
74 |
document.getElementById('fastcollapse').setAttribute('style', 'visibility: visible') |
75 |
: |
76 |
document.getElementById('fastcollapse').setAttribute('style', 'visibility: hidden'); |
77 |
*/ |
78 |
|
79 |
var listsortmenu = document.getElementById("listsort"); |
80 |
listsortmenu.setAttribute("checked", ListSort); |
81 |
var frameobjectsmenu = document.getElementById("frameobjects"); |
82 |
frameobjectsmenu.setAttribute("checked", FrameObjects); |
83 |
var slowcollapsemenu = document.getElementById("slowcollapse"); |
84 |
slowcollapsemenu.setAttribute("checked", !FastCollapse); // reverse-pref: fast -> slow |
85 |
var linkcheckmenu = document.getElementById("linkcheck"); |
86 |
linkcheckmenu.setAttribute("checked", LinkCheck); |
87 |
var pageblockmenu = document.getElementById("pageblock"); |
88 |
pageblockmenu.setAttribute("checked", PageBlock); |
89 |
|
90 |
window.setTimeout("restoreListIndex()", 0); // sets the list to the last-selected item, or the input-line if none |
91 |
//restoreListIndex(); |
92 |
} |
93 |
|
94 |
// Add a filter to the list -- now constructed for inline-editing :) |
95 |
function addFilter() { |
96 |
var newItem; |
97 |
var list = document.getElementById("list"); |
98 |
var textbox = document.getElementById("newfilter"); |
99 |
|
100 |
// if there's an actual entry |
101 |
if (textbox.value != "") { |
102 |
// if it's not a regular expression, or it is, BUT we've said 'ok' to the warning |
103 |
if (!adblockIsRegExp(textbox.value) || (adblockIsRegExp(textbox.value) && adblockWarnRegExp())) { |
104 |
var filter = makePattern(textbox.value); // must be made into pattern |
105 |
textbox.value = ""; |
106 |
var newItem = createListItem(filter); |
107 |
var newItemindex = list.getIndexOfItem(newItem); |
108 |
list.selectedIndex = newItemindex; // sets the list's selection to the new item |
109 |
list.currentItem = newItem; |
110 |
list.ensureIndexIsVisible(newItemindex); // scrolls the list to show the new item |
111 |
|
112 |
enableRevert(); |
113 |
} |
114 |
} |
115 |
//if (!textbox.focused) |
116 |
//textbox.focus(); // always return focus to the textbox |
117 |
} |
118 |
|
119 |
function fillList(Patterns, overwriteCurrentList, listSortChecked) { |
120 |
var list = document.getElementById("list"); |
121 |
/* |
122 |
var listParent = document.getElementById("listparent"); |
123 |
var listLoading = document.getElementById("listarea-description-loading"); |
124 |
*/ |
125 |
|
126 |
if (overwriteCurrentList && list.getRowCount() != 0) { // use instead for the disabled code -> && list.hasChildNodes()) { |
127 |
/* |
128 |
// hide the list and show the "loading" text |
129 |
list.hidden = true; |
130 |
listParent.hidden = true; |
131 |
listLoading.hidden = false; |
132 |
*/ |
133 |
|
134 |
clearList(true); // clear the list, sans dialog |
135 |
|
136 |
} |
137 |
|
138 |
// Sort the patterns -- would *much* rather do this with a col-header [rue] |
139 |
if (listSortChecked) |
140 |
Patterns.sort(); |
141 |
|
142 |
// fill the list with patterns |
143 |
for (var i = 0 ; i < Patterns.length; i++) |
144 |
createListItem(Patterns[i]); |
145 |
|
146 |
/* |
147 |
// show the list and hide the "loading" text |
148 |
listLoading.hidden = true; |
149 |
listParent.hidden = false; |
150 |
list.hidden = false; |
151 |
*/ |
152 |
restoreindexTimer = setInterval('clearInterval(restoreindexTimer); restoreListIndex();',10); |
153 |
} |
154 |
|
155 |
// Asks the user if he really wants to clear the list. |
156 |
function clearList(dontConfirm) { |
157 |
if (!dontConfirm) |
158 |
if (!confirm("Do you really want to clear the list?")) |
159 |
return; |
160 |
var list = document.getElementById("list"); |
161 |
list.parentNode.replaceChild(list.cloneNode(false), list); // clear the list |
162 |
|
163 |
enableRevert(); |
164 |
} |
165 |
|
166 |
// Imports filters from disc. |
167 |
function importList() { |
168 |
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(Components.interfaces.nsIFilePicker); |
169 |
var stream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream); |
170 |
var streamIO = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream); |
171 |
var overwriteCurrentList = false; |
172 |
var input; |
173 |
var inputArray; |
174 |
var validFile = false; |
175 |
|
176 |
fp.init(window, "Select a File", fp.modeOpen); |
177 |
fp.appendFilters(fp.filterText); |
178 |
|
179 |
if (fp.show() != fp.returnCancel) { |
180 |
stream.init(fp.file, 0x01, 0444, null); |
181 |
streamIO.init(stream); |
182 |
input = streamIO.read(stream.available()); |
183 |
streamIO.close(); |
184 |
stream.close(); |
185 |
|
186 |
/* |
187 |
var breakStart = 9, breakEnd = 9-1; // the index just after adblock's header |
188 |
while (/\s/.test(input.charAt(breakEnd+1)) ) |
189 |
breakEnd++; |
190 |
var linebreak = input.substring(breakStart, breakEnd);// substring takes two indices, while substr takes index + length |
191 |
*/ |
192 |
|
193 |
// now: unix + mac + dos environment-compatible |
194 |
linebreak = input.match(/(?:\[Ad[Bb]lock\])(((\n+)|(\r+))+)/m)[1]; // first: whole match -- second: backref-1 -- etc.. |
195 |
inputArray = input.split(linebreak); |
196 |
|
197 |
var headerRe = /\[Ad[Bb]lock\]/; // tests if the first line is adblock's header |
198 |
if (headerRe.test(inputArray[0])) { |
199 |
inputArray.shift(); |
200 |
overwriteCurrentList = confirm("Do you want to overwrite the current list?\n..if not, pressing 'cancel' will append."); |
201 |
fillList(inputArray, overwriteCurrentList); |
202 |
|
203 |
enableRevert(); } |
204 |
else |
205 |
alert("File not valid."); |
206 |
} |
207 |
} |
208 |
|
209 |
// Exports the current list of filters to a file on disc. |
210 |
function exportList() { |
211 |
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(Components.interfaces.nsIFilePicker); |
212 |
var stream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); |
213 |
|
214 |
fp.init(window, "Select a File", fp.modeSave); |
215 |
fp.appendFilters(fp.filterText); |
216 |
|
217 |
if (fp.show() != fp.returnCancel) { |
218 |
if (fp.file.exists()) |
219 |
fp.file.remove(true); |
220 |
fp.file.create(fp.file.NORMAL_FILE_TYPE, 0666); |
221 |
|
222 |
stream.init(fp.file, 0x02, 0x200, null); |
223 |
stream.write("[Adblock]\n", 10); |
224 |
|
225 |
Patterns = getPatterns().split(" "); |
226 |
|
227 |
for (var i = 0; i < Patterns.length ; i++) { |
228 |
stream.write(Patterns[i], Patterns[i].length); |
229 |
if (i < (Patterns.length - 1)) |
230 |
stream.write("\n", 1); |
231 |
} |
232 |
stream.close(); |
233 |
} |
234 |
} |
235 |
|
236 |
// Creates a listitem with the proper children for inline-editing -- returns the item's node -- label is set to 'value' |
237 |
function createListItem(value) { |
238 |
var list = document.getElementById("list"); |
239 |
var newItem = document.createElement("listitem"); |
240 |
|
241 |
newItem.setAttribute("label", value); // sets the listItem's label for later saving -- needed by saveSettings() -- deactivateModify does this too. |
242 |
//newItem.setAttribute("context", "listitem-contextmenu"); |
243 |
newItem.setAttribute("ondblclick", "listClickControl();"); |
244 |
|
245 |
list.appendChild(newItem); |
246 |
return newItem; |
247 |
} |
248 |
|
249 |
// This function removes illegal characters (at the moment, space) from the patterns. |
250 |
function makePattern(pat) { |
251 |
var res = ""; |
252 |
for (var i = 0 ; i < pat.length ; i++) { |
253 |
switch (pat[i]) { |
254 |
case ' ' : |
255 |
break; |
256 |
default : |
257 |
res += pat[i]; |
258 |
break; |
259 |
} |
260 |
} |
261 |
return res; |
262 |
} |
263 |
|
264 |
// receives keypresses from the list-area... and makes decisions :) |
265 |
function listKeyControl(eventX) { |
266 |
|
267 |
var keyCodeX = eventX.keyCode; // action-key or '0' |
268 |
var charCodeX = eventX.charCode; // character-key or '0' |
269 |
keyCodeX += charCodeX; // since one of them is always zero, this works |
270 |
|
271 |
var list = document.getElementById("list"); |
272 |
var modifying = findElementWithAttribute("listitem", "modifying", "true"); |
273 |
var listpress = (eventX.target.id == "list" && list.selectedItem); |
274 |
var boundParent = document.getBindingParent(eventX.originalTarget); |
275 |
var entrypress = (boundParent && boundParent.id == "newfilter" && eventX.originalTarget.value != ""); |
276 |
|
277 |
switch (keyCodeX) { |
278 |
// called by 'delete' |
279 |
case eventX.DOM_VK_BACK_SPACE: |
280 |
case eventX.DOM_VK_DELETE: |
281 |
if (!modifying && listpress) removeFilter(); // it's possible the user will click another item, but hit 'return' fast enough to be editing the previous one -- the selection index wasn't updated before the keypress |
282 |
break; |
283 |
|
284 |
// called by 'return' |
285 |
case eventX.DOM_VK_RETURN: |
286 |
case eventX.DOM_VK_ENTER: |
287 |
if (modifying) deactivateModify("return"); // if a filter was being modified |
288 |
else if (listpress) activateModify(); // if a filter was selected + focused |
289 |
else if (entrypress) addFilter(); // if the entry-area was selected + filled |
290 |
else { addFilter(); saveSettings(); } // we're finished |
291 |
break; |
292 |
|
293 |
// called by 'esc' |
294 |
case eventX.DOM_VK_ESCAPE: |
295 |
case eventX.DOM_VK_CANCEL: |
296 |
if (modifying) deactivateModify("esc"); // sends the 'esc' parameter, which signals to keep the old value |
297 |
else saveSettings(); // window.close(); // we're finished |
298 |
break; |
299 |
} |
300 |
} |
301 |
|
302 |
// receives mouseClicks from the list-area... and makes decisions :) |
303 |
function listClickControl() { |
304 |
var list = document.getElementById("list"); |
305 |
var selected = list.selectedItem; |
306 |
var modifying = findElementWithAttribute("listitem", "modifying", "true"); |
307 |
|
308 |
// if we have a filter selected and in-focus |
309 |
if (selected && selected.focus) { |
310 |
// if we're not modifying anything already |
311 |
if (!modifying) { |
312 |
activateModify(); |
313 |
} |
314 |
// we're already modifying something |
315 |
else { |
316 |
// if we want to activate a different item |
317 |
if (modifying != selected) { |
318 |
deactivateModify(); |
319 |
activateModify(); |
320 |
} |
321 |
} |
322 |
} |
323 |
// the listbox lost focus |
324 |
else { |
325 |
if (modifying &! modifying.focused) { |
326 |
deactivateModify(); |
327 |
} |
328 |
} |
329 |
} |
330 |
|
331 |
// activates the textbox for filter modification -- called by modifyFilter |
332 |
function activateModify() { |
333 |
var list = document.getElementById("list"); |
334 |
|
335 |
if (list.selectedItem) { |
336 |
var selectedX = list.selectedItem; |
337 |
var selectedXcell = document.getAnonymousNodes(selectedX)[0]; |
338 |
var selectedXfield = document.getAnonymousNodes(selectedX)[1]; |
339 |
var filter = selectedXcell.getAttribute("label"); |
340 |
var selectedIndex = list.selectedIndex; |
341 |
var scrollView = list.getIndexOfFirstVisibleRow(); |
342 |
var visibleRows = list.getNumberOfVisibleRows(); |
343 |
|
344 |
// if the selected item isn't visible, show it |
345 |
if (selectedIndex < scrollView || |
346 |
selectedIndex > (scrollView + visibleRows-1)) |
347 |
list.scrollToIndex(selectedIndex - (visibleRows/2) + ((visibleRows%2)?1:0)); // set it mid-screen, rounding up |
348 |
|
349 |
/* |
350 |
// if the selected item isn't visible, show it |
351 |
if (list.selectedIndex < list.getIndexOfFirstVisibleRow() || |
352 |
list.selectedIndex > (list.getIndexOfFirstVisibleRow()+list.getNumberOfVisibleRows()-1)) |
353 |
list.scrollToIndex(list.selectedIndex - (list.getNumberOfVisibleRows()/2)); // set it mid-screen |
354 |
*/ |
355 |
|
356 |
selectedX.setAttribute("modifying", "true"); |
357 |
selectedXcell.hidden = "true"; |
358 |
selectedXfield.value = filter; |
359 |
selectedXfield.setAttribute("hidden", "false"); |
360 |
selectedX.removeAttribute("ondblclick"); |
361 |
|
362 |
selectedXfield.focus(); |
363 |
selectedXfield.setAttribute("onblur", "deactivateModify(event.target);"); |
364 |
} |
365 |
} |
366 |
|
367 |
// deactivates the textbox for filter modification -- called by modifyFilter |
368 |
function deactivateModify(parameter) { |
369 |
var modifying = findElementWithAttribute("listitem", "modifying", "true"); |
370 |
|
371 |
if (modifying) { |
372 |
var list = document.getElementById("list"); |
373 |
var selectedIndex = list.selectedIndex; |
374 |
var selected = list.selectedItem; |
375 |
var disableX = modifying; |
376 |
var disableXcell = document.getAnonymousNodes(disableX)[0]; |
377 |
var disableXfield = document.getAnonymousNodes(disableX)[1]; |
378 |
var disableXindex = list.getIndexOfItem(disableX); |
379 |
var filter = makePattern(disableXfield.value); // note: 'value' needs to be called by method, not property |
380 |
|
381 |
// if the filter isn't blank, *And* we weren't called by the "esc" key |
382 |
if (filter != "" && parameter != "esc") { |
383 |
disableXcell.setAttribute("label", filter); // save the filter |
384 |
disableX.setAttribute("label", filter); // listitem's label: used for later saving -- needed by saveSettings() -- createListItem does this too. |
385 |
enableRevert(); |
386 |
} |
387 |
|
388 |
disableXcell.setAttribute("hidden", "false"); |
389 |
disableXfield.setAttribute("hidden", "true"); |
390 |
disableXfield.removeAttribute("onblur"); |
391 |
disableX.setAttribute("ondblclick", "listClickControl();"); |
392 |
|
393 |
list.selectedIndex = disableXindex; // restore selection |
394 |
list.currentItem = list.getItemAtIndex(list.selectedIndex); // and make it the current item |
395 |
list.focus(); // refocus the list -- doesn't interfere with refocusing on other controls, but does refocus the *Window* if we were called by losing it to another |
396 |
|
397 |
// keep this for last, so listKeyControl wont accidentally activate the wrong field |
398 |
// --(if the user deselects and then presses 'return' fast enough) |
399 |
disableX.setAttribute("modifying", "false"); } |
400 |
} |
401 |
|
402 |
// Removes the selected entry from the list and sets selection to the next item |
403 |
function removeFilter() { |
404 |
var list = document.getElementById("list"); |
405 |
var textbox = document.getElementById("newfilter"); |
406 |
|
407 |
// if we have an item to remove |
408 |
if (list.selectedItem) { |
409 |
var item = list.selectedItem; |
410 |
var itemindex = list.getIndexOfItem(item); // the current item's index |
411 |
var itemplace = itemindex + 1; // this avoids negative numbers later -- 'itemindex' is zero-based |
412 |
|
413 |
var nextitemindex = itemindex; // points to the next item -- equal, since the item below will move up the index once our current item is removed |
414 |
var previtemindex = nextitemindex - 1; // points to the previous -- in case we deleted the lowest entry |
415 |
|
416 |
var originaltotal = list.getRowCount(); // number of items in list, originally |
417 |
var currenttotal = originaltotal-1; // number of items after deletion |
418 |
var itemsfollowing = originaltotal - itemplace; // number of items following the deleted item |
419 |
|
420 |
list.removeChild(item); |
421 |
|
422 |
enableRevert(); |
423 |
|
424 |
// if we have any items left to select |
425 |
if (currenttotal > 0) { |
426 |
//if there's any items following our deleted entry |
427 |
if (itemsfollowing > 0 ) |
428 |
list.selectedIndex = nextitemindex; // sets the list-selection to the next item |
429 |
else |
430 |
list.selectedIndex = previtemindex; // sets the list-selection to the previous item |
431 |
|
432 |
list.ensureIndexIsVisible(list.selectedIndex); // scrolls the list to show the item |
433 |
} |
434 |
} |
435 |
} |
436 |
|
437 |
// Checks all elements of the specified tagname for an attribute match ON THEIR PARENT --returns first match |
438 |
function findElementWithParentAttribute(elementTagName, attributeX, valueX) { |
439 |
var elementarrayX = document.getElementsByTagName(elementTagName); |
440 |
|
441 |
for (var z = 0 ; z < elementarrayX.length ; z++) { |
442 |
var element = elementarrayX[z]; |
443 |
if (element.parentNode) { |
444 |
if (element.parentNode.hasAttribute(attributeX)) { |
445 |
if (element.parentNode.getAttribute(attributeX) == valueX) { |
446 |
return element; |
447 |
} |
448 |
} |
449 |
} |
450 |
} |
451 |
return false; // failure. |
452 |
} |
453 |
|
454 |
|
455 |
// Checks all elements of the specified tagname for an attribute match -- returns first match |
456 |
function findElementWithAttribute(elementTagName, attributeX, valueX) { |
457 |
var elementarrayX = document.getElementsByTagName(elementTagName); |
458 |
|
459 |
for (var z = 0 ; z < elementarrayX.length ; z++) { |
460 |
var element = elementarrayX[z]; |
461 |
if (element.hasAttribute(attributeX) && element.getAttribute(attributeX) == valueX) |
462 |
return element; |
463 |
} |
464 |
return false; // failure. |
465 |
} |
466 |
|
467 |
|
468 |
// sets the flag-parameter [+ timer] for button-menu "hovering" -- called by the menus when they show / hide |
469 |
function setMenuStatus(thisMenu) { |
470 |
var thisButton, activeButton, activeMenu; |
471 |
// flag to inform filter-contextMenu -- workaround for coordinate-bug" |
472 |
//if (!buttonMenusLoaded) buttonMenusLoaded = true; // MOVED: to 'popupshowing' window-listener in settings.xul |
473 |
|
474 |
if (thisMenu) { |
475 |
thisButton = document.getElementsByAttribute("popup", thisMenu.getAttribute("id"))[0]; |
476 |
thisButton.setAttribute("AdblockMenuActivated", true); |
477 |
//menuTimer = setInterval('clearInterval(menuTimer); document.getElementById(document.getElementsByAttribute("AdblockMenuActivated", "true")[0].getAttribute("popup")).hidePopup(); setMenuStatus(null);',10000); |
478 |
} |
479 |
else { |
480 |
activeButton = document.getElementsByAttribute("AdblockMenuActivated", "true"); |
481 |
activeButton = (activeButton.length) ? activeButton[0]:null; // get the first one, if anything returns |
482 |
if (activeButton) activeButton.removeAttribute("AdblockMenuActivated"); |
483 |
if (menuTimer) clearInterval(menuTimer); |
484 |
} |
485 |
} |
486 |
|
487 |
|
488 |
// handles the button-menu's, when they're active |
489 |
// -- for "menu-less" buttons, 'thisButton' is passed as null |
490 |
function checkButtonMenu(thisButton, noMenu) { |
491 |
var currentButton, currentMenu, currentMenuId, thisMenu, thisMenuId; |
492 |
var eventN; |
493 |
|
494 |
currentButton = document.getElementsByAttribute("AdblockMenuActivated", "true"); |
495 |
currentButton = (currentButton.length) ? currentButton[0]:null; // get the first one, if anything returns |
496 |
|
497 |
// if menu's are active |
498 |
if (currentButton) { |
499 |
|
500 |
// if we're hovering over a "nothing" menu-button.. |
501 |
if (noMenu) { |
502 |
// ..and the *ACTIVE* menu is not a "nothing" menu |
503 |
if (!currentButton.hasAttribute("NothingMenu")) { |
504 |
currentMenuId = currentButton.getAttribute("popup"); // get the id of the active menu |
505 |
currentMenu = document.getElementById(currentMenuId); // get the active menu |
506 |
|
507 |
currentMenu.hidePopup(); // hide the first menu |
508 |
currentMenu.removeAttribute("AdblockActivated"); // remove the "menu-active" flag |
509 |
if (menuTimer) clearInterval(menuTimer); // clear the menu-timer |
510 |
} |
511 |
thisButton.setAttribute("AdblockMenuActivated", true); // set the "menu-active" flag |
512 |
thisButton.setAttribute("NothingMenu", "true"); // we're hovering over a "nothing" menu ;P |
513 |
menuTimer = setInterval('clearInterval(menuTimer); setMenuStatus(null);',10000); // set the menu-timer |
514 |
} |
515 |
// we're hovering over a "regular" menu-button |
516 |
else { |
517 |
currentMenuId = currentButton.getAttribute("popup"); // get the id of the active menu |
518 |
currentMenu = document.getElementById(currentMenuId); // get the active menu |
519 |
thisMenuId = thisButton.getAttribute("popup"); // get the hovered button's menu |
520 |
|
521 |
// if the active menu doesn't match to the button we're hovering over |
522 |
if (currentMenuId != thisMenuId) { |
523 |
if (! currentButton.hasAttribute("NothingMenu")) currentMenu.hidePopup(); // close the active menu |
524 |
setMenuStatus(null); // remove all menu-flags |
525 |
|
526 |
// fire the button-click to activate our new menu |
527 |
eventN = document.createEvent("MouseEvents"); |
528 |
eventN.initMouseEvent("mousedown", true, false, null, |
529 |
0, 0, 0, 0, 0, |
530 |
false, false, false, false, |
531 |
0, null); |
532 |
thisButton.dispatchEvent(eventN); // fire a mouse-event to activate the second menu |
533 |
} |
534 |
} |
535 |
} |
536 |
} |
537 |
|
538 |
|
539 |
// deactivates the menu-hovering of "nothing" menus when a specified window-event is fired |
540 |
// -- currently for 'keypress' and 'click' |
541 |
function checkMenuEvent() { |
542 |
var activeButton = document.getElementsByAttribute("AdblockMenuActivated", "true"); |
543 |
activeButton = (activeButton.length) ? activeButton[0]:null; // get the first one, if anything returns |
544 |
//var activeMenuId; |
545 |
//var activeMenu; |
546 |
|
547 |
if (activeButton && activeButton.hasAttribute("NothingMenu")) { |
548 |
//activeMenuId = activeButton.getAttribute("popup"); // get the id of the active menu |
549 |
//activeMenu = document.getElementById(activeMenuId); // get the active menu |
550 |
|
551 |
//activeMenu.hidePopup(); |
552 |
setMenuStatus(null); // remove all menu-flags |
553 |
} |
554 |
} |
555 |
|
556 |
|
557 |
// selects a listitem and pops its context-menu |
558 |
function activateContext(evt) { |
559 |
var targetItem = evt.target; |
560 |
if(!targetItem || targetItem.nodeName != "listitem") return; // the user clicked on empty-space or the scrollbar |
561 |
|
562 |
var list = document.getElementById("list"); |
563 |
var listContextMenu = document.getElementById("listitem-contextmenu"); |
564 |
list.selectItem(targetItem); |
565 |
|
566 |
if (!buttonMenusLoaded) var x = evt.screenX, y = evt.screenY; // if the_button-menus/any_contextMenus have been activated, use local-coordinates.. |
567 |
else x = evt.clientX, y = evt.clientY; // ..otherwise, use global-coordinates. |
568 |
listContextMenu.showPopup(targetItem, x, y, "context", "at_pointer","at_pointer"); |
569 |
|
570 |
//if (!buttonMenusLoaded) buttonMenusLoaded = true; // the first context-click doesn't fire its 'popupshowing' event in the proper window |
571 |
} |
572 |
|
573 |
|
574 |
// sets the list-sort pref from its menuitem |
575 |
// -- this is separate from "saveSettings", so we can disable it if we somehow have it set, but don't want the list to be saved as sorted |
576 |
function setListSort(menuItem) { |
577 |
var listOptionsMenu = document.getElementById("options-menu"); |
578 |
listOptionsMenu.hidePopup(); |
579 |
|
580 |
var listSortChecked = menuItem.getAttribute("checked"); |
581 |
if (!listSortChecked) listSortChecked = false; |
582 |
else listSortChecked = true; // for some reason, this attribute passes a string instead of a boolean -! |
583 |
|
584 |
try { |
585 |
var prefObj = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService); |
586 |
var Branch = prefObj.getBranch("adblock."); |
587 |
Branch.setBoolPref("listsort", listSortChecked); |
588 |
|
589 |
if (listSortChecked == true && sortFlag == 0) |
590 |
alert("OK. The list will be sorted next time you launch the prefs."); |
591 |
|
592 |
sortFlag++; |
593 |
|
594 |
} catch(e) {} |
595 |
|
596 |
} |
597 |
|
598 |
// reselects the currentItem, since the list deselects it on scroll |
599 |
// -- this could just be my old build of mozilla, trippin'... |
600 |
// -- "dang" - the hbox also calls at each keypress when an inline-edit is active: now checks 'modifying' |
601 |
function reSelectListItem () { |
602 |
var list = document.getElementById("list"); |
603 |
var modifying = findElementWithAttribute("listitem", "modifying", "true"); |
604 |
|
605 |
// if we're not modifying anything |
606 |
if (!modifying) { |
607 |
list.selectedIndex = list.getIndexOfItem(list.currentItem); // reselect our current item |
608 |
list.currentItem = list.currentItem; } |
609 |
} |
610 |
|
611 |
// persists the list's scroll-view by saving a "backup" copy |
612 |
// --because the normal "persist-attribute" route isn't working for this one |
613 |
function persistListIndex() { |
614 |
var list = document.getElementById("list"); |
615 |
var visibleRows = list.getNumberOfVisibleRows(); |
616 |
var scrollView = list.getIndexOfFirstVisibleRow(); |
617 |
var selectedIndex = list.selectedIndex; |
618 |
|
619 |
/* DISABLED: it feels more natural not to use this |
620 |
if (scrollView > selectedIndex) |
621 |
scrollView = selectedIndex; // if the selected-item is hidden above, set the view to it |
622 |
else if (selectedIndex > (scrollView + visibleRows - 1)) |
623 |
scrollView = selectedIndex - visibleRows + 1; // if the selected-item is hidden below, set the view to show it last |
624 |
*/ |
625 |
|
626 |
list.setAttribute("persist-scrollView", scrollView); |
627 |
list.setAttribute("persist-selectedIndex", selectedIndex); |
628 |
} |
629 |
|
630 |
// restores the list's selected-index from the persisted "backup" |
631 |
function restoreListIndex() { |
632 |
var list = document.getElementById("list"); |
633 |
var textbox = document.getElementById("newfilter"); |
634 |
var savedView = list.getAttribute("persist-scrollView"); |
635 |
var savedIndex = list.getAttribute("persist-selectedIndex"); |
636 |
|
637 |
// if the list is empty |
638 |
if (list.getRowCount() == 0) { |
639 |
textbox.focus(); // throw focus to the input-line |
640 |
} |
641 |
// otherwise, restore the scroll-view and select the last-saved index |
642 |
else { |
643 |
// if we have a valid scroll-view and index |
644 |
if ((savedView && savedView != -1 && savedView <= list.getRowCount()-1) && |
645 |
(savedIndex && savedIndex != -1 && savedIndex <= list.getRowCount()-1)) { |
646 |
list.focus(); |
647 |
//list.scrollToIndex(list.getRowCount()-1); // scroll to the last item -- so our next scroll lends the "complete" view |
648 |
list.scrollToIndex(savedView); // scroll the list to show the index -- more impressive :) |
649 |
//list.ensureIndexIsVisible(savedView); // jump to the given index -- the 'fallback' in case scrollto.. fails |
650 |
list.selectedIndex = savedIndex; // select the current item |
651 |
list.currentItem = list.getItemAtIndex(savedIndex); |
652 |
//list.ensureIndexIsVisible(savedIndex); // make sure we can see the current item |
653 |
//list.selectedIndex = savedIndex; // re-select the current item (in case it wasn't visible) |
654 |
} |
655 |
else { |
656 |
list.focus(); |
657 |
list.ensureIndexIsVisible(list.getRowCount() - 1); // scroll to the last item -- -1 to make zero-based |
658 |
list.selectedIndex = list.getRowCount() - 1; // select the item we just scrolled to |
659 |
} |
660 |
} |
661 |
} |
662 |
|
663 |
// Returns the patterns in one, long string. |
664 |
// Use split(" ") to turn it into an array. |
665 |
function getPatterns() { |
666 |
var patterns = new Array(); |
667 |
var list = document.getElementById("list"); |
668 |
|
669 |
for (var i = 0 ; i < list.childNodes.length ; i++) |
670 |
if (! /^\s$/.test(list.childNodes[i].getAttribute("label")) ) |
671 |
patterns.push(list.childNodes[i].getAttribute("label")); // if the pattern isn't just whitespace, add it in |
672 |
patterns = patterns.join(" "); |
673 |
return patterns; |
674 |
} |
675 |
|
676 |
// enables the Revert-button once a setting has changed |
677 |
function enableRevert() { |
678 |
var revertButton = document.getElementById("revertbutton") |
679 |
if (revertButton.disabled) |
680 |
revertButton.setAttribute("disabled", false); |
681 |
} |
682 |
|
683 |
// confirms and executes a settings-revert to the last saved state |
684 |
function revertSettings() { |
685 |
var confirmRevert = confirm("Are you sure you want to revert?\n..all changes for this pref-session will be undone." ); |
686 |
|
687 |
if (confirmRevert) { |
688 |
loadPrefWindow(); |
689 |
var revertButton = document.getElementById("revertbutton") |
690 |
revertButton.setAttribute("disabled", true); |
691 |
} |
692 |
} |
693 |
|
694 |
// Save the settings and close the window. |
695 |
function saveSettings() { |
696 |
var revertButton = document.getElementById("revertbutton"); |
697 |
var somethingChanged = (revertButton.getAttribute("disabled") == "false"); // if the revert-button isn't disabled, something was changed |
698 |
var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService); |
699 |
var prefObj = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService); |
700 |
var Branch = prefObj.getBranch("adblock."); |
701 |
|
702 |
// only save if we've actually changed something |
703 |
if (somethingChanged) { |
704 |
var patterns = getPatterns(); |
705 |
Branch.setCharPref("patterns", patterns); |
706 |
observerService.notifyObservers(null, "Adblock-PrefChange", "FilterChange"); |
707 |
|
708 |
var enablebox = document.getElementById("enabled"); |
709 |
Branch.setBoolPref("enabled", enablebox.checked); |
710 |
|
711 |
var frameobjects = document.getElementById("frameobjects"); |
712 |
Branch.setBoolPref("frameobjects", (frameobjects.getAttribute("checked") == "true") ? true:false); |
713 |
|
714 |
var slowcollapse = document.getElementById("slowcollapse"); |
715 |
Branch.setBoolPref("fastcollapse", (slowcollapse.getAttribute("checked") == "true") ? false:true); // reverse-pref: slow -> fast |
716 |
|
717 |
var linkcheck = document.getElementById("linkcheck"); |
718 |
Branch.setBoolPref("linkcheck", (linkcheck.getAttribute("checked") == "true") ? true:false); |
719 |
|
720 |
var pageblock = document.getElementById("pageblock"); |
721 |
Branch.setBoolPref("pageblock", (pageblock.getAttribute("checked") == "true") ? true:false); |
722 |
|
723 |
var radiohide = document.getElementById("radio-hide"); |
724 |
Branch.setBoolPref("hide", radiohide.selected); |
725 |
|
726 |
//var collapsebox = document.getElementById("fastcollapse"); |
727 |
//Branch.setBoolPref("fastcollapse", collapsebox.checked); |
728 |
|
729 |
//prefObj.savePrefFile(null); // save the prefs to disk -- component.js "loadSettings()" handles this now |
730 |
observerService.notifyObservers(null, "Adblock-SavePrefFile", null); |
731 |
} |
732 |
|
733 |
persistListIndex(); // persists the current listitem's index in a separate listbox-attribute |
734 |
window.close(); // close out. -- triggers an 'onclose' handler to call "loadSettings()" *in the overlay* |
735 |
} |
736 |
|
737 |
|
738 |
// loads a help page -- needs to be passed a case-variable 'whichPage' |
739 |
function loadHelpPage(whichPage) { |
740 |
var pageUrl; |
741 |
|
742 |
switch(whichPage) { |
743 |
case "about": |
744 |
pageUrl = "http://adblock.mozdev.org/"; |
745 |
break; |
746 |
case "regexp": |
747 |
pageUrl = "http://devedge.netscape.com/library/manuals/2000/javascript/1.5/guide/regexp.html#1010689"; |
748 |
break; |
749 |
} |
750 |
|
751 |
var windowService = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator); |
752 |
var currentWindow = windowService.getMostRecentWindow("navigator:browser"); |
753 |
if (currentWindow) { |
754 |
try { |
755 |
currentWindow.delayedOpenTab(pageUrl); |
756 |
} catch(e) { currentWindow.loadURI(pageUrl); } |
757 |
} |
758 |
else |
759 |
window.open(pageUrl); |
760 |
} |
761 |
|
762 |
|
763 |
|
764 |
/* |
765 |
* Debug Routines |
766 |
*/ |
767 |
|
768 |
// lists everything in an object -- unlimited |
769 |
function unlimitedListObject(obj) { |
770 |
var res = "List: " + obj + "\n\n"; |
771 |
for(var list in obj) |
772 |
res += list + ": " + eval("obj."+list) + "\n"; //+ " -- " + (eval("obj."+list))?(eval("obj."+list+".nodeName")):null + "\n"; |
773 |
|
774 |
return res + "--\n\n"; |
775 |
} |
776 |
|
777 |
|
778 |
// appends a given string to "logfile.txt" |
779 |
function logfile(logString) { |
780 |
var streamOut = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); |
781 |
var dirService = Components.classes['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties); |
782 |
var logFile = dirService.get("UChrm", Components.interfaces.nsIFile);// lxr.mozilla.org/seamonkey/source/xpcom/io/nsAppDirectoryServiceDefs.h |
783 |
logFile.append("logfile.txt"); // "appends" the file-string to our dir file-obj |
784 |
logFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666); // uniquely name file |
785 |
|
786 |
// if the file is writable, append logString |
787 |
if (logFile.isWritable()) { |
788 |
streamOut.init(logFile, 0x02, 0x200, null); |
789 |
//streamOut.flush(); |
790 |
streamOut.write(logString, logString.length); |
791 |
streamOut.close(); |
792 |
} |
793 |
} |
794 |
|
795 |
|
796 |
function listArray(ar) { |
797 |
str = "content-size: " + ar.length + "\n"; |
798 |
for (var i = 0 ; i < ar.length; i++) { |
799 |
str += "-"+ar[i]+"-\n"; |
800 |
} |
801 |
|
802 |
return str; |
803 |
} |
804 |
|
805 |
// lists everything in an object -- Useful debug-function. |
806 |
function listObject(obj, s) { |
807 |
var res = "List: " + obj + "\n"; |
808 |
for(var list in obj) { |
809 |
if (list.indexOf(s) != -1) |
810 |
res += list + ", "; |
811 |
} |
812 |
|
813 |
return res; |
814 |
} |
815 |
|