//Note to editor: NOT executed from XUL
//Copyright 2009 Yongqian Li.
//javascript:        var scrollHeight = document.documentElement.scrollHeight;        var scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);        var clientHeight = Math.min(document.documentElement.clientHeight, document.body.clientHeight);        var remaining = scrollHeight - scrollTop - clientHeight;alert(remaining);
/*

mail.google.com / a        / 10gic.net / #inbox / p3
mail.google.com / support  / bin       / answer.py?hl=en&ctx=mail&answer=15049

<= 5 diff eliminate. still multiply
*/


$.prototype.ready = function(callback) {
    callback();
};//doesn't work on Chrome, cheap hack
$(document).ready(function()
{
    var safe_SW_getBoolPref = function(name)
    {
        try
        {
            return SW_getBoolPref(name);
        }
        catch(err)
        {
            return null;
        }
    };

    var getBoolPref = function(pref_name, callback)
    {
        callback(safe_SW_getBoolPref("extensions.smarterwiki." + pref_name));
    };
    if(window.chrome && window.chrome.extension) // detect chrome
    {
        var get_localStorage = function(key, callback) //only used for Chrome
        {    
            chrome.extension.sendRequest({"msg_type": "get_localStorage", "key": key}, function(response)
            {
                if(callback)
                {
                    callback(response.value);
                }
            });
        };

        var pref_cache = {};
        getBoolPref = function(pref_name, callback)
        {
            if(pref_name in pref_cache)
            {
                callback(pref_cache[pref_name] == "true");
            }
            else
            {
                get_localStorage("pref." + pref_name, function(str)
                {
                    pref_cache[pref_name] = str;
                    callback(str == "true");
                });
            }
        };
        
        SW_$ = $;
        SW_LOG = function(){};
        $.get = function(url, data, callback, type)
        {
            chrome.extension.sendRequest({"msg_type": "$.get", "url": url, "data": data, "type": type}, function(response)
            {
                if(callback)
                {
                    callback(response.data, response.textStatus);
                }
            });            
        };
    }    
    
    
    if(!(self === top)) //do not execute in iframe
    {
        return;
    }
    var blacklisted_domains = ["www.met.ie", 
                "blackle.com", 
                "www.getresponse.com",
                "buzz.bazooka.se", 
                "www.ebay.com", 
                "forums.macrumors.com",
                "www.tricentis.com",
                "www.backupflow.com",
                "en.espnf1.com",
                "www.tvtorrents.com",
                "www.mangaupdates.com",
                "www.tumblr.com",
                "www.eurogamer.net",
                "msdn.microsoft.com"];//does not work on those
    for(var i = 0; i < blacklisted_domains.length; i++) {
        if(blacklisted_domains[i] == window.location.host) {
            return;
        }
    }
    
    var blacklisted_urls = []; //"http://www.google.com/"];
    for(var i = 0; i < blacklisted_urls.length; i++)
    {
        if(blacklisted_urls[i] == window.location.href) {
            return;
        }
    }
    
    var is_invalid_page = $("a").length > 1500;
    if(!is_invalid_page)
    {
        $("body > *").each(function() {
            if($(this).css("position") == "absolute" && 
                $(this).css("display") != "none" && 
                $(this).css("width") != "0px" && 
                $(this).css("height") != "0px" && 
                $(this).css("visibility") != "hidden"
            ) // && $(this).css("top") != "" && $(this).attr("id") != "qlauncher-floater"
            {
                $(this).addClass("broke-endless-pages");
                is_invalid_page = true;
                return false;
            }
        });
    }
    if(is_invalid_page) {
        return;
    }
    
    
    
    var page_str = window.page_str || "Page";
    var loading_next_page_str = window.loading_next_page_str || "Loading next page...";
    
    
    
    //rel="next, Next, >>, More, Newer, >, <<, Older, ...
    //page, entry, all
    //Next: <a></a>
    //"Page 1 >> Older" - twitter
    //bigger font? lower score if have to share features?
    //ENSURE SAME DOMAIN!!!!!!!!!!
    //
    /*
    var doc = document;
    var xpath = "//a[contains(translate(.,' ', ''), 'next') or contains(translate(following-sibling::node(),'ABCDEFGHIJKLMNOPQRSTUVWXYZ ', ''), 'common')]";
    //"//[REMOVE THIS]*[contains(translate(.,' ', ''),'cat')]";
    <a href="xpath_axes.asp">
    <img width="100" height="20" border="0" alt="Next" src="/images/btn_next.gif"/>
    </a>
    var result = doc.evaluate(xpath, doc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); 
    for(var i = 0; i < result.snapshotLength; i++)
    {
        var candidate = result.snapshotItem(i);
        if(candidate.nodeName == "#cdata-section")
        {
            continue;//ignore cdata
        }
        var text = candidate.textContent;
        console.log(text);
    }
    */
    
    var shared_prefix_len = function(str_a, str_b)
    {
        var len = Math.min(str_a.length, str_b.length);
        for(var i = 0; i < len; i++)
        {
            if(str_a.charAt(i) != str_b.charAt(i))
            {
                return i;
            }
        }
        return len;
    };    
    
    var num_path_diff = function(url1, url2)
    {
        var get_paths = function(url)
        {
            return url.split(new RegExp("([^/?]*)"));
        };
        var paths1 = get_paths(url1);
        var paths2 = get_paths(url2);
        var num_common_paths = 0;
        while(num_common_paths < Math.min(paths1.length, paths2.length) && 
            (paths1[num_common_paths] == paths2[num_common_paths]))
        {
            num_common_paths++;
        }
        return paths1.length + paths2.length - 2 * num_common_paths;
    };

    var get_a_matcher = function(keywords)
    {
        keyword += " ";
        var expr1s = [];
        for(var i = 0; i < keywords.length; i++)
        {
            var keyword = keywords[i];
            //var verify_length1 = "string-length(translate(., ' ', '')) <= 20";
            //var verify_length2 = "string-length(translate(., ' ', '')) >= 1";
            var starts_with = "starts-with(concat(., ' '), '" + keyword + "')";
            var contains = "contains(translate(.,' ', ''), '" + keyword + "')";
            var maybe_ends_with = "string-length(substring-after(concat(., ' '), '" + keyword + "')) = 0";
            var keyword_expr = "("/* + verify_length1 + " and " + verify_length2 + " and ("*/ + starts_with + " or (" + contains + " and " + maybe_ends_with + "))"/* + ")"*/;
            expr1s.push(keyword_expr);
        }        
        var expr2s = [];
        var p_sib = "translate(concat(preceding-sibling::node()[2], preceding-sibling::node()[1], preceding-sibling::node()[0]), ' ', '')";
        var f_sib = "translate(concat(following-sibling::node()[0], following-sibling::node()[1], following-sibling::node()[2]), ' ', '')";
        for(var i = 0; i < keywords.length; i++)
        {
            var keyword = keywords[i];
            expr2s.push("(contains(substring(" + p_sib + ", string-length(" + p_sib + ") - 35), '" + keyword + "') or contains(substring(" + f_sib + ", string-length(" + f_sib + ")), '" + keyword + "'))");
        }
        var verify_length1 = "string-length(translate(., ' ', '')) <= 20";
        var verify_length2 = "string-length(translate(., ' ', '')) >= 1";
        var xpath_expr = ".//a[@rel='next' or (" + verify_length1 + " and " + verify_length2 + " and (" + expr1s.join(" or ") + " or " + expr2s.join(" or ") + "))]";
        //alert(xpath_expr);
        return function(contextNode) {
            var result = document.evaluate(xpath_expr, contextNode, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
            var nodes = [];
            for(var i = 0; i < result.snapshotLength; i++)
            {
                nodes.push(result.snapshotItem(i));
            }
            return nodes;
        };
    };
    
    var $a_text_tester = function(keyword)
    {
        keyword += " ";
        var verify_length1 = "string-length(translate(., ' ', '')) <= 20";
        var verify_length2 = "string-length(translate(., ' ', '')) >= 1";
        var starts_with = "starts-with(concat(., ' '), '" + keyword + "')";
        var contains = "contains(concat(., ' '), '" + keyword + "')";
        var maybe_ends_with = "string-length(substring-after(concat(., ' '), '" + keyword + "')) = 0";
        var keyword_expr = "(" + verify_length1 + " and " + verify_length2 + " and (" + starts_with + " or (" + contains + " and " + maybe_ends_with + "))" + ")";
        var xpath_expr = "" + keyword_expr + "";// or ends-with(translate(substring(., 0, 35),' ', ''), '" + keyword + "'))]";
        return function($a){
            var result = document.evaluate(xpath_expr, $a[0], null, XPathResult.BOOLEAN_TYPE, null);
            //not a perfect emulation of ends-with but XPath 1.0 is too FUCKING DUMB.
            return result.booleanValue;
        };
    };
    var $a_parent_text_tester = function(keyword)
    {
        keyword += " ";
        var p_sib = "translate(concat(preceding-sibling::node()[2], preceding-sibling::node()[1], preceding-sibling::node()[0]), ' ', '')";
        var f_sib = "translate(concat(following-sibling::node()[0], following-sibling::node()[1], following-sibling::node()[2]), ' ', '')";
        var xpath_expr = "string-length(translate(., ' ', '')) <= 20 and string-length(translate(., ' ', '')) >= 1 and ((contains(substring(" + p_sib + ", string-length(" + p_sib + ") - 35), '" + keyword + "')) or (contains(substring(" + f_sib + ", string-length(" + f_sib + ")), '" + keyword + "')))";
        //alert(xpath_expr);
        return function($a){
            var result = document.evaluate(xpath_expr, $a[0], null, XPathResult.BOOLEAN_TYPE, null);
            return result.booleanValue;
        };
    };    
    var $a_adjust_weight_on_text = function(weight, feat_len)
    {
        return function($a)
        {
            var text_len = $a.text().length;
            return text_len ? weight * feat_len / text_len : 0;
        };
    };
    var $a_identity = function(val)
    {
        return function($a) {
            return val;
        };
    };
    
    var features = [
        [function($a){
            return $a.attr("rel") == "next";
        }, 
            $a_identity(5000)],//sometimes abused :(
        [$a_text_tester("next"), //   /^.{0,5}next.{0,5}$/),
            $a_adjust_weight_on_text(6000, 4)],
        [$a_text_tester("Next"), //   /^.{0,5}next.{0,5}$/),
            $a_adjust_weight_on_text(5000, 4)],
        [$a_text_tester("\u203a"),
            $a_identity(4000)],
        [$a_text_tester("\u4e0b\u4e00\u9875"), //Chinese for Next Page
            $a_adjust_weight_on_text(5000, 4)],
        [$a_text_tester("Vorw\u00E4rts"), //German for Next Page
            $a_adjust_weight_on_text(5000, 4)],
        [$a_text_tester("Suivant"), //French for Next Page
            $a_adjust_weight_on_text(5000, 4)],
        [$a_text_tester("Volgende"), //Dutch for Next
            $a_adjust_weight_on_text(5000, 4)],
        [$a_text_tester("\u00bb"),//&#x00bb; //>>|\u00bb
            $a_identity(4000)],
        [$a_text_tester("more"),
            $a_adjust_weight_on_text(800, 4)],
        [$a_text_tester("More"),
            $a_adjust_weight_on_text(1000, 4)],
        [$a_text_tester("newer"),
            $a_adjust_weight_on_text(900, 5)],
        [$a_text_tester("Newer"),
            $a_adjust_weight_on_text(1100, 5)],
        [$a_text_tester(">>"),
            $a_identity(1000)],
        [$a_text_tester(">"),
            $a_identity(600)],
        [$a_text_tester("\u00AB"),
            $a_identity(1500)],
        [$a_text_tester("older"),
            $a_adjust_weight_on_text(600, 5)],
        [$a_text_tester("Older"),
            $a_adjust_weight_on_text(700, 5)],
        [$a_text_tester("Older Posts"),
            $a_adjust_weight_on_text(800, 5)],
        [$a_text_tester("Older posts"),
            $a_adjust_weight_on_text(780, 5)],
        [$a_text_tester("<"),
            $a_identity(350)],


        [$a_parent_text_tester("next"),
            $a_identity(500)],
        [$a_parent_text_tester("Next"),
            $a_identity(550)],
        [$a_parent_text_tester("\u4e0b\u4e00\u9875"), //Chinese for Next Page
            $a_identity(550)],
        [$a_parent_text_tester("Vorw\u00E4rts"), //German for Next Page
            $a_identity(550)],
        [$a_parent_text_tester("Suivant"), //French for Next Page
            $a_identity(550)],            
        [$a_parent_text_tester("Volgende"), //Dutch for Next Page
            $a_identity(550)],            
        [$a_parent_text_tester("\u00bb"),//&#x00bb; //>>|\u00bb
            $a_identity(500)],
        [$a_parent_text_tester("more"),
            $a_identity(300)],
        [$a_parent_text_tester("More"),
            $a_identity(300)],
        [$a_parent_text_tester("newer"),
            $a_identity(300)],
        [$a_parent_text_tester("Newer"),
            $a_identity(300)],
        [$a_parent_text_tester(">>"),
            $a_identity(400)],
        [$a_parent_text_tester(">"),
            $a_identity(100)],
        [$a_parent_text_tester("\u00AB"),
            $a_identity(200)],
        [$a_parent_text_tester("older"),
            $a_identity(100)],
        [$a_parent_text_tester("Older"),
            $a_identity(100)],
        [$a_parent_text_tester("<"),
            $a_identity(100)],
    ];
    var find_matching_as = get_a_matcher(["next", "Next", //"\u00bb", 
                "\u4e0b\u4e00\u9875", //Chinese for next page
                "Vorw\u00E4rts", //German Next page
                "Suivant", //french next page
                "Volgende", //dutch next
                "more", "More", "newer", "Newer", ">>", ">", "\u00AB",
                "older", "Older", "<"]);
                //do NOT include "\u203a" (>) because it should not be enough on its own

    var check_scroll = function() 
    {
        getBoolPref("enable_endless_pages", function(pref_value)
        {
            if(pref_value)
            {
                var scrollHeight = document.documentElement.scrollHeight;
                var scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
                var clientHeight = $(window).height(); //Math.min(document.documentElement.clientHeight, document.body.clientHeight);
                var remaining = scrollHeight - scrollTop - clientHeight;
                //alert(remaining);
                if(remaining < 400)
                {
                    setTimeout(function()
                    {
                        load_next_page();
                    }, 0);
                }
            }
        });
    };
    
    $(window).scroll(check_scroll);
    setInterval(check_scroll, 300);
    
    var is_subset_of = function(set1, set2)
    {
        if(set1.length != set2.length) {
            return false;
        }
        for(var i = 0; i < set1.length; i++) {
            if(set1[i] && !set2[i]) {
                return false;
            }
        }
        return true;
    };
    
    var $last_loaded_page = null;
    var prev_best_has_feature = null;
    var prev_features_count = null;
    var identify_next_link = function()
    {
        /*//addons.mozilla.org is https
        if(/https:/.test(window.location.href)) {
            return null;
        }
        */
        
        var is_valid_url = function(url)
        {
            return url.indexOf(window.location.host) != -1 && 
                    url.indexOf("#") == -1 && 
                    url != window.location.href && 
                    num_path_diff(url, window.location.href) <= 6 && 
                    url.indexOf("random") == -1;
        };
        
        var startTime = new Date().getTime();
        
        var features_count = [];
        for(var i = 0; i < features.length; i++)
        {
            features_count.push(0);
        }
        
        var $links = $(find_matching_as($last_loaded_page ? $last_loaded_page[0] : document));
        $links.each(function(){
            var url = this.toString();
            if(is_valid_url(url))
            {
                var $a = $(this);
                var $a_has_feature = [];
                for(var i = 0; i < features.length; i++)
                {
                    if(features[i][0]($a))
                    {
                        $a_has_feature.push(true);
                        features_count[i] += 1;
                    }
                    else
                    {
                        $a_has_feature.push(false);
                    }                    
                }
                $a.data("has_feature", $a_has_feature);
                //alert($a[0].toString() + " " + $a_has_feature);
            }
        });
        
        
        var compute_score = function(url, $a, features_count)
        {
            var $a_has_feature = $a.data("has_feature");
            var score = 0;
            for(var i = 0; i < $a_has_feature.length; i++) {
                if($a_has_feature[i]) {
                    score += features[i][1]($a) / (features_count[i] ? features_count[i] : 1);
                }
            }
            SW_LOG("before: " + score + " " + url);
            var adjustment = ((1 / num_path_diff(url, window.location.href)) +  
                (shared_prefix_len(url, window.location.href) / Math.min(url.length, window.location.href.length))) / 2;//favor longer urls over shorter ones
            score = score * adjustment;
            SW_LOG("after: " + score + " " + url);
            $a.addClass("score-" + score);
            return score;
        };
        
        
        var max_score = 0;
        var best_candidate = null;
        $links.each(function(){
            var url = this.toString();
            if(is_valid_url(url))
            {
                var $a = $(this);
                var $a_has_feature = $a.data("has_feature");
                if(prev_best_has_feature)
                {
                    if(is_subset_of(prev_best_has_feature, $a_has_feature))
                    {
                        var score = compute_score(url, $a, prev_features_count);
                        if(score > max_score)
                        {
                            max_score = score;
                            best_candidate = $a;
                        }
                        SW_LOG("matched previous");
                    }
                }
                else
                {
                    var score = compute_score(url, $a, features_count);
                    if(score > max_score)
                    {
                        max_score = score;
                        best_candidate = $a;
                    }
                }
            }
        });

        var endTime = new Date().getTime();
        if(max_score >= 400)
        {
            SW_LOG("time: " + (endTime - startTime) + " score: " + max_score + ": " + best_candidate + ": " + best_candidate[0].toString() + ": " + best_candidate.text());
        }
        
        if(max_score >= 400)
        {
            //alert(": " + features_count);
            if(!prev_best_has_feature)
            {
                prev_best_has_feature = best_candidate.data("has_feature");
                prev_features_count = features_count;
            }
            return best_candidate;
        }
        else
        {
            return null;
        }
    };
    
    var loading_next_page = false;
    var loaded_urls = {};
    //var diditonce = false;
    var load_next_page = function()
    {
        /*
        if(diditonce)
        {
            return;
        }
        else
        {   
            diditonce = true;
        }
        */
        if(loading_next_page)
        {
            return;
        }
        else
        {
            loading_next_page = true;
        }
        
        var $next_link = identify_next_link();
        if($next_link)
        {
            var url = $next_link[0].toString();
            
            if(!(url in loaded_urls))
            {
                loaded_urls[url] = null;

                $next_link.addClass("endless-pages-found-next-link");
                SW_LOG(url + ":" + $next_link.text());

                var loading_text = loading_next_page_str;
                $("body").append($('<div id="endless-pages-loading"><img id="endless-pages-loading-spinner" src="http://static.smarterfox.com/media/smarterwiki/endless_pages/spinner.gif" alt="spinner" />' + loading_text + '</div>').fadeIn());

                var insert_url = function(url)
                {
                    $.ajax({
                        type: "GET",
                        url: url,
                        success: function(data)
                        {
                            var redirect_url = insert_next_page(url, data);
                            if(redirect_url)
                            {
                                insert_url(redirect_url);
                            }
                            else
                            {
                                $("#endless-pages-loading").fadeOut(function() {
                                    $(this).remove();
                                    loading_next_page = false;
                                });
                            }
                        }, 
                        error: function()
                        {
                            $("#endless-pages-loading").fadeOut(function() {
                                $(this).remove();
                                // loading_next_page = false; // don't try again
                            });
                        },
                        dataType: "html",
                        beforeSend: function(req)
                        {
                            req.setRequestHeader("X-moz", "prefetch");
                        }
                    });
                };
                insert_url(url);
            }
        }
        //insert_next_page('<html><head></head><body>abcdef<p></body></html>', "text/xml");
    };
    
    
    /*
    var insert_next_page = function(url)
    {
        var $iframe = $('<iframe src="' + url + '" style="height: 0; width: 0; border: none; display: none;"></iframe>').append($("body"));
        $iframe.load(function()
        {
            var next_doc = $iframe[0].contentDocument;
        });
    };//what about frame busting code???
    */
    
    var num_pages_inserted = 0;
    var insert_next_page = function(url, doc_str)//mimetype="text/xml"
    {
        var match = new RegExp("<\\s*body[^<>]*>([\\s\\S]*)<\\s*/\\s*body\\s*>").exec(doc_str);
        if(!match)
        {//some people, like Google, are retarded and are too fucking penny pinching to pay for 7 bytes.
            match = new RegExp("<\\s*body[^<>]*>([\\s\\S]*)$").exec(doc_str);
        }
        if(match)
        {
            var inner_source = match[1];
            if(inner_source)
            {
                //get rid of scripts
                inner_source = inner_source.replace(new RegExp("<\\s*script[^<>]*>([\\s\\S]*?)<\\s*/\\s*script\\s*>", ["gi"]), "");
                inner_source = inner_source.replace(new RegExp("(<\\s*noscript[^<>]*>)|(<\\s*/\\s*noscript\\s*>)", ["gi"]), "");
                inner_source = inner_source.replace(new RegExp("(<\\s*noscript[^<>]*>)|(<\\s*/\\s*noscript\\s*>)", ["gi"]), "");

                var m = new RegExp('(<\\s*meta[^<>]*HTTP-EQUIV="refresh"[^<>]*content="[^<>]*url=([^<>;]*)(;[^<>]+|)"[^<>]*>)', ["gi"]).exec(inner_source);
                if(m)
                {
                    var redirect_url = m[2];
                    return redirect_url;
                }
                inner_source = inner_source.replace(new RegExp("(<\\s*meta[^<>]*>)(([\\s\\S]*?)(<\\s*/\\s*meta\\s*>))?", ["gi"]), "");
                //inner_source = inner_source.replace(new RegExp("<\\s*object[^<>]*>([\\s\\S]*?)<\\s*/\\s*object\\s*>", ["gi"]), "");
                //inner_source = inner_source.replace(new RegExp("<\\s*embed[^<>]*>([\\s\\S]*?)<\\s*/\\s*embed\\s*>", ["gi"]), "");
                //<meta HTTP-EQUIV="refresh" content="0;url=http://images.google.com/images?hl=en&client=firefox-a&rls=org.mozilla:en-US:official&um=1&q=a&sa=N&start=63&ndsp=21&gbv=1&ei=8Jd8StPqOIuYsgPboqjvCg">
                //SW_LOG(inner_source);

                var $loaded_by_endless_pages = $("#loaded-by-endless-pages");
                if($loaded_by_endless_pages.length == 0)
                {
                    $loaded_by_endless_pages = $('<div id="loaded-by-endless-pages"></div>')
                                .css("margin-top", $("body").css("margin-bottom"))
                                .css("padding-top", $("body").css("padding-bottom"))
                                                    .appendTo("body");
                }
                num_pages_inserted += 1;
                var txt = page_str + " " + (num_pages_inserted + 1) + "";
                var $page_break_div = $('<div class="endless-pages-page-break"><div class="endless-pages-page-break-desc">- <img class="smarterfox-icon" src="http://static.smarterfox.com/media/smarterwiki/smarterfox-logo.png" /><a class="endless-pages-page-break-link" href="' + url + '">' + txt + '</a> -</div></div>')
                                            .appendTo($loaded_by_endless_pages);   
                                            
                                            
                var parse_px = function(px_s)
                {
                    var m = /(\d+)px/.exec(px_s);
                    return (m && parseInt(m[1])) || 0;
                };
                var $loaded_page_div = $('<div class="endless-pages-loaded-page"></div>')
                                            .append(inner_source)
                                            .appendTo($loaded_by_endless_pages);
                $loaded_page_div.css("padding-top", 54 + parse_px($("body").css("margin-top")) + "px");
                    //$loaded_page_div.css("margin-bottom", $("body").css("margin-bottom"));
                    
                if($last_loaded_page)
                {
                    $last_loaded_page.css("margin-bottom", $("body").css("margin-bottom"))
                        .addClass("smarterwiki-clearfix");
                }
                $last_loaded_page = $loaded_page_div;
            }
            /*
            var next_doc = new DOMParser().parseFromString(doc_str, mimetype);
            $(next_doc.body, next_doc).each(function()
            {
                if(this.nodeName != "script")
                {
                    $next_page_div[0].appendChild(document.adoptNode(this));
                }
            });
            */
        }
    };
});