4chan thread updater

By anon404 Last update Dec 21, 2008 — Installed 9,556 times.

There are 6 previous versions of this script.

Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==
// @name          4chan thread updater
// @author        anon404
// @date	  12-21-2008
// @namespace     http://4chan.org/
// @version	  v0.3.0
// @description   Script to update 4chan threads whenever you want them to.
// @include       http://*.4chan.org/*/res/*.html
// @include       http://*.4chan.org/*/res/*.html#*
// @include       *.4chan.org/*/res/*.html#*
// @include       *.4chan.org/*/res/*.html
// @include       http://*.niggertits.org/*/res/*.html
// @include       http://*.niggertits.org/*/res/*.html#*
// @include       *.niggertits.org/*/res/*.html#*
// @include       *.niggertits.org/*/res/*.html
// ==/UserScript==

// Yeah, I know this is a blatant rip-off, I have some ideas that I have yet to implement.

(function() {  // fixes vim indent -> )

    var DEBUG = false; // turn this on to get log messages

    var updater = {

            last_update : null,

            init : function() {
                // bail if called on a framed document
                if (window.frameElement &&
                        window.parent.location.href.replace(/#.*/, '') == location.href) {
                    return;  
                }

                this.last_update = new Date(document.lastModified);

                timer.init(updater.poll_server, prefs.poll_interval.get());

                updater_control.init(updater.control_clicked);

                prefs.auto_update.get() && this.start();
            },
   
            start : function() {
                updater_control.set_started();
                timer.start();
            },

            stop : function() {
                updater_control.set_stopped();
                timer.stop();
            },

            toggle : function() {
                timer.is_running() ? this.stop() : this.start();
            },

            poll_server : function() {
                log_msg('polling server...');
                var request = new XMLHttpRequest();

                request.onreadystatechange = function () {
                    if (request.readyState == 4) {
                        switch (request.status) {
                            case 200: // OK
                                log_msg('updating page');
                                updater.update_page(request); 
                                // NOTE: update_page is responsible
                                // for restarting timer when it's done
                                break;
                            case 304: // Not Modified
                                log_msg('page not modified');
                                timer.start();
                                break;
                            case 404: // Not Found
                                log_msg('thread died');
                                updater.do_thread_died();
                                break;
                            default:
                                log_msg('unhandled server response: ' + request.status);
                                timer.start();
                                break;
                        }
                    }
                }

                try {
                    timer.stop(); // until request is handled

                    req_type = prefs.run_scripts.get() ? 'HEAD' : 'GET';

                    request.open(req_type, window.location.href.replace(/#.*/, ''), true);
                    request.setRequestHeader('If-Modified-Since', updater.last_update.toUTCString());
                    request.send(null);
                }  catch (e) {
                    log_msg('Error connecting to server: ' + e.toString());
                }
            },

            update_page : function (request) {
                  if (request.responseText) {

                      updater.last_update = new Date(request.getResponseHeader('Last-Modified'));

                      document.getElementsByName('delform')[0].innerHTML = 
                          request.responseText.split(/<form .*?name=["]delform["].*?>/i)[1];

                      // rerun 4chan's script to highlight replies
                      unsafeWindow.init();

                      timer.start();
                  } else {
                      // HACK HACK: we can't reload the main page into an iframe due to 
                      // a recursion guard mechanism in Fireflodge so we call
                      // 'dat.4chan.org' and let it redirect back to img.4chan.org.
                      // This both bypasses the guard and makes me cringe.
                      url = window.location.href.replace(/#.*/, '').replace('img','dat');

                      dom_from_hidden_iframe(url, updater.handle_iframe_loaded); // NOTE: handle_iframe_loaded restarts timer when page rendering is done.
                  }    
              },

            do_thread_died : function() {
                     updater.stop();

                     // display thread died message
                     var delform = document.getElementsByName('delform')[0];
                     thread_died_message = document.createElement('h2');
                     thread_died_message.style.color = 'red'; 
                     thread_died_message.style.textAlign = 'center';
                     thread_died_message.innerHTML = prefs.thread_died_notice.get();
                     delform.parentNode.insertBefore(thread_died_message, delform);

                     updater_control.set_died();

                     // add thread died message to page title
                     document.title = prefs.thread_died_title.get();

                     // disable forms if the user requested
                     prefs.disable_forms.get() && updater.disable_forms();

                     // do alert dialog if the user requested one
                     prefs.thread_died_alert.get() && alert(prefs.thread_died_notice.get());
           },

           handle_iframe_loaded : function(iframe_doc) {
                          var container, last_item;

                          // dont try to run on 404'd pages
                          if (!$X("/html/body/div[@id='header']")) {
                              return;
                          }

                          updater.last_update = new Date(iframe_doc.lastModified);

                          if ( (container = $X("//div[@class='4chan_ext_thread_replies']")) ) {
                              add_post = function(item, cont) { cont.appendChild(item); };
                          } else {
                              container = $X("//form[@name='delform']");
                              last_item = $X("//form[@name='delform']//table[last()-1]", document);

                              add_post = function(item, cont) { cont.insertBefore(item, last_item.nextSibling); };
                          }

                          current_reply_count = 
                              $X("count(//form[@name='delform']//td[@class='reply' or @class='replyhl'])", document); 

                          new_replies = $x("//form[@name='delform']//table[position() > " +
                                  current_reply_count +
                                  "]//td[@class='reply' or @class='replyhl']/ancestor::table", 
                                  iframe_doc);

                          for (var i = 0, il = new_replies.length; i < il; i++) {
                              add_post(new_replies[i], container);
                          }

                          timer.start();
                      },

                      control_clicked :function(e) { 
                             e.ctrlKey ? prefs_panel.show() : updater.toggle();
                      },

                    disable_forms : function() {
                    // change submit button title so the user knows why it's disabled
                    $X("/html/body//form[@name='post']//input[@type='submit']").value = 
                        prefs.thread_died_title.get();
                    // disable all inputs but textarea so the user can copy their text still
                    // if they want to save or use it somewhere else.
                    inputs = $x("/html/body//form[@name='post']//input");
                    for (i = 0; i < inputs.length; i++) {
                        inputs[i].disabled = true;
                    }
                }


    };

    // PROGRAM OBJECTS
    
    // timer object literal
    var timer = { 
        id : null,
        callback : null,
        interval : 0,

        init : function(callback, interval) {
           this.callback = callback;
           this.interval = interval;
        },

        is_running : function() { 
            return this.id;
        },

        set_interval : function(new_interval) {
            this.interval = new_interval;
        },

        start : function() { 
            if (this.is_running()) {
                log_msg('timer already started { %s }, skipping', this.id);
           } else {
                log_msg('starting timer');
                this.id = window.setInterval(this.callback, 1000 * this.interval);
            }
        },

        stop : function() { 
            log_msg('stop timer');
            clearInterval(this.id); 
            this.id = null; 
        },

        restart : function () {
            this.stop();
            this.start();
        }
    };

    function Pref(name, default_value) {
        this.default_value = default_value;
        this.name = name;
        this.set = function(val) { GM_setValue(this.name, val); };
        this.get = function() { return GM_getValue(this.name, this.default_value); };
    }

    var prefs = {
        auto_update :           new Pref('auto_update',         true), 
        thread_died_alert :     new Pref('thread_died_alert',   false), 
        disable_forms :         new Pref('disable_forms',       true), 
        run_scripts :           new Pref('run_scripts',         false), 
        poll_interval :         new Pref('poll_interval',       10), 
        thread_died_title :     new Pref('thread_died_title',   '(saged)'), 
        thread_died_notice :    new Pref('thread_died_notice',  'This thread no longer exists') 
    };

    var prefs_panel = { 

        prefs_div : null,

        init : function(callback)
        {
            GM_addStyle(prefs_panel_css);
            GM_addStyle('#tu_updater_prefs { text-align: left; position:fixed; bottom:25px; right:0px; display: block}');
            this.prefs_div = document.createElement('div');
            this.prefs_div.id = 'tu_updater_prefs';
            this.prefs_div.innerHTML = prefs_panel_html;
            $X('/html/body').appendChild(this.prefs_div);

            $X("//input[@id='tu_updater_prefs_submit']").addEventListener('click', prefs_panel.do_save, true);
            $X("//input[@id='tu_updater_prefs_cancel']").addEventListener('click', prefs_panel.do_cancel, true);
        },

        do_save : function() {
            prefs_panel.save();
            prefs_panel.hide();
        },

        do_cancel : function() {
            prefs_panel.hide();
        },

        save : function() {
            poll_interval_select = $X("//select[@id='poll_interval_select']");
            old_poll_interval = prefs.poll_interval.get();
            new_poll_interval = poll_interval_select[poll_interval_select.selectedIndex].value;
            if (old_poll_interval != new_poll_interval) {
                prefs.poll_interval.set(new_poll_interval);
                timer.set_interval(new_poll_interval);
                timer.restart();
            }
            prefs.auto_update.set($X("//input[@id='auto_update_checkbox']").checked);
            prefs.disable_forms.set($X("//input[@id='disable_forms_checkbox']").checked);
            prefs.run_scripts.set($X("//input[@id='run_scripts_checkbox']").checked);
        },

        load : function() {
           $X("//input[@id='auto_update_checkbox']").checked = prefs.auto_update.get();
           $X("//input[@id='disable_forms_checkbox']").checked = prefs.disable_forms.get();
           $X("//input[@id='run_scripts_checkbox']").checked = prefs.run_scripts.get();
           $X("//select[@id='poll_interval_select']//option[@value='" + 
                   prefs.poll_interval.get() + "']").selected = true;
           $X("//input[@id='auto_update_checkbox']").checked = prefs.auto_update.get();
       },

        hide : function() {
            prefs_panel.prefs_div.style.display = 'none';
        },

        show : function() {
           if (!this.inited()) {
               this.init();
           }
           prefs_panel.load();
           this.prefs_div.style.display = 'block';
        },

        inited : function() {
            return this.prefs_div != null;
        }
    }

    // updater_control object literal
    var updater_control = { 
        STARTED_LABEL : 'Updater Running',
        STOPPED_LABEL : 'Updater Stopped',
        THREAD_DIED_LABEL : ';_;',

        div : null,
        link : null,

        init : function(callback)
        {
            GM_addStyle('#updater_container { position:fixed; bottom:2px; right:0px;}');
            GM_addStyle('#updater_control { color: #FFF; padding: 3px; border: 1px; }');
            GM_addStyle('div.running { background-color: #0A0; }');
            GM_addStyle('div.thread404d { background-color: #A00; }');
            GM_addStyle('div.stopped { background-color: #000; }');

            this.link = document.createElement('a');
            this.link.id = 'updater_link';
            this.link.title = 'Ctrl-Click for Settings';
            this.link.addEventListener('click', callback, false);
            this.link.style.cursor = 'pointer';
            this.link.style.color = 'white';


            this.div = document.createElement('div');
            this.div.id = 'updater_control';
            this.div.appendChild(this.link);

            this.container_div = document.createElement('div');
            this.container_div.id = 'updater_container';
            this.container_div.appendChild(this.div);
            $X('/html/body').appendChild(this.container_div);

            this.set_stopped();
        },

        set_started : function() { 
            this.link.innerHTML = this.STARTED_LABEL;
            this.div.className = 'running';
        },

        set_stopped : function() { 
            this.link.innerHTML = this.STOPPED_LABEL;
            this.div.className = 'stopped';
        },

        set_died : function() {
            this.link.innerHTML = this.THREAD_DIED_LABEL;
            this.div.className = 'thread404d';
        }
    }

    // UTILITY FUNCTIONS

    // list nodes matching this expression, optionally relative to the node `root'
    function $x(xpath, root)
    {
        var doc = root ? root.evaluate ? root : root.ownerDocument : document, next;
        var got = doc.evaluate(xpath, root||doc, null, 0, null), result = [];

        switch (got.resultType) {
            case got.STRING_TYPE:
                return got.stringValue;
            case got.NUMBER_TYPE:
                return got.numberValue;
            case got.BOOLEAN_TYPE:
                return got.booleanValue;
            default:
                while ((next = got.iterateNext()))
                    result.push(next);
                return result;
        }
    }

    function $X(xpath, root)
    {
        var got = $x(xpath, root);
        return got instanceof Array ? got[0] : got;
    }

    function log_msg(msg)
    {
        DEBUG && console.log(msg);
    }

    function dom_from_hidden_iframe(url, callback/*( document )*/)
    {
        function loaded() {
            doc = iframe.contentDocument;
            iframe.removeEventListener('load', loaded, 0);
            doc.removeEventListener('DOMContentLoaded', loaded, 0);
            // avoid racing with GM's DOMContentLoaded callback
            setTimeout(function() { callback(doc); }, 10);
        };

        var iframe = null;

        if ( !(iframe = document.getElementById('updater_iframe')) ) {
            iframe = document.createElement('iframe');
            iframe.style.height = iframe.style.width = '0';
            iframe.style.visibility = 'hidden';
            iframe.style.position = 'absolute';
            iframe.id = 'updater_iframe';
            document.body.appendChild(iframe);
        }

        iframe.addEventListener('load', loaded, 0);
        log_msg('loading new content into iframe');
    
        try {
            iframe.src = url;
        } catch(e) {
            log_msg('Error in another script in the iframe');
        }
    }

    // RESOURCES 

    const prefs_panel_css = '#tu_prefs_title { color: #444; font-weight: bold;  background-color:#dedede; margin:0; padding:7; min-height:0; text-align: center; text-decoration:none;}\
select.tu_select { color:#333; font-size:100%; margin:1px 0; padding:1px 0 0; border-bottom:1px solid #ddd; border-left:1px solid #c3c3c3; border-right:1px solid #c3c3c3; border-top:1px solid #7c7c7c; }\
input.tu_checkbox { display:block; height:13px; line-height:1.4em; margin:6px 0 0 3px; width:13px; }\
label.choice { color:#444; display:block; font-size:100%; line-height:1.4em; margin:-1.55em 0 0 25px; padding:4px 0 5px; width:90%; }\
.tu_outerpair1 { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNC8yNS8wNk7vxRQAAAAgdEVYdFNvZnR3YXJlAE1hY3JvbWVkaWEgRmlyZXdvcmtzIE1Yu5EqJAAAAGdJREFUeJxNzksKAkEMhOGvHzqKC126c87g/e8kuFIYpt2kJYEfAlVJVcEZA3swd9BwQ0cN5hSMhhVLMpVk0vDECYckjknHA2+84kDqs3fc4wNs+OITkVvFFZdgiah/n4pj0IOWupQfFtoS9rTbP5UAAAAASUVORK5CYII%3D) right top no-repeat;}\
.tu_outerpair2 {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNC8yNS8wNk7vxRQAAAAgdEVYdFNvZnR3YXJlAE1hY3JvbWVkaWEgRmlyZXdvcmtzIE1Yu5EqJAAAAGZJREFUeJxNzsEKwkAMhOFvu2tVBI/iRfr+D6il2m69pJDAkIH5maTgihseeGHCE3eMFS10xgUjKlYsFUOo4RS+44dPRUlQwY4NM94HIO2Obwby7AlYMLcUbBH0+GcQ5qjNDWucLH966BpsOjDX/QAAAABJRU5ErkJggg%3D%3D) left bottom no-repeat; padding-top: 8px; padding-left: 8px; }\
.tu_shadowbox { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNC8yNS8wNk7vxRQAAAAgdEVYdFNvZnR3YXJlAE1hY3JvbWVkaWEgRmlyZXdvcmtzIE1Yu5EqJAAADUxJREFUeJzt3btO41AUQNGTUf6/4xcRBRWCgREPT5EYMkGiIdoRmbUky47SnHbr3mtvlmUZAACAU9hsNlczczszNzNzvb/fzszdzDz+OuNsAADAf0aAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAACc0vLVJUAAAIBTWr76U4AAAACntK52vB08vxMgAADAKb3MzOvsAuRThAgQAADglP7sr+f5CJH3CNluNpur880GAAD8cOt2q5fZhcfDzPyemaf973VFZJmZZTszt+eZEwAA+OHWrVXrdqs/s4uPu5m5n88R8radmZt+TgAA4AIcB8jz7KLjfnYR8jBH27G2M3PdzwkAAFyIw7devc4uOJ5mFx/rKsgaIIsVEAAA4DsO33L1Oh9nQdYQeZqDcyDOgAAAAN91uBVrjZDn/bU+v81+C9bdOSYEAAAuynGErG/G+uebINuZeTzLeAAAwKU5/gr68X3+AiuZhS0L93K2AAAAAElFTkSuQmCC) bottom right;\
}\
.tu_innerbox { position: relative; left: -8px; top: -8px; }\
.tu_shadowbox img {border: 10px solid #fff; vertical-align: bottom;}';

    const prefs_panel_html = '<div style="z-index:10;" class="tu_outerpair1"><div class="tu_outerpair2"><div class="tu_shadowbox"><div class="tu_innerbox">\
<table cellpadding="0" cellspacing="0" style="margin: 0; padding 0"><tr><td align="center">\
<div id="tu_form_container" style="background:#fff;border:1px solid #ccc;margin:0 auto;text-align:left;font-family:Lucida Grande, Tahoma, Arial, Verdana, sans-serif; font-size:small;">\
    <div id="tu_prefs_title">Settings</div>\
    <div style="margin:10px">\
                <span style="color:#444;margin:-1.55em 0 0 0; ">Check for new posts every </span><select class="element tu_select" id="poll_interval_select" name="poll_interval_select"> \
                    <option value="3" >3 seconds</option>\
                    <option value="5" >5 seconds</option>\
                    <option value="6" >6 seconds</option>\
                    <option value="7" >7 seconds</option>\
                    <option value="8" >8 seconds</option>\
                    <option value="9" >9 seconds</option>\
                    <option value="10" >10 seconds</option>\
                    <option value="15" >15 seconds</option>\
                    <option value="20" >20 seconds</option>\
                    <option value="30" >30 seconds</option>\
                    <option value="45" >45 seconds</option>\
                    <option value="60" >minute</option>\
                    <option value="120" >2 minutes</option>\
                    <option value="300" >5 minutes</option>\
                    <option value="600" >10 minutes</option>\
                    <option value="1800" >30 minutes</option>\
                    <option value="3600" >hour</option>\
                </select>\
            <span>\
            <div style="margin:15px;"></div>\
                <input id="auto_update_checkbox" name="auto_update_checkbox" class="element tu_checkbox" type="checkbox" value="" />\
                <label class="choice" for="auto_update_checkbox">Start updating when page loads</label>\
            </span>\
            <span>\
                <input id="disable_forms_checkbox" name="disable_forms_checkbox" class="element tu_checkbox" type="checkbox" value="1" />\
                <label class="choice" for="disable_forms_checkbox">Disable forms when thread 404s</label>\
            </span>\
            <span>\
                <input id="run_scripts_checkbox" name="run_scripts_checkbox" class="element tu_checkbox" type="checkbox" value="1" />\
                <label class="choice" for="run_scripts_checkbox">Run scripts on new posts</label>\
            </span>\
            </div>\
           <div style="margin: 5px" align="center">\
            <input id="tu_updater_prefs_submit" class="button_text" type="button" name="submit" value="Save" />\
            <input id="tu_updater_prefs_cancel" class="button_text" type="button" name="cancel" value="Cancel" />\
            </div>\
</div>\
</td></tr></table>\
</div></div></div></div>';

    // SCRIPT ENTRY POINT
    window.addEventListener("load", updater.init(), false);
})();