Anton Afanasyev avatar Anton Afanasyev committed 98215a6

prev page loading works, but may be buggy. also doesnt scroll down when loading
prev page yet, resulting in current page not being visible.
code cleanup

Comments (0)

Files changed (1)

autopagerize.user.js

     // get next page url
     var url = getNextURL(info.nextLink, document, location.href);
     if (!url) {
-        debug("getNextURL returns null.", info.nextLink);
+        debug("getNextURL(next) returns null.", info.nextLink);
+    }
+    // get prev page url
+    var urlPrev = getNextURL(info.prevLink, document, location.href);
+    if (!urlPrev) {
+        debug("getNextURL(prev) returns null.", info.prevLink);
+    }
+
+    if( !url && !urlPrev ){
+        debug( "cannot continue because not prev or next url found" );
         return;
     }
     
-    // if we have an XPath for the element before which to insert the new page, find it
-    if (info.insertBefore)
-        this.insertPoint = getFirstElementByXPath(info.insertBefore);
-
-    // dont have an elem before which to insert (either none defined, or it wasnt found)
-    if (!this.insertPoint) {
-        var lastPageElement = this.getPageElements();
-        if(lastPageElement)
-            lastPageElement = lastPageElement.pop();
-        
-        if (lastPageElement) {
-            try {
-                this.insertPoint = getFirstElementByXPath('following-sibling::node()', lastPageElement);
-            } catch(e) {
-                debug('couldnt get "following-sibling::node()" via xpath for ' + lastPageElement);
-            }
-
-            if (!this.insertPoint)
-                this.insertPoint = lastPageElement.nextSibling || lastPageElement.parentNode.appendChild(document.createTextNode(' '));
-        }
+    if( !this.updateInsertPoint( 'next' ) ) {
+        debug( "insertPoint not found.", lastPageElement, this.info.pageElement );
+        return;
     }
-
-    if (!this.insertPoint) {
-        debug("insertPoint not found.", lastPageElement, info.pageElement);
+    if( !this.updateInsertPoint( 'prev' ) ) {
+        debug( "insertPointPrev not found.", lastPageElement, this.info.pageElement );
         return;
     }
 
-    
-    // AA
     var nc = info.noCues;
     if(!(true==nc))
         nc = false;
     this.noCues = nc;
 
-    // AA
     var cr = info.replace;
     if(!(true==cr))
         cr = false;
     this.forceForce = false;
 
     this.requestURL = url;
+    this.requestURLPrev = urlPrev;
     this.loadedURLs = {};
     this.loadedURLs[location.href] = true;
     var toggle = function() { self.stateToggle(); };
     this.initHelp();
     this.icon.addEventListener("mouseover", function() { self.viewHelp(); }, true);
 
-    // AA
     var aLA = null;
     try {
         aLA = info.alwaysLoadAll;
     }
     this.shouldLoadAll = aLA;
         
-    // AA
     var rH = null;
     try {
         rH = info.remainHeight;
     }
 }
 
+AutoPager.prototype.updateInsertPoint = function( direction ) {
+    if( direction == 'next' ) {
+        // if we have an XPath for the element before which to insert the new page, find it
+        if ( this.info.insertBefore )
+            this.insertPoint = getFirstElementByXPath( this.info.insertBefore );
+
+        // dont have an elem before which to insert (either none defined, or it wasnt found)
+        if ( !this.insertPoint ) {
+            var lastPageElement = this.getPageElements();
+            if( lastPageElement )
+                lastPageElement = lastPageElement.pop();
+            
+            if ( lastPageElement ) {
+                try {
+                    this.insertPoint = getFirstElementByXPath( 'following-sibling::node()', lastPageElement );
+                } catch(e) {
+                    debug( 'couldnt get "following-sibling::node()" via xpath', lastPageElement, this.info.pageElement );
+                }
+
+                if ( !this.insertPoint )
+                    this.insertPoint = lastPageElement.nextSibling || lastPageElement.parentNode.appendChild(document.createTextNode(' '));
+            }
+        }
+
+        if ( !this.insertPoint ) {
+            return false;
+        }
+    }
+    else if( direction == 'prev' ) {
+        // TODO: add full customizable support for insertPointPrev
+        this.insertPointPrev = this.getPageElements();
+        if( this.insertPointPrev ) {
+            // but for now, we'll just be inserting right before the very first element
+            this.insertPointPrev = this.insertPointPrev[0];
+        }
+
+        if( !this.insertPointPrev ) {
+            return false;
+        }
+    }
+
+    return true;
+};
 AutoPager.prototype.getPageElements = function(doc) {
     doc = doc || document;
     var elems;
     //if (this.state == 'enable')
     {
         this.request()
-        GM_addStyle('hr.autopagerize_page_separator {clear: both;}')
     }
 }
 
     a.addEventListener('click', toggle, false);
     toggleDiv.appendChild(a);
 
-    // TODO: add paging
-    /*
-    var pagingDiv_prev = document.createElement('div');
-    pagingDiv_prev.setAttribute('style', 'margin:0 0 0 90px; cursor: pointer; text-decoration: underline;')
-    pagingDiv_prev.innerHTML = "";
-    var prevPage = function() {
-        self.stateToggle();
-        helpDiv.style.top = '-200px';
-    }
-    pagingDiv_prev.addEventListener('click', prevPage, false);
-    */
-
     // div with link to load all
     var loadAllDiv = null;
     if(!this.completeReplace) {
     
     SetupShortcutJs();
     
-    // AA - now add shortcuts
     var fnRemShortcuts = function(e) {
         shortcut.remove("Alt+]");
         shortcut.remove("Alt+\\");
         this.icon.style.background = color;
 }
 
-AutoPager.prototype.requestPrev = function() {
-    return;
-    if (!this.requestURL || this.lastRequestURL == this.requestURL) {
-        return;
-    }
-
-    /*// if the page want to load does not have a slash or a backslash, then strip the previous page of everything after the last slash (or backslash), and append the next page
-    var i = Math.max(this.requestURL.indexOf('/'), this.requestURL.indexOf('\\'));
-    if(i<0) {
-        // no slash found, so parse the previous page
-        i = Math.max(this.lastRequestURL.lastIndexOf('\\'), this.lastRequestURL.lastIndexOf('/'));
-        var t = this.lastRequestURL.substring(0, i+1);
-        this.requestURL = t + this.requestURL;
-    }*/
-
-    this.lastRequestURL = this.requestURL;
+AutoPager.prototype.requestPageCore_ = function( url, onDoneCallback ) {
     var self = this;
     var mime = 'text/html; charset=' + document.characterSet;
     var headers = {};
-    if (isSameDomain(this.requestURL)) {
+    if (isSameDomain( url )) {
         headers.Cookie = document.cookie;
     } else {
         log("requesting not from same domain");
         this.error();
         return;
     } 
-    var tt = this.requestURL;
+    var tt = url;
     var opt = {
         method: 'GET',
-        url: this.requestURL,
-        headers: headers,
-        overrideMimeType: mime,
-        onerror: function(res) {
-            log(tt + "\nrequest prev error:\nstatus: " + res.status + "\nstatusText: " + res.statusText + "\nreadyState: " + res.readyState + "\nresponseText: " + res.responseText + "\nresponseHeaders: " + res.responseHeaders + "\nfinalUrl: " + res.finalUrl);
-            self.error()
-        },
-        onload: function(res) {
-            if (res.finalUrl) {
-                var url_s = res.finalUrl.split(/[\/\?]/);
-                if (url_s[0] == location.protocol && location.host == url_s[2]) {
-                    self.requestLoadPrev.apply(self, [res]);
-                    return;
-                }
-            }
-            log("loaded prev page, but returned url not same as requested. Loaded: '" + res.finalUrl + "'; Requested: '" + location.host + "'");
-            self.error();
-        }
-    };
-    AutoPager.requestFilters.forEach(function(i) { i(opt); }, this);
-    if (opt.stop) {
-        this.requestURL = opt.url;
-    } else {
-        this.showLoading(true);
-        GM_xmlhttpRequest(opt);
-    }};
-
-AutoPager.prototype.request = function() {
-    if (!this.requestURL || this.lastRequestURL == this.requestURL) {
-        return;
-    }
-
-    /*// if the page want to load does not have a slash or a backslash, then strip the previous page of everything after the last slash (or backslash), and append the next page
-    var i = Math.max(this.requestURL.indexOf('/'), this.requestURL.indexOf('\\'));
-    if(i<0) {
-        // no slash found, so parse the previous page
-        i = Math.max(this.lastRequestURL.lastIndexOf('\\'), this.lastRequestURL.lastIndexOf('/'));
-        var t = this.lastRequestURL.substring(0, i+1);
-        this.requestURL = t + this.requestURL;
-    }*/
-
-    this.lastRequestURL = this.requestURL;
-    var self = this;
-    var mime = 'text/html; charset=' + document.characterSet;
-    var headers = {};
-    if (isSameDomain(this.requestURL)) {
-        headers.Cookie = document.cookie;
-    } else {
-        log("requesting not from same domain");
-        this.error();
-        return;
-    } 
-    var tt = this.requestURL;
-    var opt = {
-        method: 'GET',
-        url: this.requestURL,
+        url: url,
         headers: headers,
         overrideMimeType: mime,
         onerror: function(res) {
             if (res.finalUrl) {
                 var url_s = res.finalUrl.split(/[\/\?]/);
                 if (url_s[0] == location.protocol && location.host == url_s[2]) {
-                    self.requestLoad.apply(self, [res]);
+                    onDoneCallback( res );
                     return;
                 }
             }
-            log("loaded, but returned url not same as requested. Loaded: '" + res.finalUrl + "'; Requested: '" + location.host + "'");
+            log("loaded page, but returned url not same as requested. Loaded: '" + res.finalUrl + "'; Requested: '" + location.host + "'");
             self.error();
         }
     };
     AutoPager.requestFilters.forEach(function(i) { i(opt); }, this);
     if (opt.stop) {
-        this.requestURL = opt.url;
-    } else {
-        this.showLoading(true);
-        GM_xmlhttpRequest(opt);
+        return {
+            ret: false,
+            url: opt.url
+        };
     }
-} 
+
+    this.showLoading(true);
+    GM_xmlhttpRequest(opt);
+    return {
+        ret: true
+    }
+};
+AutoPager.prototype.requestPrev = function() {
+    // cant load page if there isn't on or if it is the same as the last loaded one
+    if (!this.requestURLPrev || this.lastRequestURLPrev == this.requestURLPrev) {
+        return;
+    }
+    this.lastRequestURLPrev = this.requestURLPrev;
+
+
+    var self = this;
+    var ret = this.requestPageCore_( this.requestURLPrev, function( res ) {
+        self.requestLoadPrev.apply( self, [res] );
+    });
+
+    // stopped while filtering
+    if( !ret.ret ) {
+        this.requestURLPrev = ret.url;
+    }
+};
+
+AutoPager.prototype.request = function() {
+    // cant load page if there isn't on or if it is the same as the last loaded one
+    if (!this.requestURL || this.lastRequestURL == this.requestURL) {
+        return;
+    }
+    this.lastRequestURL = this.requestURL;
+
+
+    var self = this;
+    var ret = this.requestPageCore_( this.requestURL, function( res ) {
+        self.requestLoad.apply( self, [res] );
+    });
+
+    // stopped while filtering
+    if( !ret.ret ) {
+        this.requestURL = ret.url;
+    }
+};
 
 AutoPager.prototype.showLoading = function(sw) {
     if (sw) {
     }
 }
 
-AutoPager.prototype.parseRequestLoad_ = function( res ) {
-    AutoPager.responseFilters.forEach(function(i){ i(res, this.requestURL); }, this);
+AutoPager.prototype.parseRequestLoad_ = function( loadedUrl, res ) {
+    AutoPager.responseFilters.forEach(function(i){ i(res, loadedUrl); }, this);
 
     var htmlDoc = createHTMLDocumentByString(res.responseText);
-    AutoPager.documentFilters.forEach(function(i) { i(htmlDoc, this.requestURL, this.info); }, this);
+    AutoPager.documentFilters.forEach(function(i) { i(htmlDoc, loadedUrl, this.info); }, this);
     var page;
-    var url;
     try {
         page = this.getPageElements(htmlDoc);
-        url = getNextURL(this.info.nextLink, htmlDoc, this.requestURL);
     } catch(e) {
         log(e);
         this.error();
     var ret = {
         isFinalUrl: res.finalUrl,
         page:       page,
-        htmlDoc:    htmlDoc,
-        url:        url
+        htmlDoc:    htmlDoc
     };
 
     return ret;
 };
-AutoPager.prototype.requestLoadPrev = function(res) {
-    var data = this.parseRequestLoad_( res );
+AutoPager.prototype.requestLoadPrev = function( res ) {
+    // TODO: pass this as an arg
+    var loadedUrl = this.requestURLPrev;
+
+
+    var data = this.parseRequestLoad_( loadedUrl, res );
     if( !data )
         return;
 
     if( data.finalUrl )
-        this.requestURL = data.finalURL;
+        this.requestURLPrev = data.finalURL;
+
+    var urlPrev = getNextURL( this.info.prevLink, data.htmlDoc, loadedUrl );
 
     if ( !data.page || data.page.length < 1 ) {
         debug('pageElement not found.' , this.info.pageElement);
-        this.terminate();
+        this.terminate('prev');
         return;
     }
 
-    if (this.loadedURLs[this.requestURL]) {
-        debug('page is already loaded.', this.requestURL, this.info.nextLink);
-        this.terminate();
+    if (this.loadedURLs[this.requestURLPrev]) {
+        debug('page is already loaded.', this.requestURLPrev, this.info.prevLink);
+        this.terminate('prev');
         return;
     }
 
-    this.loadedURLs[this.requestURL] = true;
-    var page = this.addPage( data.htmlDoc, data.page, true );
+    this.loadedURLs[this.requestURLPrev] = true;
+    var page = this.addPage( data.htmlDoc, data.page, this.requestURLPrev, 'prev' );
     AutoPager.filters.forEach(function(i) { i(page); });
-    this.requestURL = data.url;
+    this.requestURLPrev = urlPrev;
     this.showLoading(false);
-    if (!data.url) {
-        debug('nextLink not found.', this.info.nextLink, data.htmlDoc);
+    if (!urlPrev) {
+        debug('prevLink not found.', this.info.prevLink, data.htmlDoc);
         this.forceForce = true;
-        this.terminate();
+        this.terminate('prev');
 // no backwards-load support in LoadAll scenarios. yet, at least.
 /*
     } else { // for loading all
 */
     }
     var ev = document.createEvent('Event');
-    ev.initEvent('GM_AutoPagerizeNextPageLoaded', true, false);
+    ev.initEvent('GM_AutoPagerizePrevPageLoaded', true, false);
     document.dispatchEvent(ev);
 };
 AutoPager.prototype.requestLoad = function(res) {
-    var data = this.parseRequestLoad_( res );
+    // TODO: pass this as an arg
+    var loadedUrl = this.requestURL;
+
+
+    var data = this.parseRequestLoad_( loadedUrl, res );
     if( !data )
         return;
 
     if( data.finalUrl )
         this.requestURL = data.finalURL;
 
+    var urlPrev = getNextURL( this.info.nextLink, data.htmlDoc, loadedUrl );
+
     if ( !data.page || data.page.length < 1 ) {
         debug('pageElement not found.' , this.info.pageElement);
-        this.terminate();
+        this.terminate('next');
         return;
     }
 
     if (this.loadedURLs[this.requestURL]) {
         debug('page is already loaded.', this.requestURL, this.info.nextLink);
-        this.terminate();
+        this.terminate('next');
         return;
     }
 
     this.loadedURLs[this.requestURL] = true;
-    var page = this.addPage( data.htmlDoc, data.page );
+    var page = this.addPage( data.htmlDoc, data.page, this.requestURL, 'next' );
     AutoPager.filters.forEach(function(i) { i(page); });
-    this.requestURL = data.url;
+    this.requestURL = urlPrev;
     this.showLoading(false);
-    if (!data.url) {
+    if (!urlPrev) {
         debug('nextLink not found.', this.info.nextLink, data.htmlDoc);
         this.forceForce = true;
-        this.terminate();
+        this.terminate('next');
     } else { // for loading all
         if(this.shouldLoadAll)
             this.request();
     document.dispatchEvent(ev);
 };
 
-AutoPager.prototype.addPage = function( htmlDoc, page, before ) {
-    before = before || false;
+function getNodeBeforeWhichToInsert( insertPointNext, insertPointPrev, direction ) {
+    if( direction == 'next' )
+        return insertPointNext;
+
+    return insertPointPrev;
+};
+AutoPager.prototype.addPage = function( htmlDoc, page, loadedUrl, direction ) {
+    // TODO: infopanel should show pages in format "FIRST-LAST/TOTAL", where
+    // FIRST is the first of the pages currently shown, LAST is the last of the
+    // pages currently shown, and TOTAL is the total # of pages
     var totalPages = this.getPageCount( htmlDoc );
     this.updatePageNum( this.pageNum + 1, totalPages );
     
-    // add hash for paging to work when backing into this page
-    //var loc = document.location;
-    //loc = loc.href.substring(0, loc.href.length - loc.hash.length);
-    /*var anch = document.location.hash;
-    if(0==len(anch))
-    {*/
-        // no anchor
-        //document.location.replace(loc + '#pg_'+this.pageNum);
-    /*}
-    else
-    {
-        if(anch=='#pg_'+(this.pageNum-1))
-        {
-            document.location.replace(loc + '#pg_'+this.pageNum);
-        }
-        else if(endsWith(anch, '%pg_'+(this.pageNum-1)))
-        {
-            anch = anch.substring(
-            document.location.replace(loc + '#pg_'+this.pageNum);
-        }
-        // already have an anchor
-        document.location.replace(loc + '%pg_'+this.pageNum);
-    }*/
-    var self = this;
+    var insertBeboreThisNode = getNodeBeforeWhichToInsert( this.insertPoint, this.insertPointPrev, direction );
+    var insertParent = insertBeboreThisNode.parentNode;
 
-    // if must add link to the newly loaded page, and not doing a complete replace, add link
-    if(!this.noCues && !this.completeReplace) {
-        var HTML_NS  = 'http://www.w3.org/1999/xhtml';
-        var hr = document.createElementNS(HTML_NS, 'hr');
-        var p  = document.createElementNS(HTML_NS, 'p');
-        hr.setAttribute('class', 'autopagerize_page_separator');
-        p.setAttribute('class', 'autopagerize_page_info');
+    // depending on the direction, we will either insert the separator first, or the page data first
+    var mapRetVals = null;
+    if( direction == 'next' ) {
+        this.addPageSeparatorIfNeeded( insertBeboreThisNode, insertParent, page, loadedUrl );
+        mapRetVals = this.addPageItems_( insertBeboreThisNode, insertParent, page, loadedUrl );
+    } else if ( direction == 'prev' ) {
+        mapRetVals = this.addPageItems_( insertBeboreThisNode, insertParent, page, loadedUrl );
+        this.addPageSeparatorIfNeeded( insertBeboreThisNode, insertParent, page, loadedUrl );
+    }
 
-        // if we're inserting rows into table, add TR with page link
-        if (page[0] && /tr/i.test(page[0].tagName)) {
-            var insertParent = this.insertPoint.parentNode;
-            var colNodes = getElementsByXPath('child::tr[1]/child::*[self::td or self::th]', insertParent);
+    if( !this.updateInsertPoint( direction ) ) {
+        debug( "could not update insertPoint( '" + direction + "') after loading page", loadedUrl, this.info.pageElement );
+    }
 
-            var colums = 0;
-            for (var i = 0, l = colNodes.length; i < l; i++) {
-                var col = colNodes[i].getAttribute('colspan');
-                colums += parseInt(col, 10) || 1;
-            }
-            var td = document.createElement('td');
-            // td.appendChild(hr)
-            td.appendChild(p);
-            var tr = document.createElement('tr');
-            td.setAttribute('colspan', colums);
-            tr.appendChild(td);
-            insertParent.insertBefore(tr, this.insertPoint);
-        } else {
-            this.insertPoint.parentNode.insertBefore(hr, this.insertPoint);
-            this.insertPoint.parentNode.insertBefore(p, this.insertPoint);
-        }
+    return mapRetVals;
+}
 
-        p.innerHTML = 'page: <a class="autopagerize_link" href="' + this.requestURL.replace(/&/g, '&amp;') + '">' + this.pageNum + '</a>';
-    }
+AutoPager.prototype.addPageItems_ = function( insertBeboreThisNode, insertParent, page, loadedUrl ) {
     removeLastPageData(this, true); // force removal of old stuff
 
     this.toBeRemoved = null;
-    if(this.completeReplace) {
-        this.toBeRemoved = this.getPageElements(document);
+    if( this.completeReplace ) {
+        this.toBeRemoved = this.getPageElements( document );
     }
-    var self = this;
 
-    var mapRV = page.map(function(i) {
+    var mapRetVals = page.map(function(i) {
         var pe = document.importNode(i, true);
-        self.insertPoint.parentNode.insertBefore(pe, self.insertPoint);
+        insertParent.insertBefore(pe, insertBeboreThisNode);
         var ev = document.createEvent('MutationEvent');
-        ev.initMutationEvent('AutoPagerize_DOMNodeInserted', true, false, self.insertPoint.parentNode, null, self.requestURL, null, null);
+        ev.initMutationEvent('AutoPagerize_DOMNodeInserted', true, false, insertParent, null, loadedUrl, null, null);
         pe.dispatchEvent(ev);
         return pe;
     });
-    if(self.info.postFn) {
-        self.info.postFn(unsafeWindow, mapRV);
+    if(this.info.postFn) {
+        this.info.postFn(unsafeWindow, mapRetVals);
     }
-    
-    return mapRV;
-}
 
+    return mapRetVals;
+};
+AutoPager.prototype.addPageSeparatorIfNeeded = function( insertBeboreThisNode, insertParent, page, loadedUrl ) {
+    // if must add link to the newly loaded page, and not doing a complete replace, add link
+    if( this.noCues || this.completeReplace )
+        return;
+
+    var HTML_NS  = 'http://www.w3.org/1999/xhtml';
+    var hr = document.createElementNS(HTML_NS, 'hr');
+    var p  = document.createElementNS(HTML_NS, 'p');
+    hr.setAttribute('class', 'autopagerize_page_separator');
+    p.setAttribute('class', 'autopagerize_page_info');
+
+    // if we're inserting rows into table, add <tr><td> with page link, otherwise an <hr> and a <p>
+    if (page[0] && /tr/i.test(page[0].tagName)) {
+        var colNodes = getElementsByXPath('child::tr[1]/child::*[self::td or self::th]', insertParent);
+
+        var colums = 0;
+        for (var i = 0, l = colNodes.length; i < l; i++) {
+            var col = colNodes[i].getAttribute('colspan');
+            colums += parseInt(col, 10) || 1;
+        }
+        var td = document.createElement('td');
+        // td.appendChild(hr)
+        td.appendChild(p);
+        var tr = document.createElement('tr');
+        td.setAttribute('colspan', colums);
+        tr.appendChild(td);
+        insertParent.insertBefore(tr, insertBeboreThisNode);
+    } else {
+        insertParent.insertBefore(hr, insertBeboreThisNode);
+        insertParent.insertBefore(p, insertBeboreThisNode);
+    }
+
+    p.innerHTML = 'page: <a class="autopagerize_link" href="' + loadedUrl.replace(/&/g, '&amp;') + '">' + this.pageNum + '</a>';
+};
 AutoPager.prototype.initIcon = function() {
     var div = document.createElement("div");
     div.setAttribute('id', 'autopagerize_icon');
     }
 }
 
-AutoPager.prototype.terminate = function() {
-    window.removeEventListener('scroll', this.scroll, false);
-    window.removeEventListener('mousedown', mdown, false);
-    window.removeEventListener('mouseup', mup, false);
-    this.updateIcon('terminated');
-    var self = this;
-    setTimeout(function() {
-        if( self.icon ) {
-            self.icon.parentNode.removeChild(self.icon);
+var terminatedPagings = { next: false, prev: false };
+AutoPager.prototype.terminate = function( direction ) {
+    terminatedPagings[direction] = true;
+    var allTerminated = true;
+    for(var key in terminatedPagings) {
+        if (terminatedPagings.hasOwnProperty(key)){
+            if( !terminatedPagings[key] ) {
+                allTerminated = false;
+                break;
+            }
         }
-    }, 1500);
-}
+    }
+
+    if( allTerminated ) {
+        window.removeEventListener('scroll', this.scroll, false);
+        window.removeEventListener('mousedown', mdown, false);
+        window.removeEventListener('mouseup', mup, false);
+        this.updateIcon('terminated');
+        var self = this;
+        setTimeout(function() {
+            if( self.icon ) {
+                self.icon.parentNode.removeChild(self.icon);
+            }
+        }, 1500);
+    }
+};
 
 AutoPager.prototype.error = function() {
     this.updateIcon('error');
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.