/********************************************************************************* * The contents of this file are subject to the Mozilla Public License Version 1.1 * ("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 Initial Developer of the Original Code is Howie Wang. * Contributor(s): Alexey Romanov, Jesse Weinstein. * * Alternatively, the contents of this file may be used under the terms * of the GPL 2.0 license (http://www.gnu.org/licenses/gpl-2.0.html) or the * LGPL 2.1 license (http://www.gnu.org/licenses/lgpl-2.1.html), in which case * the provisions of this license are applicable instead of those above. ********************************************************************************/ "use strict"; window.addEventListener("load", function () { nextplease.init(); }, false); window.addEventListener("popupshowing", function () { nextplease.showHideMenuItems(); }, false); window.addEventListener("load", function (event) { nextplease.loadListener.onLoad(event); }, false); nextplease.setUnicharPref = function (aPrefName, aPrefValue) { // nextplease.logDetail("setting " + aPrefName + " to " + aPrefValue); if (nextplease.prefs) { try { var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString); str.data = aPrefValue; nextplease.prefs.setComplexValue(aPrefName, Components.interfaces.nsISupportsString, str); } catch (e) { nextplease.logError("Failed to set value of " + aPrefName + " to " + aPrefValue); } } else { nextplease.logError("nextplease.prefs is not defined"); } }; nextplease.getUnicharPref = function (aPrefName) { // nextplease.logDetail("getting " + aPrefName); if (nextplease.prefs) { try { return nextplease.prefs.getComplexValue(aPrefName, Components.interfaces.nsISupportsString).data; } catch (e) { nextplease.logError("Failed to get value for " + aPrefName); } } else { nextplease.logError("nextplease.prefs is not defined"); } return ""; }; nextplease.gotHereUsingNextplease = false; nextplease.MAX_LINK_NUM = 1000; nextplease.MAX_GALLERY_GAP = 20; nextplease.MAX_LINKS_TO_CHECK = 1000; nextplease.SEARCH_FOR_SUBMIT = 1; nextplease.directions = ["Next", "Prev", "First", "Last"]; nextplease.imageURLsCache = {first: undefined, last: undefined, size: 0, MAX_SIZE: 500, map: {}}; nextplease.SEARCH_TYPE = {Next: 1, Prev: 2, First: 3, Last: 4}; nextplease.ResultType = {Link: 0, URL: 1, Input: 2, History: 3}; nextplease.PREFETCH_ENUM = {No: 0, Yes: 1, Smart: 2}; nextplease.highlighted_old_styles = {}; // This code was stolen from brody on the mozillazine.org forums. nextplease.loadListener = { onLoad: function (event) { getBrowser().addEventListener("DOMContentLoaded", function (event) { nextplease.loadListener.onContentLoaded(event); }, true); }, onContentLoaded: function (event) { var doc = event.originalTarget; // When this becomes true it means that // all of the top level document's // subframes have also finished loading if (!this.isTopLevelDocument(doc)) { return; } // Log/alert if (nextplease.DEBUG_DETAILED) { nextplease.logDetail("loadListener\n" + "DOMContentLoaded\n" + doc.title + "\n" + doc.documentURI); } nextplease.clearStatusBar(); // no effect if nextplease.clearStatusBarTimer is invalid clearTimeout(nextplease.clearStatusBarTimer); nextplease.cacheImageLocations(); nextplease.prefetched = {}; nextplease.unhighlight(); if (nextplease.prefetchPref === nextplease.PREFETCH_ENUM.Yes) { nextplease.prefetch(); } else if ((nextplease.prefetchPref === nextplease.PREFETCH_ENUM.Smart) && nextplease.gotHereUsingNextplease) { nextplease.prefetch(); nextplease.gotHereUsingNextplease = false; } }, isTopLevelDocument: function (aDocument) { return (aDocument === aDocument.defaultView.top.document); } }; nextplease.prefObserver = { QueryInterface: function QueryInterface(aIID) { if (aIID.equals(Components.interfaces.nsIObserver) || aIID.equals(Components.interfaces.nsISupportsWeakReference) || aIID.equals(Components.interfaces.nsISupports)) { return this; } else { throw Components.results.NS_NOINTERFACE; } }, register: function () { nextplease.prefs.addObserver("", this, true); nextplease.accelKeyPrefs.addObserver("", this, true); }, observe: function (subject, topic, data) { if (topic === "nsPref:changed") { nextplease.readPreferences(); } } }; nextplease.prefObserver.register(); nextplease.checkImageCache = function () { var i, url, urlsCache = nextplease.imageURLsCache, cachedURLsNum = urlsCache.size; var messageLines = ["size=" + cachedURLsNum + "; first=" + urlsCache.first + "; last=" + urlsCache.last], message; if (cachedURLsNum > 0) { url = urlsCache.first; for (i = 0; i < cachedURLsNum; i++, url = urlsCache.map[url]) { messageLines[messageLines.length] = "i=" + i + "; url=" + url + "; urlsCache.map[url]=" + urlsCache.map[url]; if (!url) { message = messageLines.join("\n\n"); nextplease.logError(message); alert(message); return; } } if (url !== urlsCache.last) { message = messageLines.join("\n\n"); nextplease.logError(message); alert(message); return; } } else if (urlsCache.first || urlsCache.last) { message = messageLines[0]; alert(message); nextplease.logError(message); } }; nextplease.addImageToCache = function (imgUrl) { var urlToDelete, urlsCache = nextplease.imageURLsCache, incrSize = true; if (imgUrl && !urlsCache.map[imgUrl]) { // nextplease.logDetail("adding " + imgUrl + " to image cache"); if (urlsCache.size === 0) { urlsCache.first = imgUrl; urlsCache.map[imgUrl] = imgUrl; urlsCache.last = imgUrl; urlsCache.size = 1; } else { if (urlsCache.size >= urlsCache.MAX_SIZE) { urlToDelete = urlsCache.first; urlsCache.first = urlsCache.map[urlToDelete]; delete urlsCache.map[urlToDelete]; incrSize = false; } urlsCache.map[urlsCache.last] = imgUrl; urlsCache.map[imgUrl] = imgUrl; urlsCache.last = imgUrl; if (incrSize) { urlsCache.size++; } } } //else { // nextplease.logDetail("" + imgUrl + " is already in image cache"); //} }; nextplease.cacheImageLocations = function () { var i, urlsCache = nextplease.imageURLsCache; nextplease.logDetail("caching image location"); nextplease.logDetail("there are currently " + urlsCache.size + " image URLs cached"); var theDocument = window.content.document; var start = new Date(); var imgElems = theDocument.getElementsByTagName("img"), imgElemsNum = imgElems.length; if (imgElems) { var numberOfImagesToCheck = Math.min(nextplease.MAX_LINKS_TO_CHECK, imgElemsNum); nextplease.logDetail("Checking " + numberOfImagesToCheck + " elements out of " + imgElemsNum + " total."); for (i = 0; i < numberOfImagesToCheck; i++) { nextplease.addImageToCache(imgElems[i].src); } } var links = theDocument.getElementsByTagName("a"), linksNum = links.length; if (links) { var numberOfLinksToCheck = Math.min(nextplease.MAX_LINKS_TO_CHECK, linksNum); nextplease.logDetail("Checking " + numberOfLinksToCheck + " elements out of " + linksNum + " total."); for (i = 0; i < numberOfLinksToCheck; i++) { var linkUrl = links[i].href; if (!urlsCache.map[linkUrl]) { // This is technically not correct, but performance sucks // if we do a proper regex check. if (linkUrl.length > 3) { var suffix = linkUrl.slice(-3).toLowerCase(); if ((suffix === 'jpg') || (suffix === 'gif') || (suffix === 'png') || (suffix === 'bmp')) { nextplease.addImageToCache(linkUrl); } } } //else { // nextplease.logDetail("" + linkUrl + " is already in image cache"); //} } } var end = new Date(); nextplease.logDetail("caching took " + (end.getTime() - start.getTime()) + " ms"); if (nextplease.DEBUG_DETAILED) { nextplease.logDetail("checking cache correctness again"); nextplease.checkImageCache(); } nextplease.logDetail("there are currently " + urlsCache.size + " image URLs cached"); // nextplease.logDetail(nextplease.imageLocationArray.toString()); }; nextplease.initKey = function (keyId, keyPrefName, modifierPrefName) { var modString = nextplease.getModifierPref(modifierPrefName); var keyOrCharCode = nextplease.prefs.getIntPref(keyPrefName); if (keyOrCharCode === 0) { nextplease.prefs.clearUserPref(keyPrefName); keyOrCharCode = nextplease.prefs.getIntPref(keyPrefName); } var isKeyCodePrefName = "iskeycode." + keyPrefName; var isKeyCode = nextplease.prefs.getBoolPref(isKeyCodePrefName); var enablePrefName = "enable." + keyPrefName; var enable = nextplease.prefs.getBoolPref(enablePrefName); var keyString; var keyElem = document.getElementById(keyId); keyElem.setAttribute("modifiers", modString); if (isKeyCode) { keyString = nextplease.KeyCodeToNameMap[keyOrCharCode]; keyElem.removeAttribute("key"); keyElem.setAttribute("keycode", keyString); } else { keyString = String.fromCharCode(keyOrCharCode); keyElem.setAttribute("key", keyString); keyElem.removeAttribute("keycode"); } keyElem.setAttribute("disabled", !enable); if (enable) { nextplease.DisableKey(modString, keyString); } }; nextplease.initNumberKeys = function () { var modifier = nextplease.getModifierPref("numbermodifier"); var numberKey, i; for (i = 0; i < 10; i++) { numberKey = document.getElementById("nextplease" + i + "key"); numberKey.setAttribute("modifiers", modifier); numberKey.setAttribute("disabled", !nextplease.useNumberShortcuts); if (nextplease.useNumberShortcuts) { nextplease.DisableKey(modifier, "" + i); } } }; nextplease.readPreferences = function () { var i; if (!nextplease.prefs) { nextplease.logError("nextplease.prefs undefined. This shouldn't happen!"); nextplease.prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("nextplease."); } try { nextplease.DEBUG = nextplease.prefs.getBoolPref("log"); nextplease.DEBUG_DETAILED = nextplease.DEBUG && nextplease.prefs.getBoolPref("log.detailed"); // nextplease.logDetail("reading preferences"); nextplease.initKey("nextpleasekey", "nextkey", "keymodifier"); nextplease.initKey("nextpleaseprevkey", "prevkey", "prevkeymodifier"); nextplease.initKey("nextpleasefirstkey", "firstkey", "firstkeymodifier"); nextplease.initKey("nextpleaselastkey", "lastkey", "lastkeymodifier"); // nextplease.logDetail("keys read"); nextplease.useSubmit = nextplease.prefs.getBoolPref("allowsubmit"); nextplease.useNumberShortcuts = nextplease.prefs.getBoolPref("allownumbershortcuts"); nextplease.useContextMenu = nextplease.prefs.getBoolPref("allowcontextmenu"); nextplease.useSmartNext = nextplease.prefs.getBoolPref("allowsmartnext"); nextplease.prefetchPref = nextplease.prefs.getIntPref("prefetch"); nextplease.useFrames = nextplease.prefs.getBoolPref("checkframes"); // nextplease.logDetail("bools read"); var nextRegExString = nextplease.getUnicharPref("nextregex"); var prevRegExString = nextplease.getUnicharPref("prevregex"); var firstRegExString = nextplease.getUnicharPref("firstregex"); var lastRegExString = nextplease.getUnicharPref("lastregex"); var galleryRegExString = nextplease.getUnicharPref("galleryregex"); var galleryRegEx = new RegExp(galleryRegExString, "i"); var matches = galleryRegEx.exec("http://nextplease.mozdev.org/test/test101.jpg"); if (matches && (matches.length !== 4)) { nextplease.logError("Gallery regex test failed!"); nextplease.prefs.clearUserPref("galleryregex"); galleryRegExString = nextplease.getUnicharPref("galleryregex"); galleryRegEx = new RegExp(galleryRegExString, "i"); } nextplease.RegExes = {Next: new RegExp(nextRegExString, "i"), Prev: new RegExp(prevRegExString, "i"), First: new RegExp(firstRegExString, "i"), Last: new RegExp(lastRegExString, "i"), Gallery: galleryRegEx}; // nextplease.logDetail("regexes read"); // nextplease.logDetail("gallery regex read"); nextplease.initNumberKeys(); nextplease.ImageMap = {}; nextplease.PhraseMap = {}; // Read the phrases that specify a next link // by reading the preferences or defaults, // and put the phrases in a lookup table. var addPrefsToMap = function (map, phraseOrImage, direction) { var prefbranch = (direction + phraseOrImage).toLowerCase(); var tempprefname = prefbranch + ".expr0"; var prefValue = nextplease.getUnicharPref(tempprefname); var values = prefValue.split("|"); var i; // nextplease.logDetail("initializing " + prefbranch); for (i = 0; i < values.length; i++) { var value = values[i].replace(/&pipe;/g, "|"); if (value !== "") { map[value] = direction; // nextplease.logDetail("added "+value+" to "+direction+" "+phraseOrImage); } } // nextplease.logDetail("finished initializing " + prefbranch); }; for (i = 0; i < nextplease.directions.length; i++) { var direction = nextplease.directions[i]; addPrefsToMap(nextplease.PhraseMap, "Phrase", direction); addPrefsToMap(nextplease.ImageMap, "Image", direction); } nextplease.highlightColor = nextplease.prefs.getBoolPref("highlight") ? nextplease.prefs.getCharPref("highlight.color") : undefined; nextplease.highlightPrefetchedColor = nextplease.prefs.getBoolPref("highlight.prefetched") ? nextplease.prefs.getCharPref("highlight.prefetched.color") : undefined; nextplease.logDetail("preferences read"); } catch (e) { if (!nextplease.retryingToReadPreferences) { // nextplease.prefs.resetBranch() isn't implemented according to MDC var prefnames = nextplease.prefs.getChildList("", {}); for (i = 0; i < prefnames.length; i++) { nextplease.prefs.clearUserPref(prefnames[i]); } var errorMsg = nextplease.strings.GetStringFromName("readingPreferencesFailed"); alert(errorMsg); nextplease.retryingToReadPreferences = true; nextplease.readPreferences(); } } }; nextplease.DisableKey = function (modifier, keyString) { var conflictingKeys, conflictingKey, conflictingId, conflictingModifier, i; if (keyString.indexOf("VK_") >= 0) { // nextplease.logDetail("disabling keys conflicting with " + modifier + "+" + nextplease.KeyCodeToNameMap[keycode]); conflictingKeys = document.getElementsByAttribute("keycode", keyString); } else { // nextplease.logDetail("disabling keys conflicting with " + modifier + "+" + key); conflictingKeys = document.getElementsByAttribute("key", keyString.toLowerCase()); } var conflictingKeysLength = conflictingKeys.length; // nextplease.logDetail(conflictingKeysLength + " keys conflicting with " + modifier + "+" keystring); for (i = 0; i < conflictingKeysLength; i++) { conflictingKey = conflictingKeys[i]; conflictingId = conflictingKey.getAttribute("id"); if (!(/nextplease/.test(conflictingId)) && conflictingKey.hasAttribute("modifiers")) { conflictingModifier = conflictingKey.getAttribute("modifiers").replace(nextplease.accelKey, "accel"); // nextplease.logDetail("potentially conflicting key: " + conflictingId); if ((/alt/.test(modifier) === /alt/.test(conflictingModifier)) && (/control/.test(modifier) === /control/.test(conflictingModifier)) && (/meta/.test(modifier) === /meta/.test(conflictingModifier)) && (/shift/.test(modifier) === /shift/.test(conflictingModifier)) && (/accel/.test(modifier) === /accel/.test(conflictingModifier))) { // conflictingKey.parentNode.removeChild(conflictingKey); conflictingKey.setAttribute("disabled", true); nextplease.log("Disabled conflicting key " + conflictingId); } } } }; nextplease.init = function () { nextplease.log("initializing"); nextplease.retryingToReadPreferences = false; nextplease.readPreferences(); nextplease.statusBar = document.getElementById("nextplease_statusbar_panel"); }; nextplease.clearStatusBar = function () { if (nextplease.statusBar) { nextplease.statusBar.collapsed = true; nextplease.statusBar.label = ""; } }; nextplease.showInStatusBar = function (text) { if (nextplease.statusBar) { nextplease.statusBar.collapsed = false; nextplease.statusBar.label = text; } }; nextplease.notifyLinkNotFound = function () { nextplease.showInStatusBar(nextplease.strings.GetStringFromName("linkNotFound")); nextplease.clearStatusBarTimer = setTimeout(nextplease.clearStatusBar, 5000); }; nextplease.directionFromRel = function (link) { // Look for rel attributes for next/prev/first/last if (link.rel && link.href) { var rel = link.rel.toLowerCase(); if (rel === "next") { nextplease.log('found rel="' + link.rel + '": ' + link.href); return "Next"; } else if ((rel === "prev") || (rel === "previous")) { nextplease.log('found rel="' + link.rel + '": ' + link.href); return "Prev"; } else if ((rel === "start") || (rel === "first")) { nextplease.log('found rel="' + link.rel + '": ' + link.href); return "First"; } else if ((rel === "end") || (rel === "last")) { nextplease.log('found rel="' + link.rel + '": ' + link.href); return "Last"; } } return undefined; }; nextplease.directionFromText = function (text, direction, prefetching) { var i; if (text) { var direction1 = nextplease.PhraseMap[text]; if (direction1) { nextplease.log('found text match for "' + text + '"'); return direction1; } else { if (prefetching) { for (i = 0; i < nextplease.directions.length; i++) { var direction2 = nextplease.directions[i]; if (!nextplease.prefetched[direction2] && nextplease.RegExes[direction2].test(text)) { nextplease.log('found regex match for "' + text + '"'); return direction2; } } } else if (nextplease.RegExes[direction].test(text)) { nextplease.log('found regex match for "' + text + '"'); return direction; } } } return undefined; }; nextplease.directionFromImage = function (imageElem, direction, prefetching) { var imgtext = imageElem.alt ? imageElem.alt : imageElem.title; var direction1 = nextplease.ImageMap[imageElem.src]; if (direction1) { nextplease.log("found image match with URL " + imageElem.src); if (!prefetching || !nextplease.prefetched[direction1]) { return direction1; } else { return undefined; } } else { return nextplease.directionFromText(imgtext, direction, prefetching); } }; nextplease.ignoreRels = function (curWindow) { var url = curWindow.location.href; // viewtopic.php is used in PHPBB, index.php in SMF // both (at least some versions) use tags incorrectly return url.match(/(viewtopic|index)\.php/); }; // Looks through all the links on the page // and tries to look for one whose text matches // one of the phrases or images. If so, it goes to/ // the corresponding link. // pages with frames. nextplease.getLink = function (curWindow, direction) { var doc = curWindow.document; var i, j; var prefetching = (direction === "Prefetch"); var direction1; var text; var isInt = /^\s*\[?\s*(\d+)\s*,?\]?\s*$/; var pageNumLinks = {Next: null, Prev: null, First: null, Last: null, Tmp: null}; var firstPageNum; var currentPageNum = 100000; // Init to arbitrarily large num var tmpPageNum = 100000; // Init to arbitrarily large num var greatestNum = 1; var insideNumberBlock = false; var link; var temp; if (prefetching) { nextplease.logDetail("prefetching..."); } else { nextplease.logDetail("looking for a link..."); } var range = doc.createRange(); var finishPrefetch = function () { return prefetching && nextplease.prefetched.Next && nextplease.prefetched.Prev && nextplease.prefetched.First && nextplease.prefetched.Last; }; if (nextplease.useSmartNext) { if (getBrowser().canGoForward) { nextplease.log("forward in history"); link = [nextplease.ResultType.History, 1]; if (direction === "Next") { return link; } else if (prefetching) { nextplease.prefetched.Next = link; } } if (getBrowser().canGoBack) { nextplease.log("back in history"); link = [nextplease.ResultType.History, -1]; if (direction === "Prev") { return link; } else if (prefetching) { nextplease.prefetched.Prev = link; } } } if (!nextplease.ignoreRels(curWindow)) { // Look for tags nextplease.logDetail("checking tags"); var linktags = doc.getElementsByTagName("link"), linktagsNum = linktags.length; for (i = 0; i < linktagsNum; i++) { link = linktags[i]; direction1 = nextplease.directionFromRel(link); if (direction === direction1) { return [nextplease.ResultType.Link, link]; } else if (direction1 && prefetching) { nextplease.prefetched[direction1] = [nextplease.ResultType.Link, link]; continue; } } } if (finishPrefetch()) { return true; } // Look for tags nextplease.logDetail("checking tags"); var alinks = doc.links, alinksNum = alinks.length; var curWindowUrl = doc.location.href; // Search through each link for (i = 0; i < alinksNum; i++) { link = alinks[i]; if (link.href === curWindowUrl) { continue; } direction1 = nextplease.directionFromRel(link); if (direction === direction1) { return [nextplease.ResultType.Link, link]; } else if (direction1 && prefetching) { nextplease.prefetched[direction1] = [nextplease.ResultType.Link, link]; continue; } range.selectNode(link); text = nextplease.Trim(range.toString()); if (link.href.indexOf("/dictionary") < 0) { direction1 = nextplease.directionFromText(text, direction, prefetching); if (direction === direction1) { return [nextplease.ResultType.Link, link]; } else if (direction1 && prefetching) { nextplease.prefetched[direction1] = [nextplease.ResultType.Link, link]; continue; } } direction1 = nextplease.directionFromText(link.title, direction, prefetching); if (direction === direction1) { return [nextplease.ResultType.Link, link]; } else if (direction1 && prefetching) { nextplease.prefetched[direction1] = [nextplease.ResultType.Link, link]; continue; } // See if there's an image tag var imgElems = link.getElementsByTagName("img"); if (imgElems.length > 0) { nextplease.logDetail("checking images inside ..."); // If the image matches, go to the URL. //alert(imgElems[0].src); direction1 = nextplease.directionFromImage(imgElems[0], direction, prefetching); if (direction === direction1) { return [nextplease.ResultType.Link, link]; } else if (direction1 && prefetching) { nextplease.prefetched[direction1] = [nextplease.ResultType.Link, link]; continue; } } if (finishPrefetch()) { return true; } var intMatches = isInt.exec(text); if (intMatches) { var linkPageNum = parseInt(intMatches[1], 10); // If the number is greater than nextplease.MAX_LINK_NUM // it probably doesn't have anything to do with // a next/prev link. // alert(linkPageNum); if (linkPageNum < nextplease.MAX_LINK_NUM) { nextplease.logDetail("found link number " + linkPageNum + ", checking..."); // Try to figure out what the current page and // next/prev links are for pages that just have // numbered links like 1 2 x 4 5. // if (linkPageNum === 1) { // We're seeing a number link that is smaller // than a previous one so assume that we're // starting a new set of number links, and // count from the beginning. if (linkPageNum <= tmpPageNum) { insideNumberBlock = true; // alert(linkPageNum); // alert(currentPageNum); pageNumLinks.First = link; firstPageNum = linkPageNum; greatestNum = linkPageNum; pageNumLinks.Prev = null; pageNumLinks.Next = null; pageNumLinks.Last = null; currentPageNum = linkPageNum; pageNumLinks.First = link; greatestNum = linkPageNum; pageNumLinks.Last = null; //} else if (currentPageNum === linkPageNum) { // currentPageNum++; // pageNumLinks.Prev = link; } else if (tmpPageNum + 1 === linkPageNum) { pageNumLinks.Last = link; } else if (tmpPageNum + 2 === linkPageNum) { pageNumLinks.Prev = pageNumLinks.Tmp; pageNumLinks.Next = link; pageNumLinks.Last = link; } else if (insideNumberBlock) { pageNumLinks.Last = link; } tmpPageNum = linkPageNum; pageNumLinks.Tmp = link; } } else { insideNumberBlock = false; } } // next and prev are null so that means // we have a solid block of numbers, e.g. 3,4,5,6,... if ((pageNumLinks.Next === null) && (pageNumLinks.Prev === null)) { // If we start with 1, we're probably on last page. // Set prev to be lastPage if (firstPageNum === 1) { pageNumLinks.Prev = pageNumLinks.Last; pageNumLinks.Last = null; } // If we start with 2, we're probably on first page. // Set next to be first page if (firstPageNum === 2) { pageNumLinks.Next = pageNumLinks.First; pageNumLinks.First = null; } if (firstPageNum > 2) { pageNumLinks.Next = pageNumLinks.First; pageNumLinks.Prev = pageNumLinks.Last; } } if (pageNumLinks.First && pageNumLinks.Last && (pageNumLinks.First.text === pageNumLinks.Last.text)) { pageNumLinks.First = null; } nextplease.logDetail("first page seems to be " + pageNumLinks.First); nextplease.logDetail("previous page seems to be " + pageNumLinks.Prev); nextplease.logDetail("next page seems to be " + pageNumLinks.Next); nextplease.logDetail("last page seems to be " + pageNumLinks.Last); // Try to find a match using our number algorithm if (prefetching) { for (i = 0; i < nextplease.directions.length; i++) { direction1 = nextplease.directions[i]; if (!nextplease.prefetched[direction1] && pageNumLinks[direction1]) { nextplease.prefetched[direction1] = [nextplease.ResultType.Link, pageNumLinks[direction1]]; } } if (finishPrefetch()) { return true; } } else if (pageNumLinks[direction]) { return [nextplease.ResultType.Link, pageNumLinks[direction]]; } // Otherwise try looking for next/prev submit buttons // if the user allows it. if (nextplease.useSubmit) { temp = nextplease.getForm(direction, prefetching); if (temp) { return temp; } } // See if we can increment the URL to get to next/prev/first var galleryURL = nextplease.getGalleryNumberURL(curWindow, direction); // alert(galleryURL); // alert(nextplease.imageLocationArray.length); // if (galleryURL) {curWindow.open(galleryURL, "_self", "");} if (galleryURL && !prefetching) { return [nextplease.ResultType.URL, galleryURL]; } // None of it worked, so make a recursive call to // nextplease.getLink on the frame windows. if (nextplease.useFrames) { var frames = curWindow.frames, framesNum = frames.length; for (j = 0; j < framesNum; j++) { temp = nextplease.getLink(frames[j], direction); if (temp) { return temp; } } } return finishPrefetch(); }; nextplease.getGalleryNumberURL = function (curWindow, direction) { nextplease.logDetail("trying to change the URL by a suitable number"); var i; // alert(nextplease.imageLocationArray); var matches = nextplease.RegExes.Gallery.exec(decodeURI(curWindow.location.href)); var prefixUrl, suffixUrl, numberUrlPartLength, curNumber, urlNumber, padStr, linkUrl; var urlsCache = nextplease.imageURLsCache; if (matches && (matches.length === 4)) { prefixUrl = matches[1]; numberUrlPartLength = matches[2].length; suffixUrl = matches[3]; nextplease.logDetail("URL prefix is " + prefixUrl + ", URL suffix is " + suffixUrl); if (direction === "Next") { curNumber = parseInt(matches[2], 10); for (i = 1; i < nextplease.MAX_GALLERY_GAP; i++) { urlNumber = curNumber + i; padStr = nextplease.padNumber(numberUrlPartLength, urlNumber); linkUrl = prefixUrl + padStr + suffixUrl; if (urlsCache.map[linkUrl]) { nextplease.log("gallery URL found: " + linkUrl); return linkUrl; } } urlNumber = curNumber + 1; padStr = nextplease.padNumber(numberUrlPartLength, urlNumber); linkUrl = prefixUrl + padStr + suffixUrl; return linkUrl; } else if (direction === "Prev") { curNumber = parseInt(matches[2], 10); var maxToSubtract = Math.min(curNumber, nextplease.MAX_GALLERY_GAP); for (i = 1; i <= maxToSubtract; i++) { urlNumber = curNumber - i; padStr = nextplease.padNumber(numberUrlPartLength, urlNumber); linkUrl = prefixUrl + padStr + suffixUrl; if (urlsCache.map[linkUrl]) { nextplease.log("gallery URL found: " + linkUrl); return linkUrl; } } urlNumber = curNumber - 1; if (urlNumber >= 0) { padStr = nextplease.padNumber(numberUrlPartLength, urlNumber); linkUrl = prefixUrl + padStr + suffixUrl; nextplease.log("gallery URL found: " + linkUrl); return linkUrl; } } else if (direction === "First") { urlNumber = 1; padStr = nextplease.padNumber(numberUrlPartLength, urlNumber); linkUrl = prefixUrl + padStr + suffixUrl; nextplease.log("gallery URL found: " + linkUrl); return linkUrl; } } return undefined; }; nextplease.padNumber = function (length, newNum) { var padStr = "" + newNum; var padLen = length - padStr.length; var i; for (i = 0; i < padLen; i++) { padStr = "0" + padStr; } return padStr; }; // Look through all the HTML inputs for submit buttons // that have a value that matches our phrases. If it // finds a match, it calls input.click() nextplease.getForm = function (direction, prefetching) { var finishPrefetch = function () { return prefetching && nextplease.prefetched.Next && nextplease.prefetched.Prev && nextplease.prefetched.First && nextplease.prefetched.Last; }; var i, text, direction1; nextplease.logDetail("looking for submit buttons"); // Probably would be a little faster to // only check forms, but I'm getting problems // with them. I'm not sure if it's only on // malformed HTML pages, or if it's a Firefox bug. var inputs = window.content.document.getElementsByTagName("input"), inputsNum = inputs.length; for (i = 0; i < inputsNum; i++) { var input = inputs[i]; text = nextplease.Trim(input.value); direction1 = nextplease.directionFromText(text, direction, prefetching); if (direction === direction1) { return [nextplease.ResultType.Input, input]; } else if (direction1 && prefetching) { nextplease.prefetched[direction1] = [nextplease.ResultType.Input, input]; } } var buttons = window.content.document.getElementsByTagName("button"), buttonsNum = buttons.length; var range = document.createRange(); for (i = 0; i < buttonsNum; i++) { var button = buttons[i]; range.selectNode(button); text = nextplease.Trim(range.toString()); direction1 = nextplease.directionFromText(text, direction, prefetching); if (direction === direction1) { return [nextplease.ResultType.Input, button]; } else if (direction1 && prefetching) { nextplease.prefetched[direction1] = [nextplease.ResultType.Input, button]; } var imgElems = buttons[i].getElementsByTagName("img"); if (imgElems.length > 0) { nextplease.logDetail("checking images inside ..."); // If the image matches, go to the URL. //alert(imgElems[0].src); direction1 = nextplease.directionFromImage(imgElems[0], direction, prefetching); if (direction === direction1) { return [nextplease.ResultType.Input, button]; } else if (direction1 && prefetching) { nextplease.prefetched[direction1] = [nextplease.ResultType.Input, button]; continue; } } } return finishPrefetch(); }; nextplease.openResult = function (curWindow, result) { if (nextplease.highlightColor) { nextplease.highlight(result, nextplease.highlightColor); } switch (result[0]) { case nextplease.ResultType.URL: var url = result[1]; curWindow.open(url, "_self", ""); return true; case nextplease.ResultType.Link: var linkNode = result[1]; if (!linkNode) { nextplease.logError("Tried to open undefined link, this should never happen!"); return false; } // If it's got an onclick attr, then try to // simulate a mouse click to activate link. if (linkNode.hasAttribute("onclick")) { // alert(linkNode.getAttribute("onclick")); var e = document.createEvent("MouseEvents"); // e.initMouseEvent("click", 1, 1, window, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, linkNode); // From https://developer.mozilla.org/en/DOM/event.initMouseEvent e.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); linkNode.dispatchEvent(e); } else { curWindow.open(linkNode.href, "_self", ""); } nextplease.gotHereUsingNextplease = true; return true; case nextplease.ResultType.Input: var input = result[1]; input.click(); return true; case nextplease.ResultType.History: var num = result[1]; curWindow.history.go(num); return true; } }; // Looks through all the links and finds the link // that matches the linkNum (an integer between // 1 and 9). If it finds a match, it will go to // that link. nextplease.openNumberedLink = function (curWindow, linkNum) { var text; var isInt = /^\s*\[?\s*(\d+)\s*\]?\s*$/; var i, j; nextplease.logDetail("looking for a link numbered " + linkNum); var alinks = curWindow.document.links; // Search through each link for (i = alinks.length - 1; i >= 0; i--) { var link = alinks[i]; text = nextplease.Trim(link.text); var intMatches = isInt.exec(text); if (intMatches) { var linkPageNum = parseInt(intMatches[1], 10); if (linkPageNum === linkNum) { return nextplease.openResult(curWindow, [nextplease.ResultType.Link, link]); //curWindow.open(link.href, "_self", ""); //return true; } } } if (nextplease.useFrames) { var frames = curWindow.frames; for (j = 0; j < frames.length; j++) { if (nextplease.openNumberedLink(frames[j], linkNum)) { return true; } } } nextplease.notifyLinkNotFound(); return false; }; nextplease.openDirection = function (direction) { var result = nextplease.prefetched[direction] || nextplease.getLink(window.content, direction); if (result) { return nextplease.openResult(window.content, result); } else { nextplease.notifyLinkNotFound(); return false; } }; nextplease.openNextLink = function () { nextplease.logDetail("Looking for next link"); return nextplease.openDirection("Next"); }; nextplease.openPrevLink = function () { nextplease.logDetail("Looking for prev link"); return nextplease.openDirection("Prev"); }; nextplease.openFirstLink = function () { nextplease.logDetail("Looking for first link"); return nextplease.openDirection("First"); }; nextplease.openLastLink = function () { nextplease.logDetail("Looking for last link"); return nextplease.openDirection("Last"); }; nextplease.prefetch = function () { nextplease.getLink(window.content, "Prefetch"); if (nextplease.highlightPrefetchedColor) { nextplease.highlight(nextplease.prefetched.Next, nextplease.highlightPrefetchedColor); nextplease.highlight(nextplease.prefetched.Prev, nextplease.highlightPrefetchedColor); nextplease.highlight(nextplease.prefetched.First, nextplease.highlightPrefetchedColor); nextplease.highlight(nextplease.prefetched.Last, nextplease.highlightPrefetchedColor); } }; // old names retained because of http://www.mousegestures.org/exchange/details.php?mappingID=295 nextplease.getNextLink = nextplease.openNextLink; nextplease.getPrevLink = nextplease.openPrevLink; nextplease.getFirstLink = nextplease.openFirstLink; nextplease.getLastLink = nextplease.openLastLink; nextplease.highlight = function (result, color) { if (result) { var element; switch (result[0]) { case nextplease.ResultType.URL: break; case nextplease.ResultType.Link: case nextplease.ResultType.Input: element = result[1]; break; // case nextplease.ResultType.History: // TODO The forward button doesn't get unhighlighted correctly // when it becomes disabled. // switch (result[1]) { // case 1: element = document.getElementById("forward-button"); break; // case -1: element = document.getElementById("back-button"); break; // } } if (element) { if (!nextplease.highlighted_old_styles[element]) { nextplease.highlighted_old_styles[element] = element.style; } element.style.background = color; } } }; nextplease.unhighlight = function () { var element; for (element in nextplease.highlighted_old_styles) { element.style = nextplease.highlighted_old_styles[element]; delete nextplease.highlighted_old_styles[element]; } }; nextplease.showHideMenuItems = function () { var i, elem; var direction, numDirections = nextplease.directions.length; var propertyKey; var getCMItemByDir = function (direction) { var elemId = "nextPleaseAddRemove" + direction; return document.getElementById(elemId); }; if (gContextMenu) { gContextMenu.showItem("nextplease.topMenu", nextplease.useContextMenu); if (nextplease.useContextMenu) { if (!gContextMenu.onLink && !gContextMenu.onImage) { gContextMenu.showItem("nextplease-separator", false); gContextMenu.showItem("nextpleaseTextOrURL", false); gContextMenu.showItem("nextPleaseAddRemoveNext", false); gContextMenu.showItem("nextPleaseAddRemovePrev", false); gContextMenu.showItem("nextPleaseAddRemoveFirst", false); gContextMenu.showItem("nextPleaseAddRemoveLast", false); } else { var textOrUrl = nextplease.getTextOrUrlUnderPopup(); var phraseOrImage = gContextMenu.onImage ? "Image" : "Phrase"; document.getElementById("nextpleaseTextOrURL").label = gContextMenu.onImage ? textOrUrl : '"' + textOrUrl + '"'; var directionForItem = nextplease[phraseOrImage + "Map"][textOrUrl]; for (i = 0; i < numDirections; i++) { direction = nextplease.directions[i]; elem = getCMItemByDir(direction); propertyKey = (direction === directionForItem ? "remove" : "add") + phraseOrImage + "ContextMenu"; // alert("str = " + nextplease.strings.GetStringFromName(propertyKey) + "; param = " + nextplease.getDirectionString(direction)); elem.label = nextplease.strings.formatStringFromName(propertyKey, [nextplease.getDirectionString(direction)], 1); } gContextMenu.showItem("nextplease-separator", true); gContextMenu.showItem("nextpleaseTextOrURL", true); gContextMenu.showItem("nextPleaseAddRemoveNext", true); gContextMenu.showItem("nextPleaseAddRemovePrev", true); gContextMenu.showItem("nextPleaseAddRemoveFirst", true); gContextMenu.showItem("nextPleaseAddRemoveLast", true); } } } }; nextplease.addRemoveCM = function (direction) { var phraseOrImage = gContextMenu.onImage ? "Image" : "Phrase"; var textOrUrl = nextplease.getTextOrUrlUnderPopup(); var currentDirection = nextplease[phraseOrImage + "Map"][textOrUrl]; if (currentDirection === direction) { // removing if (nextplease.confirmRemove(phraseOrImage, textOrUrl, currentDirection)) { nextplease.addOrRemovePhraseOrImage(currentDirection, textOrUrl, phraseOrImage, "Remove"); } } else if (currentDirection) { // replacing if (nextplease.confirmReplace(phraseOrImage, textOrUrl, currentDirection, direction)) { nextplease.addOrRemovePhraseOrImage(currentDirection, textOrUrl, phraseOrImage, "Remove"); nextplease.addOrRemovePhraseOrImage(direction, textOrUrl, phraseOrImage, "Add"); } } else { nextplease.addOrRemovePhraseOrImage(direction, textOrUrl, phraseOrImage, "Add"); } }; nextplease.addOrRemovePhraseOrImage = function (direction, textOrUrl, whichPhraseOrImage, whichAddOrRemove) { var prefname, map, logMessage; if (whichPhraseOrImage === "Phrase") { prefname = direction.toLowerCase() + "phrase"; map = nextplease.PhraseMap; logMessage = (whichAddOrRemove === "Add") ? "adding phrase '" + textOrUrl + "' to " + prefname : "removing phrase '" + textOrUrl + "' from " + prefname; nextplease.logDetail(logMessage); } else { prefname = direction.toLowerCase() + "image"; map = nextplease.ImageMap; logMessage = (whichAddOrRemove === "Add") ? "adding image URL " + textOrUrl + " to " + prefname : "removing image URL " + textOrUrl + " from " + prefname; nextplease.logDetail(logMessage); } if (whichAddOrRemove === "Add") { if (map[textOrUrl] !== direction) { map[textOrUrl] = direction; nextplease.addToPrefs(prefname, textOrUrl); } else { nextplease.log("error: already present in the list!"); } } else { if (map[textOrUrl] === direction) { delete map[textOrUrl]; nextplease.removeFromPrefs(prefname, textOrUrl); } else { nextplease.log("error: not present in the list!"); } } }; nextplease.getTextOrUrlUnderPopup = function () { if (gContextMenu && gContextMenu.onLink) { if (gContextMenu.onImage) { return document.popupNode.src; } else { var range = document.createRange(); range.selectNode(document.popupNode); return nextplease.Trim(range.toString()); } } else { return undefined; } }; nextplease.addToPrefs = function (prefbranch, text) { nextplease.logDetail("adding " + text + " to " + prefbranch); var tempprefname = prefbranch + '.expr0'; var prefvalue = nextplease.getUnicharPref(tempprefname); var resultprefvalue = prefvalue + "|" + text.replace(/\|/g, "&pipe;"); nextplease.setUnicharPref(tempprefname, resultprefvalue); }; nextplease.removeFromPrefs = function (prefbranch, text) { nextplease.logDetail("removing " + text + " from " + prefbranch); var tempprefname = prefbranch + '.expr0'; var prefvalue = nextplease.getUnicharPref(tempprefname); var text1 = new RegExp("\\|" + text.replace(/\|/g, "&pipe;") + "(?=\\||$)", "g"); var resultprefvalue = prefvalue.replace(text1, ""); nextplease.setUnicharPref(tempprefname, resultprefvalue); }; nextplease.linkNumber = 0; nextplease.handleNumberShortcut = function (digit) { // no effect if nextplease.NumberShortcutTimer is invalid clearTimeout(nextplease.NumberShortcutTimer); nextplease.linkNumber = nextplease.linkNumber * 10 + digit; nextplease.showInStatusBar( nextplease.strings.formatStringFromName("lookingForNumberedLink", [nextplease.linkNumber], 1)); nextplease.NumberShortcutTimer = setTimeout(nextplease.finishNumberShortcut, 500); }; nextplease.finishNumberShortcut = function () { nextplease.openNumberedLink(window.content, nextplease.linkNumber); nextplease.linkNumber = 0; clearTimeout(nextplease.NumberShortcutTimer); };