User:Phlip/Greasemonkey

From Homestar Runner Wiki

(Difference between revisions)
Jump to: navigation, search
(Disable flipper; add @grant metadata for newer GM versions)
(Version 4.0 release!)
Line 1: Line 1:
-
// If you want to install this script, then (if you don't have them already) you'll need to install [http://www.mozilla.org/firefox/ Firefox] and [https://addons.mozilla.org/en-US/firefox/addon/748 Greasemonkey], then restart Firefox and return to this page.<br>
+
/*
-
// Then, just click on <span class="plainlinks">[http://www.hrwiki.org/w/index.php?title=User:Phlip/Greasemonkey&action=raw&ctype=text/javascript&fakeextension=.user.js this link]</span> to install the script.<br>
+
==Installation instructions==
-
// To upgrade a new version when it's updated, just click the install link again &ndash; it'll automagically replace the old version. If the option is enabled, the script will automatically check for updates for you. <pre>
+
===Firefox===
 +
If you don't have it already, you'll need to install [https://addons.mozilla.org/en-US/firefox/addon/748 Greasemonkey], then restart Firefox and return to this page.
 +
 
 +
Then, just click on <span class="plainlinks">[{{fullurl:{{FULLPAGENAME}}|action=raw&ctype=text/javascript&fakeextension=.user.js}} this link]</span> to install the script.
 +
 
 +
To upgrade a new version when it's updated, just click the install link again &ndash; it'll automagically replace the old version. If the option is enabled, the script will automatically check for updates for you.
 +
 
 +
===Chrome===
 +
This script can be installed as [https://chrome.google.com/webstore/detail/homestar-all-in-one/ekecfcebbojjfaiendgjgcadampmppdb an extension from the Chrome Web Store]. Chrome will then automatically keep it up-to-date for you via the normal update process.
 +
 
 +
==Script code==
 +
<pre>*/
// Homestar All-In-One
// Homestar All-In-One
-
// version 3.2
+
// version 4.0
-
// 2006-09-27
+
// 2014-10-08
-
// Copyright (c) Phillip Bradbury, Loafing, T Rice, Jesse Ruderman
+
// Copyright (c) Phillip Bradbury, Loafing
-
//
+
-
// A combination of several useful scripts for Homestar Runner cartoons:
+
-
//  Homestar-Fullon, a greasemonkey script for making H*R cartoons fullscreen.
+
-
//  Seek Bar, a bookmarklet that adds a progress bar to flash cartoons
+
-
//  (with modifications).
+
-
//  Previous/Next buttons for Strong Bad Emails, TGS, Marzipan's
+
-
//  Answering Machine, Biz Cas Fri, Puppet Jams and main pages.
+
-
//  An HRWiki link on all pages.
+
-
//  The ability, if you turn on the "fullscreen" option, to keep the actual
+
-
//  stage the same size (so you can see "outside the frame").
+
-
//  Turning everything upside-down, like on April Fools Day, 2006.
+
-
//  A plain-HTML navbar, to replace the Flash one. This is mostly for me
+
-
//  as the navbar doesn't work right on my computer (font problems).
+
-
//  It could also be useful for others, letting you middle-click the navbar
+
-
//  to open things in new tabs.
+
-
//  Loading subtitles from the wiki and displaying them beneath the toon.
+
-
// All of these can easily be turned on or off with the "Preferences" box
+
-
//  in the top left.
+
-
//
+
-
// Released under the GPL.
+
-
//
+
-
// Homestar-Fullon written by T Rice <timgm@bcheck.net> and is
+
-
//  released under the GPL.
+
-
//  http://dana.ucc.nau.edu/~tsr22/apps/greasemonkey/
+
-
//
+
-
// Seek bar written by Jesse Ruderman <jruderman@hmc.edu> and is
+
-
//  distributed with permission ("You may modify and/or distribute
+
-
//  up to three bookmarklets from this site in any way you want.")
+
-
//  http://www.squarefree.com/bookmarklets/
+
-
//  Originally it was just the Pause button and the seek bar,
+
-
//  I modified it to add a frame counter, frame step buttons and zoom buttons.
+
-
//  I also modified it so the Pause button automatically updates when the flash
+
-
//  movie pauses itself (eg at the end of the toon).
+
-
//
+
-
// Previous/Next buttons written by Phillip Bradbury, but inspired by
+
-
//  StrongBad Emails: Prev & Next from http://userscripts.org/scripts/show/1015
+
-
//
+
-
// HRWiki link also written by Phillip Bradbury, but inspired by an attempt
+
-
//  by Tom Preuss to do the same thing - except it did the translation from
+
-
//  URL to Wiki page name in the script, this does it at the HRWiki server
+
-
//  so that when a new toon comes out, the script doesn't need to be changed.
+
-
//  http://www.hrwiki.org/wiki/User:Tom/Greasemonkey_Script
+
-
//
+
-
// Subtitles written in collaboration with "Loafing"
+
-
//  http://www.hrwiki.org/wiki/User:Loafing
+
-
//
+
-
// Direct any comments to
+
-
//  http://www.hrwiki.org/wiki/User_talk:Phlip/Greasemonkey
+
-
//
+
-
// --------------------------------------------------------------------
+
-
//
+
-
// WARNING: This script explicitly avoids use of one of Greasemonkey's security
+
-
//  features. THIS SCRIPT SHOULD NOT BE USED on ANY page where you do not trust
+
-
//  the page writer. Not that it would make sense to anyway, given it's rather
+
-
//  Homestar-specific. Your use of this script is stating that you trust
+
-
//  The Brothers Chaps to not insert malicious code into the Homestar Runner site,
+
-
//  and the HRWiki admins to not put any on the mirror.
+
-
//
+
-
// One of the security features of Greasemonkey, used to plug its holes in
+
-
//  previous versions, is Mozilla's XPCNativeWrapper, which is used to ensure
+
-
//  that you're calling the real functions of objects on the page, and not
+
-
//  weird and potentially hazardous ones written by the page designer. However
+
-
//  this also blocks functions like flashmovie.CurrentFrame() which are needed
+
-
//  by the seek bar. Thus to make the seek bar work, I needed to turn this off.
+
-
//
+
-
// --------------------------------------------------------------------
+
-
//
+
-
// This is a Greasemonkey user script.
+
-
//
+
-
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
+
-
// Then restart Firefox and revisit this script.
+
-
//
+
-
// To uninstall, go to Tools/Manage User Scripts,
+
-
// select "Homestar All-In-One", and click Uninstall.
+
//
//
// --------------------------------------------------------------------
// --------------------------------------------------------------------
Line 103: Line 40:
// @name          Homestar All-In-One
// @name          Homestar All-In-One
// @namespace    http://www.hrwiki.org/
// @namespace    http://www.hrwiki.org/
-
// @description   Combination of many Homestar Runner scripts. Version 3.2.
+
// @description  Combination of many Homestar Runner scripts. Version 4.0.
-
// @include       http://homestarrunner.com/*
+
// @version       4.0.63
-
// @include      http://www.homestarrunner.com/*
+
// @downloadURL  http://www.hrwiki.org/w/index.php?title=User:Phlip/Greasemonkey&action=raw&ctype=text/javascript&fakeextension=.user.js
-
// @include      http://podstar.homestarrunner.com/*
+
// @icon          http://www.hrwiki.org/w/images/thumb/1/1b/logo.png/32px-logo.png
-
// @include      http://videlectrix.com/*
+
// @match        http://homestarrunner.com/*
-
// @include      http://www.videlectrix.com/*
+
// @match        http://www.homestarrunner.com/*
-
// @include      http://hrwiki.org/mirror/*
+
// @match        http://podstar.homestarrunner.com/*
-
// @include      http://www.hrwiki.org/mirror/*
+
// @match        http://videlectrix.com/*
-
// @include      https://secure.homestarrunner.com/heythanks.html*
+
// @match        http://www.videlectrix.com/*
 +
// @match        http://hrwiki.org/mirror/*
 +
// @match        http://www.hrwiki.org/mirror/*
 +
// @match        https://secure.homestarrunner.com/heythanks.html*
// @grant        GM_getValue
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        GM_xmlhttpRequest
-
// @grant        GM_addStyle
 
-
// @grant        unsafeWindow
 
// ==/UserScript==
// ==/UserScript==
-
// Returned by Special:Getversion
+
(function(){
-
// <versionstring>3.2.11=http://www.hrwiki.org/w/index.php?title=User:Phlip/Greasemonkey&action=raw&ctype=text/javascript&fakeextension=.user.js</versionstring>
+
function Utils()
-
var currentversion = [3,2,11];
+
{
-
 
+
this.guessisplaying = {
-
// Podstar/Videlectrix (stock IIS), HRWiki and stock Apache error pages, respectively. Don't do anything on those pages.
+
lastframe: -1,
-
if (document.title != "The page cannot be found" && document.title != "Homestar Runner Wiki - 404 Not Found" && document.title != "404 Not Found")
+
lastframeat: new Date(),
-
{
+
state: true
-
var whichsite = 0;
+
};
-
if (location.hostname.indexOf("podstar") >= 0) whichsite = 1;
+
}
-
if (location.hostname.indexOf("videlectrix") >= 0) whichsite = 2;
+
-
if (location.pathname.indexOf("/mirror/") >= 0) whichsite = 3;
+
-
// icons, as Base64-encoded PNG files.
+
// Taken from http://diveintogreasemonkey.org/patterns/add-css.html
-
// the hrwiki one used to hotlink http://www.hrwiki.org/favicon.ico but now
+
Utils.prototype.addGlobalStyle = function addGlobalStyle(css)
-
// use "data:" to avoid bandwidth-leeching (even this minor)
+
{
-
var image_hrwiki = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQ" +
+
var head, style;
-
"CAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1QTFR" +
+
head = document.getElementsByTagName('head')[0];
-
"F////2wAzgZDJAiGNAB6Lenp6ABCEABKFAAyDjp3O8gAAipjLlaPPFUixAB6OAA6C/f//fY" +
+
if (!head) return;
-
"3JABaIhJXK///50gAn///4CymXyQAaAA+DOFCm1QAmDiuX//zvnV2IfI3IQ0h7ABSFN0+qZ" +
+
style = document.createElement('style');
-
"Xm9ABSG9PTxABiK2wAkuQAdSWW5WGu4cILCgYy209PZGRdjABeH0AALDiyYASGOhJTL2bi8" +
+
style.type = 'text/css';
-
"k5OTzgAj1QAdLkilAAiDABiQIiCBzwAbyAAk//31ABSO0gAXDB95c5nZDAxeoRhHOVCp7u3" +
+
style.appendChild(document.createTextNode(css));
-
"lfx1W1LrCxQYtwwApQVitwAQpJj2bAAyFKSODfI3GYna86urqysfL9fT0NUyXMDGGNk6cxg" +
+
head.appendChild(style);
-
"ASy9rkAB2OQ1qzTmOzu8Pa4d/b+v//58zRFEqw09XR25yrIjyh9P//g5PLAAN+foy/uRY92" +
+
};
-
"treh6neAASDXXC9jJvKjJvL6enpiJfKDzejNk6r2wE1N0+rABKEAB6KxAAn0tPWyQAZRFuv" +
+
-
"XXfB/f392AAgKiib2QAyABaJhJTH2XeNEy+ZzgAwBiSRKUOlgI27urrP7t/iCghS0AAfk3S" +
+
-
"oyBc+iChf3vH1VWq426GvgI/HizduboDCPEOXABCDSmu/DyeD///6P2K4OUJ/HByRlKHOAB" +
+
-
"+O8AAA2QI1hZPHg5TI9PT0ABuJiZjM1tbdf43CzgApAB2We4vD7e3rwgAseInHAyGWi5rOU" +
+
-
"me3hIuqFTGaWG25dojDd5LQ5Ki1AAyMASCNcYHEAyKOABqLACSWHDeR+vr6uwAiIyBjipnJ" +
+
-
"1AIyjZvMmJyaITylAByMAB2L5wAlHDeeCCaUcHCjWGy4wBQy/7AMAgAAARFJREFUeNpiONF" +
+
-
"euLWjfL4RM1/R4tXyx5kZdrNHZ8za5DjT3n/KimUtLDsZhPbFJilY14d5cC3o1dRnaWPILa" +
+
-
"ucozf3DAND3DmnxqWnmRjyd+046NzJwGB6dMJ6xVNHJjPIrGvq1mVgYGBlZQg9xjlxD4MKh" +
+
-
"2+wKgMILLET00mPYmCqFpctYIAAEamz3AzaG4TdgmohAllpgsoMbBw5y9fshwiEW0qyM7jG" +
+
-
"bIlMjWcQCOlKMKnR8rZgyDOe3e95oErCJqLOPDGQ8xBDCv8qF9tWRkb1SStPTvNTU2JgK83" +
+
-
"OrDjMaKbB0Gwgt23zdIap83h9vBZKJ4MMdZ/Bs5EhwHBvz9qSBoftDAx9olbFiwACDABkK1" +
+
-
"N43Z86KwAAAABJRU5ErkJggg==";
+
-
var image_prefs = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQC" +
+
-
"AMAAAAoLQ9TAAAAllBMVEUAGQASEhIfHx8fJy8pKSk2NjZBQUFJR0ZQUE9RUVFSUlJNX3No" +
+
-
"aGhsaWdramlycG1meY98fHx+fn5wgpV0iqKKh4R4jaR9jJx8kad9kad/mbONmaWEnrmEnrq" +
+
-
"koZy3t7fIx8bKyMHT0c3S0dDU09DV1NPP1t3W1dXY2Njb2tfe29bf3tzj4uHr6+js6+r39/" +
+
-
"f5+PgAAABrL3yvAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTA" +
+
-
"QCanBgAAAAHdElNRQfWBRoFKh31UQ8DAAAAgUlEQVQY022OxxLCMAwFRSc4BEIPJZQQ08v+" +
+
-
"/8+RsTExDDpIe3ijfSJ/hx9g62Dt4GaAI+8YT0t27+BxxvvE/no5pYT10lGFrE34Ja40W3g" +
+
-
"1oMGmW7YZ6hnCYexKTPVkXivuvWe1Cz1aKqPNI3N0slI2TNYZiARJX30qERc7wBPKC4WRDz" +
+
-
"WdWHfmAAAAAElFTkSuQmCC";
+
-
var image_close = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQC" +
+
-
"AQAAAC1+jfqAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfW" +
+
-
"BRkTNhxuPxLkAAAAHXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBUaGUgR0lNUO9kJW4AAAE" +
+
-
"KSURBVCjPhdGxSgNBFAXQMzpgYWwsLEQUDBJBQgqFIChZEPR7/DA/QCGQTgQtJE1ENoWohY" +
+
-
"UgbGKQyFjErNv52nObe19wqGWg7z0l5YVgVdOu+wUt507tqIVQ4Zodp861ooELe15M5KFI6" +
+
-
"Zfr9u25MIj6Jl4cmSIPBWrq2o5cufO4aOJDYSozNTa2pK4t03PtwUdMKRRykAmW0dTRcyNX" +
+
-
"pBQpI8GJDTR050zkNzK0bMMZLvUNZ8yCfy6Wvbc1NVyi4dloXjqWvds6uvp41pFmpVOKJWd" +
+
-
"6bgwxkmTMIotWKpwrfBkZl7uMonUHf5wSlV2+fUZrjnXdzrmyy7djD8GWTW9e51z557o1Tz" +
+
-
"85FH/WkOkaHQAAAABJRU5ErkJggg==";
+
-
var image_update = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABG" +
+
-
"CAMAAABG8BK2AAAC8VBMVEUAAAD/AAD+AQH/AQH/AgL+AwP/AwP+BAT/BAT/BQX+Bgb/Bgb" +
+
-
"/Bwf+CAj/CAj/CQn/Cgr+Cwv/Cwv+DAz/DAz/DQ3/Dg7+Dw//Dw//EBD+ERH/ERH/EhL/Ex" +
+
-
"P+FBT/FRX/Fhb/Fxf+GBj/GBj/GRn/Ghr/Gxv/HBz/HR3/Hh7/Hx//ICD+ISH/ISH/IiL/I" +
+
-
"yP/JCT/JSX/Jib/Jyf/KSn/Kyv/LCz/LS3/Ly//MDD/MTH+MjL/MjL/MzP/NDT/NTX/Njb+" +
+
-
"Nzf/Nzf/ODj+OTn/OTn/Ojr/PDz/Pj7/Pz//QUH/QkL+Q0P/RUX/Rkb/R0f/SEj/SUn/Skr" +
+
-
"/S0v/TEz/TU3/Tk7/T0//UFD/UVH/UlL/VFT/VVX/Vlb/WFj/WVn/Wlr/W1v/XFz/XV3/Xl" +
+
-
"7/X1//YGD/YWH/YmL/Y2P/ZWX/Zmb/Z2f/aGj/aWn/amr/a2v/bGz/bW3/bm7/b2//cHD/c" +
+
-
"XH/cnL/dHT/dnb/d3f/eHj/eXn/e3v/fX3/fn7/f3//gID/gYH/goL/g4P/hIT/hob/h4f/" +
+
-
"iIj/iYn/ior/i4v/jIz/jY3/jo7+kJD/kJD/kZH/kpL/lJT/lpb/l5f/mJj/mZn/mpr/m5v" +
+
-
"/nJz/nZ3/n5//oKD/oaH/oqL/o6P/pqb/p6f/qKj/qan/qqr/q6v/rKz/ra3/r6//sLD/sb" +
+
-
"H/srL/s7P/tLT/tbX/trb/t7f/uLj/urr/u7v/vLz/vb3/vr7/v7//wMD/wcH/wsL/w8P/x" +
+
-
"MT/xcX/xsb+x8f/x8f/yMj/ycn/ysr/y8v/zMz/zc3/zs7/z8//0ND/0dH/0tL/09P+1NT/" +
+
-
"1NT/1tb/19f+2Nj/2Nj/2dn/29v/3Nz/3d3/39//4OD/4eH/4uL/4+P/5OT/5eX/5ub/5+f" +
+
-
"/6Oj/6en/6ur/6+v/7Oz/7e3/7u7/7+/+8PD/8fH/8vL/8/P/9PT/9fX/9vb/9/f/+Pj/+f" +
+
-
"n/+vr/+/v//Pz//f3+/v7//v7////+AAA5GkRyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFH" +
+
-
"UgAAAAJcEhZcwAADzoAAA+IAUHKF/gAAAAHdElNRQfXCRYICgxGxxkcAAAEL0lEQVRYw63X" +
+
-
"e1wURRwA8Pm1G0KcHdGBkKAYjxC0yLJITUl7cr7RUjAos4AuraCH2pWCVlZaRpD5AEXDwAe" +
+
-
"mQRFdmgQeCgWUPKTk4JJHomAq5PBXu/fC2zt2Z7fdf+Y38/nc9zPz+83M7iEQ9VBDjCNxSt" +
+
-
"KGG5xJSBSjWPV+c3m0nxNFDEP/XBf3ZkPLuvGOigiG2oLrhyvVJX26abdzFXKGWtrUPRXA5" +
+
-
"aasRjyD5ijkzJjd/2aMNkXqhCiKoxAzU9bg3nmDdXe1V4iZJIzTBnvhH9xrpxAzKbj1cYDY" +
+
-
"2Ww8AMuOL7NTiBg6koZX2rruhFhjLJsVP5iv8bFTSBj6xxo/CHqxXftwYxFTKwhY/aj9iog" +
+
-
"YOgfrRwCM/vr0qXOmpUQ0pXAVYYZa19tuymc8xqvY1u0nnOXCUQQZ6vnf/p5jiibpqgOYxq" +
+
-
"cctwRwFUEmqrD/1VvMYWppjGrUE7/ghkAHRYhxy8QdG6x79u2DBbru/mLHuQgyr+H9HYatC" +
+
-
"kvv2U3Hdmv9nSgCzKyW/MnBpW1HvSz9gRHsMUAiGe/1OA5A9XlX/TQv7pkmZtzB/Y1UNvBM" +
+
-
"P2NIDOVTeJjpT49lJNOjXHHq/Mb7eRQe5pnavAm2W3jRt33Fjw2t8C3qG3z8AWvsOnFba6Y" +
+
-
"bNZTCw9yYYsg2qkfabqpZPkPOhXc2ET2bk3FpAvDXSJBxbSsZ29O1fz2BwrtvVlzSNb60vX" +
+
-
"5ruEJI4WVUxxoTISSp46hWJaA4MtSw2dlVRXlq5jy6H65hRzw+XasSUBwYOu2rC4YO/bmWM" +
+
-
"0EesPRQsGnsZiGFy9AlVbmRzG9dQrMr1NSEE1OEs+uEoXbivUGW+EBrIGh3KYkUDuP7bu3J" +
+
-
"PZ7mOKSsgFr4ggeRwmE87/FfW9Pqbb74vqgOg3Ay5XqGmpRe9+U7vsvL/0oybZRE9rIhU65" +
+
-
"j6Az9tZL0ffn3jdtyadNdzEAaTiZVBhn6O9y+YBxAUw64fnR+hxoUVXg5qWJjqBzcFsbutY" +
+
-
"rDwwBWHvr9rUrc5E+q2JjQExceYduHruQqBgAe3NhvLBhDrNiYyD79agXzTtXg98xs9CIvc" +
+
-
"sXGRPzQc7F68R23NlxZQtk+pZEohnoyBuDuqI9P99Y244rhJPeLMyZQ90exJgyUU/dgfPEp" +
+
-
"KYp5UeHak83fT2Tf0pXX8hMlKMj6Znu57HIMcwjmZmCcI15BVICvWfLK7ExmKnzbPH3fJ6I" +
+
-
"V9NzZLG/LKo4Y49kmOHKUaAVB2T8h1pzGGMeLrrSVmX71iPUzaOafMyRk15Lios4EixONl0" +
+
-
"hU2ErldW82O5rOORIVU8ELDZ8xDq2sPRsmUTHvm8LuyvjFr/+Kc30kKpbtt6OuC+OefSOlK" +
+
-
"rYTHqf5MNVPsoLs/2QjGZj/oSB5FCSPguRRkDwKkkdB8ihIHgXJoyB5FCSPguRRkDzKf7Z6" +
+
-
"NUd33kmjAAAAAElFTkSuQmCC";
+
-
settingnames = [
+
// Based on http://userscripts.org/topics/41177
-
{name: 'resize',    title: 'Resize flash to full-screen',            tooltip: 'Resizes the toon so it fills the entire window',                              def: 1},
+
Utils.prototype.useGMFunctions = function useGMFunctions()
-
{name: 'noscale',  title: ' Show outside-the-frame action',        tooltip: 'Lets you see what\'s happening beyond the frames',                            def: 0},
+
{
-
// {name: 'showmenu',  title: 'Enable right-click menu',                tooltip: 'Enables the right-click context menu',                                        def: 1},
+
// We can't just test if GM_getValue exists, because in Chrome they do exist
-
{name: 'seekbar',  title: 'Show seek bar',                          tooltip: 'Lets you fast forward and rewind',                                            def: 1},
+
// but they don't actually do anything, just report failure to console.log
-
{name: 'frames',    title: ' Show frame counter on seek bar',        tooltip: 'Shows you exactly where you are',                                            def: 1},
+
-
{name: 'hrwiki',    title: 'Add HRWiki link',                        tooltip: 'Adds a link to the appropriate page on the Homestar Runner Wiki',            def: 1},
+
-
{name: 'prevnext',  title: 'Show previous/next buttons',            tooltip: 'Lets you easily move through SBEmails, TGS, etc',                            def: 1},
+
-
{name: 'checknext', title: ' Check if next exists',                  tooltip: 'Doesn\'t add a "next" link on the latest SBEmail, etc',                      def: 0},
+
-
//{name: 'flipper',  title: 'Turn everything upside-down',            tooltip: 'Relive the mania of inverted April Fools Day Homestar Runner',                def: 0},
+
-
{name: 'navbar',    title: 'Plain HTML navbar',                      tooltip: 'Replaces the flash navbar with normal links... so you can open in tabs, etc', def: 0},
+
-
{name: 'randot',    title: ' Include Big Toons in rando',            tooltip: 'Limit the "rando" function to what you like to watch',                        def: 1},
+
-
{name: 'randosh',  title: ' Include Shorts in rando',              tooltip: 'Limit the "rando" function to what you like to watch',                        def: 1},
+
-
{name: 'randoho',  title: ' Include Holiday toons in rando',        tooltip: 'Limit the "rando" function to what you like to watch',                        def: 1},
+
-
{name: 'randop',    title: ' Include Puppet Stuff in rando',        tooltip: 'Limit the "rando" function to what you like to watch',                        def: 1},
+
-
{name: 'randoteh',  title: ' Include Powered by The Cheat in rando', tooltip: 'Limit the "rando" function to what you like to watch',                        def: 1},
+
-
{name: 'randosb',  title: ' Include Strong Bad Emails in rando',    tooltip: 'Limit the "rando" function to what you like to watch',                        def: 1},
+
-
{name: 'randoam',  title: ' Include Answering Machine in rando',    tooltip: 'Limit the "rando" function to what you like to watch',                        def: 1},
+
-
{name: 'randotgs',  title: ' Include Teen Girl Squad in rando',      tooltip: 'Limit the "rando" function to what you like to watch',                        def: 1},
+
-
{name: 'subtitles', title: 'Show subtitles',                        tooltip: 'Shows subtitles or captions below the toon, if any are available',           def: 0},
+
-
{name: 'captions',  title: ' Show captions',                        tooltip: 'Include sound effects in the subtitles',                                      def: 1},
+
-
{name: 'colours',  title: ' Use colours',                          tooltip: 'Distinguish characters by colour effects (turn off if colourblind)',          def: 1},
+
-
{name: 'testsubs',  title: ' Test subtitles script',                tooltip: 'Use this to test a subtitles script (copy/paste into a text box)',           def: 0},
+
-
{name: 'updates',  title: 'Check for updates',                      tooltip: 'Regularly check for updates to the All-in-one script',                        def: 1}
+
-
];
+
-
settings = {};
+
-
for (i = 0; i < settingnames.length; i++)
+
-
settings[settingnames[i].name] = GM_getValue(settingnames[i].name, settingnames[i].def) != 0;
+
-
settings['language'] = GM_getValue('language', 'en');
+
-
defaultxmlfile = escape('<?xml version="1.0" encoding="utf-8"?>\n<transcript xml:lang="en-us">\n<line start="" end="" speaker=""></line>\n</transcript>');
+
-
settings['testsubsdata'] = unescape(GM_getValue('testsubsdata', defaultxmlfile));
+
-
settings['names'] = GM_getValue('names', 0);
+
-
extrasettings = [];
+
-
addsettingsbox();
+
// We don't want it to actually write anything to console.log, though, so
 +
// let's stop that
 +
var log = console.log;
 +
console.log = function log(){};
 +
var gmstorage = typeof(GM_getValue) == "function" && GM_getValue("this-value-doesn't-exist-I-promise", true);
 +
console.log = log;
-
document.body.style.margin = "0px";
+
return gmstorage;
 +
};
 +
// Only really need to do this once...
 +
Utils.prototype.useGMFunctions = Utils.prototype.useGMFunctions();
 +
Utils.prototype.getPref = function getPref(key, def)
 +
{
 +
// Have to do it like this instead of like "if(window.GM_getValue)"
 +
// because apparently this function isn't actually on "window", and I don't
 +
// know where it actually lives...
 +
if (this.useGMFunctions)
 +
return GM_getValue(key, def);
 +
else if (window.localStorage)
 +
{
 +
var value = localStorage.getItem("hr-allinone-" + key);
 +
if (value === null)
 +
return def;
 +
var type = value[0];
 +
value = value.substring(1);
 +
if (type == 'b')
 +
return Number(value) != 0;
 +
else if (type == 'n')
 +
return Number(value);
 +
else
 +
return value;
 +
}
 +
else
 +
{
 +
alert("Homestar Runner All-in-one is not supported on this platform");
 +
throw "Couldn't find a local storage provider";
 +
}
 +
};
 +
Utils.prototype.setPref = function setPref(key, value)
 +
{
 +
if (this.useGMFunctions)
 +
GM_setValue(key, value);
 +
else if (window.localStorage)
 +
{
 +
if (typeof(value) == "string")
 +
localStorage.setItem("hr-allinone-" + key, "s" + value);
 +
else if (typeof(value) == "number")
 +
localStorage.setItem("hr-allinone-" + key, "n" + value);
 +
else if (typeof(value) == "boolean")
 +
localStorage.setItem("hr-allinone-" + key, "b" + (value ? 1 : 0));
 +
else
 +
throw "Unexpected type for storage: " + typeof(value);
 +
}
 +
else
 +
{
 +
alert("Homestar Runner All-in-one is not supported on this platform");
 +
throw "Couldn't find a local storage provider";
 +
}
 +
};
-
// find flash objects
+
Utils.prototype.downloadPage = function downloadPage(url, loadcb, errorcb, method)
-
switch (whichsite)
+
{
{
-
case 0: // www.homestarrunner.com
+
if (!method)
-
var objs = document.getElementsByTagName("EMBED");
+
method = 'GET';
-
if (objs && objs.length >= 2)
+
if (typeof GM_xmlhttpRequest == 'function')
 +
{
 +
var opts = {
 +
method: method,
 +
url: url,
 +
onload: function onload(res) {loadcb(res.responseText, res.status, res.statusText, res.responseHeaders);}
 +
};
 +
if (errorcb)
 +
opts.onerror = function onerror(res) {errorcb(res.status, res.statusText, res.responseHeaders);};
 +
GM_xmlhttpRequest(opts);
 +
}
 +
else
 +
{
 +
var xhr = new XMLHttpRequest();
 +
xhr.onload = function onload() {loadcb(xhr.responseText, xhr.status, xhr.statusText, xhr.getAllResponseHeaders());};
 +
if (errorcb)
 +
xhr.onerror = function onerror() {errorcb(xhr.status, xhr.statusText, xhr.getAllResponseHeaders());};
 +
xhr.open(method, url);
 +
xhr.send();
 +
}
 +
};
 +
Utils.prototype.buildWikiUrl = function buildWikiUrl(page)
 +
{
 +
var url = escape(page.replace(/ /g, '_'));
 +
return "http://www.hrwiki.org/w/index.php?title=" + url + "&action=raw&source=allinone&cachedodge=" + this.getPref('cachedodge', 0);
 +
};
 +
Utils.prototype.downloadWiki = function downloadWiki(page, loadcb, errorcb)
 +
{
 +
this.downloadPage(this.buildWikiUrl(page), this.wikiPageDownloaded.bind(this, loadcb, errorcb, 0), errorcb);
 +
};
 +
Utils.prototype.wikiPageDownloaded = function wikiPageDownloaded(loadcb, errorcb, timesredirected, text, status, statusText)
 +
{
 +
// check for redirects
 +
var matches = text.match(/^\s*#\s*REDIRECT\s*\[\[(.*)\]\]/i);
 +
if (matches)
 +
{
 +
if (timesredirected >= 3) // follow 3 redirects, but no more
{
{
-
var flashmovie = objs[0];
+
errorcb(500, "Too many redirects");
-
var navbar = objs[1];
+
return;
}
}
-
else if (objs && objs.length >= 1)
+
// Get the page name out of the redirect text
 +
text = matches[1];
 +
if ((matches = text.match(/^(.*)\|/)))
 +
text = matches[1];
 +
if ((matches = text.match(/^(.*)\#/)))
 +
text = matches[1];
 +
text = text.replace(/^\s+|\s+$/g, '');
 +
this.downloadPage(this.buildWikiUrl(text), this.wikiPageDownloaded.bind(this, loadcb, errorcb, timesredirected + 1), errorcb);
 +
return;
 +
}
 +
loadcb(text, status, statusText);
 +
};
 +
Utils.prototype.downloadWikiXML = function downloadWikiXML(page, loadcb, errorcb)
 +
{
 +
this.downloadWiki(page, this.wikiXMLDownloaded.bind(this, loadcb, errorcb), errorcb);
 +
};
 +
Utils.prototype.wikiXMLDownloaded = function wikiXMLDownloaded(loadcb, errorcb, text, status, statusText)
 +
{
 +
// strip various things - templates and <pre> tags for wiki formatting, and <noinclude> sections...
 +
// <includeonly> tags are stripped (but their contents kept) for consistency.
 +
text = text.replace(/{{.*?}}/g, "");
 +
text = text.replace(/<\/?pre[^>]*>/g, "");
 +
text = text.replace(/<noinclude[^>]*>.*?<\/noinclude[^>]*>/g, "");
 +
text = text.replace(/<includeonly[^>]*>(.*?)<\/includeonly[^>]*>/g, "$1");
 +
text = text.replace(/^\s+/g, "");
 +
 +
var parser = new DOMParser();
 +
try
 +
{
 +
var doc = parser.parseFromString(text, "application/xml");
 +
}
 +
catch (e)
 +
{
 +
errorcb(500, "Error in XML:\n" + e.toString());
 +
return;
 +
}
 +
// check if returned document is an error message
 +
if (doc.getElementsByTagName('parsererror').length > 0)
 +
{
 +
var error = doc.getElementsByTagName('parsererror')[0];
 +
if (error.firstChild.nodeType == doc.TEXT_NODE && error.lastChild.nodeType == doc.ELEMENT_NODE && error.lastChild.nodeName == "sourcetext")
{
{
-
var flashmovie = objs[0];
+
// Firefox's errors look like this:
-
var navbar = false;
+
// <parsererror>Error details<sourcetext>Source text</sourcetext></parsererror>
 +
errorcb(500,
 +
error.firstChild.nodeValue.replace(/Location: .*\n/, "") + "\n" +
 +
doc.documentElement.lastChild.textContent
 +
);
}
}
-
else
+
else if (error.getElementsByTagName('div').length > 0)
{
{
-
var flashmovie = false;
+
// Chrome's errors look like this:
-
var navbar = false;
+
// <someRoot><parsererror style="..."><h3>Generic error message</h3><div style="...">Error details</div><h3>Generic footer</h3><attempted parsing of page/></someRoot>
-
}
+
errorcb(500,
-
if (!flashmovie)
+
"Error in XML:\n" +
-
{
+
error.getElementsByTagName('div')[0].textContent
-
objs = document.getElementsByTagName("OBJECT");
+
);
-
if (objs && objs.length >= 1)
+
-
var flashmovie = objs[0];
+
}
}
-
break;
 
-
case 1: // podstar.homestarrunner.com
 
-
var objs = document.getElementsByTagName("EMBED");
 
-
var flashmovie = false;
 
-
if (objs && objs.length >= 1)
 
-
var navbar = objs[0];
 
else
else
-
var navbar = false;
 
-
break;
 
-
case 2: // videlectrix
 
-
var objs = document.getElementsByTagName("EMBED");
 
-
var navbar = false;
 
-
if (objs && objs.length >= 1)
 
-
var flashmovie = objs[0];
 
-
else
 
-
var flashmovie = false;
 
-
settings.navbar = false;
 
-
break;
 
-
case 3: // mirror
 
-
var objs = document.getElementsByTagName("EMBED");
 
-
var flashmovie = false;
 
-
if (objs && objs.length >= 1)
 
-
var flashmovie = objs[0];
 
-
if (!flashmovie)
 
{
{
-
objs = document.getElementsByTagName("OBJECT");
+
// Try to at least return something
-
if (objs && objs.length >= 1)
+
errorcb(500,
-
var flashmovie = objs[0];
+
"Error in XML:\n" +
 +
error.textContent
 +
);
}
}
-
navbar = document.getElementById('navbar');
+
return;
-
if (!navbar)
+
}
-
settings.navbar = false;
+
loadcb(doc, status, statusText);
-
var flashcontainer = document.getElementById('flash');
+
};
-
if (flashcontainer)
+
-
flashcontainer.style.width = "auto";
+
// Documentation for the Flash interface is really lacking...
-
break;
+
// Adobe removed the docs from their website.
-
}
+
// Luckily, the Wayback Machine captures all
-
if (flashmovie)
+
// http://web.archive.org/web/20100710000820/http://www.adobe.com/support/flash/publishexport/scriptingwithflash/scriptingwithflash_03.html
 +
// http://web.archive.org/web/20090210205955/http://www.adobe.com/support/flash/publishexport/scriptingwithflash/scriptingwithflash_04.html
 +
 +
Utils.prototype.currentFrame = function currentFrame(flashmovie)
{
{
-
//expose Flash plugin-added methods
+
if (!flashmovie)
-
flashmovie = flashmovie.wrappedJSObject;
+
flashmovie = globals.flashmovie;
-
+
if (!flashmovie)
-
// confirm that this is really a flash file
+
return false;
-
// and not (for example) the embedded background sound on SB's website
+
-
if (flashmovie.nodeName.toLowerCase() == "object")
+
var a;
 +
if (flashmovie === globals.flashmovie && globals.is_puppets)
{
{
-
var src = flashmovie.getAttribute('src');
+
if (!flashmovie.TCurrentFrame)
-
if (src)
+
return -1;
 +
a = flashmovie.TCurrentFrame("/videoplayer");
 +
 +
if (typeof(a) != 'number' && a < 0)
 +
return -1;
 +
 +
// Keep track of whether the current frame is changing, for isPlaying()
 +
// If we stay on the same frame for more than, say, a second, guess
 +
// that we're paused.
 +
if (a != this.guessisplaying.lastframe)
{
{
-
if (src.substring(src.length - 4).toLowerCase() != ".swf")
+
this.guessisplaying.lastframe = a;
-
flashmovie = false;
+
this.guessisplaying.lastframeat = new Date();
 +
this.guessisplaying.state = true;
}
}
-
else
+
else if (new Date() - this.guessisplaying.lastframeat > 1000)
{
{
-
a = flashmovie.getElementsByTagName('param').namedItem("movie");
+
this.guessisplaying.state = false;
-
if (!a || a.value.substring(a.value.length - 4).toLowerCase() != ".swf")
+
-
flashmovie = false;
+
}
}
 +
 +
return a;
}
}
-
else if (flashmovie.nodeName.toLowerCase() == "embed")
+
else
{
{
-
var src = flashmovie.getAttribute('src');
+
a = flashmovie.CurrentFrame;
-
if (!src || src.substring(src.length - 4).toLowerCase() != ".swf")
+
if (typeof(a) == 'function')
-
flashmovie = false;
+
a = flashmovie.CurrentFrame();
 +
 +
if (typeof(a) == 'number' && a >= 0)
 +
return a;
 +
else
 +
return -1;
}
}
-
}
+
};
 +
Utils.prototype.totalFrames = function totalFrames(flashmovie)
 +
{
 +
if (!flashmovie)
 +
flashmovie = globals.flashmovie;
 +
if (!flashmovie)
 +
return false;
-
if (!flashmovie)
+
var a;
-
settings.resize = settings.seekbar = settings.frames = settings.flipper = settings.noscale = settings.showmenu = settings.subtitles = false;
+
if (flashmovie === globals.flashmovie && globals.is_puppets)
-
if (settings.flipper)
+
{
-
settings.seekbar = settings.subtitles = false;
+
if (!flashmovie.TGetPropertyAsNumber)
 +
return -1;
 +
a = flashmovie.TGetPropertyAsNumber("/videoplayer", 5); // TOTAL_FRAMES
 +
}
 +
else
 +
{
 +
a = flashmovie.TotalFrames;
 +
if (typeof(a) == 'function')
 +
a = flashmovie.TotalFrames();
 +
}
 +
if (typeof(a) == 'number' && a >= 0)
 +
return a;
 +
else
 +
return -1;
 +
};
 +
Utils.prototype.isPlaying = function isPlaying(flashmovie)
 +
{
 +
if (!flashmovie)
 +
flashmovie = globals.flashmovie;
 +
if (!flashmovie)
 +
return false;
-
// globals
+
if (flashmovie === globals.flashmovie && globals.is_puppets)
-
var settingsbox;
+
{
-
var seekbar;
+
// There isn't a telltarget version of IsPlaying, there's no flag for it in
-
var isPercentage = false;
+
// TGetProperty, and it doesn't seem to be gettable via GetVariable (though
-
var aspect = 1.0;
+
// it's possible I just haven't tried the right thing)...
-
var randolink;
+
// So, for puppet toons, we need to try to track whether it seems to be playing...
-
var randourls = [];
+
return this.guessisplaying.state;
-
var subtitleholder;
+
-
var transcript = [];
+
-
var nosubtitles = document.createComment("");
+
-
var currentsubtitles = nosubtitles;
+
-
var characters = {
+
-
"sfx": {
+
-
"color": "#FFF",
+
-
"sfx": true,
+
-
"name": {"en": ""}
+
}
}
 +
 +
var a = flashmovie.IsPlaying;
 +
if (typeof(a) == 'function')
 +
a = flashmovie.IsPlaying();
 +
if (typeof(a) == 'boolean')
 +
return a;
 +
else if (typeof(a) == 'number')
 +
return a != 0;
 +
else
 +
return false;
};
};
-
var transcriptErrors;
+
Utils.prototype.framesLoaded = function framesLoaded(flashmovie)
-
var subtitleLoop = false;
+
{
 +
if (!flashmovie)
 +
flashmovie = globals.flashmovie;
 +
if (!flashmovie)
 +
return false;
-
// used by wikilink, prevnext, flipper... put it out here.
+
if (!flashmovie.TGetPropertyAsNumber)
-
var filename = location.pathname.toLowerCase();
+
return -1;
-
i = filename.lastIndexOf('/');
+
var a;
-
if (i >= 0)
+
if (flashmovie === globals.flashmovie && globals.is_puppets)
-
filename = filename.substr(i + 1);
+
a = flashmovie.TGetPropertyAsNumber('/videoplayer', 12); // property 12 is _framesloaded
-
i = filename.lastIndexOf('.');
+
else
-
if (i >= 0)
+
a = flashmovie.TGetPropertyAsNumber('/', 12);
-
filename = filename.substr(0,i);
+
if (typeof(a) == 'number' && a >= 0)
 +
return a;
 +
else
 +
return -1;
 +
};
 +
Utils.prototype.isLoaded = function isLoaded(flashmovie)
 +
{
 +
return this.currentFrame(flashmovie) >= 0;
 +
};
 +
Utils.prototype.whenLoaded = function whenLoaded(callback, flashmovie)
 +
{
 +
if (!flashmovie)
 +
flashmovie = globals.flashmovie;
 +
if (!flashmovie)
 +
return;
-
// pull puppet stuff out of its background
+
if (this.currentFrame(flashmovie) >= 0)
-
if ((settings.flipper || settings.seekbar || settings.subtitles) && flashmovie.src=="puppet_background.swf")
+
callback();
 +
else
 +
setTimeout(this.whenLoaded.bind(this, callback, flashmovie), 100);
 +
};
 +
Utils.prototype.stop = function stop(flashmovie)
{
{
-
var flashvars = getFlashVars();
+
if (!flashmovie)
-
if (flashvars.videoName)
+
flashmovie = globals.flashmovie;
-
replaceFlash(flashvars.videoName);
+
if (!flashmovie)
-
}
+
return;
-
if (settings.navbar)
+
if (flashmovie === globals.flashmovie && globals.is_puppets)
-
replacenavbar();
+
{
-
if (settings.subtitles)
+
if (!flashmovie.TStopPlay)
-
setupSubtitles();
+
return;
-
if (settings.seekbar)
+
flashmovie.TStopPlay("/videoplayer");
-
addFlashControls(flashmovie);
+
-
if (settings.resize)
+
-
resize();
+
-
if (settings.resize && settings.noscale)
+
-
noscale();
+
-
//if (settings.showmenu)
+
-
// showmenu();
+
-
if (settings.hrwiki)
+
-
wikilink();
+
-
if (settings.prevnext)
+
-
prevnext();
+
-
if (settings.flipper && whichsite == 0)
+
-
flipper();
+
-
if (settings.updates)
+
-
checkupdates();
+
-
// mess with popup windows - make them bigger and resizeable
+
this.currentFrame();
-
var oldopen = unsafeWindow.open;
+
this.guessisplaying.state = false;
-
unsafeWindow.open = function(a,b,c)
+
}
 +
else
 +
{
 +
if (!flashmovie.StopPlay)
 +
return;
 +
flashmovie.StopPlay();
 +
}
 +
};
 +
Utils.prototype.play = function play(flashmovie)
{
{
-
c = c.split(/,/);
+
if (!flashmovie)
-
var opts = {};
+
flashmovie = globals.flashmovie;
-
for (var i = 0; i < c.length; i++)
+
if (!flashmovie)
 +
return;
 +
 +
if (flashmovie === globals.flashmovie && globals.is_puppets)
{
{
-
var d = c[i].split(/=/);
+
if (!flashmovie.TPlay)
-
if (d.length == 2)
+
return;
-
opts[d[0]] = d[1];
+
flashmovie.TPlay("/videoplayer");
 +
 +
this.currentFrame();
 +
this.guessisplaying.state = true;
 +
this.guessisplaying.lastframeat = new Date();
}
}
-
opts["width"] = window.screen.width * 0.8;
+
else
-
opts["height"] = window.screen.height * 0.8;
+
-
opts["resizeable"] = "yes";
+
-
c = "";
+
-
for (var i in opts)
+
{
{
-
c += i + "=" + opts[i] + ",";
+
if (!flashmovie.Play)
 +
return;
 +
flashmovie.Play();
}
}
-
return oldopen(a,b,c);
+
};
-
}
+
Utils.prototype.goto = function goto(frame, flashmovie)
-
}
+
{
-
 
+
if (!flashmovie)
-
function replaceFlash(url)
+
flashmovie = globals.flashmovie;
-
{
+
if (!flashmovie)
-
where = flashmovie;
+
return;
-
while (where && where.parentNode && where.parentNode.nodeName.toLowerCase() == "object")
+
-
where = where.parentNode;
+
-
var newFlash=document.createElement("object");
+
-
newFlash.setAttribute("width",flashmovie.width);
+
-
newFlash.setAttribute("height",flashmovie.height);
+
-
newFlash.setAttribute("type","application/x-shockwave-flash");
+
-
newFlash.setAttribute("bgcolor",flashmovie.bgcolor ? flashmovie.bgcolor : "#FFFFFF");
+
-
newFlash.setAttribute("data",url);
+
-
where.parentNode.replaceChild(newFlash,where);
+
if (flashmovie === globals.flashmovie && globals.is_puppets)
 +
{
 +
if (!flashmovie.TGotoFrame)
 +
return;
 +
flashmovie.TGotoFrame("/videoplayer", frame);
-
flashmovie=newFlash.wrappedJSObject;
+
this.currentFrame();
-
}
+
this.guessisplaying.state = false;
-
function getSWFFilename()
+
}
-
{
+
else
-
// try to find the filename
+
{
-
var a = flashmovie.src;
+
if (!flashmovie.GotoFrame)
-
if (!a)
+
return;
-
a = flashmovie.data;
+
flashmovie.GotoFrame(frame);
-
if (!a)
+
}
 +
};
 +
Utils.prototype.zoomOut = function zoomOut(factor, flashmovie)
{
{
-
a = flashmovie.getElementsByTagName('param').namedItem('movie');
+
if (!flashmovie)
-
if (a)
+
flashmovie = globals.flashmovie;
-
a = a.value;
+
if (!flashmovie)
-
}
+
return;
-
return a;
+
-
}
+
if (!flashmovie.Zoom)
-
function getFlashVars()
+
return;
-
{
+
flashmovie.Zoom(100 * factor);
-
var flashvars = new Object();
+
};
-
var flashvarsstring = flashmovie.getAttribute("FlashVars");
+
Utils.prototype.zoomIn = function zoomIn(factor, flashmovie)
-
if (!flashvarsstring)
+
{
{
-
flashvarsstring = flashmovie.getElementsByTagName('param').namedItem('FlashVars');
+
this.zoomOut(1 / factor, flashmovie);
-
if (flashvarsstring)
+
};
-
flashvarsstring = flashvarsstring.value;
+
Utils.prototype.zoomReset = function zoomReset(flashmovie)
-
}
+
-
// parse the string
+
-
if (flashvarsstring)
+
{
{
-
var flashvarsparts = flashvarsstring.split('&');
+
this.zoomOut(0, flashmovie);
-
for (var i = 0; i < flashvarsparts.length; i++)
+
};
 +
 +
Utils.prototype.insertAfter = function insertAfter(newElement, referenceElement)
 +
{
 +
if(referenceElement.nextSibling)
 +
referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling);
 +
else
 +
referenceElement.parentNode.appendChild(newElement);
 +
};
 +
 
 +
function Globals()
 +
{
 +
this.whichsite = 0;
 +
if (location.hostname.indexOf("podstar") >= 0) this.whichsite = 1;
 +
if (location.hostname.indexOf("videlectrix") >= 0) this.whichsite = 2;
 +
if (location.pathname.indexOf("/mirror/") >= 0) this.whichsite = 3;
 +
 +
// icons, as Base64-encoded PNG files.
 +
this.images = {
 +
close:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAAm' +
 +
'JLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfWBRkTNhxuPxLkAAAAHX' +
 +
'RFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBUaGUgR0lNUO9kJW4AAAEKSURBVCjPhdGxSgNBFA' +
 +
'XQMzpgYWwsLEQUDBJBQgqFIChZEPR7/DA/QCGQTgQtJE1ENoWohYUgbGKQyFjErNv52nObe1' +
 +
'9wqGWg7z0l5YVgVdOu+wUt507tqIVQ4Zodp861ooELe15M5KFI6Zfr9u25MIj6Jl4cmSIPBW' +
 +
'rq2o5cufO4aOJDYSozNTa2pK4t03PtwUdMKRRykAmW0dTRcyNXpBQpI8GJDTR050zkNzK0bM' +
 +
'MZLvUNZ8yCfy6Wvbc1NVyi4dloXjqWvds6uvp41pFmpVOKJWd6bgwxkmTMIotWKpwrfBkZl7' +
 +
'uMonUHf5wSlV2+fUZrjnXdzrmyy7djD8GWTW9e51z557o1Tz85FH/WkOkaHQAAAABJRU5Erk' +
 +
'Jggg==',
 +
ffwd:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
 +
'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
 +
'sTAQCanBgAAAAHdElNRQfeCgQNLh+v5c+DAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
 +
'BHSU1QV4EOFwAAAC9JREFUCNcVisENAEAIwjo6ozmKI/j0YfS4hAeUIhFBJlV0M8Mudz8uno' +
 +
'a+LFiTHqCuHAU1qtJ6AAAAAElFTkSuQmCC',
 +
hrwiki:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGX' +
 +
'RFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1QTFRF////2wAzgZDJAiGNAB' +
 +
'6Lenp6ABCEABKFAAyDjp3O8gAAipjLlaPPFUixAB6OAA6C/f//fY3JABaIhJXK///50gAn//' +
 +
'/4CymXyQAaAA+DOFCm1QAmDiuX//zvnV2IfI3IQ0h7ABSFN0+qZXm9ABSG9PTxABiK2wAkuQ' +
 +
'AdSWW5WGu4cILCgYy209PZGRdjABeH0AALDiyYASGOhJTL2bi8k5OTzgAj1QAdLkilAAiDAB' +
 +
'iQIiCBzwAbyAAk//31ABSO0gAXDB95c5nZDAxeoRhHOVCp7u3lfx1W1LrCxQYtwwApQVitwA' +
 +
'QpJj2bAAyFKSODfI3GYna86urqysfL9fT0NUyXMDGGNk6cxgASy9rkAB2OQ1qzTmOzu8Pa4d' +
 +
'/b+v//58zRFEqw09XR25yrIjyh9P//g5PLAAN+foy/uRY92treh6neAASDXXC9jJvKjJvL6e' +
 +
'npiJfKDzejNk6r2wE1N0+rABKEAB6KxAAn0tPWyQAZRFuvXXfB/f392AAgKiib2QAyABaJhJ' +
 +
'TH2XeNEy+ZzgAwBiSRKUOlgI27urrP7t/iCghS0AAfk3SoyBc+iChf3vH1VWq426GvgI/Hiz' +
 +
'duboDCPEOXABCDSmu/DyeD///6P2K4OUJ/HByRlKHOAB+O8AAA2QI1hZPHg5TI9PT0ABuJiZ' +
 +
'jM1tbdf43CzgApAB2We4vD7e3rwgAseInHAyGWi5rOUme3hIuqFTGaWG25dojDd5LQ5Ki1AA' +
 +
'yMASCNcYHEAyKOABqLACSWHDeR+vr6uwAiIyBjipnJ1AIyjZvMmJyaITylAByMAB2L5wAlHD' +
 +
'eeCCaUcHCjWGy4wBQy/7AMAgAAARFJREFUeNpiONFeuLWjfL4RM1/R4tXyx5kZdrNHZ8za5D' +
 +
'jT3n/KimUtLDsZhPbFJilY14d5cC3o1dRnaWPILaucozf3DAND3DmnxqWnmRjyd+046NzJwG' +
 +
'B6dMJ6xVNHJjPIrGvq1mVgYGBlZQg9xjlxD4MKh2+wKgMILLET00mPYmCqFpctYIAAEamz3A' +
 +
'zaG4TdgmohAllpgsoMbBw5y9fshwiEW0qyM7jGbIlMjWcQCOlKMKnR8rZgyDOe3e95oErCJq' +
 +
'LOPDGQ8xBDCv8qF9tWRkb1SStPTvNTU2JgK83OrDjMaKbB0Gwgt23zdIap83h9vBZKJ4MMdZ' +
 +
'/Bs5EhwHBvz9qSBoftDAx9olbFiwACDABkK1N43Z86KwAAAABJRU5ErkJggg==',
 +
next:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
 +
'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
 +
'sTAQCanBgAAAAHdElNRQfeCgQNLSOrp+DHAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
 +
'BHSU1QV4EOFwAAACtJREFUCNdjULBhMLBhsLBhsLFhsLNhsAeiPQz2f8BoD4hrB5ayACtTsA' +
 +
'EA6J8JvyvoxNYAAAAASUVORK5CYII=',
 +
pause:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
 +
'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
 +
'sTAQCanBgAAAAHdElNRQfeCgQNLS1MH83AAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
 +
'BHSU1QV4EOFwAAAA5JREFUCNdjsLFhIAUBALQwB4FBHjsqAAAAAElFTkSuQmCC',
 +
play:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
 +
'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
 +
'sTAQCanBgAAAAHdElNRQfeCgQNLjLqOpP2AAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
 +
'BHSU1QV4EOFwAAACdJREFUCNcdirEJAAAMg/z/qpzUAwJpG3ARRTBgyCEyxCTFVX1yN7Ejqh' +
 +
'alykITkQAAAABJRU5ErkJggg==',
 +
prefs:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAll' +
 +
'BMVEUAGQASEhIfHx8fJy8pKSk2NjZBQUFJR0ZQUE9RUVFSUlJNX3NoaGhsaWdramlycG1meY' +
 +
'98fHx+fn5wgpV0iqKKh4R4jaR9jJx8kad9kad/mbONmaWEnrmEnrqkoZy3t7fIx8bKyMHT0c' +
 +
'3S0dDU09DV1NPP1t3W1dXY2Njb2tfe29bf3tzj4uHr6+js6+r39/f5+PgAAABrL3yvAAAAAX' +
 +
'RSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfWBR' +
 +
'oFKh31UQ8DAAAAgUlEQVQY022OxxLCMAwFRSc4BEIPJZQQ08v+/8+RsTExDDpIe3ijfSJ/hx' +
 +
'9g62Dt4GaAI+8YT0t27+BxxvvE/no5pYT10lGFrE34Ja40W3g1oMGmW7YZ6hnCYexKTPVkXi' +
 +
'vuvWe1Cz1aKqPNI3N0slI2TNYZiARJX30qERc7wBPKC4WRDzWdWHfmAAAAAElFTkSuQmCC',
 +
prev:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
 +
'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
 +
'sTAQCanBgAAAAHdElNRQfeCgQNLgFV6vLgAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
 +
'BHSU1QV4EOFwAAACxJREFUCNdjsGFhsOFhsJFhsLFhsKlhsPnDYPuHwR6MgAwgFyRoA1YAVM' +
 +
'YCABGLC3k4wQ8QAAAAAElFTkSuQmCC',
 +
rewind:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
 +
'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
 +
'sTAQCanBgAAAAHdElNRQfeCgQNLhgxgVogAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
 +
'BHSU1QV4EOFwAAAC9JREFUCNdjYGRkYGZmYGdn4OdnkJdnsLdnqK9n+P8fhIAMIBcoCJQCKg' +
 +
'AqY2QEALxwB9ke+WHMAAAAAElFTkSuQmCC',
 +
stop:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA1' +
 +
'BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9' +
 +
'4KBA0uOX3oSn4AAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADElEQV' +
 +
'QI12NgIA0AAAAwAAHHqoWOAAAAAElFTkSuQmCC',
 +
update:
 +
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABGCAMAAABG8BK2AAAC8V' +
 +
'BMVEUAAAD/AAD+AQH/AQH/AgL+AwP/AwP+BAT/BAT/BQX+Bgb/Bgb/Bwf+CAj/CAj/CQn/Cg' +
 +
'r+Cwv/Cwv+DAz/DAz/DQ3/Dg7+Dw//Dw//EBD+ERH/ERH/EhL/ExP+FBT/FRX/Fhb/Fxf+GB' +
 +
'j/GBj/GRn/Ghr/Gxv/HBz/HR3/Hh7/Hx//ICD+ISH/ISH/IiL/IyP/JCT/JSX/Jib/Jyf/KS' +
 +
'n/Kyv/LCz/LS3/Ly//MDD/MTH+MjL/MjL/MzP/NDT/NTX/Njb+Nzf/Nzf/ODj+OTn/OTn/Oj' +
 +
'r/PDz/Pj7/Pz//QUH/QkL+Q0P/RUX/Rkb/R0f/SEj/SUn/Skr/S0v/TEz/TU3/Tk7/T0//UF' +
 +
'D/UVH/UlL/VFT/VVX/Vlb/WFj/WVn/Wlr/W1v/XFz/XV3/Xl7/X1//YGD/YWH/YmL/Y2P/ZW' +
 +
'X/Zmb/Z2f/aGj/aWn/amr/a2v/bGz/bW3/bm7/b2//cHD/cXH/cnL/dHT/dnb/d3f/eHj/eX' +
 +
'n/e3v/fX3/fn7/f3//gID/gYH/goL/g4P/hIT/hob/h4f/iIj/iYn/ior/i4v/jIz/jY3/jo' +
 +
'7+kJD/kJD/kZH/kpL/lJT/lpb/l5f/mJj/mZn/mpr/m5v/nJz/nZ3/n5//oKD/oaH/oqL/o6' +
 +
'P/pqb/p6f/qKj/qan/qqr/q6v/rKz/ra3/r6//sLD/sbH/srL/s7P/tLT/tbX/trb/t7f/uL' +
 +
'j/urr/u7v/vLz/vb3/vr7/v7//wMD/wcH/wsL/w8P/xMT/xcX/xsb+x8f/x8f/yMj/ycn/ys' +
 +
'r/y8v/zMz/zc3/zs7/z8//0ND/0dH/0tL/09P+1NT/1NT/1tb/19f+2Nj/2Nj/2dn/29v/3N' +
 +
'z/3d3/39//4OD/4eH/4uL/4+P/5OT/5eX/5ub/5+f/6Oj/6en/6ur/6+v/7Oz/7e3/7u7/7+' +
 +
'/+8PD/8fH/8vL/8/P/9PT/9fX/9vb/9/f/+Pj/+fn/+vr/+/v//Pz//f3+/v7//v7////+AA' +
 +
'A5GkRyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAADzoAAA+IAUHKF/gAAA' +
 +
'AHdElNRQfXCRYICgxGxxkcAAAEL0lEQVRYw63Xe1wURRwA8Pm1G0KcHdGBkKAYjxC0yLJITU' +
 +
'l7cr7RUjAos4AuraCH2pWCVlZaRpD5AEXDwAemQRFdmgQeCgWUPKTk4JJHomAq5PBXu/fC2z' +
 +
't2Z7fdf+Y38/nc9zPz+83M7iEQ9VBDjCNxStKGG5xJSBSjWPV+c3m0nxNFDEP/XBf3ZkPLuv' +
 +
'GOigiG2oLrhyvVJX26abdzFXKGWtrUPRXA5aasRjyD5ijkzJjd/2aMNkXqhCiKoxAzU9bg3n' +
 +
'mDdXe1V4iZJIzTBnvhH9xrpxAzKbj1cYDY2Ww8AMuOL7NTiBg6koZX2rruhFhjLJsVP5iv8b' +
 +
'FTSBj6xxo/CHqxXftwYxFTKwhY/aj9iogYOgfrRwCM/vr0qXOmpUQ0pXAVYYZa19tuymc8xq' +
 +
'vY1u0nnOXCUQQZ6vnf/p5jiibpqgOYxqcctwRwFUEmqrD/1VvMYWppjGrUE7/ghkAHRYhxy8' +
 +
'QdG6x79u2DBbru/mLHuQgyr+H9HYatCkvv2U3Hdmv9nSgCzKyW/MnBpW1HvSz9gRHsMUAiGe' +
 +
'/1OA5A9XlX/TQv7pkmZtzB/Y1UNvBMP2NIDOVTeJjpT49lJNOjXHHq/Mb7eRQe5pnavAm2W3' +
 +
'jRt33Fjw2t8C3qG3z8AWvsOnFba6YbNZTCw9yYYsg2qkfabqpZPkPOhXc2ET2bk3FpAvDXSJ' +
 +
'BxbSsZ29O1fz2BwrtvVlzSNb60vX5ruEJI4WVUxxoTISSp46hWJaA4MtSw2dlVRXlq5jy6H6' +
 +
'5hRzw+XasSUBwYOu2rC4YO/bmWM0EesPRQsGnsZiGFy9AlVbmRzG9dQrMr1NSEE1OEs+uEoX' +
 +
'bivUGW+EBrIGh3KYkUDuP7bu3JPZ7mOKSsgFr4ggeRwmE87/FfW9Pqbb74vqgOg3Ay5XqGmp' +
 +
'Re9+U7vsvL/0oybZRE9rIhU65j6Az9tZL0ffn3jdtyadNdzEAaTiZVBhn6O9y+YBxAUw64fn' +
 +
'R+hxoUVXg5qWJjqBzcFsbutYrDwwBWHvr9rUrc5E+q2JjQExceYduHruQqBgAe3NhvLBhDrN' +
 +
'iYyD79agXzTtXg98xs9CIvcsXGRPzQc7F68R23NlxZQtk+pZEohnoyBuDuqI9P99Y244rhJP' +
 +
'eLMyZQ90exJgyUU/dgfPEpKYp5UeHak83fT2Tf0pXX8hMlKMj6Znu57HIMcwjmZmCcI15BVI' +
 +
'CvWfLK7ExmKnzbPH3fJ6IV9NzZLG/LKo4Y49kmOHKUaAVB2T8h1pzGGMeLrrSVmX71iPUzaO' +
 +
'afMyRk15Lios4EixONl0hU2ErldW82O5rOORIVU8ELDZ8xDq2sPRsmUTHvm8LuyvjFr/+Kc3' +
 +
'0kKpbtt6OuC+OefSOlKrYTHqf5MNVPsoLs/2QjGZj/oSB5FCSPguRRkDwKkkdB8ihIHgXJoy' +
 +
'B5FCSPguRRkDzKf7Z6NUd33kmjAAAAAElFTkSuQmCC'
 +
};
 +
 +
// find flash objects
 +
var objs;
 +
switch (this.whichsite)
{
{
-
var a = flashvarsparts[i].split('=');
+
case 0: // www.homestarrunner.com
-
if (a.length == 2)
+
objs = document.getElementsByTagName("EMBED");
-
flashvars[unescape(a[0])] = unescape(a[1]);
+
if (objs && objs.length >= 2)
 +
{
 +
this.flashmovie = objs[0];
 +
this.navbar = objs[1];
 +
}
 +
else if (objs && objs.length >= 1)
 +
{
 +
this.flashmovie = objs[0];
 +
this.navbar = false;
 +
}
 +
else
 +
{
 +
this.flashmovie = false;
 +
this.navbar = false;
 +
}
 +
if (!this.flashmovie)
 +
{
 +
objs = document.getElementsByTagName("OBJECT");
 +
if (objs && objs.length >= 1)
 +
this.flashmovie = objs[0];
 +
}
 +
break;
 +
case 1: // podstar.homestarrunner.com
 +
objs = document.getElementsByTagName("EMBED");
 +
this.flashmovie = false;
 +
if (objs && objs.length >= 1)
 +
this.navbar = objs[0];
 +
else
 +
this.navbar = false;
 +
break;
 +
case 2: // videlectrix
 +
objs = document.getElementsByTagName("EMBED");
 +
this.navbar = false;
 +
if (objs && objs.length >= 1)
 +
this.flashmovie = objs[0];
 +
else
 +
this.flashmovie = false;
 +
/*settings.navbar = false;*/
 +
break;
 +
case 3: // mirror
 +
objs = document.getElementsByTagName("EMBED");
 +
this.flashmovie = false;
 +
if (objs && objs.length >= 1)
 +
this.flashmovie = objs[0];
 +
if (!this.flashmovie)
 +
{
 +
objs = document.getElementsByTagName("OBJECT");
 +
if (objs && objs.length >= 1)
 +
this.flashmovie = objs[0];
 +
}
 +
this.navbar = document.getElementById('navbar');
 +
/*if (!this.navbar)
 +
settings.navbar = false;*/
 +
var flashcontainer = document.getElementById('flash');
 +
if (flashcontainer)
 +
flashcontainer.style.width = "auto";
 +
break;
}
}
-
}
+
if (this.flashmovie)
-
var filename = getSWFFilename();
+
-
var i = filename.indexOf('?');
+
-
if (i >= 0)
+
-
var flashvarsstring = filename.substring(i + 1);
+
-
if (flashvarsstring)
+
-
{
+
-
var flashvarsparts = flashvarsstring.split('&');
+
-
for (var i = 0; i < flashvarsparts.length; i++)
+
{
{
-
var a = flashvarsparts[i].split('=');
+
//expose Flash plugin-added methods
-
if (a.length == 2)
+
if (this.flashmovie.wrappedJSObject)
-
flashvars[unescape(a[0])] = unescape(a[1]);
+
this.flashmovie = this.flashmovie.wrappedJSObject;
 +
 +
// confirm that this is really a flash file
 +
// and not (for example) the embedded background sound on SB's website
 +
var src = this.flashmovie.getAttribute('src');
 +
if (this.flashmovie.nodeName.toLowerCase() == "object")
 +
{
 +
if (src)
 +
{
 +
if (src.substring(src.length - 4).toLowerCase() != ".swf")
 +
this.flashmovie = false;
 +
}
 +
else
 +
{
 +
var a = this.flashmovie.getElementsByTagName('param').namedItem("movie");
 +
if (!a || a.value.substring(a.value.length - 4).toLowerCase() != ".swf")
 +
this.flashmovie = false;
 +
else
 +
src = a.value;
 +
}
 +
}
 +
else if (this.flashmovie.nodeName.toLowerCase() == "embed")
 +
{
 +
if (!src || src.substring(src.length - 4).toLowerCase() != ".swf")
 +
this.flashmovie = false;
 +
}
 +
 +
// puppet_background.swf is a wrapper around the puppet stuff popup toons
 +
// This flag tells things like seekbar to control the wrapped movie clip
 +
if (src)
 +
this.is_puppets = src == "puppet_background.swf" || src.substring(src.length - 22) == "/puppet_background.swf";
}
}
 +
// Don't run large flash objects inline (gets rid of some extra padding from
 +
// having the movie sitting on the baseline)
 +
if (this.flashmovie)
 +
{
 +
this.flashmovie.style.display = "block";
 +
this.flashmovie.style.margin = "0 auto";
 +
}
 +
if (this.navbar)
 +
{
 +
this.navbar.style.display = "block";
 +
this.navbar.style.margin = "0 auto";
 +
}
 +
 +
this.filename = window.location.pathname.toLowerCase();
 +
var i = this.filename.lastIndexOf('/');
 +
if (i >= 0)
 +
this.filename = this.filename.substr(i + 1);
 +
i = this.filename.lastIndexOf('.');
 +
if (i >= 0)
 +
this.filename = this.filename.substr(0,i);
}
}
-
return flashvars;
+
Globals.prototype.initModules = function initModules()
-
}
+
{
 +
this.modules = {};
 +
this.modules.settingspane = new SettingsPane();
 +
this.modules.fullscreen = new Fullscreen();
 +
this.modules.seekbar = new Seekbar();
 +
this.modules.wikilink = new WikiLink();
 +
this.modules.nextprev = new NextPrev();
 +
this.modules.navbar = new Navbar();
 +
this.modules.subtitles = new Subtitles();
 +
this.modules.updates = new Updates();
 +
for (var i in this.modules)
 +
this.modules[i].init();
 +
this.modules.settingspane.initComplete();
 +
};
-
function addsettingsbox()
+
function SettingsPane()
-
{
+
-
settingsbox = document.createElement('div');
+
-
settingsbox.style.borderRight = settingsbox.style.borderBottom = '1px solid #666';
+
-
settingsbox.style.background = '#EEE';
+
-
settingsbox.style.color = '#000';
+
-
settingsbox.style.position = 'fixed';
+
-
settingsbox.style.overflow = 'auto';
+
-
settingsbox.style.left = 0;
+
-
settingsbox.style.top = 0;
+
-
settingsbox.style.width = '350px';
+
-
settingsbox.style.font = '12px sans-serif';
+
-
settingsbox.style.textAlign = 'left';
+
-
settingsbox.style.display = 'none';
+
-
settingsbox.style.zIndex = 2;
+
-
document.body.appendChild(settingsbox);
+
-
var titlebar = document.createElement('div');
+
-
titlebar.style.fontWeight = "bolder";
+
-
titlebar.style.background = "#CCC";
+
-
titlebar.style.borderBottom = "1px solid #666";
+
-
titlebar.style.padding = '3px';
+
-
settingsbox.appendChild(titlebar);
+
-
var closebutton = document.createElement('img');
+
-
closebutton.src = image_close;
+
-
closebutton.title = "Click to hide preferences";
+
-
closebutton.style.cssFloat = "right";
+
-
closebutton.style.verticalAlign = "text-bottom";
+
-
closebutton.style.cursor = "pointer";
+
-
closebutton.style.display = "block";
+
-
closebutton.addEventListener('click', function(){settingsbox.style.display = "none"; settingslink.style.display = "block";}, false);
+
-
titlebar.appendChild(closebutton);
+
-
var prefslogo = document.createElement('img');
+
-
prefslogo.src = image_prefs;
+
-
prefslogo.style.verticalAlign = "text-bottom";
+
-
titlebar.appendChild(prefslogo);
+
-
titlebar.appendChild(document.createTextNode(" Preferences"));
+
-
var settingsform = document.createElement('form');
+
-
settingsform.style.margin = 0;
+
-
settingsform.style.padding = '3px';
+
-
settingsbox.appendChild(settingsform);
+
-
var settingslist = document.createElement('ul');
+
-
settingslist.style.listStyle = "none";
+
-
var a = window.innerHeight - 75;
+
-
if (a < 40) a = 40;
+
-
settingslist.style.maxHeight = a + 'px';
+
-
settingslist.style.overflow = 'auto'; // vertical scrollbar if needed
+
-
window.addEventListener('resize', function()
+
{
{
 +
}
 +
SettingsPane.prototype.init = function init()
 +
{
 +
utils.addGlobalStyle(
 +
'#settingsbox, #settingslink\n' +
 +
'{\n' +
 +
'\tborder-right: 1px solid #666;\n' +
 +
'\tborder-bottom: 1px solid #666;\n' +
 +
'\tbackground: #EEE;\n' +
 +
'\tcolor: #000;\n' +
 +
'\tposition: fixed;\n' +
 +
'\toverflow: auto;\n' +
 +
'\tleft: 0;\n' +
 +
'\ttop: 0;\n' +
 +
'\tfont: 12px sans-serif;\n' +
 +
'\ttext-align: left;\n' +
 +
'\tz-index: 2;\n' +
 +
'}\n' +
 +
'#settingsbox\n' +
 +
'{\n' +
 +
'\twidth: 350px;\n' +
 +
'}\n' +
 +
'#settingstitlebar\n' +
 +
'{\n' +
 +
'\tfont-weight: bolder;\n' +
 +
'\tbackground: #CCC;\n' +
 +
'\tborder-bottom: 1px solid #666;\n' +
 +
'\tpadding: 3px;\n' +
 +
'}\n' +
 +
'#settingstitlebar img\n' +
 +
'{\n' +
 +
'\tvertical-align: text-bottom;\n' +
 +
'}\n' +
 +
'#settingstitlebar .prefsicon\n' +
 +
'{\n' +
 +
'\tfloat: left;\n' +
 +
'\tmargin-right: 0.5em;\n' +
 +
'}\n' +
 +
'#settingstitlebar .buttonimage, #settingslink .buttonimage\n' +
 +
'{\n' +
 +
'\tcursor: pointer;\n' +
 +
'\tdisplay: block;\n' +
 +
'}\n' +
 +
'#settingstitlebar .buttonimage\n' +
 +
'{\n' +
 +
'\tfloat: right;\n' +
 +
'}\n' +
 +
'#settingsbox form\n' +
 +
'{\n' +
 +
'\tmargin: 0;\n' +
 +
'\tpadding: 3px;\n' +
 +
'}\n' +
 +
'#settingsbox ul, #settingsbox li\n' +
 +
'{\n' +
 +
'\tlist-style: none;\n' +
 +
'\tmargin: 0;\n' +
 +
'\tpadding: 0;\n' +
 +
'}\n' +
 +
'#settingsbox ul ul\n' +
 +
'{\n' +
 +
'\tmargin-left: 2em;\n' +
 +
'}\n' +
 +
'#settingsbox input[type="checkbox"]\n' +
 +
'{\n' +
 +
'\tmargin-right: 0.25em;\n' +
 +
'}\n' +
 +
'#settingsbuttons\n' +
 +
'{\n' +
 +
'\ttext-align: center;\n' +
 +
'}\n' +
 +
'#settingslink\n' +
 +
'{\n' +
 +
'\tpadding: 3px;\n' +
 +
'}\n' +
 +
""
 +
);
 +
 +
var settingsbox = document.createElement('div');
 +
this.settingsbox = settingsbox;
 +
settingsbox.id = 'settingsbox';
 +
settingsbox.style.display = 'none';
 +
document.body.appendChild(settingsbox);
 +
var titlebar = document.createElement('div');
 +
titlebar.id = 'settingstitlebar';
 +
settingsbox.appendChild(titlebar);
 +
var closebutton = document.createElement('img');
 +
closebutton.src = globals.images.close;
 +
closebutton.title = "Click to hide preferences";
 +
closebutton.className = 'buttonimage';
 +
closebutton.addEventListener('click', this.hidePane.bind(this), false);
 +
titlebar.appendChild(closebutton);
 +
var prefslogo = document.createElement('img');
 +
prefslogo.src = globals.images.prefs;
 +
prefslogo.className = 'prefsicon';
 +
titlebar.appendChild(prefslogo);
 +
titlebar.appendChild(document.createTextNode("Preferences"));
 +
var settingsform = document.createElement('form');
 +
settingsbox.appendChild(settingsform);
 +
var settingslist = document.createElement('ul');
 +
this.settingslist = settingslist;
var a = window.innerHeight - 75;
var a = window.innerHeight - 75;
if (a < 40) a = 40;
if (a < 40) a = 40;
settingslist.style.maxHeight = a + 'px';
settingslist.style.maxHeight = a + 'px';
-
}, false);
+
settingslist.style.overflow = 'auto'; // vertical scrollbar if needed
-
settingslist.style.padding = settingslist.style.margin = 0;
+
window.addEventListener('resize', this.resizeWindow.bind(this), true);
-
settingsform.appendChild(settingslist);
+
settingsform.appendChild(settingslist);
-
for (i = 0; i < settingnames.length; i++)
+
 +
var div = document.createElement('div');
 +
div.id = 'settingsbuttons';
 +
settingsform.appendChild(div);
 +
var savebutton = document.createElement('input');
 +
savebutton.type = "submit";
 +
savebutton.value = "Save and Apply";
 +
div.appendChild(savebutton);
 +
var nocachebutton = document.createElement('input');
 +
nocachebutton.type = "submit";
 +
nocachebutton.value = "Clear subtitles cache";
 +
nocachebutton.addEventListener("click", this.cacheDodge.bind(this), false);
 +
div.appendChild(document.createTextNode(" "));
 +
div.appendChild(nocachebutton);
 +
settingsform.addEventListener("submit", this.saveSettings.bind(this), false);
 +
 +
var settingslink = document.createElement('div');
 +
this.settingslink = settingslink;
 +
settingslink.id = 'settingslink';
 +
var settingslinkimage = document.createElement('img');
 +
settingslinkimage.src = globals.images.prefs;
 +
settingslinkimage.title = "Click to show preferences";
 +
settingslinkimage.className = 'prefsicon buttonimage';
 +
settingslinkimage.addEventListener('click', this.showPane.bind(this), false);
 +
settingslink.appendChild(settingslinkimage);
 +
document.body.appendChild(settingslink);
 +
 +
this.hidePanels = [];
 +
};
 +
SettingsPane.prototype.saveSettings = function saveSettings(e)
{
{
-
var settingrow = document.createElement('li');
+
// stop the form from actually being submitted
-
settingnames[i].li = settingrow;
+
if (e && e.preventDefault)
-
var settingcheckbox = document.createElement('input');
+
e.preventDefault();
-
settingcheckbox.type = "checkbox";
+
-
settingcheckbox.checked = settings[settingnames[i].name];
+
-
settingcheckbox.title = settingnames[i].tooltip;
+
-
settingcheckbox.id = "setting_" + settingnames[i].name;
+
-
settingcheckbox.addEventListener('click', enabledisable, false);
+
-
settingrow.appendChild(settingcheckbox);
+
-
settingnames[i].checkbox = settingcheckbox;
+
-
var settinglabel = document.createElement('label');
+
-
settinglabel.htmlFor = "setting_" + settingnames[i].name;
+
-
settinglabel.appendChild(document.createTextNode(' ' + settingnames[i].title));
+
-
settinglabel.title = settingnames[i].tooltip;
+
-
settingrow.appendChild(settinglabel);
+
-
settingnames[i].label = settinglabel;
+
-
if (settingnames[i].title.charAt(0) == ' ')
+
for (var i in globals.modules)
 +
globals.modules[i].updateSettings();
 +
 +
return false;
 +
};
 +
SettingsPane.prototype.updateSettings = function updateSettings(){};
 +
SettingsPane.prototype.showPane = function showPane()
 +
{
 +
this.settingsbox.style.display = "block";
 +
this.settingslink.style.display = "none";
 +
};
 +
SettingsPane.prototype.hidePane = function hidePane()
 +
{
 +
this.settingsbox.style.display = "none";
 +
this.settingslink.style.display = "block";
 +
};
 +
SettingsPane.prototype.resizeWindow = function resizeWindow()
 +
{
 +
var a = window.innerHeight - 75;
 +
if (a < 40) a = 40;
 +
this.settingslist.style.maxHeight = a + 'px';
 +
};
 +
SettingsPane.prototype.cacheDodge = function cacheDodge()
 +
{
 +
utils.setPref("cachedodge", Math.random().toString());
 +
};
 +
 +
SettingsPane.prototype.addSettingRow = function addSettingRow(parent)
 +
{
 +
if (!parent)
 +
parent = this.settingslist;
 +
else
{
{
-
// get number of spaces;
+
var checkbox = undefined;
-
var numspaces = 1;
+
if (parent.tagName.toLowerCase() == "input")
-
while (settingnames[i].title.charAt(numspaces) == ' ') numspaces++;
+
-
var previousindex = i - 1;
+
-
while (previousindex > 0 && settingnames[previousindex].title.substring(0, numspaces).match(/^ +$/))
+
-
previousindex--;
+
-
if (previousindex >= 0)
+
{
{
-
settingnames[i].parent = settingnames[previousindex];
+
checkbox = parent;
-
if (!settingnames[previousindex].ul)
+
parent = parent.parentNode;
 +
}
 +
var ul = parent.getElementsByTagName("ul");
 +
if (ul.length)
 +
parent = ul[ul.length - 1];
 +
else
 +
{
 +
ul = document.createElement("ul");
 +
parent.appendChild(ul);
 +
parent = ul;
 +
 +
if (checkbox)
{
{
-
settingnames[previousindex].ul = document.createElement('ul');
+
this.hidePanels.push({checkbox: checkbox, panel: ul});
-
settingnames[previousindex].ul.style.listStyle = "none";
+
checkbox.addEventListener("click", this.showHidePanel.bind(this, checkbox, ul), false);
-
settingnames[previousindex].ul.style.padding = settingnames[previousindex].ul.style.margin = 0;
+
-
settingnames[previousindex].ul.style.marginLeft = "2em";
+
-
settingnames[previousindex].li.appendChild(settingnames[previousindex].ul);
+
}
}
-
settingnames[previousindex].ul.appendChild(settingrow);
 
}
}
-
else
 
-
settingslist.appendChild(settingrow);
 
}
}
-
else
+
var settingrow = document.createElement('li');
-
settingslist.appendChild(settingrow);
+
parent.appendChild(settingrow);
-
}
+
return settingrow;
-
var div = document.createElement('div');
+
};
-
div.style.textAlign = "center";
+
SettingsPane.prototype.addCheckbox = function addCheckbox(id, label, title, checked, parent)
-
settingsform.appendChild(div);
+
-
var savebutton = document.createElement('input');
+
-
savebutton.type = "submit";
+
-
savebutton.value = "Save and Apply";
+
-
div.appendChild(savebutton);
+
-
var nocachebutton = document.createElement('input');
+
-
nocachebutton.type = "submit";
+
-
nocachebutton.value = "Clear subtitles cache";
+
-
nocachebutton.addEventListener("click", function(){GM_setValue("cachedodge", Math.random().toString())}, false);
+
-
div.appendChild(document.createTextNode(" "));
+
-
div.appendChild(nocachebutton);
+
-
settingsform.addEventListener("submit", savesettings, false);
+
-
+
-
var settingslink = document.createElement('div');
+
-
settingslink.style.borderRight = settingslink.style.borderBottom = '1px solid #666';
+
-
settingslink.style.background = '#EEE';
+
-
settingslink.style.display = "block";
+
-
settingslink.style.position = 'fixed';
+
-
settingslink.style.overflow = 'auto';
+
-
settingslink.style.left = '0px';
+
-
settingslink.style.top = '0px';
+
-
settingslink.style.padding = '3px';
+
-
settingslink.style.zIndex = 2;
+
-
var settingslinkimage = document.createElement('img');
+
-
settingslinkimage.src = image_prefs;
+
-
settingslinkimage.title = "Click to show preferences";
+
-
settingslinkimage.style.cursor = "pointer";
+
-
settingslinkimage.style.display = "block";
+
-
settingslinkimage.addEventListener('click', function(){settingsbox.style.display = "block"; settingslink.style.display = "none";}, false);
+
-
settingslink.appendChild(settingslinkimage);
+
-
document.body.appendChild(settingslink);
+
-
+
-
// add some extra settings other than checkboxes that aren't handled in the structure above
+
-
extrasettings[0] = {'name': 'language'};
+
-
var settingrow = document.createElement('li');
+
-
extrasettings[0].li = settingrow;
+
-
var settinglabel = document.createElement('label');
+
-
settinglabel.htmlFor = "setting_language";
+
-
settinglabel.appendChild(document.createTextNode('Subtitle Language: '));
+
-
settinglabel.title = 'Display subtitles in this language, if any';
+
-
settingrow.appendChild(settinglabel);
+
-
extrasettings[0].label = settinglabel;
+
-
var settingselect = document.createElement('select');
+
-
settingselect.title = 'Display subtitles in this language, if any';
+
-
settingselect.id = "setting_language";
+
-
settingrow.appendChild(settingselect);
+
-
extrasettings[0].select = settingselect;
+
-
for (i = 0; i < settingnames.length; i++)
+
{
{
-
if (settingnames[i].name == "subtitles")
+
var settingrow = this.addSettingRow(parent);
-
{
+
var settingcheckbox = document.createElement('input');
-
extrasettings[0].parent = settingnames[i];
+
settingcheckbox.type = 'checkbox';
-
settingnames[i].ul.insertBefore(settingrow, settingnames[i].ul.firstChild);
+
settingcheckbox.checked = checked;
-
}
+
settingcheckbox.title = title;
-
}
+
settingcheckbox.id = 'setting_' + id;
-
extrasettings[0].populated = false;
+
settingrow.appendChild(settingcheckbox);
 +
var settinglabel = document.createElement('label');
 +
settinglabel.htmlFor = 'setting_' + id;
 +
settinglabel.appendChild(document.createTextNode(label));
 +
settinglabel.title = settingcheckbox.title;
 +
settingrow.appendChild(settinglabel);
 +
return settingcheckbox;
 +
};
-
extrasettings[1] = {'name': 'testsubsdata'};
+
SettingsPane.prototype.showHidePanel = function showHidePanel(checkbox, panel)
-
var div = document.createElement('div');
+
{
-
div.style.display = "block";
+
panel.style.display = checkbox.checked ? "" : "none";
-
div.style.margin = "5px 10px 5px 2em";
+
};
-
div.style.textAlign = "center";
+
SettingsPane.prototype.initComplete = function initComplete()
-
extrasettings[1].div = div;
+
{
-
var settingtextarea = document.createElement('textarea');
+
for (var i = 0; i < this.hidePanels.length; i++)
-
settingtextarea.title = 'Paste your XML data here';
+
this.showHidePanel(this.hidePanels[i].checkbox, this.hidePanels[i].panel);
-
settingtextarea.id = "setting_testsubsdata";
+
};
-
settingtextarea.rows = 10;
+
 
-
settingtextarea.style.width = "100%";
+
function Fullscreen()
-
settingtextarea.style.fontSize = "10px";
+
-
settingtextarea.style.textAlign = "left";
+
-
settingtextarea.appendChild(document.createTextNode(settings['testsubsdata']));
+
-
div.appendChild(settingtextarea);
+
-
extrasettings[1].textarea = settingtextarea;
+
-
// only enable the refresh option if we're already set up for testing subtitles
+
-
//  - I'm not sure if it's robust otherwise.
+
-
//
+
-
// The interface for this thing is kinda confusing, so it's not in the script by default
+
-
// change the "false" to a "true" to include it.
+
-
if (settings.testsubs && false)
+
{
{
-
var settingbutton = document.createElement('input');
+
this.shouldresize = utils.getPref('resize', true);
-
settingbutton.type = 'button';
+
this.noscale = utils.getPref('noscale', false);
-
settingbutton.title = 'Reload test XML data';
+
-
settingbutton.value = 'Reload Subtitles';
+
-
settingbutton.addEventListener('click', reloadSubtitles, false);
+
-
div.appendChild(settingbutton);
+
-
extrasettings[1].button = settingbutton;
+
}
}
-
for (i = 0; i < settingnames.length; i++)
+
Fullscreen.prototype.init = function init()
{
{
-
if (settingnames[i].name == "testsubs")
+
this.setting_main = globals.modules.settingspane.addCheckbox('resize', "Resize flash to full-screen", "Resizes the toon so it fills the entire window", this.shouldresize);
 +
this.setting_noscale = globals.modules.settingspane.addCheckbox('noscale', "Show behind the black", "Lets you see what's happening beyond the frames", this.noscale, this.setting_main);
 +
 +
if (!globals.flashmovie)
 +
return;
 +
 +
this.initwidth = globals.flashmovie.width;
 +
this.initheight = globals.flashmovie.height;
 +
if (this.initwidth.toString().indexOf('%') >= 0 || this.initwidth.toString().indexOf('%') >= 0)
{
{
-
extrasettings[1].parent = settingnames[i];
+
this.isPercentage = true;
-
settingnames[i].li.appendChild(div);
+
this.aspect = 1.0;
}
}
-
}
+
else
-
+
-
extrasettings[2] = {'name': 'names'};
+
-
var settingrow = document.createElement('li');
+
-
extrasettings[2].li = settingrow;
+
-
var settinglabel = document.createElement('label');
+
-
settinglabel.htmlFor = "setting_names";
+
-
settinglabel.appendChild(document.createTextNode('Show speakers\' names: '));
+
-
settinglabel.title = 'Show the speakers\' names before their lines';
+
-
settingrow.appendChild(settinglabel);
+
-
extrasettings[2].label = settinglabel;
+
-
var settingselect = document.createElement('select');
+
-
settingselect.title = 'Show the speakers\' names before their lines';
+
-
settingselect.id = "setting_names";
+
-
settingrow.appendChild(settingselect);
+
-
extrasettings[2].select = settingselect;
+
-
var option = document.createElement('option');
+
-
option.value = "0";
+
-
option.appendChild(document.createTextNode("Never"));
+
-
if (settings['names'] == 0)
+
-
option.selected = true;
+
-
settingselect.appendChild(option);
+
-
var option = document.createElement('option');
+
-
option.value = "1";
+
-
option.appendChild(document.createTextNode("On voiceovers"));
+
-
if (settings['names'] == 1)
+
-
option.selected = true;
+
-
settingselect.appendChild(option);
+
-
var option = document.createElement('option');
+
-
option.value = "2";
+
-
option.appendChild(document.createTextNode("Always"));
+
-
if (settings['names'] == 2)
+
-
option.selected = true;
+
-
settingselect.appendChild(option);
+
-
for (i = 0; i < settingnames.length; i++)
+
-
{
+
-
if (settingnames[i].name == "colours")
+
{
{
-
extrasettings[2].parent = settingnames[i].parent;
+
this.isPercentage = false;
-
settingnames[i].parent.ul.insertBefore(settingrow, settingnames[i].li.nextSibling);
+
this.aspect = this.initwidth / this.initheight;
}
}
-
}
+
window.addEventListener('resize', this.doResize.bind(this), true);
-
+
this.doResize();
-
enabledisable();
+
if (this.noscale)
-
}
+
this.setScaleMode("noScale");
-
function enabledisable()
+
};
-
{
+
Fullscreen.prototype.doResize = function doResize()
-
for (i = 0; i < settingnames.length; i++)
+
{
{
-
if (!settingnames[i].parent)
+
if (!globals.flashmovie)
-
continue;
+
return;
-
enabled = true;
+
-
p = settingnames[i].parent;
+
if (!this.shouldresize)
-
while(p)
+
{
{
-
if (!p.checkbox.checked)
+
globals.flashmovie.style.width = this.initwidth + "px";
-
{
+
globals.flashmovie.style.height = this.initheight + "px";
-
enabled = false;
+
if (globals.modules.seekbar.seekbar)
-
break;
+
globals.modules.seekbar.seekbar.style.width = Math.max(this.initwidth, 450) + "px";
-
}
+
return;
-
p = p.parent;
+
}
}
-
settingnames[i].checkbox.disabled = !enabled;
+
-
settingnames[i].label.style.color = enabled ? "inherit" : "#666";
+
var dw = window.innerWidth;
-
}
+
var dh = window.innerHeight;
-
enabled = true;
+
var a = document.defaultView.getComputedStyle(document.body, null);
-
p = extrasettings[0].parent;
+
// parseInt will take the number part at the start, turning eg "10px" into 10
-
while(p)
+
dw -= parseInt(a.marginLeft,10);
-
{
+
dw -= parseInt(a.marginRight,10);
-
if (!p.checkbox.checked)
+
dh -= parseInt(a.marginTop,10);
 +
dh -= parseInt(a.marginBottom,10);
 +
 +
if (globals.navbar)
{
{
-
enabled = false;
+
a = document.defaultView.getComputedStyle(globals.navbar, null);
-
break;
+
dh -= parseInt(a.height,10);
 +
dh -= parseInt(a.marginTop,10);
 +
dh -= parseInt(a.marginBottom,10);
}
}
-
p = p.parent;
+
if (globals.modules.seekbar.seekbar)
-
}
+
-
extrasettings[0].select.disabled = !enabled;
+
-
extrasettings[0].label.style.color = enabled ? "inherit" : "#666";
+
-
if (enabled && !extrasettings[0].populated)
+
-
{
+
-
var option = document.createElement('option');
+
-
option.appendChild(document.createTextNode("Loading..."));
+
-
option.selected = true;
+
-
extrasettings[0].select.appendChild(option);
+
-
downloadxmlfromwiki("Subtitles:Languages", populatelanguagelist);
+
-
}
+
-
 
+
-
+
-
enabled = true;
+
-
p = extrasettings[1].parent;
+
-
while(p)
+
-
{
+
-
if (!p.checkbox.checked)
+
{
{
-
enabled = false;
+
a = document.defaultView.getComputedStyle(globals.modules.seekbar.seekbar, null);
-
break;
+
dh -= parseInt(a.height,10);
 +
dh -= parseInt(a.marginTop,10);
 +
dh -= parseInt(a.marginBottom,10);
}
}
-
p = p.parent;
+
if (globals.modules.subtitles.subtitleholder)
-
}
+
-
extrasettings[1].textarea.disabled = !enabled;
+
-
if (extrasettings[1].button)
+
-
extrasettings[1].button.disabled = !enabled;
+
-
// hide it entirely if immediate parent is disabled
+
-
extrasettings[1].div.style.display = extrasettings[1].parent.checkbox.checked ? "block" : "none";
+
-
settingsbox.style.width = extrasettings[1].parent.checkbox.checked ? "500px" : "350px";
+
-
 
+
-
enabled = true;
+
-
p = extrasettings[2].parent;
+
-
while(p)
+
-
{
+
-
if (!p.checkbox.checked)
+
{
{
-
enabled = false;
+
a = document.defaultView.getComputedStyle(globals.modules.subtitles.subtitleholder, null);
-
break;
+
dh -= parseInt(a.height,10);
 +
dh -= parseInt(a.marginTop,10);
 +
dh -= parseInt(a.marginBottom,10);
}
}
-
p = p.parent;
+
if (globals.modules.subtitles.errorsholder)
-
}
+
-
extrasettings[2].select.disabled = !enabled;
+
-
extrasettings[2].label.style.color = enabled ? "inherit" : "#666";
+
-
}
+
-
function savesettings(e)
+
-
{
+
-
for (i = 0; i < settingnames.length; i++)
+
-
{
+
-
settingbox = settingnames[i].checkbox;
+
-
if (settingbox)
+
{
{
-
settings[settingnames[i].name] = settingbox.checked;
+
a = document.defaultView.getComputedStyle(globals.modules.subtitles.errorsholder, null);
-
GM_setValue(settingnames[i].name, settingbox.checked ? 1 : 0);
+
dh -= parseInt(a.height,10);
 +
dh -= parseInt(a.marginTop,10);
 +
dh -= parseInt(a.marginBottom,10);
 +
}
 +
// enforce a (rather small) minimum size, regardless of how much crap is squeezed below the frame
 +
if (dw < 100) dw = 100;
 +
if (dh < 100) dh = 100;
 +
// if it was a percentage size, or we're looking outside the frame, just fill the whole window.
 +
// otherwise, keep the aspect ratio correct... "touch inside" style.
 +
if (!this.isPercentage && !this.noscale)
 +
{
 +
if(dw <= dh * this.aspect)
 +
dh = Math.floor(dw / this.aspect);
 +
else
 +
dw = Math.floor(dh * this.aspect);
}
}
-
}
 
-
if (extrasettings[0].populated)
+
// set embed's size
 +
globals.flashmovie.style.width = dw + "px";
 +
globals.flashmovie.style.height = dh + "px";
 +
if (globals.modules.seekbar.seekbar)
 +
globals.modules.seekbar.seekbar.style.width = Math.max(dw, 450) + "px";
 +
};
 +
Fullscreen.prototype.setScaleMode = function setScaleMode(scaleMode)
{
{
-
settings['language'] = extrasettings[0].select.value;
+
utils.whenLoaded(function(){
-
GM_setValue('language', extrasettings[0].select.value);
+
globals.flashmovie.SetVariable("Stage.scaleMode", scaleMode);
-
}
+
});
-
settings['testsubsdata'] = extrasettings[1].textarea.value;
+
};
-
GM_setValue('testsubsdata', escape(extrasettings[1].textarea.value));
+
Fullscreen.prototype.updateSettings = function updateSettings()
-
// stop the form from actually being submitted
+
-
if (e && e.preventDefault)
+
{
{
-
location.reload();
+
this.shouldresize = this.setting_main.checked;
-
e.preventDefault();
+
utils.setPref("resize", this.shouldresize);
-
}
+
var old_noscale = this.noscale;
-
settings['names'] = extrasettings[2].select.value;
+
this.noscale = this.setting_noscale.checked;
-
GM_setValue('names', extrasettings[2].select.value);
+
utils.setPref("noscale", this.noscale);
-
}
+
this.doResize();
 +
if (this.noscale && !old_noscale)
 +
this.setScaleMode("noScale");
 +
else if (!this.noscale && old_noscale)
 +
this.setScaleMode("showAll");
 +
};
-
// modified from Homestar-Fullon
+
function Seekbar()
-
function doResize()
+
-
{
+
-
var dw = window.innerWidth;
+
-
var dh = window.innerHeight - 15; // things get weird sometimes... -15 to make sure we don't get scrollbars.
+
-
if (navbar)
+
{
{
-
// parseInt will take the number part at the start, turning eg "10px" into 10
+
this.enabled = utils.getPref('seekbar', true);
-
a = document.defaultView.getComputedStyle(navbar, null);
+
this.framecounter = utils.getPref('frames', false);
-
dh -= parseInt(a.height,10);
+
this.zoom = utils.getPref('zoom', false);
-
dh -= parseInt(a.marginTop,10);
+
-
dh -= parseInt(a.marginBottom,10);
+
}
}
-
if (seekbar)
+
Seekbar.prototype.init = function init() {
 +
this.setting_enabled = globals.modules.settingspane.addCheckbox('seekbar', "Show seek bar", "Lets you fast forward and rewind", this.enabled);
 +
this.setting_framecounter = globals.modules.settingspane.addCheckbox('framecounter', "Show frame counter on seek bar", "Shows you exactly where you are", this.framecounter, this.setting_enabled);
 +
this.setting_zoom = globals.modules.settingspane.addCheckbox('zoom', "Show zooming controls", "Allows zooming in on the toon", this.zoom, this.setting_enabled);
 +
 +
if (!globals.flashmovie)
 +
return;
 +
 +
if (this.enabled)
 +
this.addSeekbar();
 +
 +
this.dragging = false;
 +
this.paused = !utils.isPlaying();
 +
document.addEventListener("mousemove", this.dragMousemove.bind(this), false);
 +
document.addEventListener("mouseup", this.release.bind(this), false);
 +
 +
window.setInterval(this.update.bind(this), 50);
 +
};
 +
Seekbar.prototype.updateSettings = function updateSettings()
{
{
-
a = document.defaultView.getComputedStyle(seekbar, null)
+
if (this.enabled)
-
dh -= parseInt(a.height,10);
+
this.removeSeekbar();
-
dh -= parseInt(a.marginTop,10);
+
this.enabled = this.setting_enabled.checked;
-
dh -= parseInt(a.marginBottom,10);
+
utils.setPref("seekbar", this.enabled);
-
}
+
this.framecounter = this.setting_framecounter.checked;
-
if (subtitleholder)
+
utils.setPref("frames", this.framecounter);
 +
this.zoom = this.setting_zoom.checked;
 +
utils.setPref("zoom", this.zoom);
 +
if (this.enabled && globals.flashmovie)
 +
this.addSeekbar();
 +
};
 +
Seekbar.prototype.addSeekbar = function addSeekbar()
{
{
-
a = document.defaultView.getComputedStyle(subtitleholder, null)
+
this.dragging = false;
-
dh -= parseInt(a.height,10);
+
this.paused = !utils.isPlaying();
-
dh -= parseInt(a.marginTop,10);
+
-
dh -= parseInt(a.marginBottom,10);
+
this.seekbar = document.createElement("div");
-
}
+
var where = globals.flashmovie;
-
if (transcriptErrors)
+
while(where.parentNode.tagName.toLowerCase()=="object" || where.parentNode.tagName.toLowerCase()=="embed")
-
{
+
where=where.parentNode;
-
a = document.defaultView.getComputedStyle(transcriptErrors, null)
+
utils.insertAfter(this.seekbar, where);
-
dh -= parseInt(a.height,10);
+
this.seekbar.style.width = globals.flashmovie.width;
-
dh -= parseInt(a.marginTop,10);
+
this.seekbar.style.margin = "0 auto";
-
dh -= parseInt(a.marginBottom,10);
+
-
}
+
var table=document.createElement("table");
-
// enforce a (rather small) minimum size, regardless of how much crap is squeezed below the frame
+
table.style.width="100%";
-
if (dw < 100) dw = 100;
+
this.seekbar.appendChild(table);
-
if (dh < 100) dh = 100;
+
var row=table.insertRow();
-
// if it was a percentage size, or we're looking outside the frame, just fill the whole window.
+
this.pauseButton=document.createElement("button");
-
// otherwise, keep the aspect ratio correct... "touch inside" style.
+
this.pauseButtonImg = document.createElement("img");
-
if (!isPercentage && !settings.noscale)
+
this.pauseButtonImg.src = globals.images.pause;
-
{
+
this.pauseButton.appendChild(this.pauseButtonImg);
-
if(dw/aspect <= dh)
+
var buttonCell=row.insertCell();
-
dh = Math.floor(dw / aspect);
+
buttonCell.appendChild(this.pauseButton);
 +
var rewindCell=row.insertCell();
 +
this.rewindButton=document.createElement("button");
 +
var img = document.createElement("img");
 +
img.src = globals.images.rewind;
 +
this.rewindButton.appendChild(img);
 +
rewindCell.appendChild(this.rewindButton);
 +
var prevCell=row.insertCell();
 +
this.prevButton=document.createElement("button");
 +
img = document.createElement("img");
 +
img.src = globals.images.prev;
 +
this.prevButton.appendChild(img);
 +
prevCell.appendChild(this.prevButton);
 +
 +
this.slider=row.insertCell();
 +
this.slider.width="100%";
 +
var visibleSlider=document.createElement("div");
 +
visibleSlider.style.position="relative";
 +
visibleSlider.style.height="0.5em";
 +
visibleSlider.style.width="100%";
 +
visibleSlider.style.borderRadius="0.25em";
 +
visibleSlider.style.background="#333";
 +
this.slider.appendChild(visibleSlider);
 +
this.loadmeter=document.createElement("div");
 +
this.loadmeter.style.position="absolute";
 +
this.loadmeter.style.top=this.loadmeter.style.left = "0";
 +
this.loadmeter.style.height="0.5em";
 +
this.loadmeter.style.width="0";
 +
this.loadmeter.style.borderRadius="0.25em";
 +
this.loadmeter.style.background="#aaa";
 +
visibleSlider.appendChild(this.loadmeter);
 +
this.thumb=document.createElement("div");
 +
this.thumb.style.position="absolute";
 +
this.thumb.style.height="1em";
 +
this.thumb.style.width="0.5em";
 +
this.thumb.style.top="-0.25em";
 +
this.thumb.style.borderRadius="0.25em";
 +
this.thumb.style.background="#666";
 +
visibleSlider.appendChild(this.thumb);
 +
 +
var nextCell=row.insertCell();
 +
this.nextButton=document.createElement("button");
 +
img = document.createElement("img");
 +
img.src = globals.images.next;
 +
this.nextButton.appendChild(img);
 +
nextCell.appendChild(this.nextButton);
 +
var ffCell=row.insertCell();
 +
this.ffButton=document.createElement("button");
 +
img = document.createElement("img");
 +
img.src = globals.images.ffwd;
 +
this.ffButton.appendChild(img);
 +
ffCell.appendChild(this.ffButton);
 +
 +
if (this.framecounter)
 +
{
 +
var frameCell=row.insertCell();
 +
var framediv=document.createElement("div");
 +
framediv.style.background="#ccc";
 +
framediv.style.color="#000";
 +
framediv.style.fontWeight="bold";
 +
framediv.style.padding = "0 5px";
 +
frameCell.appendChild(framediv);
 +
this.framecountertext=document.createTextNode("");
 +
framediv.appendChild(this.framecountertext);
 +
}
else
else
-
dw = Math.floor(dh * aspect);
+
this.framecountertext = false;
-
}
+
-
 
+
-
// set embed's size
+
-
flashmovie.width = dw;
+
-
flashmovie.height = dh;
+
-
if (seekbar)
+
-
seekbar.style.width = Math.max(dw, 450) + "px";
+
-
}
+
-
function resize()
+
-
{
+
-
if (flashmovie.width.toString().indexOf('%') >= 0 || flashmovie.width.toString().indexOf('%') >= 0)
+
-
isPercentage = true;
+
-
else
+
-
aspect = flashmovie.width/flashmovie.height;
+
-
window.addEventListener('resize', doResize, false);
+
-
doResize();
+
-
}
+
-
 
+
-
// modified from the seek bar bookmarklet
+
-
function addFlashControls(flash)
+
-
{
+
-
seekbar=document.createElement("div");
+
-
seekbar.style.width=Math.max(parseInt(flash.width)||0,450) + "px";
+
-
seekbar.style.margin = "0 auto";
+
-
var where=flash;
+
-
while(where.parentNode.tagName.toLowerCase()=="object")
+
-
where=where.parentNode;
+
-
where.parentNode.insertBefore(seekbar,where.nextSibling);
+
-
var table=document.createElement("table");
+
-
table.style.width="100%";
+
-
seekbar.appendChild(table);
+
-
var row=table.insertRow(-1);
+
-
var pauseButton=document.createElement("button");
+
-
pauseButton.appendChild(document.createTextNode("Pause"));
+
-
var buttonCell=row.insertCell(-1);
+
-
buttonCell.appendChild(pauseButton);
+
-
var rewindCell=row.insertCell(-1);
+
-
var rewindButton=document.createElement("button");
+
-
rewindButton.appendChild(document.createTextNode("<<"));
+
-
rewindCell.appendChild(rewindButton);
+
-
var prevCell=row.insertCell(-1);
+
-
var prevButton=document.createElement("button");
+
-
prevButton.appendChild(document.createTextNode("|<"));
+
-
prevCell.appendChild(prevButton);
+
-
var slider=row.insertCell(-1);
+
-
slider.width="100%";
+
-
var visibleSlider=document.createElement("div");
+
-
visibleSlider.style.position="relative";
+
-
visibleSlider.style.height="10px";
+
-
visibleSlider.style.width="100%";
+
-
visibleSlider.style.MozBorderRadius="4px";
+
-
visibleSlider.style.background="#333";
+
-
slider.appendChild(visibleSlider);
+
-
var loadmeter=document.createElement("div");
+
-
loadmeter.style.position="absolute";
+
-
loadmeter.style.top=loadmeter.style.left = "0";
+
-
loadmeter.style.height="10px";
+
-
loadmeter.style.width="0px";
+
-
loadmeter.style.MozBorderRadius="4px";
+
-
loadmeter.style.background="#aaa";
+
-
visibleSlider.appendChild(loadmeter);
+
-
var thumb=document.createElement("div");
+
-
thumb.style.position="absolute";
+
-
thumb.style.height="20px";
+
-
thumb.style.width="10px";
+
-
thumb.style.top="-5px";
+
-
thumb.style.MozBorderRadius="4px";
+
-
thumb.style.background="#666";
+
-
visibleSlider.appendChild(thumb);
+
-
var nextCell=row.insertCell(-1);
+
-
var nextButton=document.createElement("button");
+
-
nextButton.appendChild(document.createTextNode(">|"));
+
-
nextCell.appendChild(nextButton);
+
-
var ffCell=row.insertCell(-1);
+
-
var ffButton=document.createElement("button");
+
-
ffButton.appendChild(document.createTextNode(">>"));
+
-
ffCell.appendChild(ffButton);
+
-
if (settings.frames)
+
-
{
+
-
var frameCell=row.insertCell(-1);
+
-
var framecounter=document.createElement("div");
+
-
framecounter.style.background="#ccc";
+
-
framecounter.style.color="#000";
+
-
framecounter.style.fontWeight="bold";
+
-
framecounter.style.padding = "0 5px";
+
-
frameCell.appendChild(framecounter);
+
-
framecountertext=document.createTextNode("");
+
-
framecounter.appendChild(framecountertext);
+
-
}
+
-
else
+
-
framecountertext = false;
+
-
if (!settings.noscale)
+
-
{
+
-
var zoomOutCell=row.insertCell(-1);
+
-
var zoomOutButton=document.createElement("button");
+
-
// \u2212 is &minus;
+
-
zoomOutButton.appendChild(document.createTextNode("\u2212"));
+
-
zoomOutCell.appendChild(zoomOutButton);
+
-
var zoomNormalCell=row.insertCell(-1);
+
-
var zoomNormalButton=document.createElement("button");
+
-
zoomNormalButton.appendChild(document.createTextNode("0"));
+
-
zoomNormalCell.appendChild(zoomNormalButton);
+
-
var zoomInCell=row.insertCell(-1);
+
-
var zoomInButton=document.createElement("button");
+
-
zoomInButton.appendChild(document.createTextNode("+"));
+
-
zoomInCell.appendChild(zoomInButton);
+
-
}
+
-
var sliderWidth;
+
-
var paused=false;
+
-
var dragging=false;
+
-
var lastframe=-1;
+
-
function pauseUnpause(){paused=flash.IsPlaying();pauseButton.style.borderStyle=paused?"inset":"";if(paused)flash.StopPlay();else flash.Play();}
+
if (this.zoom && !globals.modules.fullscreen.noscale)
-
function rewind(){flash.GotoFrame(0); flash.Play();}
+
{
-
function fastforward(){flash.GotoFrame(totalFrames() - 1);}
+
var zoomOutCell=row.insertCell();
-
function prevFrame(){flash.GotoFrame(flash.CurrentFrame()-1);}
+
this.zoomOutButton=document.createElement("button");
-
function nextFrame(){flash.GotoFrame(flash.CurrentFrame()+1);}
+
// \u2212 is &minus;
-
function zoomIn(){flash.Zoom(67);}
+
this.zoomOutButton.appendChild(document.createTextNode("\u2212"));
-
function zoomOut(){flash.Zoom(150);}
+
zoomOutCell.appendChild(this.zoomOutButton);
-
function zoomNormal(){flash.Zoom(0);}
+
var zoomNormalCell=row.insertCell();
-
pauseButton.addEventListener("click",pauseUnpause,false);
+
this.zoomNormalButton=document.createElement("button");
-
rewindButton.addEventListener("click",rewind,false);
+
this.zoomNormalButton.appendChild(document.createTextNode("0"));
-
prevButton.addEventListener("click",prevFrame,false);
+
zoomNormalCell.appendChild(this.zoomNormalButton);
-
nextButton.addEventListener("click",nextFrame,false);
+
var zoomInCell=row.insertCell();
-
ffButton.addEventListener("click",fastforward,false);
+
this.zoomInButton=document.createElement("button");
-
if (!settings.noscale)
+
this.zoomInButton.appendChild(document.createTextNode("+"));
 +
zoomInCell.appendChild(this.zoomInButton);
 +
}
 +
else
 +
{
 +
this.zoomOutButton = false;
 +
this.zoomNormalButton = false;
 +
this.zoomInButton = false;
 +
}
 +
 +
this.slider.addEventListener("mousedown", this.drag.bind(this), false);
 +
this.pauseButton.addEventListener("click",this.pauseUnpause.bind(this),false);
 +
this.rewindButton.addEventListener("click",this.rewind.bind(this),false);
 +
this.prevButton.addEventListener("click",this.prevFrame.bind(this),false);
 +
this.nextButton.addEventListener("click",this.nextFrame.bind(this),false);
 +
this.ffButton.addEventListener("click",this.fastforward.bind(this),false);
 +
if (this.zoomOutButton)
 +
{
 +
this.zoomOutButton.addEventListener("click",this.zoomOut.bind(this),false);
 +
this.zoomNormalButton.addEventListener("click",this.zoomNormal.bind(this),false);
 +
this.zoomInButton.addEventListener("click",this.zoomIn.bind(this),false);
 +
}
 +
 +
globals.modules.fullscreen.doResize();
 +
};
 +
Seekbar.prototype.removeSeekbar = function removeSeekbar()
{
{
-
zoomOutButton.addEventListener("click",zoomOut,false);
+
if (!this.seekbar)
-
zoomNormalButton.addEventListener("click",zoomNormal,false);
+
return;
-
zoomInButton.addEventListener("click",zoomIn,false);
+
this.seekbar.parentNode.removeChild(this.seekbar);
-
}
+
this.seekbar = undefined;
 +
globals.modules.fullscreen.doResize();
 +
};
-
function update()
+
Seekbar.prototype.update = function update()
{
{
-
var fullSliderWidth=parseInt(getWidth(slider));
+
if (!this.seekbar)
-
sliderWidth=parseInt(fullSliderWidth-getWidth(thumb));
+
return;
-
var tot=totalFrames();
+
 +
var fullSliderWidth = parseInt(document.defaultView.getComputedStyle(this.slider, null).width, 10);
 +
var sliderWidth = fullSliderWidth - parseInt(document.defaultView.getComputedStyle(this.thumb, null).width, 10);
 +
var tot = utils.totalFrames();
if (tot > 0)
if (tot > 0)
{
{
-
var frame=flash.CurrentFrame();
+
var frame = utils.currentFrame();
if (frame < 0)
if (frame < 0)
frame = 0;
frame = 0;
-
if (framecountertext)
+
if (this.framecountertext)
{
{
var a = tot.toString();
var a = tot.toString();
Line 1,038: Line 1,304:
while (b.length < a.length)
while (b.length < a.length)
b = "\u2007" + b; // U+2007 FIGURE SPACE
b = "\u2007" + b; // U+2007 FIGURE SPACE
-
framecountertext.nodeValue=b+"/"+a;
+
this.framecountertext.nodeValue = b+"/"+a;
}
}
-
if(!dragging)
+
if(!this.dragging)
{
{
if (tot > 1)
if (tot > 1)
-
thumb.style.left=(frame/(tot - 1)*sliderWidth)+"px";
+
this.thumb.style.left = (frame/(tot - 1)*sliderWidth)+"px";
else
else
-
thumb.style.left="0px";
+
this.thumb.style.left = "0";
-
paused=!flash.IsPlaying();
+
this.paused = !utils.isPlaying();
-
pauseButton.style.borderStyle=paused?"inset":"";
+
this.pauseButtonImg.src = this.paused ? globals.images.play : globals.images.pause;
}
}
-
frame=flash.TGetProperty('/', 12); // property 12 is _framesloaded
+
frame = utils.framesLoaded();
-
loadmeter.style.width=(frame/tot*fullSliderWidth)+"px";
+
this.loadmeter.style.width = (frame/tot*fullSliderWidth)+"px";
}
}
-
else if (framecountertext)
+
else if (this.framecountertext)
{
{
-
framecountertext.nodeValue="Loading...";
+
this.framecountertext.nodeValue = "Loading...";
}
}
-
}
+
};
-
window.setInterval(update,50);
+
-
function dragMousemove(e)
+
Seekbar.prototype.pauseUnpause = function pauseUnpause()
{
{
-
if (!dragging) return;
+
this.paused = utils.isPlaying();
-
var pageX=e.clientX+document.body.scrollLeft;
+
this.pauseButtonImg.src = this.paused ? globals.images.play : globals.images.pause;
-
var pos=bounds(0,pageX-getX(slider)-5,sliderWidth);
+
if (this.paused)
-
t = totalFrames();
+
utils.stop();
-
if (t > 1)
+
else
-
{
+
utils.play();
-
var frame=bounds(0,Math.round((t - 1)*pos/sliderWidth),t - 1);
+
};
-
flash.GotoFrame(frame);
+
Seekbar.prototype.rewind = function rewind()
-
}
+
-
thumb.style.left=pos+"px";
+
-
}
+
-
function release(e)
+
{
{
-
if (!dragging) return;
+
utils.goto(0);
-
if(!paused)
+
utils.play();
-
flash.Play();
+
};
-
dragging=false;
+
Seekbar.prototype.fastforward = function fastforward()
-
}
+
-
function drag(e)
+
{
{
-
dragging=true;
+
utils.goto(utils.totalFrames() - 1);
-
dragMousemove(e);
+
};
 +
Seekbar.prototype.prevFrame = function prevFrame()
 +
{
 +
utils.goto(utils.currentFrame() - 1);
 +
};
 +
Seekbar.prototype.nextFrame = function nextFrame()
 +
{
 +
utils.goto(utils.currentFrame() + 1);
 +
};
 +
Seekbar.prototype.zoomIn = function zoomIn()
 +
{
 +
utils.zoomIn(1.5);
 +
};
 +
Seekbar.prototype.zoomOut = function zoomOut()
 +
{
 +
utils.zoomOut(1.5);
 +
};
 +
Seekbar.prototype.zoomNormal = function zoomNormal()
 +
{
 +
utils.zoomReset();
 +
};
 +
 +
Seekbar.prototype.drag = function drag(e)
 +
{
 +
this.dragging=true;
 +
this.dragMousemove(e);
e.preventDefault();
e.preventDefault();
return false;
return false;
-
}
+
};
-
function bounds(min,val,max){return Math.min(Math.max(min,val),max);}
+
Seekbar.prototype.dragMousemove = function dragMousemove(e)
-
function totalFrames(){if(typeof flash.TotalFrames=="number")return flash.TotalFrames;else if(typeof flash.TotalFrames=="function" || typeof flash.TotalFrames=="object")return flash.TotalFrames();else return 1;}
+
{
-
function getWidth(elem){if(document.defaultView&&document.defaultView.getComputedStyle)return parseFloat(document.defaultView.getComputedStyle(elem,null).getPropertyValue("width"));else return parseFloat(elem.offsetWidth);}
+
if (!this.dragging) return;
-
function getX(elem){if(!elem) return 0;return(elem.offsetLeft)+getX(elem.offsetParent);}
+
var pageX = e.clientX + document.body.scrollLeft;
-
slider.addEventListener("mousedown",drag,false);
+
var rect = this.slider.getBoundingClientRect();
-
document.addEventListener("mousemove",dragMousemove,false);
+
var thumbWidth = parseInt(document.defaultView.getComputedStyle(this.thumb, null).width, 10);
-
document.addEventListener("mouseup",release,false);
+
var width = rect.right - rect.left - thumbWidth;
-
}
+
var pos = (pageX - rect.left - thumbWidth/2) / width;
-
 
+
if (pos < 0)
-
function noscale()
+
pos = 0;
-
{
+
if (pos > 1)
-
// have to wait until the Flash has loaded, otherwise SetVariable will fail.
+
pos = 1;
-
// best way I have found to test this, is if the current frame is >= 0...
+
var t = utils.totalFrames();
-
// before it's loaded, it's -1.
+
if (t > 1)
-
// Before Flash itself initialises, CurrentFrame doesn't exist, so check for that too.
+
{
-
var a = flashmovie.CurrentFrame;
+
var frame = Math.round(t * pos);
-
if (typeof(a) == "function" || typeof(a) == "object")
+
utils.goto(frame);
-
a = flashmovie.CurrentFrame();
+
}
-
if (typeof(a) == "number" && a >= 0 && flashmovie.SetVariable)
+
this.thumb.style.left = (pos * width) + "px";
-
flashmovie.SetVariable("Stage.scaleMode", "noScale");
+
};
-
else
+
Seekbar.prototype.release = function release()
-
setTimeout(noscale, 10);
+
{
-
}
+
if (!this.dragging) return;
-
function showmenu()
+
if (!this.paused)
-
{
+
utils.play();
-
var a = flashmovie.CurrentFrame;
+
this.dragging = false;
-
if (typeof(a) == "function" || typeof(a) == "object")
+
};
-
a = flashmovie.CurrentFrame();
+
-
if (typeof(a) == "number" && a >= 0 && flashmovie.SetVariable)
+
-
flashmovie.SetVariable("Stage.showMenu", 1);
+
-
else
+
-
setTimeout(showmenu, 10);
+
-
}
+
-
function addHRWikiLink(pagename, isurl)
+
function WikiLink()
-
{
+
-
div = document.createElement("div")
+
-
div.style.borderLeft = div.style.borderBottom = '1px solid #666';
+
-
div.style.background = '#EEE';
+
-
div.style.position = "fixed";
+
-
div.style.overflow = 'auto';
+
-
div.style.right = "0px";
+
-
div.style.top = "0px";
+
-
div.style.padding = "3px";
+
-
link = document.createElement("a");
+
-
if (isurl)
+
-
link.href = pagename;
+
-
else
+
-
link.href = "http://www.hrwiki.org/wiki/" + escape(pagename.replace(/ /g, '_'));
+
-
link.title = "See the HRWiki article for this page";
+
-
link.style.display = "block";
+
-
link.style.textDecoration = "none";
+
-
div.appendChild(link);
+
-
img=document.createElement("img");
+
-
img.style.border="0px";
+
-
img.style.display="block";
+
-
img.src=image_hrwiki;
+
-
link.appendChild(img);
+
-
if (settings.subtitles)
+
{
{
-
link = document.createElement("a");
+
this.enabled = utils.getPref('hrwiki', true);
-
link.href = "http://www.hrwiki.org/wiki/Subtitles:" + escape(filename.replace(/ /g, '_')) + "/" + escape(settings.language);
+
-
link.title = "See the HRWiki article for this page's subtitles";
+
-
link.style.display = "block";
+
-
link.style.textDecoration = "none"
+
-
link.style.textAlign = "center";
+
-
link.style.fontSize = link.style.lineHeight = "16px";
+
-
link.style.marginTop = "3px";
+
-
div.appendChild(link);
+
-
link.appendChild(document.createTextNode('S'));
+
}
}
-
document.body.appendChild(div);
+
WikiLink.prototype.init = function init()
-
}
+
-
function wikilink()
+
-
{
+
-
// many pages on the mirror have an "info" link in the navbar (thanks Tom!)... use that
+
-
if (whichsite == 3)
+
{
{
-
var a = document.getElementById("navbar");
+
this.setting_enabled = globals.modules.settingspane.addCheckbox('hrwiki', "Add HRWiki link", "Adds a link to the appropriate page on the Homestar Runner Wiki", this.enabled);
-
if (a) a = a.getElementsByTagName("a");
+
-
if (a)
+
this.buildWikiLink();
 +
this.showWikiLink();
 +
};
 +
WikiLink.prototype.updateSettings = function updateSettings()
 +
{
 +
this.enabled = this.setting_enabled.checked;
 +
utils.setPref("hrwiki", this.enabled);
 +
// This is called before Subtitles.updateSettings, so delay until after that happens
 +
// so we can update the subtitles link as appropriate
 +
window.setTimeout(this.showWikiLink.bind(this), 0);
 +
};
 +
 +
WikiLink.prototype.buildWikiLink = function buildWikiLink()
 +
{
 +
// many pages on the mirror have an "info" link in the navbar (thanks Tom!)... use that
 +
if (globals.whichsite === 3)
{
{
-
for (i = 0; i < a.length; i++)
+
var navbar;
 +
if (globals.modules.navbar && globals.modules.navbar.originalnavbar)
 +
navbar = globals.modules.navbar.originalnavbar;
 +
else
 +
navbar = globals.navbar;
 +
if (navbar)
{
{
-
if (a[i].firstChild.nodeType == 3 && a[i].firstChild.nodeValue == "info")
+
var a = navbar.getElementsByTagName("a");
 +
for (var i = 0; i < a.length; i++)
{
{
-
addHRWikiLink(a[i].href, true);
+
if (a[i].firstChild.nodeType === 3 && a[i].firstChild.nodeValue === "info")
-
return;
+
{
 +
this.addHRWikiLink(a[i].href, true);
 +
return;
 +
}
}
}
}
}
}
}
-
}
+
 +
// pull the filename from the url, use it as a link to HRWiki
 +
// all the filenames except a couple of special-cases are
 +
//  redirects to their articles
 +
// don't link to certain pages, they aren't redirects, but already existing pages
 +
// also detect a 404 error and special-case Strong Sad's Lament
 +
    if (document.title === "Oops! You bwoke it.")
 +
this.addHRWikiLink("404'd");
 +
else if (globals.filename === "interview")
 +
this.addHRWikiLink("The_Interview");
 +
else if (globals.filename === "fhqwhgads")
 +
this.addHRWikiLink("Everybody_to_the_Limit");
 +
else if (globals.filename === "trogdor")
 +
this.addHRWikiLink("TROGDOR!");
 +
else if (globals.filename === "marshie")
 +
this.addHRWikiLink("Meet_Marshie");
 +
else if (globals.filename === "eggs")
 +
this.addHRWikiLink("Eggs_(toon)");
 +
else if (globals.filename === "fireworks")
 +
this.addHRWikiLink("Happy_Fireworks");
 +
else if (globals.filename === "sbemail100")
 +
this.addHRWikiLink("Not_the_100th_Email!!!");
 +
else if (globals.filename === "sbemail200")
 +
this.addHRWikiLink("Page_Load_Error");
 +
else if (globals.filename === "sbcg4ap")
 +
this.addHRWikiLink("Strong_Bad's_Cool_Game_for_Attractive_People_Advertisement");
 +
else if (globals.filename === "dangeresque")
 +
this.addHRWikiLink("Dangeresque_Roomisode_1:_Behind_the_Dangerdesque");
 +
else if (location.pathname.substr(0, 12) === "/sadjournal/" && globals.filename != "wonderyears" && globals.filename != "super8")
 +
this.addHRWikiLink("Strong_Sad's_Lament");
 +
else if (location.pathname.substr(0,5) === "/vii/" && (globals.filename === "" || globals.filename === "index"))
 +
this.addHRWikiLink("Viidelectrix");
 +
else if (globals.filename === "" || globals.filename === "index")
 +
{
 +
    if (globals.whichsite === 0)
 +
this.addHRWikiLink("Index_Page");
 +
else if (globals.whichsite === 1)
 +
this.addHRWikiLink("Podstar_Runner");
 +
else if (globals.whichsite === 2)
 +
this.addHRWikiLink("Videlectrix");
 +
//else if (globals.whichsite === 3)
 +
// ; // this will be a 403 page - do nothing.
 +
}
 +
else
 +
this.addHRWikiLink(globals.filename);
 +
};
-
// pull the filename from the url, use it as a link to HRWiki
+
WikiLink.prototype.addHRWikiLink = function addHRWikiLink(pagename, isurl)
-
// all the filenames except a couple of special-cases are
+
-
//  redirects to their articles
+
-
// don't link to certain pages, they aren't redirects, but already existing pages
+
-
// also detect a 404 error and special-case Strong Sad's Lament
+
-
    if (document.title == "Oops! You bwoke it.")
+
-
addHRWikiLink("404'd");
+
-
else if (filename == "interview")
+
-
addHRWikiLink("The_Interview");
+
-
else if (filename == "fhqwhgads")
+
-
addHRWikiLink("Everybody_to_the_Limit");
+
-
else if (filename == "trogdor")
+
-
addHRWikiLink("TROGDOR!");
+
-
else if (filename == "marshie")
+
-
addHRWikiLink("Meet_Marshie");
+
-
else if (filename == "eggs")
+
-
addHRWikiLink("Eggs_(toon)");
+
-
else if (filename == "fireworks")
+
-
addHRWikiLink("Happy_Fireworks");
+
-
else if (filename == "sbemail100")
+
-
addHRWikiLink("Not_the_100th_Email!!!");
+
-
else if (filename == "sbemail200")
+
-
addHRWikiLink("Page_Load_Error");
+
-
else if (filename == "sbcg4ap")
+
-
addHRWikiLink("Strong_Bad's_Cool_Game_for_Attractive_People_Advertisement");
+
-
else if (filename == "dangeresque")
+
-
addHRWikiLink("Dangeresque_Roomisode_1:_Behind_the_Dangerdesque");
+
-
else if (location.pathname.substr(0, 12) == "/sadjournal/" && filename != "wonderyears" && filename != "super8")
+
-
addHRWikiLink("Strong_Sad's_Lament");
+
-
else if (location.pathname.substr(0,5) == "/vii/" && (filename == "" || filename == "index"))
+
-
addHRWikiLink("Viidelectrix");
+
-
else if (filename == "" || filename == "index")
+
{
{
-
    if (whichsite == 0)
+
this.linkdiv = document.createElement("div");
-
addHRWikiLink("Index_Page");
+
this.linkdiv.style.borderLeft = this.linkdiv.style.borderBottom = '1px solid #666';
-
else if (whichsite == 1)
+
this.linkdiv.style.background = '#EEE';
-
addHRWikiLink("Podstar_Runner");
+
this.linkdiv.style.position = "fixed";
-
else if (whichsite == 2)
+
this.linkdiv.style.overflow = 'auto';
-
addHRWikiLink("Videlectrix");
+
this.linkdiv.style.right = "0px";
-
else if (whichsite == 3)
+
this.linkdiv.style.top = "0px";
-
; // this will be a 403 page - do nothing.
+
this.linkdiv.style.padding = "3px";
-
}
+
-
else
+
-
addHRWikiLink(filename);
+
-
}
+
-
 
+
-
// Prev/Next links
+
-
function addprevnextlinks(prefix,number)
+
-
{
+
-
if (number > 1)
+
-
{
+
-
var prevnum = (number - 1).toString();
+
var link = document.createElement("a");
var link = document.createElement("a");
-
if (prefix == "sbemail" && number == 101)
+
if (isurl)
-
link.href="sbemailahundred.html";
+
link.href = pagename;
-
else if (prefix == "sbemail" && number == 152)
+
-
link.href="kotpoptoon.html";
+
-
else if (prefix == "sbemail" && number == 201)
+
-
link.href="sbemailtwohundred.html";
+
-
else if (prefix == "sbemail" && number == 202)
+
-
link.href="hremail3184.html";
+
else
else
-
link.href=prefix+prevnum+".html";
+
link.href = "http://www.hrwiki.org/wiki/" + escape(pagename.replace(/ /g, '_'));
-
link.style.position="fixed";
+
link.title = "See the HRWiki article for this page";
-
link.style.left="0px";
+
link.style.display = "block";
-
link.style.bottom="0px";
+
link.style.textDecoration = "none";
-
link.style.padding="3px";
+
this.linkdiv.appendChild(link);
-
link.style.background="white";
+
var img=document.createElement("img");
-
link.style.border="1px solid black";
+
img.style.border="0px";
-
link.style.textDecoration="none";
+
img.style.display="block";
-
link.appendChild(document.createTextNode('<'));
+
img.src=globals.images.hrwiki;
-
document.body.appendChild(link);
+
link.appendChild(img);
-
}
+
this.sublink = document.createElement("a");
-
 
+
this.sublink.title = "See the HRWiki article for this page's subtitles";
-
var nextnum = (number + 1).toString();
+
this.sublink.style.display = "block";
-
var link = document.createElement("a");
+
this.sublink.style.textDecoration = "none";
-
if (prefix == "sbemail" && number == 99)
+
this.sublink.style.textAlign = "center";
-
link.href="sbemailahundred.html";
+
this.sublink.style.fontSize = this.sublink.style.lineHeight = "16px";
-
else if (prefix == "sbemail" && number == 150)
+
this.sublink.style.marginTop = "3px";
-
link.href="kotpoptoon.html";
+
this.linkdiv.appendChild(this.sublink);
-
else if (prefix == "sbemail" && number == 199)
+
this.sublink.appendChild(document.createTextNode('S'));
-
link.href="sbemailtwohundred.html";
+
document.body.appendChild(this.linkdiv);
-
else if (prefix == "sbemail" && number == 200)
+
};
-
link.href="hremail3184.html";
+
-
else
+
WikiLink.prototype.showWikiLink = function showWikiLink()
-
link.href=prefix+nextnum+".html";
+
-
link.style.position="fixed";
+
-
link.style.right="0px";
+
-
link.style.bottom="0px";
+
-
link.style.padding="3px";
+
-
link.style.background="white";
+
-
link.style.border="1px solid black";
+
-
link.style.textDecoration="none";
+
-
link.appendChild(document.createTextNode('>'));
+
-
if (settings['checknext'])
+
{
{
-
GM_xmlhttpRequest({
+
if (this.enabled)
-
method: "HEAD",
+
{
-
url: link.href + "?cachedodge=" + GM_getValue('cachedodge', 0),
+
this.linkdiv.style.display = "block";
-
onload: function (results)
+
if (globals.modules.subtitles && globals.modules.subtitles.enabled)
{
{
-
if (results.status == 200 && results.responseHeaders.indexOf("404error.html") < 0)
+
this.sublink.style.display = "block";
-
document.body.appendChild(link);
+
this.sublink.href = "http://www.hrwiki.org/wiki/Subtitles:" + escape(globals.filename.replace(/ /g, '_')) + "/" + escape(globals.modules.subtitles.language);
}
}
-
});
+
else
-
}
+
this.sublink.style.display = "none";
-
else
+
}
-
document.body.appendChild(link);
+
else
-
}
+
this.linkdiv.style.display = "none";
-
function prevnext()
+
};
-
{
+
 
-
// this is coded like this instead of just looking for /(\d+)/ so that it
+
function NextPrev()
-
// doesn't find pages like commandos3 or xmas04
+
-
var result;
+
-
if ((result = filename.match(/^(sbemail|tgs|answer|bizcasfri|puppetjam|main)(\d+)$/)))
+
{
{
-
// sbemail100 and sbemail200 aren't actually sbemails
+
this.enabled = utils.getPref('prevnext', true);
-
if (!(result[1] == "sbemail" && (result[2] == "100" || result[2] == "200")))
+
this.docheck = utils.getPref('checknext', true);
-
addprevnextlinks(result[1],parseInt(result[2],10));
+
}
}
-
else if (filename == "sbemailahundred")
+
NextPrev.prototype.init = function init()
-
addprevnextlinks("sbemail", 100);
+
-
else if (filename == "kotpoptoon")
+
-
addprevnextlinks("sbemail", 151);
+
-
else if (filename == "sbemailtwohundred")
+
-
addprevnextlinks("sbemail", 200);
+
-
else if (filename == "hremail3184")
+
-
addprevnextlinks("sbemail", 201);
+
-
else if (filename == "dween_tgs")
+
-
addprevnextlinks("tgs", 6);
+
-
}
+
-
 
+
-
function flipper()
+
-
{
+
-
a = getSWFFilename();
+
-
if (!a)
+
-
return;
+
-
    if (filename == "toons")
+
-
replaceFlash("theyCallHimFlipperToons.swf?contentURL=" + escape(a));
+
-
else if (filename == "games")
+
-
replaceFlash("theyCallHimFlipperGames.swf?contentURL=" + escape(a));
+
-
else if (filename == "payplus")
+
-
replaceFlash("theyCallHimFlipperPayPlus.swf?contentURL=" + escape(a));
+
-
else if (filename == "main22")
+
-
replaceFlash("theyCallHimFlipperVirusMain.swf?contentURL=" + escape(a));
+
-
else if (filename == "sbemail118")
+
-
// virus *wasn't* flipped correctly originally, but I think having it work is better than
+
-
// preserving the glitch.
+
-
replaceFlash("theyCallHimFlipperVirusMain.swf?contentURL=" + escape(a));
+
-
else if (filename == "sbemailahundred")
+
-
replaceFlash("theyCallHimFlipperHundred.swf?contentURL=" + escape(a));
+
-
else
+
-
replaceFlash("theyCallHimFlipper.swf?contentURL=" + escape(a));
+
-
}
+
-
 
+
-
function addnavbarlink(ul,href,title, extraclass)
+
-
{
+
-
var li = document.createElement("li");
+
-
var link = document.createElement("a");
+
-
link.href = href;
+
-
link.appendChild(document.createTextNode(title));
+
-
if (extraclass)
+
-
link.className = extraclass;
+
-
li.appendChild(link);
+
-
ul.appendChild(li);
+
-
return link;
+
-
}
+
-
function replacenavbar()
+
-
{
+
-
// need to add the styles as a stylesheet, rather than inline styles
+
-
// so that we can use :hover
+
-
GM_addStyle(
+
-
"#newnavbar { margin: 0; padding: 0; text-align: center; text-transform: lowercase; } " +
+
-
"#newnavbar li { margin: 0; padding: 0; display: inline; } " +
+
-
"#newnavbar :link, #newnavbar :visited { color: #666; font-family: sans-serif; text-decoration: none; padding: 0 1em; } " +
+
-
"#newnavbar :link:hover, #newnavbar :visited:hover { color: #999; } " +
+
-
// for overriding podstar's settings:
+
-
"#newnavbar :link, #newnavbar :visited { font-weight: normal; } " +
+
-
"#newnavbar :link:hover, #newnavbar :visited:hover { background: transparent; font-weight: normal; } "
+
-
);
+
-
var newnavbar = document.createElement("ul");
+
-
newnavbar.id = "newnavbar";
+
-
newnavbar.style.height = newnavbar.style.fontSize = newnavbar.style.lineHeight = "10px";
+
-
if (!settings.seekbar)
+
-
newnavbar.style.marginTop = "10px";
+
-
if (navbar)
+
{
{
-
var where = navbar;
+
this.setting_enabled = globals.modules.settingspane.addCheckbox('prevnext', "Show previous/next buttons", "Lets you easily move through SBEmails, TGS, etc", this.enabled);
-
while(where.parentNode.tagName.toLowerCase() == "object")
+
this.setting_docheck = globals.modules.settingspane.addCheckbox('checknext', "Check if next exists", 'Doesn\'t add a "next" link on the latest SBEmail, etc', this.docheck, this.setting_enabled);
-
where = where.parentNode;
+
-
where.parentNode.insertBefore(newnavbar, where);
+
this.createPrevNext();
-
if (whichsite == 3)
+
this.showPrevNext();
-
where.style.display = "none";
+
};
 +
NextPrev.prototype.updateSettings = function updateSettings()
 +
{
 +
this.enabled = this.setting_enabled.checked;
 +
utils.setPref("prevnext", this.enabled);
 +
this.docheck = this.setting_docheck.checked;
 +
utils.setPref("checknext", this.docheck);
 +
this.showPrevNext();
 +
};
 +
 +
NextPrev.prototype.createPrevNext = function createPrevNext()
 +
{
 +
// this is coded like this instead of just looking for /(\d+)/ so that it
 +
// doesn't find pages like commandos3 or xmas04
 +
var result;
 +
if ((result = globals.filename.match(/^(sbemail|tgs|answer|bizcasfri|puppetjam|main)(\d+)$/)))
 +
{
 +
// sbemail100 and sbemail200 aren't actually sbemails
 +
if (!(result[1] == "sbemail" && (result[2] == "100" || result[2] == "200")))
 +
this.addPrevNextlinks(result[1],parseInt(result[2],10));
 +
}
 +
else if (globals.filename == "sbemailahundred")
 +
this.addPrevNextlinks("sbemail", 100);
 +
else if (globals.filename == "kotpoptoon")
 +
this.addPrevNextlinks("sbemail", 151);
 +
else if (globals.filename == "sbemailtwohundred")
 +
this.addPrevNextlinks("sbemail", 200);
 +
else if (globals.filename == "hremail3184")
 +
this.addPrevNextlinks("sbemail", 201);
 +
else if (globals.filename == "dween_tgs")
 +
this.addPrevNextlinks("tgs", 6);
 +
};
 +
NextPrev.prototype.addPrevNextlinks = function addPrevNextlinks(series, num)
 +
{
 +
if (num > 1)
 +
{
 +
this.prevlink = document.createElement("a");
 +
this.prevlink.href = this.makeLink(series, num - 1);
 +
this.prevlink.style.position="fixed";
 +
this.prevlink.style.left="0px";
 +
this.prevlink.style.bottom="0px";
 +
this.prevlink.style.padding="3px";
 +
this.prevlink.style.background="white";
 +
this.prevlink.style.border="1px solid black";
 +
this.prevlink.style.textDecoration="none";
 +
this.prevlink.style.display = "none";
 +
var img = document.createElement("img");
 +
img.style.border = "none";
 +
img.src = globals.images.prev;
 +
this.prevlink.appendChild(img);
 +
document.body.appendChild(this.prevlink);
 +
}
 +
 +
this.nextlink = document.createElement("a");
 +
this.nextlink.href = this.makeLink(series, num + 1);
 +
this.nextlink.style.position="fixed";
 +
this.nextlink.style.right="0px";
 +
this.nextlink.style.bottom="0px";
 +
this.nextlink.style.padding="3px";
 +
this.nextlink.style.background="white";
 +
this.nextlink.style.border="1px solid black";
 +
this.nextlink.style.textDecoration="none";
 +
this.nextlink.style.display = "none";
 +
img = document.createElement("img");
 +
img.style.border = "none";
 +
img.src = globals.images.next;
 +
this.nextlink.appendChild(img);
 +
document.body.appendChild(this.nextlink);
 +
 +
this.checkedNext = false;
 +
};
 +
NextPrev.prototype.makeLink = function makeLink(series, num)
 +
{
 +
if (series == "sbemail" && num == 100)
 +
return "sbemailahundred.html";
 +
else if (series == "sbemail" && num == 151)
 +
return "kotpoptoon.html";
 +
else if (series == "sbemail" && num == 200)
 +
return "sbemailtwohundred.html";
 +
else if (series == "sbemail" && num == 201)
 +
return "hremail3184.html";
else
else
-
where.parentNode.removeChild(where);
+
return series + num + ".html";
-
}
+
};
-
else
+
-
document.body.appendChild(newnavbar);
+
-
mainlink = addnavbarlink(newnavbar, "http://www.homestarrunner.com/main" + Math.floor(Math.random() * 25 + 1) + ".html", "Main");
+
-
// just for fun, re-randomise on each mouse-over (for the status bar)
+
-
mainlink.addEventListener("mouseout",function(){mainlink.href="http://www.homestarrunner.com/main" + Math.floor(Math.random() * 25 + 1) + ".html"}, false);
+
-
addnavbarlink(newnavbar, "http://www.homestarrunner.com/toons.html", "Toons");
+
-
addnavbarlink(newnavbar, "http://www.homestarrunner.com/games.html", "Games");
+
-
addnavbarlink(newnavbar, "http://www.homestarrunner.com/characters2.html", "Characters");
+
-
addnavbarlink(newnavbar, "http://www.homestarrunner.com/downloads.html", "Downloads");
+
-
addnavbarlink(newnavbar, "http://homestarrunner.stores.yahoo.net/", "Store", "storelink");
+
-
addnavbarlink(newnavbar, "http://www.homestarrunner.com/sbemail.html", "SB Emails");
+
-
addnavbarlink(newnavbar, "http://feeds.feedburner.com/HomestarRunner", "Subscribe");
+
-
addnavbarlink(newnavbar, "http://www.homestarrunner.com/email.html", "Contact");
+
-
//addnavbarlink(newnavbar, "http://podstar.homestarrunner.com/", "Podcast");
+
-
addnavbarlink(newnavbar, "http://www.homestarrunner.com/legal.html", "Legal");
+
-
randolink = addnavbarlink(newnavbar, "javascript:alert('rando.xml not loaded yet... be patient')", "Rando");
+
-
// load rando.xml and handle all that jazz
+
-
GM_xmlhttpRequest({
+
-
method: "GET",
+
-
url: "http://www.homestarrunner.com/rando.xml?cachedodge=" + GM_getValue('cachedodge', 0),
+
-
onload: randoxml_loaded
+
-
});
+
-
navbar = newnavbar;
+
NextPrev.prototype.showPrevNext = function showPrevNext()
-
}
+
-
function randoxml_loaded(results)
+
-
{
+
-
// info on DOMParser stole from http://www.webreference.com/programming/javascript/domwrapper/3.html
+
-
var parser = new DOMParser();
+
-
// fix invalid XML...
+
-
// add missing root element
+
-
var doc = results.responseText.replace(/<\?xml.*?\?>/g, ""); // strip <?xml ?> tag
+
-
doc = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<rando>" + doc + "</rando>";
+
-
// fix bad ampersands
+
-
doc = doc.replace(/&(?!\w*;)/g, "&amp;");
+
-
doc = parser.parseFromString(doc, "application/xml");
+
-
var sbemailcounter = 0;
+
-
for (i = 0; i < doc.documentElement.childNodes.length; i++)
+
{
{
-
if (doc.documentElement.childNodes[i].nodeType == 1)
+
if (this.enabled)
{
{
-
var type = doc.documentElement.childNodes[i].nodeName.toLowerCase();
+
if (this.prevlink)
-
if (settings['rando' + type] == false) // == false so that it's considered "true" for undefined... if they add a new toon type
+
this.prevlink.style.display = "block";
-
continue;
+
if (this.docheck && !this.checkedNext && this.nextlink)
-
var u = doc.documentElement.childNodes[i].getAttribute('u');
+
utils.downloadPage(this.nextlink.href + "?cachedodge=" + GM_getValue('cachedodge', 0), this.onCheckLoad.bind(this), this.onCheckError.bind(this), "HEAD");
-
var n = doc.documentElement.childNodes[i].getAttribute('n');
+
else if (this.nextlink)
-
if (!n) n = "Untitled";
+
this.nextlink.style.display = "block";
-
if (type == "sb")
+
-
{
+
-
sbemailcounter++;
+
-
n = "SBEmail: " + n;
+
-
}
+
-
if (u)
+
-
randourls[randourls.length] = {u: "http://www.homestarrunner.com/" + u, n: n};
+
-
else
+
-
randourls[randourls.length] = {u: "http://www.homestarrunner.com/sbemail" + sbemailcounter + ".html", n: n};
+
}
}
-
}
+
else
-
newrandolink()
+
{
-
// just for fun, re-randomise on each mouse-over (for the status bar)
+
if (this.prevlink)
-
randolink.addEventListener('mouseout', newrandolink, false);
+
this.prevlink.style.display = "none";
-
}
+
if (this.nextlink)
-
function newrandolink()
+
this.nextlink.style.display = "none";
-
{
+
}
-
if (randourls.length > 0)
+
};
 +
NextPrev.prototype.onCheckLoad = function onCheckLoad(text, status, statustext, headers)
{
{
-
var r = randourls[Math.floor(Math.random() * randourls.length)];
+
if (status == 200 && headers.indexOf("404error.html") < 0)
-
randolink.href = r.u;
+
{
-
randolink.title = r.n;
+
this.checkedNext = true;
-
}
+
this.showPrevNext();
-
else
+
}
 +
else if (this.nextlink)
 +
{
 +
this.nextlink.parentNode.removeChild(this.nextlink);
 +
this.nextlink = undefined;
 +
}
 +
};
 +
NextPrev.prototype.onCheckError = function onCheckError()
{
{
-
randolink.href = "javascript:alert('Nothing to choose from')";
+
this.nextlink.parentNode.removeChild(this.nextlink);
-
randolink.title = "Nothing to choose from";
+
this.nextlink = undefined;
-
}
+
};
-
}
+
-
function downloadxmlfromwiki(url, callback)
+
function Navbar()
-
{
+
-
url = escape(url.replace(/ /g, '_'));
+
-
GM_xmlhttpRequest({
+
-
method: "GET",
+
-
url: "http://www.hrwiki.org/w/index.php?title=" + url + "&action=raw&cachedodge=" + GM_getValue('cachedodge', 0),
+
-
onload: function(x) { xmldownloaded(x, callback, false, false); }
+
-
});
+
-
}
+
-
function xmldownloaded(results, callback, redirected, showerrors)
+
-
{
+
-
var text = results.responseText;
+
-
// strip various things - templates and <pre> tags for wiki formatting, and <noinclude> sections...
+
-
// <includeonly> tags are stripped (but their contents kept) for consistency.
+
-
text = text.replace(/{{.*?}}/g, "");
+
-
text = text.replace(/<\/?pre[^>]*>/g, "");
+
-
text = text.replace(/<noinclude[^>]*>.*?<\/noinclude[^>]*>/g, "");
+
-
text = text.replace(/<includeonly[^>]*>(.*?)<\/includeonly[^>]*>/g, "$1");
+
-
text = text.replace(/^\s+/g, "");
+
-
// check for redirects
+
-
var matches = text.match(/^#REDIRECT\s*\[\[(.*)\]\]/i);
+
-
if (matches)
+
{
{
-
if (redirected) // trap double-redirects
+
this.enabled = utils.getPref('navbar', false);
-
{
+
this.rando = {};
-
removeSubtitles();
+
for (var i in this.SECTIONS)
 +
this.rando[i] = utils.getPref('rando' + i, true);
 +
}
 +
Navbar.prototype.SECTIONS = {
 +
t: "Big Toons",
 +
sh: "Shorts",
 +
ho: "Holday Toons",
 +
p: "Puppet Stuff",
 +
teh: "Powered by The Cheat",
 +
sb: "Strong Bad Emails",
 +
am: "Marzipan's Answering Machine",
 +
tgs: "Teen Girl Squad"
 +
};
 +
Navbar.prototype.MAIN_COUNT = 26;
 +
Navbar.prototype.init = function init() {
 +
utils.addGlobalStyle(
 +
'#newnavbar\n' +
 +
'{\n' +
 +
'\tmargin: 0;\n' +
 +
'\tpadding: 0;\n' +
 +
'\ttext-align: center;\n' +
 +
'\ttext-transform: lowercase;\n' +
 +
'\theight: 10px;\n' +
 +
'\tfont: 10px/10px sans-serif;\n' +
 +
'}\n' +
 +
'#newnavbar li\n' +
 +
'{\n' +
 +
'\tmargin: 0;\n' +
 +
'\tpadding: 0;\n' +
 +
'\tdisplay: inline;\n' +
 +
'}\n' +
 +
'#newnavbar :link, #newnavbar :visited\n' +
 +
'{\n' +
 +
'\tcolor: #666;\n' +
 +
'\tfont-family: sans-serif;\n' +
 +
'\ttext-decoration: none;\n' +
 +
'\tpadding: 0 1em;\n' +
 +
'}\n' +
 +
'#newnavbar :link:hover, #newnavbar :visited:hover\n' +
 +
'{\n' +
 +
'\tcolor: #999;\n' +
 +
'}\n' +
 +
'\n' +
 +
"/* for overriding podstar's settings: */\n" +
 +
'#newnavbar :link, #newnavbar :visited\n' +
 +
'{\n' +
 +
'\tfont-weight: normal;\n' +
 +
'}\n' +
 +
'#newnavbar :link:hover, #newnavbar :visited:hover\n' +
 +
'{\n' +
 +
'\tbackground: transparent;\n' +
 +
'\tfont-weight: normal;\n' +
 +
'}\n' +
 +
""
 +
);
 +
 +
this.setting_enabled = globals.modules.settingspane.addCheckbox('navbar', "Plain HTML navbar", "Replaces the flash navbar with normal links, so you can open in tabs, etc", this.enabled);
 +
this.setting_rando = {};
 +
for (var i in this.SECTIONS)
 +
this.setting_rando[i] = globals.modules.settingspane.addCheckbox('rando' + i, "Include " + this.SECTIONS[i] + " in rando", 'Limit the "rando" function to what you like to watch', this.rando[i], this.setting_enabled);
 +
 +
this.allrandourls = false;
 +
this.randourls = false;
 +
 +
this.originalnavbar = globals.navbar;
 +
this.newnavbar = this.buildNavbar(this.originalnavbar);
 +
this.showNavbar();
 +
};
 +
Navbar.prototype.updateSettings = function updateSettings()
 +
{
 +
this.enabled = this.setting_enabled.checked;
 +
utils.setPref("navbar", this.enabled);
 +
for (var i in this.SECTIONS)
 +
{
 +
this.rando[i] = this.setting_rando[i].checked;
 +
utils.setPref("rando" + i, this.rando[i]);
 +
}
 +
this.filterRando();
 +
this.showNavbar();
 +
};
 +
 +
Navbar.prototype.showNavbar = function showNavbar()
 +
{
 +
if (this.enabled)
 +
{
 +
if (this.originalnavbar)
 +
this.originalnavbar.style.display = "none";
 +
this.newnavbar.style.display = "";
 +
this.newnavbar.style.marginTop = (globals.modules.seekbar.enabled ? "0" : "10px");
 +
globals.navbar = this.newnavbar;
 +
this.loadRandoXML();
 +
}
 +
else
 +
{
 +
if (this.originalnavbar)
 +
this.originalnavbar.style.display = "";
 +
this.newnavbar.style.display = "none";
 +
globals.navbar = this.originalnavbar;
 +
}
 +
globals.modules.fullscreen.doResize();
 +
};
 +
 +
Navbar.prototype.buildNavbar = function buildNavbar(where)
 +
{
 +
var newnavbar = document.createElement("ul");
 +
newnavbar.id = "newnavbar";
 +
if (where)
 +
{
 +
while(where.parentNode.tagName.toLowerCase() == "object")
 +
where = where.parentNode;
 +
utils.insertAfter(newnavbar, where);
 +
}
 +
else
 +
document.body.appendChild(newnavbar);
 +
 +
this.mainlink = this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/main" + Math.floor(Math.random() * this.MAIN_COUNT + 1) + ".html", "Main");
 +
// just for fun, re-randomise on each mouse-over (for the status bar)
 +
this.mainlink.addEventListener("mouseout", this.newMainLink.bind(this), false);
 +
this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/toons.html", "Toons");
 +
this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/games.html", "Games");
 +
this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/characters2.html", "Characters");
 +
this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/downloads.html", "Downloads");
 +
this.addnavbarlink(newnavbar, "http://homestarrunner.stores.yahoo.net/", "Store", "storelink");
 +
this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/sbemail.html", "SB Emails");
 +
this.addnavbarlink(newnavbar, "http://feeds.feedburner.com/HomestarRunner", "Subscribe");
 +
this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/email.html", "Contact");
 +
//this.addnavbarlink(newnavbar, "http://podstar.homestarrunner.com/", "Podcast");
 +
this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/legal.html", "Legal");
 +
this.randolink = this.addnavbarlink(newnavbar, "javascript:void(alert('rando.xml not loaded yet... be patient'))", "Rando");
 +
this.randolink.addEventListener("mouseout", this.newRandoLink.bind(this), false);
 +
 +
return newnavbar;
 +
};
 +
Navbar.prototype.addnavbarlink = function addnavbarlink(ul, href, title, extraclass)
 +
{
 +
var li = document.createElement("li");
 +
var link = document.createElement("a");
 +
link.href = href;
 +
link.appendChild(document.createTextNode(title));
 +
if (extraclass)
 +
link.className = extraclass;
 +
li.appendChild(link);
 +
ul.appendChild(li);
 +
return link;
 +
};
 +
 +
Navbar.prototype.newMainLink = function newMainLink()
 +
{
 +
this.mainlink.href="http://www.homestarrunner.com/main" + Math.floor(Math.random() * this.MAIN_COUNT + 1) + ".html";
 +
};
 +
Navbar.prototype.newRandoLink = function newRandoLink()
 +
{
 +
if (!this.randourls)
return;
return;
 +
 +
if (this.randourls.length > 0)
 +
{
 +
var r = this.randourls[Math.floor(Math.random() * this.randourls.length)];
 +
this.randolink.href = r.u;
 +
this.randolink.title = r.n;
}
}
-
text = matches[1];
+
else
-
if (matches = text.match(/^(.*)\|/))
+
{
-
text = matches[1];
+
this.randolink.href = "javascript:void(alert('Nothing to choose from'))";
-
if (matches = text.match(/^(.*)\#/))
+
this.randolink.title = "Nothing to choose from";
-
text = matches[1];
+
}
-
text = text.replace(/^\s+|\s+$/g, '');
+
};
-
text = text.replace(/ /g, '_');
+
-
GM_xmlhttpRequest({
+
Navbar.prototype.loadRandoXML = function loadRandoXML()
-
method: "GET",
+
-
url: "http://www.hrwiki.org/w/index.php?title=" + escape(text) + "&action=raw&cachedodge=" + GM_getValue('cachedodge', 0),
+
-
onload: function(x) { xmldownloaded(x, callback, true, showerrors); }
+
-
});
+
-
return;
+
-
}
+
-
var parser = new DOMParser();
+
-
try
+
{
{
-
var doc = parser.parseFromString(text, "application/xml");
+
// Only run this once
-
}
+
if (this.haveLoadedXML)
-
catch (e)
+
return;
 +
this.haveLoadedXML = true;
 +
 +
utils.downloadPage(
 +
"http://www.homestarrunner.com/rando.xml?cachedodge=" + utils.getPref('cachedodge', 0),
 +
this.randoXMLLoaded.bind(this),
 +
this.randoXMLError.bind(this)
 +
);
 +
};
 +
Navbar.prototype.randoXMLLoaded = function randoXMLLoaded(responseText)
{
{
-
if (showerrors)
+
var parser = new DOMParser();
 +
// fix invalid XML...
 +
// add missing root element
 +
var doc = responseText.replace(/<\?xml.*?\?>/g, ""); // strip <?xml ?> tag
 +
doc = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<rando>" + doc + "</rando>";
 +
// fix bad ampersands
 +
doc = doc.replace(/&(?!\w*;)/g, "&amp;");
 +
doc = parser.parseFromString(doc, "application/xml");
 +
var sbemailcounter = 0;
 +
this.allrandourls = [];
 +
for (var i = 0; i < doc.documentElement.childNodes.length; i++)
{
{
-
transcriptError("Error in test subtitles:");
+
var node = doc.documentElement.childNodes[i];
-
var pre = document.createElement("pre");
+
if (node.nodeType == 1)
-
pre.style.font = "inherit";
+
{
-
pre.style.margin = "0.5em 0 0";
+
var type = node.nodeName.toLowerCase();
-
pre.appendChild(document.createTextNode(e.toString()))
+
var u = node.getAttribute('u');
-
transcriptErrors.appendChild(pre);
+
var n = node.getAttribute('n');
 +
if (!n) n = "Untitled";
 +
if (type == "sb")
 +
{
 +
sbemailcounter++;
 +
n = "SBEmail: " + n;
 +
}
 +
if (u)
 +
this.allrandourls.push({u: "http://www.homestarrunner.com/" + u, n: n, type: type});
 +
else
 +
this.allrandourls.push({u: "http://www.homestarrunner.com/sbemail" + sbemailcounter + ".html", n: n, type: type});
 +
}
}
}
-
removeSubtitles();
+
this.filterRando();
-
return;
+
};
-
}
+
Navbar.prototype.randoXMLError = function randoXMLError()
-
// check if returned document is an error message
+
-
if (doc.documentElement.lastChild && doc.documentElement.lastChild.nodeName == 'sourcetext')
+
{
{
-
// the xml document looks like
+
this.randolink.href = "javascript:void(alert('Error loading rando.xml... try refreshing'))";
-
// <parsererror>Error details<sourcetext>Source text</sourcetext></parsererror>
+
};
-
if (showerrors)
+
Navbar.prototype.filterRando = function filterRando()
 +
{
 +
if (!this.allrandourls)
 +
return;
 +
this.randourls = [];
 +
for (var i in this.allrandourls)
{
{
-
transcriptError("Error in test subtitles:");
+
var r = this.allrandourls[i];
-
var pre = document.createElement("pre");
+
if (this.rando[r.type] === false) // === false so that it's considered "true" for undefined... if they add a new toon type
-
pre.style.font = "inherit";
+
continue;
-
pre.style.margin = "0.5em 0 0";
+
this.randourls.push(r);
-
pre.appendChild(document.createTextNode(doc.documentElement.firstChild.nodeValue.replace(/Location: .*\n/, "")))
+
-
transcriptErrors.appendChild(pre);
+
-
pre = document.createElement("pre");
+
-
pre.style.margin = "0.5em 0 0";
+
-
pre.appendChild(document.createTextNode(doc.documentElement.lastChild.firstChild.nodeValue))
+
-
transcriptErrors.appendChild(pre);
+
}
}
-
removeSubtitles();
+
this.newRandoLink();
-
return;
+
};
-
}
+
-
callback(doc);
+
-
}
+
-
function populatelanguagelist(xml)
+
function Subtitles()
-
{
+
-
while (extrasettings[0].select.firstChild)
+
-
extrasettings[0].select.removeChild(extrasettings[0].select.firstChild);
+
-
a = xml.getElementsByTagName('language');
+
-
for (i = 0; i < a.length; i++)
+
{
{
-
// sanity-check the node
+
this.enabled = utils.getPref('subtitles', false);
-
if (a[i].hasAttribute('xml:lang') && a[i].firstChild && (a[i].firstChild.nodeType == xml.TEXT_NODE || a[i].firstChild.nodeType == xml.CDATA_SECTION_NODE))
+
this.captions = utils.getPref('captions', true);
 +
this.colours = utils.getPref('colours', true);
 +
this.testsubs = utils.getPref('testsubs', false);
 +
this.language = utils.getPref('language', "en");
 +
this.testsubsdata = unescape(utils.getPref('testsubsdata', this.DEFAULTXML));
 +
this.names = utils.getPref('names', 0);
 +
}
 +
Subtitles.prototype.DEFAULTXML = escape('<?xml version="1.0" encoding="utf-8"?>\n<transcript xml:lang="en-us">\n<line start="" end="" speaker=""></line>\n</transcript>');
 +
Subtitles.prototype.NAMES_OPTS = ["Never", "Voiceovers", "Always"];
 +
Subtitles.prototype.NO_SUBTITLES = document.createComment("");
 +
Subtitles.prototype.init = function init()
 +
{
 +
utils.addGlobalStyle(
 +
'.subtitles\n' +
 +
'{\n' +
 +
'\tbackground: black;\n' +
 +
'\tcolor: white;\n' +
 +
'\tfont: 20px/25px sans-serif;\n' +
 +
'\theight: 100px;\n' +
 +
'\ttext-align: center;\n' +
 +
'}\n' +
 +
'\n' +
 +
'.subtitle_errors\n' +
 +
'{\n' +
 +
'\tbackground: black;\n' +
 +
'\tcolor: red;\n' +
 +
'\tfont: 12pt sans-serif;\n' +
 +
'\ttext-align: left;\n' +
 +
'\tmargin: 0.5em;\n' +
 +
'}\n' +
 +
'\n' +
 +
'.subtitles .italic\n' +
 +
'{\n' +
 +
'\tfont-style: italic;\n' +
 +
'}\n' +
 +
'.subtitles .italic em, .subtitles .italic cite, .subtitles .italic i\n' +
 +
'{\n' +
 +
'\tfont-style: normal;\n' +
 +
'}\n' +
 +
""
 +
);
 +
 +
this.setting_enabled = globals.modules.settingspane.addCheckbox('subtitles', "Show subtitles", "Shows subtitles or captions below the toon, if any are available", this.enabled);
 +
 +
var settingrow = globals.modules.settingspane.addSettingRow(this.setting_enabled);
 +
var settinglabel = document.createElement('label');
 +
settinglabel.htmlFor = "setting_language";
 +
settinglabel.appendChild(document.createTextNode('Subtitle Language: '));
 +
settinglabel.title = 'Display subtitles in this language, if any';
 +
settingrow.appendChild(settinglabel);
 +
this.setting_language = document.createElement('select');
 +
this.setting_language.title = 'Display subtitles in this language, if any';
 +
this.setting_language.id = "setting_language";
 +
this.setting_language.disabled = true;
 +
settingrow.appendChild(this.setting_language);
 +
 +
this.language_populated = false;
 +
this.populateLanguage();
 +
 +
this.setting_captions = globals.modules.settingspane.addCheckbox('captions', "Show captions", "Include sound effects in the subtitles", this.captions, this.setting_enabled);
 +
this.setting_colours = globals.modules.settingspane.addCheckbox('colours', "Use colours", "Distinguish characters by colour effects (turn off if colourblind)", this.colours, this.setting_enabled);
 +
 +
settingrow = globals.modules.settingspane.addSettingRow(this.setting_enabled);
 +
settinglabel = document.createElement('label');
 +
settinglabel.htmlFor = "setting_names";
 +
settinglabel.appendChild(document.createTextNode('Show speakers\' names: '));
 +
settinglabel.title = 'Show the speakers\' names before their lines';
 +
settingrow.appendChild(settinglabel);
 +
this.setting_names = document.createElement('select');
 +
this.setting_names.title = 'Show the speakers\' names before their lines';
 +
this.setting_names.id = "setting_names";
 +
settingrow.appendChild(this.setting_names);
 +
for (var i = 0; i < this.NAMES_OPTS.length; i++)
{
{
var option = document.createElement('option');
var option = document.createElement('option');
-
option.appendChild(document.createTextNode(a[i].firstChild.nodeValue));
+
option.value = i;
-
option.lang = option.value = a[i].getAttribute('xml:lang');
+
option.appendChild(document.createTextNode(this.NAMES_OPTS[i]));
-
if (option.lang == settings['language'])
+
if (this.names == i)
option.selected = true;
option.selected = true;
-
option.dir = "ltr";
+
this.setting_names.appendChild(option);
-
if (a[i].hasAttribute('dir'))
+
-
option.dir = a[i].getAttribute('dir');
+
-
extrasettings[0].select.appendChild(option);
+
}
}
-
}
 
-
extrasettings[0].populated = true;
 
-
}
 
-
 
-
function setupSubtitles()
 
-
{
 
-
subtitleholder = document.createElement('div');
 
-
subtitleholder.style.color = "white";
 
-
subtitleholder.style.font = "20px/25px sans-serif";
 
-
subtitleholder.style.backgroundColor = "black";
 
-
subtitleholder.style.height = "100px";
 
-
//subtitleholder.style.marginTop = "10px";
 
-
subtitleholder.style.textAlign = "center";
 
-
where = flashmovie;
 
-
while(where.parentNode.tagName.toLowerCase() == "object")
 
-
where = where.parentNode;
 
-
where.parentNode.insertBefore(subtitleholder, where.nextSibling);
 
-
GM_addStyle(
+
this.setting_testsubs = globals.modules.settingspane.addCheckbox('testsubs', "Test subtitles script", "Use this to test a subtitles script (copy/paste into a text box)", this.testsubs, this.setting_enabled);
-
'.italic { font-style: italic; } ' +
+
-
'.italic em, .italic cite, .italic i { font-style: normal; }'
+
-
);
+
-
subtitleholder.appendChild(currentsubtitles = document.createTextNode("Loading subtitles..."));
+
settingrow = globals.modules.settingspane.addSettingRow(this.setting_testsubs);
 +
this.setting_testsubsdata = document.createElement('textarea');
 +
this.setting_testsubsdata.title = 'Paste your XML data here';
 +
this.setting_testsubsdata.id = "setting_testsubsdata";
 +
this.setting_testsubsdata.style.width = "100%";
 +
this.setting_testsubsdata.style.height = "10em";
 +
this.setting_testsubsdata.style.fontSize = "8px";
 +
this.setting_testsubsdata.style.textAlign = "left";
 +
this.setting_testsubsdata.appendChild(document.createTextNode(this.testsubsdata));
 +
settingrow.appendChild(this.setting_testsubsdata);
-
downloadxmlfromwiki('Subtitles:Characters', charactersLoaded);
+
this.charsready = false;
-
}
+
this.subsready = false;
-
function removeSubtitles()
+
-
{
+
-
if (subtitleholder)
+
-
subtitleholder.parentNode.removeChild(subtitleholder);
+
-
subtitleholder = false;
+
-
if (settings.resize)
+
-
doResize();
+
-
if (subtitleLoop)
+
-
clearInterval(subtitleLoop);
+
-
}
+
-
function reloadSubtitles()
+
-
{
+
-
savesettings(false);
+
-
if (!subtitleholder)
+
this.setupSubtitles();
 +
 +
window.setInterval(this.update.bind(this), 50);
 +
};
 +
Subtitles.prototype.updateSettings = function updateSettings()
{
{
-
subtitleholder = document.createElement('div');
+
this.enabled = this.setting_enabled.checked;
-
subtitleholder.style.color = "white";
+
utils.setPref('subtitles', this.enabled);
-
subtitleholder.style.font = "20px/25px sans-serif";
+
if (this.language_populated)
-
subtitleholder.style.backgroundColor = "black";
+
{
-
subtitleholder.style.height = "100px";
+
this.language = this.setting_language.value;
-
//subtitleholder.style.marginTop = "10px";
+
utils.setPref('language', this.language);
-
subtitleholder.style.textAlign = "center";
+
}
-
where = flashmovie;
+
this.captions = this.setting_captions.checked;
-
while(where.parentNode.tagName.toLowerCase() == "object")
+
utils.setPref('captions', this.captions);
-
where = where.parentNode;
+
this.colours = this.setting_colours.checked;
-
where.parentNode.insertBefore(subtitleholder, where.nextSibling);
+
utils.setPref('colours', this.colours);
-
subtitleholder.appendChild(currentsubtitles = nosubtitles);
+
this.names = this.setting_names.value;
-
}
+
utils.setPref('names', this.names);
-
if (transcriptErrors)
+
this.testsubs = this.setting_testsubs.checked;
-
{
+
utils.setPref('testsubs', this.testsubs);
-
transcriptErrors.parentNode.removeChild(transcriptErrors);
+
this.testsubsdata = this.setting_testsubsdata.value;
-
transcriptErrors = false;
+
utils.setPref('testsubsdata', escape(this.testsubsdata));
-
}
+
-
transcript = [];
+
-
if (subtitleLoop)
+
-
clearInterval(subtitleLoop);
+
-
subtitleLoop = false;
+
-
xmldownloaded({'responseText': settings.testsubsdata}, transcriptLoaded, false, true);
+
this.setupSubtitles();
-
}
+
};
-
 
+
-
function charactersLoaded(doc)
+
Subtitles.prototype.populateLanguage = function populateLanguage()
-
{
+
-
var speakers = doc.getElementsByTagName("speaker");
+
-
for (var i = 0; i < speakers.length; i++)
+
{
{
-
var speakername = speakers[i].getAttribute("id");
+
var option = document.createElement('option');
-
characters[speakername] = {"color": speakers[i].getAttribute("color"), "sfx": speakers[i].hasAttribute("sfx"), "name": {"en": ""}};
+
option.appendChild(document.createTextNode("Loading..."));
-
var names = speakers[i].getElementsByTagName("name");
+
option.selected = true;
-
for (var j = 0; j < names.length; j++)
+
this.setting_language.appendChild(option);
 +
utils.downloadWikiXML("Subtitles:Languages", this.languageListDownloaded.bind(this), this.languageListError.bind(this));
 +
};
 +
Subtitles.prototype.languageListDownloaded = function languageListDownloaded(xml)
 +
{
 +
while (this.setting_language.firstChild)
 +
this.setting_language.removeChild(this.setting_language.firstChild);
 +
 +
var languages = xml.getElementsByTagName('language');
 +
for (var i = 0; i < languages.length; i++)
{
{
-
var lang = names[j].getAttribute("xml:lang");
+
var node = languages[i];
-
if (names[j].firstChild && (names[j].firstChild.nodeType == doc.TEXT_NODE || names[j].firstChild.nodeType == doc.CDATA_SECTION_NODE))
+
// sanity-check the node
-
characters[speakername].name[lang] = names[j].firstChild.nodeValue;
+
if (node.hasAttribute('xml:lang') && node.firstChild && (node.firstChild.nodeType == xml.TEXT_NODE || node.firstChild.nodeType == xml.CDATA_SECTION_NODE))
 +
{
 +
var option = document.createElement('option');
 +
option.appendChild(document.createTextNode(node.firstChild.nodeValue));
 +
option.lang = option.value = node.getAttribute('xml:lang');
 +
if (option.lang == this.language)
 +
option.selected = true;
 +
option.dir = "ltr";
 +
if (node.hasAttribute('dir'))
 +
option.dir = node.getAttribute('dir');
 +
this.setting_language.appendChild(option);
 +
}
}
}
-
}
+
-
if (!settings.testsubs)
+
this.setting_language.disabled = false;
-
downloadxmlfromwiki('Subtitles:' + filename + '/' + settings.language, transcriptLoaded);
+
this.language_populated = true;
-
else
+
};
-
xmldownloaded({'responseText': settings.testsubsdata}, transcriptLoaded, false, true);
+
Subtitles.prototype.languageListError = function languageListError()
-
}
+
-
 
+
-
function transcriptLoaded(doc)
+
-
{
+
-
// set some defaults
+
-
if (!doc.documentElement.getAttribute("xml:lang")) doc.documentElement.setAttribute("xml:lang", "en");
+
-
if (!doc.documentElement.getAttribute("dir"))      doc.documentElement.setAttribute("dir",      "ltr");
+
-
// inherit languages to all subnodes
+
-
inheritLanguages(doc.documentElement);
+
-
// now parse the lines into divs and get start and end frames
+
-
var lines = doc.getElementsByTagName("line");
+
-
var previousEnd = NaN;
+
-
for (var i = 0; i < lines.length; i++)
+
{
{
-
var line = new Object();
+
while (this.setting_language.firstChild)
-
// ignore lines with missing start/end values
+
this.setting_language.removeChild(this.setting_language.firstChild);
-
// so you can add all the lines and not worry about timing them until later
+
var option = document.createElement('option');
-
if (!lines[i].getAttribute("start") || !lines[i].getAttribute("end"))
+
option.appendChild(document.createTextNode("Error loading languages"));
-
continue;
+
option.selected = true;
-
line.start = parseInt(lines[i].getAttribute("start"), 10);
+
this.setting_language.appendChild(option);
-
line.end = parseInt(lines[i].getAttribute("end"), 10);
+
};
-
if (settings.testsubs)
+
 +
Subtitles.prototype.removeSubtitles = function removeSubtitles()
 +
{
 +
if (this.subtitleholder)
{
{
-
if (isNaN(line.start))
+
this.subtitleholder.parentNode.removeChild(this.subtitleholder);
-
transcriptError("Start value \"" + lines[i].getAttribute("start") + "\" is not a number");
+
this.subtitleholder = undefined;
-
if (isNaN(line.end))
+
}
-
transcriptError("End value \"" + lines[i].getAttribute("end") + "\" is not a number");
+
if (this.errorsholder)
-
if (line.end < line.start)
+
{
-
transcriptError("Line beginning frame " + line.start + " ends before it begins.");
+
this.errorsholder.parentNode.removeChild(this.errorsholder);
-
if (line.start < previousEnd)
+
this.errorsholder = undefined;
-
transcriptError("Line beginning frame " + line.start + " starts before the previous frame ends.");
+
-
previousEnd = line.end;
+
}
}
-
line.text = importNodes(lines[i]);
 
-
transcript.push(line);
 
-
}
 
-
if (settings.resize)
+
globals.modules.fullscreen.doResize();
-
doResize();
+
};
 +
Subtitles.prototype.createSubtitleHolder = function createSubtitleHolder()
 +
{
 +
this.subtitleholder = document.createElement('div');
 +
this.subtitleholder.className = "subtitles";
 +
var where = globals.flashmovie;
 +
if (globals.modules.seekbar && globals.modules.seekbar.seekbar)
 +
where = globals.modules.seekbar.seekbar;
 +
while(where.parentNode.tagName.toLowerCase() == "object")
 +
where = where.parentNode;
 +
utils.insertAfter(this.subtitleholder, where);
 +
this.subtitleholder.appendChild(this.NO_SUBTITLES);
 +
this.currentsubtitles = this.NO_SUBTITLES;
-
subtitleLoop = setInterval(refreshSubtitles, 50);
+
globals.modules.fullscreen.doResize();
-
}
+
};
-
function inheritLanguages(node)
+
Subtitles.prototype.createErrorsHolder = function createErrorsHolder()
-
{
+
-
for (var i = node.firstChild; i; i = i.nextSibling)
+
{
{
-
if (i.nodeType == i.ELEMENT_NODE)
+
this.errorsholder = document.createElement('div');
 +
this.errorsholder.className = "subtitle_errors";
 +
var where = globals.flashmovie;
 +
if (globals.modules.seekbar && globals.modules.seekbar.seekbar)
 +
where = globals.modules.seekbar.seekbar;
 +
while(where.parentNode.tagName.toLowerCase() == "object")
 +
where = where.parentNode;
 +
utils.insertAfter(this.errorsholder, where);
 +
 +
globals.modules.fullscreen.doResize();
 +
};
 +
Subtitles.prototype.transcriptError = function transcriptError(message)
 +
{
 +
if (!this.errorsholder)
 +
this.createErrorsHolder();
 +
var div = document.createElement("div");
 +
div.appendChild(document.createTextNode(message));
 +
this.errorsholder.appendChild(div);
 +
 +
globals.modules.fullscreen.doResize();
 +
};
 +
 +
Subtitles.prototype.setupSubtitles = function setupSubtitles()
 +
{
 +
this.removeSubtitles();
 +
 +
if (!this.enabled)
 +
return;
 +
 +
this.createSubtitleHolder();
 +
this.setSubtitles(document.createTextNode("Loading subtitles..."));
 +
 +
if (!this.charsready)
 +
utils.downloadWikiXML('Subtitles:Characters', this.charactersLoaded.bind(this), this.downloadSubsError.bind(this));
 +
else
 +
this.reloadSubs();
 +
};
 +
Subtitles.prototype.charactersLoaded = function charactersLoaded(xml)
 +
{
 +
var speakers = xml.getElementsByTagName("speaker");
 +
this.characters = {
 +
sfx: {
 +
color: "#FFF",
 +
sfx: true,
 +
name: {en: ""}
 +
}
 +
};
 +
for (var i = 0; i < speakers.length; i++)
{
{
-
if (!i.hasAttribute("xml:lang")) i.setAttribute("xml:lang", node.getAttribute("xml:lang"));
+
var speakername = speakers[i].getAttribute("id");
-
if (!i.hasAttribute("dir"))      i.setAttribute("dir",      node.getAttribute("dir"));
+
this.characters[speakername] = {color: speakers[i].getAttribute("color"), sfx: speakers[i].hasAttribute("sfx"), name: {en: ""}};
-
inheritLanguages(i);
+
var names = speakers[i].getElementsByTagName("name");
 +
for (var j = 0; j < names.length; j++)
 +
{
 +
var lang = names[j].getAttribute("xml:lang");
 +
if (names[j].firstChild && (names[j].firstChild.nodeType == xml.TEXT_NODE || names[j].firstChild.nodeType == xml.CDATA_SECTION_NODE))
 +
this.characters[speakername].name[lang] = names[j].firstChild.nodeValue;
 +
}
}
}
-
}
+
this.charsready = true;
-
}
+
this.reloadSubs();
-
// replacement for document.importNode() that actually works properly
+
};
-
// turning eg <span> into an HTML span element, rather than an unknown element
+
Subtitles.prototype.downloadSubsError = function downloadSubsError(status, statusText)
-
// that happens to be called "span".
+
-
function importNodes(node)
+
-
{
+
-
var name = node.nodeName.toLowerCase();
+
-
if (characters[name])
+
{
{
-
node.setAttribute("speaker", name);
+
this.removeSubtitles();
-
name = "speaker";
+
if (this.testsubs)
-
}
+
this.transcriptError(statusText);
-
if (name == "line" || name == "speaker")
+
};
 +
Subtitles.prototype.reloadSubs = function reloadSubs()
 +
{
 +
if (!this.charsready)
 +
return;
 +
this.subsready = false;
 +
 +
this.removeSubtitles();
 +
this.createSubtitleHolder();
 +
this.setSubtitles(document.createTextNode("Loading subtitles..."));
 +
 +
if (!this.testsubs)
 +
utils.downloadWikiXML('Subtitles:' + globals.filename + '/' + this.language, this.transcriptLoaded.bind(this), this.downloadSubsError.bind(this));
 +
else
 +
utils.wikiXMLDownloaded(this.transcriptLoaded.bind(this), this.downloadSubsError.bind(this), this.testsubsdata, 200, "OK");
 +
};
 +
 +
Subtitles.prototype.transcriptLoaded = function transcriptLoaded(xml)
{
{
-
// format the speaker appropriately as a div
+
// set some defaults
-
var speaker = node.getAttribute("speaker");
+
if (!xml.documentElement.getAttribute("xml:lang")) xml.documentElement.setAttribute("xml:lang", this.language);
-
if (!settings.captions && (speaker == "sfx" || node.hasAttribute("sfx")))
+
if (!xml.documentElement.getAttribute("dir"))      xml.documentElement.setAttribute("dir",      "ltr");
-
return document.createComment(""); // return nothing
+
// inherit languages to all subnodes
-
newNode = document.createElement("div");
+
this.inheritLanguages(xml.documentElement);
-
var char = characters[speaker];
+
// now parse the lines into divs and get start and end frames
-
if (!char)
+
var lines = xml.getElementsByTagName("line");
 +
var previousEnd = NaN;
 +
this.transcript = [];
 +
for (var i = 0; i < lines.length; i++)
{
{
-
if (settings.testsubs && speaker)
+
var line = {};
 +
// ignore lines with missing start/end values
 +
// so you can add all the lines and not worry about timing them until later
 +
if (!lines[i].getAttribute("start") || !lines[i].getAttribute("end"))
 +
continue;
 +
line.start = parseInt(lines[i].getAttribute("start"), 10);
 +
line.end = parseInt(lines[i].getAttribute("end"), 10);
 +
if (this.testsubs)
{
{
-
line = node;
+
if (isNaN(line.start))
-
while (line && line.nodeName != "line")
+
this.transcriptError("Start value \"" + lines[i].getAttribute("start") + "\" is not a number");
-
line = line.parentNode;
+
if (isNaN(line.end))
-
if (line)
+
this.transcriptError("End value \"" + lines[i].getAttribute("end") + "\" is not a number");
-
transcriptError("Line beginning frame " + line.getAttribute("start") + " has an unrecognised speaker name \"" + speaker + '"');
+
if (line.end < line.start)
 +
this.transcriptError("Line beginning frame " + line.start + " ends before it begins.");
 +
if (line.start < previousEnd)
 +
this.transcriptError("Line beginning frame " + line.start + " starts before the previous frame ends.");
 +
previousEnd = line.end;
}
}
-
char = {"color": "#FFF", "name": {"en": ""}};
+
line.text = this.importNodes(lines[i]);
 +
this.transcript.push(line);
}
}
-
if (settings.colours)
+
this.subsready = true;
-
newNode.style.color = char.color;
+
};
-
if (node.hasAttribute("voiceover"))
+
Subtitles.prototype.inheritLanguages = function inheritLanguages(node)
-
newNode.className = "italic";
+
{
-
if (node.hasAttribute("volume"))
+
-
{
+
-
newNode.style.fontSize = (node.getAttribute("volume") * 100) + "%";
+
-
newNode.style.lineHeight = "1.25em";
+
-
}
+
-
newNode.lang = node.getAttribute("xml:lang");
+
-
newNode.dir = node.getAttribute("dir");
+
-
var hasSpeakerChildren = false;
+
for (var i = node.firstChild; i; i = i.nextSibling)
for (var i = node.firstChild; i; i = i.nextSibling)
{
{
if (i.nodeType == i.ELEMENT_NODE)
if (i.nodeType == i.ELEMENT_NODE)
{
{
-
newNode.appendChild(importNodes(i));
+
if (!i.hasAttribute("xml:lang")) i.setAttribute("xml:lang", node.getAttribute("xml:lang"));
-
var a = i.nodeName.toLowerCase();
+
if (!i.hasAttribute("dir"))      i.setAttribute("dir",      node.getAttribute("dir"));
-
if (i == "line" || i == "speaker" || characters[i])
+
this.inheritLanguages(i);
-
hasSpeakerChildren = true;
+
}
}
-
else if (i.nodeType == i.TEXT_NODE || i.nodeType == i.CDATA_SECTION_NODE)
 
-
newNode.appendChild(document.importNode(i, true));
 
}
}
-
if (!hasSpeakerChildren)
+
};
 +
Subtitles.prototype.importNodes = function importNodes(node)
 +
{
 +
var name = node.nodeName.toLowerCase();
 +
if (this.characters[name])
{
{
-
// this is a normal text node - do some extra text stuff
+
node.setAttribute("speaker", name);
-
if (char.sfx || node.hasAttribute("sfx"))
+
name = "speaker";
 +
}
 +
if (name == "line" || name == "speaker")
 +
{
 +
// format the speaker appropriately as a div
 +
var speaker = node.getAttribute("speaker");
 +
if (!this.captions && (speaker == "sfx" || node.hasAttribute("sfx")))
 +
return document.createComment(""); // return nothing
 +
newNode = document.createElement("div");
 +
var char = this.characters[speaker];
 +
if (!char)
{
{
-
newNode.insertBefore(document.createTextNode('('), newNode.firstChild);
+
if (this.testsubs && speaker)
-
newNode.appendChild(document.createTextNode(')'))
+
{
 +
var line = node;
 +
while (line && line.nodeName != "line")
 +
line = line.parentNode;
 +
if (line)
 +
this.transcriptError("Line beginning frame " + line.getAttribute("start") + " has an unrecognised speaker name \"" + speaker + '"');
 +
}
 +
char = {color: "#FFF", name: {en: ""}};
 +
}
 +
if (this.colours)
 +
newNode.style.color = char.color;
 +
if (node.hasAttribute("voiceover"))
newNode.className = "italic";
newNode.className = "italic";
 +
if (node.hasAttribute("volume"))
 +
{
 +
newNode.style.fontSize = (node.getAttribute("volume") * 100) + "%";
 +
newNode.style.lineHeight = "1.25em";
}
}
-
if (settings.names == 2 || (node.hasAttribute("voiceover") && settings.names == 1))
+
newNode.lang = node.getAttribute("xml:lang");
 +
newNode.dir = node.getAttribute("dir");
 +
var hasSpeakerChildren = false;
 +
for (var i = node.firstChild; i; i = i.nextSibling)
{
{
-
// find the language with the longest prefix match
+
if (i.nodeType == i.ELEMENT_NODE)
-
// fall back to "en" if none found
+
{
-
var bestmatch = "en";
+
newNode.appendChild(this.importNodes(i));
-
var langbits = node.getAttribute("xml:lang").split("-");
+
var a = i.nodeName.toLowerCase();
-
for (i = langbits.length; i >= 1; i--)
+
if (a == "line" || a == "speaker" || this.characters[a])
 +
hasSpeakerChildren = true;
 +
}
 +
else if (i.nodeType == i.TEXT_NODE || i.nodeType == i.CDATA_SECTION_NODE)
 +
newNode.appendChild(document.importNode(i, true));
 +
}
 +
if (!hasSpeakerChildren)
 +
{
 +
// this is a normal text node - do some extra text stuff
 +
if (char.sfx || node.hasAttribute("sfx"))
 +
{
 +
newNode.insertBefore(document.createTextNode('('), newNode.firstChild);
 +
newNode.appendChild(document.createTextNode(')'));
 +
newNode.className = "italic";
 +
}
 +
if (this.names == 2 || (node.hasAttribute("voiceover") && this.names == 1))
{
{
-
var lang = langbits.slice(0, i).join("-");
+
// find the language with the longest prefix match
-
if (char.name[lang])
+
// fall back to "en" if none found
 +
var bestmatch = "en";
 +
var langbits = node.getAttribute("xml:lang").split("-");
 +
for (i = langbits.length; i >= 1; i--)
{
{
-
bestmatch = lang;
+
var lang = langbits.slice(0, i).join("-");
-
break;
+
if (char.name[lang])
 +
{
 +
bestmatch = lang;
 +
break;
 +
}
}
}
 +
if (char.name[bestmatch] != '')
 +
newNode.insertBefore(document.createTextNode(char.name[bestmatch] + ": "), newNode.firstChild);
}
}
-
if (char.name[bestmatch] != '')
 
-
newNode.insertBefore(document.createTextNode(char.name[bestmatch] + ": "), newNode.firstChild);
 
}
}
 +
return newNode;
}
}
-
return newNode;
+
else
-
}
+
-
else
+
-
{
+
-
// check element blacklist
+
-
if (name == "script" ||
+
-
    name == "style"  ||
+
-
    name == "object" ||
+
-
    name == "param"  ||
+
-
    name == "embed"  ||
+
-
    name == "a"      ||
+
-
    name == "img"    ||
+
-
    name == "applet" ||
+
-
    name == "map"    ||
+
-
    name == "frame"  ||
+
-
    name == "iframe" ||
+
-
    name == "meta"  ||
+
-
    name == "link"  ||
+
-
    name == "form"  ||
+
-
    name == "input")
+
-
{
+
-
if (settings.testsubs)
+
-
transcriptError("Blacklisted element \"" + name + "\" stripped.");
+
-
return document.createComment(""); // return nothing
+
-
}
+
-
var newNode = document.createElement(name);
+
-
// copy across attributes
+
-
for (var i = 0; i < node.attributes.length; i++)
+
{
{
-
name = node.attributes[i].nodeName.toLowerCase();
+
// check element blacklist
-
// check attribute blacklist
+
if (name == "script" ||
-
// javascript, and anything that might load stuff from offsite
+
    name == "style" ||
-
if (name != "href" && name != "src" && name.substring(0, 2) != "on")
+
    name == "object" ||
 +
    name == "param"  ||
 +
    name == "embed"  ||
 +
    name == "a"      ||
 +
    name == "img"    ||
 +
    name == "applet" ||
 +
    name == "map"    ||
 +
    name == "frame"  ||
 +
    name == "iframe" ||
 +
    name == "meta"  ||
 +
    name == "link"  ||
 +
    name == "form"  ||
 +
    name == "input")
{
{
-
if (name == "style")
+
if (this.testsubs)
 +
this.transcriptError("Blacklisted element \"" + name + "\" stripped.");
 +
return document.createComment(""); // return nothing
 +
}
 +
var newNode = document.createElement(name);
 +
// copy across attributes
 +
for (i = 0; i < node.attributes.length; i++)
 +
{
 +
name = node.attributes[i].nodeName.toLowerCase();
 +
// check attribute blacklist
 +
// javascript, and anything that might load stuff from offsite
 +
if (name != "href" && name != "src" && name.substring(0, 2) != "on")
{
{
-
// regex taken from MediaWiki Sanitizer.php
+
if (name == "style")
-
if (!node.attributes[i].nodeValue.match(/(expression|tps*:\/\/|url\\s*\()/i))
+
{
-
newNode.setAttribute("style", node.attributes[i].nodeValue);
+
// regex taken from MediaWiki Sanitizer.php
 +
if (!node.attributes[i].value.match(/(expression|tps*:\/\/|url\\s*\()/i))
 +
newNode.setAttribute("style", node.attributes[i].value);
 +
}
 +
else if (name == "xml:lang")
 +
{
 +
newNode.lang = node.attributes[i].value;
 +
}
 +
else
 +
newNode.setAttribute(node.attributes[i].nodeName, node.attributes[i].value);
}
}
-
else if (name == "xml:lang")
+
else if (this.testsubs)
-
{
+
this.transcriptError("Blacklisted attribute \"" + name + "\" stripped.");
-
newNode.lang = node.attributes[i].nodeValue;
+
}
-
}
+
// copy across children
-
else
+
for (i = node.firstChild; i; i = i.nextSibling)
-
newNode.setAttribute(node.attributes[i].nodeName, node.attributes[i].nodeValue);
+
{
 +
if (i.nodeType == i.ELEMENT_NODE)
 +
newNode.appendChild(this.importNodes(i));
 +
else if (i.nodeType == i.TEXT_NODE || i.nodeType == i.CDATA_SECTION_NODE)
 +
newNode.appendChild(document.importNode(i, true));
}
}
-
else if (settings.testsubs)
+
return newNode;
-
transcriptError("Blacklisted attribute \"" + name + "\" stripped.");
+
}
}
-
// copy across children
+
return document.createComment(""); // fallthrough
-
for (var i = node.firstChild; i; i = i.nextSibling)
+
};
-
{
+
-
if (i.nodeType == i.ELEMENT_NODE)
+
Subtitles.prototype.update = function update()
-
newNode.appendChild(importNodes(i));
+
-
else if (i.nodeType == i.TEXT_NODE || i.nodeType == i.CDATA_SECTION_NODE)
+
-
newNode.appendChild(document.importNode(i, true));
+
-
}
+
-
return newNode;
+
-
}
+
-
document.createComment(""); // fallthrough
+
-
}
+
-
 
+
-
function setSubtitles(node)
+
-
{
+
-
if (!node)
+
-
node = nosubtitles;
+
-
if (currentsubtitles != node)
+
{
{
-
subtitleholder.replaceChild(node, subtitleholder.firstChild);
+
if (!this.enabled || !this.charsready || !this.subsready || !this.subtitleholder)
-
currentsubtitles = node;
+
return;
-
}
+
-
}
+
var frame = utils.currentFrame();
-
function refreshSubtitles()
+
if (frame < 0)
-
{
+
return;
-
var frame = false;
+
frame++; // Make 1-based
-
if (!flashmovie)
+
-
return;
+
-
if (typeof(flashmovie.CurrentFrame) == "function" || typeof(flashmovie.CurrentFrame) == "object")
+
-
frame = flashmovie.CurrentFrame();
+
-
if (typeof(frame) == "number" && frame >= 0)
+
-
{
+
-
// change from native 0-based to more friendly 1-based
+
-
frame++;
+
// binary search to find the right transcript line
// binary search to find the right transcript line
var first = 0;
var first = 0;
-
var last = transcript.length;
+
var last = this.transcript.length;
while(first < (last - 1))
while(first < (last - 1))
{
{
var mid = (first + last) >> 1;
var mid = (first + last) >> 1;
-
if (frame >= transcript[mid].start)
+
if (frame >= this.transcript[mid].start)
 +
{
first = mid;
first = mid;
 +
if (frame <= this.transcript[mid].end)
 +
break;
 +
}
else
else
last = mid;
last = mid;
}
}
// should we actually show the line?
// should we actually show the line?
-
if(transcript[first] && transcript[first].start <= frame && transcript[first].end >= frame)
+
if(this.transcript[first] && this.transcript[first].start <= frame && this.transcript[first].end >= frame)
-
setSubtitles(transcript[first].text);
+
this.setSubtitles(this.transcript[first].text);
else
else
-
setSubtitles(false);
+
this.setSubtitles(false);
-
}
+
};
-
}
+
-
function transcriptError(str)
+
Subtitles.prototype.setSubtitles = function setSubtitles(node)
-
{
+
-
if (!transcriptErrors)
+
{
{
-
transcriptErrors = document.createElement('div')
+
if (!this.subtitleholder)
-
transcriptErrors.style.color = "red";
+
return;
-
transcriptErrors.style.background = "black";
+
if (!node)
-
transcriptErrors.style.font = "12pt sans-serif";
+
node = this.NO_SUBTITLES;
-
transcriptErrors.style.textAlign = "left";
+
if (this.currentsubtitles != node)
-
transcriptErrors.style.margin = "0.5em";
+
{
-
document.body.appendChild(transcriptErrors);
+
this.subtitleholder.replaceChild(node, this.subtitleholder.firstChild);
-
}
+
this.currentsubtitles = node;
-
else
+
}
-
transcriptErrors.appendChild(document.createElement('br'));
+
};
-
transcriptErrors.appendChild(document.createTextNode(str));
+
-
}
+
-
function checkupdates()
+
// Returned by Special:Getversion
-
{
+
// <versionstring>4.0.63=http://www.hrwiki.org/w/index.php?title=User:Phlip/Greasemonkey&action=raw&ctype=text/javascript&fakeextension=.user.js</versionstring>
-
var now = new Date().getTime();
+
-
var then = new Number(GM_getValue('lastchecktime', 0));
+
function Updates()
-
if (now - then > 86400000) // only check at most once per day
+
{
{
-
GM_xmlhttpRequest({
+
this.enabled = utils.getPref('updates', true);
-
method: "GET",
+
-
url: "http://www.hrwiki.org/wiki/Special:Getversion/User:Phlip/Greasemonkey?cachedodge=" + Math.random(),
+
-
onload: function(r){GM_setValue('lastchecktime', ""+now);GM_setValue('lastcheckstring', r.responseText);return updatesstr_loaded(r.responseText);}
+
-
});
+
}
}
-
else
+
Updates.CURRENT_VERSION = [4, 0, 63];
-
updatesstr_loaded(GM_getValue('lastcheckstring', ''));
+
Updates.prototype.init = function init()
-
}
+
-
function updatesstr_loaded(str)
+
-
{
+
-
var parts = str.split("@@");
+
-
for (var i = 0; i < parts.length; i++)
+
{
{
-
var matches = parts[i].match(/^(\d+)\.(\d+)\.(\d+)=(.*)$/);
+
// We don't need to do this update checking on Chrome - the Chrome Web Store
-
if (!matches) continue;
+
// will handle that for us
-
if (matches[1] > currentversion[0] ||
+
if (!utils.useGMFunctions)
-
    (matches[1] == currentversion[0] && matches[2] > currentversion[1]) ||
+
-
    (matches[1] == currentversion[0] && matches[2] == currentversion[1] && matches[3] > currentversion[2]))
+
{
{
-
var updatelink = document.createElement('a');
+
delete globals.modules.updates;
-
updatelink.href=matches[4];
+
-
updatelink.style.display = "block";
+
-
updatelink.style.position = 'fixed';
+
-
updatelink.style.left = '0px';
+
-
updatelink.style.top = '0px';
+
-
updatelink.style.border = 'none';
+
-
updatelink.style.zIndex = 1;
+
-
var updatelinkimage = document.createElement('img');
+
-
updatelinkimage.src = image_update;
+
-
var oldversionstr = currentversion[0] + "." + currentversion[1] + "." + currentversion[2];
+
-
var newversionstr = matches[1] + "." + matches[2] + "." + matches[3];
+
-
updatelinkimage.title = "Click here to update from script version " + oldversionstr + " to " + newversionstr;
+
-
updatelinkimage.style.display = "block";
+
-
updatelinkimage.style.border = 'none';
+
-
updatelink.appendChild(updatelinkimage);
+
-
document.body.appendChild(updatelink);
+
return;
return;
}
}
-
}
+
-
}
+
this.setting_enabled = globals.modules.settingspane.addCheckbox('updates', "Check for updates", "Regularly check for updates to the All-in-one script", this.enabled);
 +
 +
this.havechecked = false;
 +
this.doCheck();
 +
};
 +
Updates.prototype.updateSettings = function updateSettings()
 +
{
 +
this.enabled = this.setting_enabled.checked;
 +
utils.setPref("updates", this.enabled);
 +
this.doCheck();
 +
};
 +
 +
Updates.prototype.doCheck = function doCheck()
 +
{
 +
if (this.havechecked || !this.enabled)
 +
return;
 +
this.havechecked = true;
 +
 +
if (Date.now() - utils.getPref("lastchecktime", 0) > 86400000)
 +
{
 +
utils.downloadPage("http://www.hrwiki.org/wiki/Special:Getversion/User:Phlip/Greasemonkey?cachedodge=" + Math.random(), this.onLoad.bind(this));
 +
}
 +
else
 +
this.handleUpdateString(utils.getPref("lastcheckstring", ""));
 +
};
 +
Updates.prototype.onLoad = function onLoad(textcontent)
 +
{
 +
utils.setPref("lastchecktime", Date.now());
 +
utils.setPref("lastcheckstring", textcontent);
 +
this.handleUpdateString(textcontent);
 +
};
 +
Updates.prototype.handleUpdateString = function handleUpdateString(str)
 +
{
 +
var parts = str.split("@@");
 +
for (var i = 0; i < parts.length; i++)
 +
{
 +
var matches = parts[i].match(/^(\d+)\.(\d+)\.(\d+)=(.*)$/);
 +
if (!matches) continue;
 +
if (matches[1] > Updates.CURRENT_VERSION[0] ||
 +
    (matches[1] == Updates.CURRENT_VERSION[0] && matches[2] > Updates.CURRENT_VERSION[1]) ||
 +
    (matches[1] == Updates.CURRENT_VERSION[0] && matches[2] == Updates.CURRENT_VERSION[1] && matches[3] > Updates.CURRENT_VERSION[2]))
 +
{
 +
var updatelink = document.createElement('a');
 +
updatelink.href=matches[4];
 +
updatelink.style.display = "block";
 +
updatelink.style.position = 'fixed';
 +
updatelink.style.left = '0px';
 +
updatelink.style.top = '0px';
 +
updatelink.style.border = 'none';
 +
updatelink.style.zIndex = 1;
 +
var updatelinkimage = document.createElement('img');
 +
updatelinkimage.src = globals.images.update;
 +
var oldversionstr = Updates.CURRENT_VERSION[0] + "." + Updates.CURRENT_VERSION[1] + "." + Updates.CURRENT_VERSION[2];
 +
var newversionstr = matches[1] + "." + matches[2] + "." + matches[3];
 +
updatelinkimage.title = "Click here to update from script version " + oldversionstr + " to " + newversionstr;
 +
updatelinkimage.style.display = "block";
 +
updatelinkimage.style.border = 'none';
 +
updatelink.appendChild(updatelinkimage);
 +
document.body.appendChild(updatelink);
 +
return;
 +
}
 +
}
 +
};
 +
 
 +
// Podstar/Videlectrix (stock IIS), HRWiki and stock Apache error pages, respectively. Don't do anything on those pages.
 +
if (document.title == "The page cannot be found" || document.title == "Homestar Runner Wiki - 404 Not Found" || document.title == "404 Not Found")
 +
return;
 +
 +
var utils = new Utils();
 +
var globals = new Globals();
 +
globals.initModules();
 +
})();
 +
 
//</pre>
//</pre>

Revision as of 21:03, 7 October 2014

/*

Contents

Installation instructions

Firefox

If you don't have it already, you'll need to install Greasemonkey, then restart Firefox and return to this page.

Then, just click on this link to install the script.

To upgrade a new version when it's updated, just click the install link again – it'll automagically replace the old version. If the option is enabled, the script will automatically check for updates for you.

Chrome

This script can be installed as an extension from the Chrome Web Store. Chrome will then automatically keep it up-to-date for you via the normal update process.

Script code

*/

// Homestar All-In-One
// version 4.0
// 2014-10-08
// Copyright (c) Phillip Bradbury, Loafing
//
// --------------------------------------------------------------------
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name          Homestar All-In-One
// @namespace     http://www.hrwiki.org/
// @description   Combination of many Homestar Runner scripts. Version 4.0.
// @version       4.0.63
// @downloadURL   http://www.hrwiki.org/w/index.php?title=User:Phlip/Greasemonkey&action=raw&ctype=text/javascript&fakeextension=.user.js
// @icon          http://www.hrwiki.org/w/images/thumb/1/1b/logo.png/32px-logo.png
// @match         http://homestarrunner.com/*
// @match         http://www.homestarrunner.com/*
// @match         http://podstar.homestarrunner.com/*
// @match         http://videlectrix.com/*
// @match         http://www.videlectrix.com/*
// @match         http://hrwiki.org/mirror/*
// @match         http://www.hrwiki.org/mirror/*
// @match         https://secure.homestarrunner.com/heythanks.html*
// @grant         GM_getValue
// @grant         GM_setValue
// @grant         GM_xmlhttpRequest
// ==/UserScript==

(function(){
	function Utils()
	{
		this.guessisplaying = {
			lastframe: -1,
			lastframeat: new Date(),
			state: true
		};
	}
	
	// Taken from http://diveintogreasemonkey.org/patterns/add-css.html
	Utils.prototype.addGlobalStyle = function addGlobalStyle(css)
	{
		var head, style;
		head = document.getElementsByTagName('head')[0];
		if (!head) return;
		style = document.createElement('style');
		style.type = 'text/css';
		style.appendChild(document.createTextNode(css));
		head.appendChild(style);
	};
	
	// Based on http://userscripts.org/topics/41177
	Utils.prototype.useGMFunctions = function useGMFunctions()
	{
		// We can't just test if GM_getValue exists, because in Chrome they do exist
		// but they don't actually do anything, just report failure to console.log
	
		// We don't want it to actually write anything to console.log, though, so
		// let's stop that
		var log = console.log;
		console.log = function log(){};
		var gmstorage = typeof(GM_getValue) == "function" && GM_getValue("this-value-doesn't-exist-I-promise", true);
		console.log = log;
	
		return gmstorage;
	};
	// Only really need to do this once...
	Utils.prototype.useGMFunctions = Utils.prototype.useGMFunctions();
	Utils.prototype.getPref = function getPref(key, def)
	{
		// Have to do it like this instead of like "if(window.GM_getValue)"
		// because apparently this function isn't actually on "window", and I don't
		// know where it actually lives...
		if (this.useGMFunctions)
			return GM_getValue(key, def);
		else if (window.localStorage)
		{
			var value = localStorage.getItem("hr-allinone-" + key);
			if (value === null)
				return def;
			var type = value[0];
			value = value.substring(1);
			if (type == 'b')
				return Number(value) != 0;
			else if (type == 'n')
				return Number(value);
			else
				return value;
		}
		else
		{
			alert("Homestar Runner All-in-one is not supported on this platform");
			throw "Couldn't find a local storage provider";
		}
	};
	Utils.prototype.setPref = function setPref(key, value)
	{
		if (this.useGMFunctions)
			GM_setValue(key, value);
		else if (window.localStorage)
		{
			if (typeof(value) == "string")
				localStorage.setItem("hr-allinone-" + key, "s" + value);
			else if (typeof(value) == "number")
				localStorage.setItem("hr-allinone-" + key, "n" + value);
			else if (typeof(value) == "boolean")
				localStorage.setItem("hr-allinone-" + key, "b" + (value ? 1 : 0));
			else
				throw "Unexpected type for storage: " + typeof(value);
		}
		else
		{
			alert("Homestar Runner All-in-one is not supported on this platform");
			throw "Couldn't find a local storage provider";
		}
	};
	
	Utils.prototype.downloadPage = function downloadPage(url, loadcb, errorcb, method)
	{
		if (!method)
			method = 'GET';
		if (typeof GM_xmlhttpRequest == 'function')
		{
			var opts = {
				method: method,
				url: url,
				onload: function onload(res) {loadcb(res.responseText, res.status, res.statusText, res.responseHeaders);}
			};
			if (errorcb)
				opts.onerror = function onerror(res) {errorcb(res.status, res.statusText, res.responseHeaders);};
			GM_xmlhttpRequest(opts);
		}
		else
		{
			var xhr = new XMLHttpRequest();
			xhr.onload = function onload() {loadcb(xhr.responseText, xhr.status, xhr.statusText, xhr.getAllResponseHeaders());};
			if (errorcb)
				xhr.onerror = function onerror() {errorcb(xhr.status, xhr.statusText, xhr.getAllResponseHeaders());};
			xhr.open(method, url);
			xhr.send();
		}
	};
	Utils.prototype.buildWikiUrl = function buildWikiUrl(page)
	{
		var url = escape(page.replace(/ /g, '_'));
		return "http://www.hrwiki.org/w/index.php?title=" + url + "&action=raw&source=allinone&cachedodge=" + this.getPref('cachedodge', 0);
	};
	Utils.prototype.downloadWiki = function downloadWiki(page, loadcb, errorcb)
	{
		this.downloadPage(this.buildWikiUrl(page), this.wikiPageDownloaded.bind(this, loadcb, errorcb, 0), errorcb);
	};
	Utils.prototype.wikiPageDownloaded = function wikiPageDownloaded(loadcb, errorcb, timesredirected, text, status, statusText)
	{
		// check for redirects
		var matches = text.match(/^\s*#\s*REDIRECT\s*\[\[(.*)\]\]/i);
		if (matches)
		{
			if (timesredirected >= 3) // follow 3 redirects, but no more
			{
				errorcb(500, "Too many redirects");
				return;
			}
			// Get the page name out of the redirect text
			text = matches[1];
			if ((matches = text.match(/^(.*)\|/)))
				text = matches[1];
			if ((matches = text.match(/^(.*)\#/)))
				text = matches[1];
			text = text.replace(/^\s+|\s+$/g, '');
			this.downloadPage(this.buildWikiUrl(text), this.wikiPageDownloaded.bind(this, loadcb, errorcb, timesredirected + 1), errorcb);
			return;
		}
		loadcb(text, status, statusText);
	};
	Utils.prototype.downloadWikiXML = function downloadWikiXML(page, loadcb, errorcb)
	{
		this.downloadWiki(page, this.wikiXMLDownloaded.bind(this, loadcb, errorcb), errorcb);
	};
	Utils.prototype.wikiXMLDownloaded = function wikiXMLDownloaded(loadcb, errorcb, text, status, statusText)
	{
		// strip various things - templates and <pre> tags for wiki formatting, and <noinclude> sections...
		// <includeonly> tags are stripped (but their contents kept) for consistency.
		text = text.replace(/{{.*?}}/g, "");
		text = text.replace(/<\/?pre[^>]*>/g, "");
		text = text.replace(/<noinclude[^>]*>.*?<\/noinclude[^>]*>/g, "");
		text = text.replace(/<includeonly[^>]*>(.*?)<\/includeonly[^>]*>/g, "$1");
		text = text.replace(/^\s+/g, "");
	
		var parser = new DOMParser();
		try
		{
			var doc = parser.parseFromString(text, "application/xml");
		}
		catch (e)
		{
			errorcb(500, "Error in XML:\n" + e.toString());
			return;
		}
		// check if returned document is an error message
		if (doc.getElementsByTagName('parsererror').length > 0)
		{
			var error = doc.getElementsByTagName('parsererror')[0];
			if (error.firstChild.nodeType == doc.TEXT_NODE && error.lastChild.nodeType == doc.ELEMENT_NODE && error.lastChild.nodeName == "sourcetext")
			{
				// Firefox's errors look like this:
				// <parsererror>Error details<sourcetext>Source text</sourcetext></parsererror>
				errorcb(500,
					error.firstChild.nodeValue.replace(/Location: .*\n/, "") + "\n" +
					doc.documentElement.lastChild.textContent
				);
			}
			else if (error.getElementsByTagName('div').length > 0)
			{
				// Chrome's errors look like this:
				// <someRoot><parsererror style="..."><h3>Generic error message</h3><div style="...">Error details</div><h3>Generic footer</h3><attempted parsing of page/></someRoot>
				errorcb(500,
					"Error in XML:\n" +
					error.getElementsByTagName('div')[0].textContent
				);
			}
			else
			{
				// Try to at least return something
				errorcb(500,
					"Error in XML:\n" +
					error.textContent
				);
			}
			return;
		}
		loadcb(doc, status, statusText);
	};
	
	// Documentation for the Flash interface is really lacking...
	// Adobe removed the docs from their website.
	// Luckily, the Wayback Machine captures all
	// http://web.archive.org/web/20100710000820/http://www.adobe.com/support/flash/publishexport/scriptingwithflash/scriptingwithflash_03.html
	// http://web.archive.org/web/20090210205955/http://www.adobe.com/support/flash/publishexport/scriptingwithflash/scriptingwithflash_04.html
	
	Utils.prototype.currentFrame = function currentFrame(flashmovie)
	{
		if (!flashmovie)
			flashmovie = globals.flashmovie;
		if (!flashmovie)
			return false;
	
		var a;
		if (flashmovie === globals.flashmovie && globals.is_puppets)
		{
			if (!flashmovie.TCurrentFrame)
				return -1;
			a = flashmovie.TCurrentFrame("/videoplayer");
	
			if (typeof(a) != 'number' && a < 0)
				return -1;
	
			// Keep track of whether the current frame is changing, for isPlaying()
			// If we stay on the same frame for more than, say, a second, guess
			// that we're paused.
			if (a != this.guessisplaying.lastframe)
			{
				this.guessisplaying.lastframe = a;
				this.guessisplaying.lastframeat = new Date();
				this.guessisplaying.state = true;
			}
			else if (new Date() - this.guessisplaying.lastframeat > 1000)
			{
				this.guessisplaying.state = false;
			}
			
			return a;
		}
		else
		{
			a = flashmovie.CurrentFrame;
			if (typeof(a) == 'function')
				a = flashmovie.CurrentFrame();
	
			if (typeof(a) == 'number' && a >= 0)
				return a;
			else
				return -1;
		}
	};
	Utils.prototype.totalFrames = function totalFrames(flashmovie)
	{
		if (!flashmovie)
			flashmovie = globals.flashmovie;
		if (!flashmovie)
			return false;
	
		var a;
		if (flashmovie === globals.flashmovie && globals.is_puppets)
		{
			if (!flashmovie.TGetPropertyAsNumber)
				return -1;
			a = flashmovie.TGetPropertyAsNumber("/videoplayer", 5); // TOTAL_FRAMES
		}
		else
		{
			a = flashmovie.TotalFrames;
			if (typeof(a) == 'function')
				a = flashmovie.TotalFrames();
		}
		if (typeof(a) == 'number' && a >= 0)
			return a;
		else
			return -1;
	};
	Utils.prototype.isPlaying = function isPlaying(flashmovie)
	{
		if (!flashmovie)
			flashmovie = globals.flashmovie;
		if (!flashmovie)
			return false;
	
		if (flashmovie === globals.flashmovie && globals.is_puppets)
		{
			// There isn't a telltarget version of IsPlaying, there's no flag for it in
			// TGetProperty, and it doesn't seem to be gettable via GetVariable (though
			// it's possible I just haven't tried the right thing)...
			// So, for puppet toons, we need to try to track whether it seems to be playing...
			return this.guessisplaying.state;
		}
	
		var a = flashmovie.IsPlaying;
		if (typeof(a) == 'function')
			a = flashmovie.IsPlaying();
		if (typeof(a) == 'boolean')
			return a;
		else if (typeof(a) == 'number')
			return a != 0;
		else
			return false;
	};
	Utils.prototype.framesLoaded = function framesLoaded(flashmovie)
	{
		if (!flashmovie)
			flashmovie = globals.flashmovie;
		if (!flashmovie)
			return false;
	
		if (!flashmovie.TGetPropertyAsNumber)
			return -1;
		var a;
		if (flashmovie === globals.flashmovie && globals.is_puppets)
			a = flashmovie.TGetPropertyAsNumber('/videoplayer', 12); // property 12 is _framesloaded
		else
			a = flashmovie.TGetPropertyAsNumber('/', 12);
		if (typeof(a) == 'number' && a >= 0)
			return a;
		else
			return -1;
	};
	Utils.prototype.isLoaded = function isLoaded(flashmovie)
	{
		return this.currentFrame(flashmovie) >= 0;
	};
	Utils.prototype.whenLoaded = function whenLoaded(callback, flashmovie)
	{
		if (!flashmovie)
			flashmovie = globals.flashmovie;
		if (!flashmovie)
			return;
	
		if (this.currentFrame(flashmovie) >= 0)
			callback();
		else
			setTimeout(this.whenLoaded.bind(this, callback, flashmovie), 100);
	};
	Utils.prototype.stop = function stop(flashmovie)
	{
		if (!flashmovie)
			flashmovie = globals.flashmovie;
		if (!flashmovie)
			return;
	
		if (flashmovie === globals.flashmovie && globals.is_puppets)
		{
			if (!flashmovie.TStopPlay)
				return;
			flashmovie.TStopPlay("/videoplayer");
	
			this.currentFrame();
			this.guessisplaying.state = false;
		}
		else
		{
			if (!flashmovie.StopPlay)
				return;
			flashmovie.StopPlay();
		}
	};
	Utils.prototype.play = function play(flashmovie)
	{
		if (!flashmovie)
			flashmovie = globals.flashmovie;
		if (!flashmovie)
			return;
	
		if (flashmovie === globals.flashmovie && globals.is_puppets)
		{
			if (!flashmovie.TPlay)
				return;
			flashmovie.TPlay("/videoplayer");
	
			this.currentFrame();
			this.guessisplaying.state = true;
			this.guessisplaying.lastframeat = new Date();
		}
		else
		{
			if (!flashmovie.Play)
				return;
			flashmovie.Play();
		}
	};
	Utils.prototype.goto = function goto(frame, flashmovie)
	{
		if (!flashmovie)
			flashmovie = globals.flashmovie;
		if (!flashmovie)
			return;
	
		if (flashmovie === globals.flashmovie && globals.is_puppets)
		{
			if (!flashmovie.TGotoFrame)
				return;
			flashmovie.TGotoFrame("/videoplayer", frame);
	
			this.currentFrame();
			this.guessisplaying.state = false;
		}
		else
		{
			if (!flashmovie.GotoFrame)
				return;
			flashmovie.GotoFrame(frame);
		}
	};
	Utils.prototype.zoomOut = function zoomOut(factor, flashmovie)
	{
		if (!flashmovie)
			flashmovie = globals.flashmovie;
		if (!flashmovie)
			return;
	
		if (!flashmovie.Zoom)
			return;
		flashmovie.Zoom(100 * factor);
	};
	Utils.prototype.zoomIn = function zoomIn(factor, flashmovie)
	{
		this.zoomOut(1 / factor, flashmovie);
	};
	Utils.prototype.zoomReset = function zoomReset(flashmovie)
	{
		this.zoomOut(0, flashmovie);
	};
	
	Utils.prototype.insertAfter = function insertAfter(newElement, referenceElement)
	{
		if(referenceElement.nextSibling)
			referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling);
		else
			referenceElement.parentNode.appendChild(newElement);
	};

	function Globals()
	{
		this.whichsite = 0;
		if (location.hostname.indexOf("podstar") >= 0) this.whichsite = 1;
		if (location.hostname.indexOf("videlectrix") >= 0) this.whichsite = 2;
		if (location.pathname.indexOf("/mirror/") >= 0) this.whichsite = 3;
	
		// icons, as Base64-encoded PNG files.
		this.images = {
			close:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAAm' +
				'JLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfWBRkTNhxuPxLkAAAAHX' +
				'RFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBUaGUgR0lNUO9kJW4AAAEKSURBVCjPhdGxSgNBFA' +
				'XQMzpgYWwsLEQUDBJBQgqFIChZEPR7/DA/QCGQTgQtJE1ENoWohYUgbGKQyFjErNv52nObe1' +
				'9wqGWg7z0l5YVgVdOu+wUt507tqIVQ4Zodp861ooELe15M5KFI6Zfr9u25MIj6Jl4cmSIPBW' +
				'rq2o5cufO4aOJDYSozNTa2pK4t03PtwUdMKRRykAmW0dTRcyNXpBQpI8GJDTR050zkNzK0bM' +
				'MZLvUNZ8yCfy6Wvbc1NVyi4dloXjqWvds6uvp41pFmpVOKJWd6bgwxkmTMIotWKpwrfBkZl7' +
				'uMonUHf5wSlV2+fUZrjnXdzrmyy7djD8GWTW9e51z557o1Tz85FH/WkOkaHQAAAABJRU5Erk' +
				'Jggg==',
			ffwd:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
				'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
				'sTAQCanBgAAAAHdElNRQfeCgQNLh+v5c+DAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
				'BHSU1QV4EOFwAAAC9JREFUCNcVisENAEAIwjo6ozmKI/j0YfS4hAeUIhFBJlV0M8Mudz8uno' +
				'a+LFiTHqCuHAU1qtJ6AAAAAElFTkSuQmCC',
			hrwiki:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGX' +
				'RFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAm1QTFRF////2wAzgZDJAiGNAB' +
				'6Lenp6ABCEABKFAAyDjp3O8gAAipjLlaPPFUixAB6OAA6C/f//fY3JABaIhJXK///50gAn//' +
				'/4CymXyQAaAA+DOFCm1QAmDiuX//zvnV2IfI3IQ0h7ABSFN0+qZXm9ABSG9PTxABiK2wAkuQ' +
				'AdSWW5WGu4cILCgYy209PZGRdjABeH0AALDiyYASGOhJTL2bi8k5OTzgAj1QAdLkilAAiDAB' +
				'iQIiCBzwAbyAAk//31ABSO0gAXDB95c5nZDAxeoRhHOVCp7u3lfx1W1LrCxQYtwwApQVitwA' +
				'QpJj2bAAyFKSODfI3GYna86urqysfL9fT0NUyXMDGGNk6cxgASy9rkAB2OQ1qzTmOzu8Pa4d' +
				'/b+v//58zRFEqw09XR25yrIjyh9P//g5PLAAN+foy/uRY92treh6neAASDXXC9jJvKjJvL6e' +
				'npiJfKDzejNk6r2wE1N0+rABKEAB6KxAAn0tPWyQAZRFuvXXfB/f392AAgKiib2QAyABaJhJ' +
				'TH2XeNEy+ZzgAwBiSRKUOlgI27urrP7t/iCghS0AAfk3SoyBc+iChf3vH1VWq426GvgI/Hiz' +
				'duboDCPEOXABCDSmu/DyeD///6P2K4OUJ/HByRlKHOAB+O8AAA2QI1hZPHg5TI9PT0ABuJiZ' +
				'jM1tbdf43CzgApAB2We4vD7e3rwgAseInHAyGWi5rOUme3hIuqFTGaWG25dojDd5LQ5Ki1AA' +
				'yMASCNcYHEAyKOABqLACSWHDeR+vr6uwAiIyBjipnJ1AIyjZvMmJyaITylAByMAB2L5wAlHD' +
				'eeCCaUcHCjWGy4wBQy/7AMAgAAARFJREFUeNpiONFeuLWjfL4RM1/R4tXyx5kZdrNHZ8za5D' +
				'jT3n/KimUtLDsZhPbFJilY14d5cC3o1dRnaWPILaucozf3DAND3DmnxqWnmRjyd+046NzJwG' +
				'B6dMJ6xVNHJjPIrGvq1mVgYGBlZQg9xjlxD4MKh2+wKgMILLET00mPYmCqFpctYIAAEamz3A' +
				'zaG4TdgmohAllpgsoMbBw5y9fshwiEW0qyM7jGbIlMjWcQCOlKMKnR8rZgyDOe3e95oErCJq' +
				'LOPDGQ8xBDCv8qF9tWRkb1SStPTvNTU2JgK83OrDjMaKbB0Gwgt23zdIap83h9vBZKJ4MMdZ' +
				'/Bs5EhwHBvz9qSBoftDAx9olbFiwACDABkK1N43Z86KwAAAABJRU5ErkJggg==',
			next:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
				'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
				'sTAQCanBgAAAAHdElNRQfeCgQNLSOrp+DHAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
				'BHSU1QV4EOFwAAACtJREFUCNdjULBhMLBhsLBhsLFhsLNhsAeiPQz2f8BoD4hrB5ayACtTsA' +
				'EA6J8JvyvoxNYAAAAASUVORK5CYII=',
			pause:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
				'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
				'sTAQCanBgAAAAHdElNRQfeCgQNLS1MH83AAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
				'BHSU1QV4EOFwAAAA5JREFUCNdjsLFhIAUBALQwB4FBHjsqAAAAAElFTkSuQmCC',
			play:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
				'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
				'sTAQCanBgAAAAHdElNRQfeCgQNLjLqOpP2AAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
				'BHSU1QV4EOFwAAACdJREFUCNcdirEJAAAMg/z/qpzUAwJpG3ARRTBgyCEyxCTFVX1yN7Ejqh' +
				'alykITkQAAAABJRU5ErkJggg==',
			prefs:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAll' +
				'BMVEUAGQASEhIfHx8fJy8pKSk2NjZBQUFJR0ZQUE9RUVFSUlJNX3NoaGhsaWdramlycG1meY' +
				'98fHx+fn5wgpV0iqKKh4R4jaR9jJx8kad9kad/mbONmaWEnrmEnrqkoZy3t7fIx8bKyMHT0c' +
				'3S0dDU09DV1NPP1t3W1dXY2Njb2tfe29bf3tzj4uHr6+js6+r39/f5+PgAAABrL3yvAAAAAX' +
				'RSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfWBR' +
				'oFKh31UQ8DAAAAgUlEQVQY022OxxLCMAwFRSc4BEIPJZQQ08v+/8+RsTExDDpIe3ijfSJ/hx' +
				'9g62Dt4GaAI+8YT0t27+BxxvvE/no5pYT10lGFrE34Ja40W3g1oMGmW7YZ6hnCYexKTPVkXi' +
				'vuvWe1Cz1aKqPNI3N0slI2TNYZiARJX30qERc7wBPKC4WRDzWdWHfmAAAAAElFTkSuQmCC',
			prev:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
				'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
				'sTAQCanBgAAAAHdElNRQfeCgQNLgFV6vLgAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
				'BHSU1QV4EOFwAAACxJREFUCNdjsGFhsOFhsJFhsLFhsKlhsPnDYPuHwR6MgAwgFyRoA1YAVM' +
				'YCABGLC3k4wQ8QAAAAAElFTkSuQmCC',
			rewind:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABl' +
				'BMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAA' +
				'sTAQCanBgAAAAHdElNRQfeCgQNLhgxgVogAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aC' +
				'BHSU1QV4EOFwAAAC9JREFUCNdjYGRkYGZmYGdn4OdnkJdnsLdnqK9n+P8fhIAMIBcoCJQCKg' +
				'AqY2QEALxwB9ke+WHMAAAAAElFTkSuQmCC',
			stop:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA1' +
				'BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9' +
				'4KBA0uOX3oSn4AAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADElEQV' +
				'QI12NgIA0AAAAwAAHHqoWOAAAAAElFTkSuQmCC',
			update:
				'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABGCAMAAABG8BK2AAAC8V' +
				'BMVEUAAAD/AAD+AQH/AQH/AgL+AwP/AwP+BAT/BAT/BQX+Bgb/Bgb/Bwf+CAj/CAj/CQn/Cg' +
				'r+Cwv/Cwv+DAz/DAz/DQ3/Dg7+Dw//Dw//EBD+ERH/ERH/EhL/ExP+FBT/FRX/Fhb/Fxf+GB' +
				'j/GBj/GRn/Ghr/Gxv/HBz/HR3/Hh7/Hx//ICD+ISH/ISH/IiL/IyP/JCT/JSX/Jib/Jyf/KS' +
				'n/Kyv/LCz/LS3/Ly//MDD/MTH+MjL/MjL/MzP/NDT/NTX/Njb+Nzf/Nzf/ODj+OTn/OTn/Oj' +
				'r/PDz/Pj7/Pz//QUH/QkL+Q0P/RUX/Rkb/R0f/SEj/SUn/Skr/S0v/TEz/TU3/Tk7/T0//UF' +
				'D/UVH/UlL/VFT/VVX/Vlb/WFj/WVn/Wlr/W1v/XFz/XV3/Xl7/X1//YGD/YWH/YmL/Y2P/ZW' +
				'X/Zmb/Z2f/aGj/aWn/amr/a2v/bGz/bW3/bm7/b2//cHD/cXH/cnL/dHT/dnb/d3f/eHj/eX' +
				'n/e3v/fX3/fn7/f3//gID/gYH/goL/g4P/hIT/hob/h4f/iIj/iYn/ior/i4v/jIz/jY3/jo' +
				'7+kJD/kJD/kZH/kpL/lJT/lpb/l5f/mJj/mZn/mpr/m5v/nJz/nZ3/n5//oKD/oaH/oqL/o6' +
				'P/pqb/p6f/qKj/qan/qqr/q6v/rKz/ra3/r6//sLD/sbH/srL/s7P/tLT/tbX/trb/t7f/uL' +
				'j/urr/u7v/vLz/vb3/vr7/v7//wMD/wcH/wsL/w8P/xMT/xcX/xsb+x8f/x8f/yMj/ycn/ys' +
				'r/y8v/zMz/zc3/zs7/z8//0ND/0dH/0tL/09P+1NT/1NT/1tb/19f+2Nj/2Nj/2dn/29v/3N' +
				'z/3d3/39//4OD/4eH/4uL/4+P/5OT/5eX/5ub/5+f/6Oj/6en/6ur/6+v/7Oz/7e3/7u7/7+' +
				'/+8PD/8fH/8vL/8/P/9PT/9fX/9vb/9/f/+Pj/+fn/+vr/+/v//Pz//f3+/v7//v7////+AA' +
				'A5GkRyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAADzoAAA+IAUHKF/gAAA' +
				'AHdElNRQfXCRYICgxGxxkcAAAEL0lEQVRYw63Xe1wURRwA8Pm1G0KcHdGBkKAYjxC0yLJITU' +
				'l7cr7RUjAos4AuraCH2pWCVlZaRpD5AEXDwAemQRFdmgQeCgWUPKTk4JJHomAq5PBXu/fC2z' +
				't2Z7fdf+Y38/nc9zPz+83M7iEQ9VBDjCNxStKGG5xJSBSjWPV+c3m0nxNFDEP/XBf3ZkPLuv' +
				'GOigiG2oLrhyvVJX26abdzFXKGWtrUPRXA5aasRjyD5ijkzJjd/2aMNkXqhCiKoxAzU9bg3n' +
				'mDdXe1V4iZJIzTBnvhH9xrpxAzKbj1cYDY2Ww8AMuOL7NTiBg6koZX2rruhFhjLJsVP5iv8b' +
				'FTSBj6xxo/CHqxXftwYxFTKwhY/aj9iogYOgfrRwCM/vr0qXOmpUQ0pXAVYYZa19tuymc8xq' +
				'vY1u0nnOXCUQQZ6vnf/p5jiibpqgOYxqcctwRwFUEmqrD/1VvMYWppjGrUE7/ghkAHRYhxy8' +
				'QdG6x79u2DBbru/mLHuQgyr+H9HYatCkvv2U3Hdmv9nSgCzKyW/MnBpW1HvSz9gRHsMUAiGe' +
				'/1OA5A9XlX/TQv7pkmZtzB/Y1UNvBMP2NIDOVTeJjpT49lJNOjXHHq/Mb7eRQe5pnavAm2W3' +
				'jRt33Fjw2t8C3qG3z8AWvsOnFba6YbNZTCw9yYYsg2qkfabqpZPkPOhXc2ET2bk3FpAvDXSJ' +
				'BxbSsZ29O1fz2BwrtvVlzSNb60vX5ruEJI4WVUxxoTISSp46hWJaA4MtSw2dlVRXlq5jy6H6' +
				'5hRzw+XasSUBwYOu2rC4YO/bmWM0EesPRQsGnsZiGFy9AlVbmRzG9dQrMr1NSEE1OEs+uEoX' +
				'bivUGW+EBrIGh3KYkUDuP7bu3JPZ7mOKSsgFr4ggeRwmE87/FfW9Pqbb74vqgOg3Ay5XqGmp' +
				'Re9+U7vsvL/0oybZRE9rIhU65j6Az9tZL0ffn3jdtyadNdzEAaTiZVBhn6O9y+YBxAUw64fn' +
				'R+hxoUVXg5qWJjqBzcFsbutYrDwwBWHvr9rUrc5E+q2JjQExceYduHruQqBgAe3NhvLBhDrN' +
				'iYyD79agXzTtXg98xs9CIvcsXGRPzQc7F68R23NlxZQtk+pZEohnoyBuDuqI9P99Y244rhJP' +
				'eLMyZQ90exJgyUU/dgfPEpKYp5UeHak83fT2Tf0pXX8hMlKMj6Znu57HIMcwjmZmCcI15BVI' +
				'CvWfLK7ExmKnzbPH3fJ6IV9NzZLG/LKo4Y49kmOHKUaAVB2T8h1pzGGMeLrrSVmX71iPUzaO' +
				'afMyRk15Lios4EixONl0hU2ErldW82O5rOORIVU8ELDZ8xDq2sPRsmUTHvm8LuyvjFr/+Kc3' +
				'0kKpbtt6OuC+OefSOlKrYTHqf5MNVPsoLs/2QjGZj/oSB5FCSPguRRkDwKkkdB8ihIHgXJoy' +
				'B5FCSPguRRkDzKf7Z6NUd33kmjAAAAAElFTkSuQmCC'
		};
	
		// find flash objects
		var objs;
		switch (this.whichsite)
		{
			case 0: // www.homestarrunner.com
				objs = document.getElementsByTagName("EMBED");
				if (objs && objs.length >= 2)
				{
					this.flashmovie = objs[0];
					this.navbar = objs[1];
				}
				else if (objs && objs.length >= 1)
				{
					this.flashmovie = objs[0];
					this.navbar = false;
				}
				else
				{
					this.flashmovie = false;
					this.navbar = false;
				}
				if (!this.flashmovie)
				{
					objs = document.getElementsByTagName("OBJECT");
					if (objs && objs.length >= 1)
						this.flashmovie = objs[0];
				}
				break;
			case 1: // podstar.homestarrunner.com
				objs = document.getElementsByTagName("EMBED");
				this.flashmovie = false;
				if (objs && objs.length >= 1)
					this.navbar = objs[0];
				else
					this.navbar = false;
				break;
			case 2: // videlectrix
				objs = document.getElementsByTagName("EMBED");
				this.navbar = false;
				if (objs && objs.length >= 1)
					this.flashmovie = objs[0];
				else
					this.flashmovie = false;
				/*settings.navbar = false;*/
				break;
			case 3: // mirror
				objs = document.getElementsByTagName("EMBED");
				this.flashmovie = false;
				if (objs && objs.length >= 1)
					this.flashmovie = objs[0];
				if (!this.flashmovie)
				{
					objs = document.getElementsByTagName("OBJECT");
					if (objs && objs.length >= 1)
						this.flashmovie = objs[0];
				}
				this.navbar = document.getElementById('navbar');
				/*if (!this.navbar)
					settings.navbar = false;*/
				var flashcontainer = document.getElementById('flash');
				if (flashcontainer)
					flashcontainer.style.width = "auto";
				break;
		}
		if (this.flashmovie)
		{
			//expose Flash plugin-added methods
			if (this.flashmovie.wrappedJSObject)
				this.flashmovie = this.flashmovie.wrappedJSObject;
			
			// confirm that this is really a flash file
			// and not (for example) the embedded background sound on SB's website
			var src = this.flashmovie.getAttribute('src');
			if (this.flashmovie.nodeName.toLowerCase() == "object")
			{
				if (src)
				{
					if (src.substring(src.length - 4).toLowerCase() != ".swf")
						this.flashmovie = false;
				}
				else
				{
					var a = this.flashmovie.getElementsByTagName('param').namedItem("movie");
					if (!a || a.value.substring(a.value.length - 4).toLowerCase() != ".swf")
						this.flashmovie = false;
					else
						src = a.value;
				}
			}
			else if (this.flashmovie.nodeName.toLowerCase() == "embed")
			{
				if (!src || src.substring(src.length - 4).toLowerCase() != ".swf")
					this.flashmovie = false;
			}
	
			// puppet_background.swf is a wrapper around the puppet stuff popup toons
			// This flag tells things like seekbar to control the wrapped movie clip
			if (src)
				this.is_puppets = src == "puppet_background.swf" || src.substring(src.length - 22) == "/puppet_background.swf";
		}
		// Don't run large flash objects inline (gets rid of some extra padding from
		// having the movie sitting on the baseline)
		if (this.flashmovie)
		{
			this.flashmovie.style.display = "block";
			this.flashmovie.style.margin = "0 auto";
		}
		if (this.navbar)
		{
			this.navbar.style.display = "block";
			this.navbar.style.margin = "0 auto";
		}
	
		this.filename = window.location.pathname.toLowerCase();
		var i = this.filename.lastIndexOf('/');
		if (i >= 0)
			this.filename = this.filename.substr(i + 1);
		i = this.filename.lastIndexOf('.');
		if (i >= 0)
			this.filename = this.filename.substr(0,i);
	}
	Globals.prototype.initModules = function initModules()
	{
		this.modules = {};
		this.modules.settingspane = new SettingsPane();
		this.modules.fullscreen = new Fullscreen();
		this.modules.seekbar = new Seekbar();
		this.modules.wikilink = new WikiLink();
		this.modules.nextprev = new NextPrev();
		this.modules.navbar = new Navbar();
		this.modules.subtitles = new Subtitles();
		this.modules.updates = new Updates();
		for (var i in this.modules)
			this.modules[i].init();
		this.modules.settingspane.initComplete();
	};

	function SettingsPane()
	{
	}
	SettingsPane.prototype.init = function init()
	{
		utils.addGlobalStyle(
			'#settingsbox, #settingslink\n' +
			'{\n' +
			'\tborder-right: 1px solid #666;\n' +
			'\tborder-bottom: 1px solid #666;\n' +
			'\tbackground: #EEE;\n' +
			'\tcolor: #000;\n' +
			'\tposition: fixed;\n' +
			'\toverflow: auto;\n' +
			'\tleft: 0;\n' +
			'\ttop: 0;\n' +
			'\tfont: 12px sans-serif;\n' +
			'\ttext-align: left;\n' +
			'\tz-index: 2;\n' +
			'}\n' +
			'#settingsbox\n' +
			'{\n' +
			'\twidth: 350px;\n' +
			'}\n' +
			'#settingstitlebar\n' +
			'{\n' +
			'\tfont-weight: bolder;\n' +
			'\tbackground: #CCC;\n' +
			'\tborder-bottom: 1px solid #666;\n' +
			'\tpadding: 3px;\n' +
			'}\n' +
			'#settingstitlebar img\n' +
			'{\n' +
			'\tvertical-align: text-bottom;\n' +
			'}\n' +
			'#settingstitlebar .prefsicon\n' +
			'{\n' +
			'\tfloat: left;\n' +
			'\tmargin-right: 0.5em;\n' +
			'}\n' +
			'#settingstitlebar .buttonimage, #settingslink .buttonimage\n' +
			'{\n' +
			'\tcursor: pointer;\n' +
			'\tdisplay: block;\n' +
			'}\n' +
			'#settingstitlebar .buttonimage\n' +
			'{\n' +
			'\tfloat: right;\n' +
			'}\n' +
			'#settingsbox form\n' +
			'{\n' +
			'\tmargin: 0;\n' +
			'\tpadding: 3px;\n' +
			'}\n' +
			'#settingsbox ul, #settingsbox li\n' +
			'{\n' +
			'\tlist-style: none;\n' +
			'\tmargin: 0;\n' +
			'\tpadding: 0;\n' +
			'}\n' +
			'#settingsbox ul ul\n' +
			'{\n' +
			'\tmargin-left: 2em;\n' +
			'}\n' +
			'#settingsbox input[type="checkbox"]\n' +
			'{\n' +
			'\tmargin-right: 0.25em;\n' +
			'}\n' +
			'#settingsbuttons\n' +
			'{\n' +
			'\ttext-align: center;\n' +
			'}\n' +
			'#settingslink\n' +
			'{\n' +
			'\tpadding: 3px;\n' +
			'}\n' +
			""
		);
		
		var settingsbox = document.createElement('div');
		this.settingsbox = settingsbox;
		settingsbox.id = 'settingsbox';
		settingsbox.style.display = 'none';
		document.body.appendChild(settingsbox);
		var titlebar = document.createElement('div');
		titlebar.id = 'settingstitlebar';
		settingsbox.appendChild(titlebar);
		var closebutton = document.createElement('img');
		closebutton.src = globals.images.close;
		closebutton.title = "Click to hide preferences";
		closebutton.className = 'buttonimage';
		closebutton.addEventListener('click', this.hidePane.bind(this), false);
		titlebar.appendChild(closebutton);
		var prefslogo = document.createElement('img');
		prefslogo.src = globals.images.prefs;
		prefslogo.className = 'prefsicon';
		titlebar.appendChild(prefslogo);
		titlebar.appendChild(document.createTextNode("Preferences"));
		var settingsform = document.createElement('form');
		settingsbox.appendChild(settingsform);
		var settingslist = document.createElement('ul');
		this.settingslist = settingslist;
		var a = window.innerHeight - 75;
		if (a < 40) a = 40;
		settingslist.style.maxHeight = a + 'px';
		settingslist.style.overflow = 'auto'; // vertical scrollbar if needed
		window.addEventListener('resize', this.resizeWindow.bind(this), true);
		settingsform.appendChild(settingslist);
	
		var div = document.createElement('div');
		div.id = 'settingsbuttons';
		settingsform.appendChild(div);
		var savebutton = document.createElement('input');
		savebutton.type = "submit";
		savebutton.value = "Save and Apply";
		div.appendChild(savebutton);
		var nocachebutton = document.createElement('input');
		nocachebutton.type = "submit";
		nocachebutton.value = "Clear subtitles cache";
		nocachebutton.addEventListener("click", this.cacheDodge.bind(this), false);
		div.appendChild(document.createTextNode(" "));
		div.appendChild(nocachebutton);
		settingsform.addEventListener("submit", this.saveSettings.bind(this), false);
		
		var settingslink = document.createElement('div');
		this.settingslink = settingslink;
		settingslink.id = 'settingslink';
		var settingslinkimage = document.createElement('img');
		settingslinkimage.src = globals.images.prefs;
		settingslinkimage.title = "Click to show preferences";
		settingslinkimage.className = 'prefsicon buttonimage';
		settingslinkimage.addEventListener('click', this.showPane.bind(this), false);
		settingslink.appendChild(settingslinkimage);
		document.body.appendChild(settingslink);
		
		this.hidePanels = [];
	};
	SettingsPane.prototype.saveSettings = function saveSettings(e)
	{
		// stop the form from actually being submitted
		if (e && e.preventDefault)
			e.preventDefault();
		
		for (var i in globals.modules)
			globals.modules[i].updateSettings();
		
		return false;
	};
	SettingsPane.prototype.updateSettings = function updateSettings(){};
	SettingsPane.prototype.showPane = function showPane()
	{
		this.settingsbox.style.display = "block";
		this.settingslink.style.display = "none";
	};
	SettingsPane.prototype.hidePane = function hidePane()
	{
		this.settingsbox.style.display = "none";
		this.settingslink.style.display = "block";
	};
	SettingsPane.prototype.resizeWindow = function resizeWindow()
	{
		var a = window.innerHeight - 75;
		if (a < 40) a = 40;
		this.settingslist.style.maxHeight = a + 'px';
	};
	SettingsPane.prototype.cacheDodge = function cacheDodge()
	{
		utils.setPref("cachedodge", Math.random().toString());	
	};
	
	SettingsPane.prototype.addSettingRow = function addSettingRow(parent)
	{
		if (!parent)
			parent = this.settingslist;
		else
		{
			var checkbox = undefined;
			if (parent.tagName.toLowerCase() == "input")
			{
				checkbox = parent;
				parent = parent.parentNode;
			}
			var ul = parent.getElementsByTagName("ul");
			if (ul.length)
				parent = ul[ul.length - 1];
			else
			{
				ul = document.createElement("ul");
				parent.appendChild(ul);
				parent = ul;
	
				if (checkbox)
				{
					this.hidePanels.push({checkbox: checkbox, panel: ul});
					checkbox.addEventListener("click", this.showHidePanel.bind(this, checkbox, ul), false);
				}
			}
		}
		var settingrow = document.createElement('li');
		parent.appendChild(settingrow);
		return settingrow;
	};
	SettingsPane.prototype.addCheckbox = function addCheckbox(id, label, title, checked, parent)
	{
		var settingrow = this.addSettingRow(parent);
		var settingcheckbox = document.createElement('input');
		settingcheckbox.type = 'checkbox';
		settingcheckbox.checked = checked;
		settingcheckbox.title = title;
		settingcheckbox.id = 'setting_' + id;
		settingrow.appendChild(settingcheckbox);
		var settinglabel = document.createElement('label');
		settinglabel.htmlFor = 'setting_' + id;
		settinglabel.appendChild(document.createTextNode(label));
		settinglabel.title = settingcheckbox.title;
		settingrow.appendChild(settinglabel);
		return settingcheckbox;
	};
	
	SettingsPane.prototype.showHidePanel = function showHidePanel(checkbox, panel)
	{
		panel.style.display = checkbox.checked ? "" : "none";
	};
	SettingsPane.prototype.initComplete = function initComplete()
	{
		for (var i = 0; i < this.hidePanels.length; i++)
			this.showHidePanel(this.hidePanels[i].checkbox, this.hidePanels[i].panel);
	};

	function Fullscreen()
	{
		this.shouldresize = utils.getPref('resize', true);
		this.noscale = utils.getPref('noscale', false);
	}
	Fullscreen.prototype.init = function init()
	{
		this.setting_main = globals.modules.settingspane.addCheckbox('resize', "Resize flash to full-screen", "Resizes the toon so it fills the entire window", this.shouldresize);
		this.setting_noscale = globals.modules.settingspane.addCheckbox('noscale', "Show behind the black", "Lets you see what's happening beyond the frames", this.noscale, this.setting_main);
		
		if (!globals.flashmovie)
			return;
	
		this.initwidth = globals.flashmovie.width;
		this.initheight = globals.flashmovie.height;
		if (this.initwidth.toString().indexOf('%') >= 0 || this.initwidth.toString().indexOf('%') >= 0)
		{
			this.isPercentage = true;
			this.aspect = 1.0;
		}
		else
		{
			this.isPercentage = false;
			this.aspect = this.initwidth / this.initheight;
		}
		window.addEventListener('resize', this.doResize.bind(this), true);
		this.doResize();
		if (this.noscale)
			this.setScaleMode("noScale");
	};
	Fullscreen.prototype.doResize = function doResize()
	{
		if (!globals.flashmovie)
			return;
		
		if (!this.shouldresize)
		{
			globals.flashmovie.style.width = this.initwidth + "px";
			globals.flashmovie.style.height = this.initheight + "px";
			if (globals.modules.seekbar.seekbar)
				globals.modules.seekbar.seekbar.style.width = Math.max(this.initwidth, 450) + "px";
			return;
		}
		
		var dw = window.innerWidth;
		var dh = window.innerHeight;
	
		var a = document.defaultView.getComputedStyle(document.body, null);
		// parseInt will take the number part at the start, turning eg "10px" into 10
		dw -= parseInt(a.marginLeft,10);
		dw -= parseInt(a.marginRight,10);
		dh -= parseInt(a.marginTop,10);
		dh -= parseInt(a.marginBottom,10);
	
		if (globals.navbar)
		{
			a = document.defaultView.getComputedStyle(globals.navbar, null);
			dh -= parseInt(a.height,10);
			dh -= parseInt(a.marginTop,10);
			dh -= parseInt(a.marginBottom,10);
		}
		if (globals.modules.seekbar.seekbar)
		{
			a = document.defaultView.getComputedStyle(globals.modules.seekbar.seekbar, null);
			dh -= parseInt(a.height,10);
			dh -= parseInt(a.marginTop,10);
			dh -= parseInt(a.marginBottom,10);
		}
		if (globals.modules.subtitles.subtitleholder)
		{
			a = document.defaultView.getComputedStyle(globals.modules.subtitles.subtitleholder, null);
			dh -= parseInt(a.height,10);
			dh -= parseInt(a.marginTop,10);
			dh -= parseInt(a.marginBottom,10);
		}
		if (globals.modules.subtitles.errorsholder)
		{
			a = document.defaultView.getComputedStyle(globals.modules.subtitles.errorsholder, null);
			dh -= parseInt(a.height,10);
			dh -= parseInt(a.marginTop,10);
			dh -= parseInt(a.marginBottom,10);
		}
		// enforce a (rather small) minimum size, regardless of how much crap is squeezed below the frame
		if (dw < 100) dw = 100;
		if (dh < 100) dh = 100;
		// if it was a percentage size, or we're looking outside the frame, just fill the whole window.
		// otherwise, keep the aspect ratio correct... "touch inside" style.
		if (!this.isPercentage && !this.noscale)
		{
			if(dw <= dh * this.aspect)
				dh = Math.floor(dw / this.aspect);
			else
				dw = Math.floor(dh * this.aspect);
		}
	
		// set embed's size
		globals.flashmovie.style.width = dw + "px";
		globals.flashmovie.style.height = dh + "px";
		if (globals.modules.seekbar.seekbar)
			globals.modules.seekbar.seekbar.style.width = Math.max(dw, 450) + "px";
	};
	Fullscreen.prototype.setScaleMode = function setScaleMode(scaleMode)
	{
		utils.whenLoaded(function(){
			globals.flashmovie.SetVariable("Stage.scaleMode", scaleMode);
		});
	};
	Fullscreen.prototype.updateSettings = function updateSettings()
	{
		this.shouldresize = this.setting_main.checked;
		utils.setPref("resize", this.shouldresize);
		var old_noscale = this.noscale;
		this.noscale = this.setting_noscale.checked;
		utils.setPref("noscale", this.noscale);
		this.doResize();
		if (this.noscale && !old_noscale)
			this.setScaleMode("noScale");
		else if (!this.noscale && old_noscale)
			this.setScaleMode("showAll");
	};

	function Seekbar()
	{
		this.enabled = utils.getPref('seekbar', true);
		this.framecounter = utils.getPref('frames', false);
		this.zoom = utils.getPref('zoom', false);
	}
	Seekbar.prototype.init = function init() {
		this.setting_enabled = globals.modules.settingspane.addCheckbox('seekbar', "Show seek bar", "Lets you fast forward and rewind", this.enabled);
		this.setting_framecounter = globals.modules.settingspane.addCheckbox('framecounter', "Show frame counter on seek bar", "Shows you exactly where you are", this.framecounter, this.setting_enabled);
		this.setting_zoom = globals.modules.settingspane.addCheckbox('zoom', "Show zooming controls", "Allows zooming in on the toon", this.zoom, this.setting_enabled);
		
		if (!globals.flashmovie)
			return;
	
		if (this.enabled)
			this.addSeekbar();
	
		this.dragging = false;
		this.paused = !utils.isPlaying();
		document.addEventListener("mousemove", this.dragMousemove.bind(this), false);
		document.addEventListener("mouseup", this.release.bind(this), false);
	
		window.setInterval(this.update.bind(this), 50);
	};
	Seekbar.prototype.updateSettings = function updateSettings()
	{
		if (this.enabled)
			this.removeSeekbar();
		this.enabled = this.setting_enabled.checked;
		utils.setPref("seekbar", this.enabled);
		this.framecounter = this.setting_framecounter.checked;
		utils.setPref("frames", this.framecounter);
		this.zoom = this.setting_zoom.checked;
		utils.setPref("zoom", this.zoom);
		if (this.enabled && globals.flashmovie)
			this.addSeekbar();
	};
	Seekbar.prototype.addSeekbar = function addSeekbar()
	{
		this.dragging = false;
		this.paused = !utils.isPlaying();
	
		this.seekbar = document.createElement("div");
		var where = globals.flashmovie;
		while(where.parentNode.tagName.toLowerCase()=="object" || where.parentNode.tagName.toLowerCase()=="embed")
			where=where.parentNode;
		utils.insertAfter(this.seekbar, where);
		this.seekbar.style.width = globals.flashmovie.width;
		this.seekbar.style.margin = "0 auto";
	
		var table=document.createElement("table");
		table.style.width="100%";
		this.seekbar.appendChild(table);
		var row=table.insertRow();
		this.pauseButton=document.createElement("button");
		this.pauseButtonImg = document.createElement("img");
		this.pauseButtonImg.src = globals.images.pause;
		this.pauseButton.appendChild(this.pauseButtonImg);
		var buttonCell=row.insertCell();
		buttonCell.appendChild(this.pauseButton);
		var rewindCell=row.insertCell();
		this.rewindButton=document.createElement("button");
		var img = document.createElement("img");
		img.src = globals.images.rewind;
		this.rewindButton.appendChild(img);
		rewindCell.appendChild(this.rewindButton);
		var prevCell=row.insertCell();
		this.prevButton=document.createElement("button");
		img = document.createElement("img");
		img.src = globals.images.prev;
		this.prevButton.appendChild(img);
		prevCell.appendChild(this.prevButton);
	
		this.slider=row.insertCell();
		this.slider.width="100%";
		var visibleSlider=document.createElement("div");
		visibleSlider.style.position="relative";
		visibleSlider.style.height="0.5em";
		visibleSlider.style.width="100%";
		visibleSlider.style.borderRadius="0.25em";
		visibleSlider.style.background="#333";
		this.slider.appendChild(visibleSlider);
		this.loadmeter=document.createElement("div");
		this.loadmeter.style.position="absolute";
		this.loadmeter.style.top=this.loadmeter.style.left = "0";
		this.loadmeter.style.height="0.5em";
		this.loadmeter.style.width="0";
		this.loadmeter.style.borderRadius="0.25em";
		this.loadmeter.style.background="#aaa";
		visibleSlider.appendChild(this.loadmeter);
		this.thumb=document.createElement("div");
		this.thumb.style.position="absolute";
		this.thumb.style.height="1em";
		this.thumb.style.width="0.5em";
		this.thumb.style.top="-0.25em";
		this.thumb.style.borderRadius="0.25em";
		this.thumb.style.background="#666";
		visibleSlider.appendChild(this.thumb);
	
		var nextCell=row.insertCell();
		this.nextButton=document.createElement("button");
		img = document.createElement("img");
		img.src = globals.images.next;
		this.nextButton.appendChild(img);
		nextCell.appendChild(this.nextButton);
		var ffCell=row.insertCell();
		this.ffButton=document.createElement("button");
		img = document.createElement("img");
		img.src = globals.images.ffwd;
		this.ffButton.appendChild(img);
		ffCell.appendChild(this.ffButton);
	
		if (this.framecounter)
		{
			var frameCell=row.insertCell();
			var framediv=document.createElement("div");
			framediv.style.background="#ccc";
			framediv.style.color="#000";
			framediv.style.fontWeight="bold";
			framediv.style.padding = "0 5px";
			frameCell.appendChild(framediv);
			this.framecountertext=document.createTextNode("");
			framediv.appendChild(this.framecountertext);
		}
		else
			this.framecountertext = false;
	
		if (this.zoom && !globals.modules.fullscreen.noscale)
		{
			var zoomOutCell=row.insertCell();
			this.zoomOutButton=document.createElement("button");
			// \u2212 is −
			this.zoomOutButton.appendChild(document.createTextNode("\u2212"));
			zoomOutCell.appendChild(this.zoomOutButton);
			var zoomNormalCell=row.insertCell();
			this.zoomNormalButton=document.createElement("button");
			this.zoomNormalButton.appendChild(document.createTextNode("0"));
			zoomNormalCell.appendChild(this.zoomNormalButton);
			var zoomInCell=row.insertCell();
			this.zoomInButton=document.createElement("button");
			this.zoomInButton.appendChild(document.createTextNode("+"));
			zoomInCell.appendChild(this.zoomInButton);
		}
		else
		{
			this.zoomOutButton = false;
			this.zoomNormalButton = false;
			this.zoomInButton = false;
		}
	
		this.slider.addEventListener("mousedown", this.drag.bind(this), false);
		this.pauseButton.addEventListener("click",this.pauseUnpause.bind(this),false);
		this.rewindButton.addEventListener("click",this.rewind.bind(this),false);
		this.prevButton.addEventListener("click",this.prevFrame.bind(this),false);
		this.nextButton.addEventListener("click",this.nextFrame.bind(this),false);
		this.ffButton.addEventListener("click",this.fastforward.bind(this),false);
		if (this.zoomOutButton)
		{
			this.zoomOutButton.addEventListener("click",this.zoomOut.bind(this),false);
			this.zoomNormalButton.addEventListener("click",this.zoomNormal.bind(this),false);
			this.zoomInButton.addEventListener("click",this.zoomIn.bind(this),false);
		}
	
		globals.modules.fullscreen.doResize();
	};
	Seekbar.prototype.removeSeekbar = function removeSeekbar()
	{
		if (!this.seekbar)
			return;
		this.seekbar.parentNode.removeChild(this.seekbar);
		this.seekbar = undefined;
		globals.modules.fullscreen.doResize();
	};
	
	Seekbar.prototype.update = function update()
	{
		if (!this.seekbar)
			return;
	
		var fullSliderWidth = parseInt(document.defaultView.getComputedStyle(this.slider, null).width, 10);
		var sliderWidth = fullSliderWidth - parseInt(document.defaultView.getComputedStyle(this.thumb, null).width, 10);
		var tot = utils.totalFrames();
		if (tot > 0)
		{
			var frame = utils.currentFrame();
			if (frame < 0)
				frame = 0;
			if (this.framecountertext)
			{
				var a = tot.toString();
				var b = (frame+1).toString();
				while (b.length < a.length)
					b = "\u2007" + b; // U+2007 FIGURE SPACE
				this.framecountertext.nodeValue = b+"/"+a;
			}
			if(!this.dragging)
			{
				if (tot > 1)
					this.thumb.style.left = (frame/(tot - 1)*sliderWidth)+"px";
				else
					this.thumb.style.left = "0";
				this.paused = !utils.isPlaying();
				this.pauseButtonImg.src = this.paused ? globals.images.play : globals.images.pause;
			}
			frame = utils.framesLoaded();
			this.loadmeter.style.width = (frame/tot*fullSliderWidth)+"px";
		}
		else if (this.framecountertext)
		{
			this.framecountertext.nodeValue = "Loading...";
		}
	};
	
	Seekbar.prototype.pauseUnpause = function pauseUnpause()
	{
		this.paused = utils.isPlaying();
		this.pauseButtonImg.src = this.paused ? globals.images.play : globals.images.pause;
		if (this.paused)
			utils.stop();
		else
			utils.play();
	};
	Seekbar.prototype.rewind = function rewind()
	{
		utils.goto(0);
		utils.play();
	};
	Seekbar.prototype.fastforward = function fastforward()
	{
		utils.goto(utils.totalFrames() - 1);
	};
	Seekbar.prototype.prevFrame = function prevFrame()
	{
		utils.goto(utils.currentFrame() - 1);
	};
	Seekbar.prototype.nextFrame = function nextFrame()
	{
		utils.goto(utils.currentFrame() + 1);
	};
	Seekbar.prototype.zoomIn = function zoomIn()
	{
		utils.zoomIn(1.5);
	};
	Seekbar.prototype.zoomOut = function zoomOut()
	{
		utils.zoomOut(1.5);
	};
	Seekbar.prototype.zoomNormal = function zoomNormal()
	{
		utils.zoomReset();
	};
	
	Seekbar.prototype.drag = function drag(e)
	{
		this.dragging=true;
		this.dragMousemove(e);
		e.preventDefault();
		return false;
	};
	Seekbar.prototype.dragMousemove = function dragMousemove(e)
	{
		if (!this.dragging) return;
		var pageX = e.clientX + document.body.scrollLeft;
		var rect = this.slider.getBoundingClientRect();
		var thumbWidth = parseInt(document.defaultView.getComputedStyle(this.thumb, null).width, 10);
		var width = rect.right - rect.left - thumbWidth;
		var pos = (pageX - rect.left - thumbWidth/2) / width;
		if (pos < 0)
			pos = 0;
		if (pos > 1)
			pos = 1;
		var t = utils.totalFrames();
		if (t > 1)
		{
			var frame = Math.round(t * pos);
			utils.goto(frame);
		}
		this.thumb.style.left = (pos * width) + "px";
	};
	Seekbar.prototype.release = function release()
	{
		if (!this.dragging) return;
		if (!this.paused)
			utils.play();
		this.dragging = false;
	};

	function WikiLink()
	{
		this.enabled = utils.getPref('hrwiki', true);
	}
	WikiLink.prototype.init = function init()
	{
		this.setting_enabled = globals.modules.settingspane.addCheckbox('hrwiki', "Add HRWiki link", "Adds a link to the appropriate page on the Homestar Runner Wiki", this.enabled);
	
		this.buildWikiLink();
		this.showWikiLink();
	};
	WikiLink.prototype.updateSettings = function updateSettings()
	{
		this.enabled = this.setting_enabled.checked;
		utils.setPref("hrwiki", this.enabled);
		// This is called before Subtitles.updateSettings, so delay until after that happens
		// so we can update the subtitles link as appropriate
		window.setTimeout(this.showWikiLink.bind(this), 0);
	};
	
	WikiLink.prototype.buildWikiLink = function buildWikiLink()
	{
		// many pages on the mirror have an "info" link in the navbar (thanks Tom!)... use that
		if (globals.whichsite === 3)
		{
			var navbar;
			if (globals.modules.navbar && globals.modules.navbar.originalnavbar)
				navbar = globals.modules.navbar.originalnavbar;
			else
				navbar = globals.navbar;
			if (navbar)
			{
				var a = navbar.getElementsByTagName("a");
				for (var i = 0; i < a.length; i++)
				{
					if (a[i].firstChild.nodeType === 3 && a[i].firstChild.nodeValue === "info")
					{
						this.addHRWikiLink(a[i].href, true);
						return;
					}
				}
			}
		}
		
		// pull the filename from the url, use it as a link to HRWiki
		// all the filenames except a couple of special-cases are
		//  redirects to their articles
		// don't link to certain pages, they aren't redirects, but already existing pages
		// also detect a 404 error and special-case Strong Sad's Lament
		     if (document.title === "Oops! You bwoke it.")
			this.addHRWikiLink("404'd");
		else if (globals.filename === "interview")
			this.addHRWikiLink("The_Interview");
		else if (globals.filename === "fhqwhgads")
			this.addHRWikiLink("Everybody_to_the_Limit");
		else if (globals.filename === "trogdor")
			this.addHRWikiLink("TROGDOR!");
		else if (globals.filename === "marshie")
			this.addHRWikiLink("Meet_Marshie");
		else if (globals.filename === "eggs")
			this.addHRWikiLink("Eggs_(toon)");
		else if (globals.filename === "fireworks")
			this.addHRWikiLink("Happy_Fireworks");
		else if (globals.filename === "sbemail100")
			this.addHRWikiLink("Not_the_100th_Email!!!");
		else if (globals.filename === "sbemail200")
			this.addHRWikiLink("Page_Load_Error");
		else if (globals.filename === "sbcg4ap")
			this.addHRWikiLink("Strong_Bad's_Cool_Game_for_Attractive_People_Advertisement");
		else if (globals.filename === "dangeresque")
			this.addHRWikiLink("Dangeresque_Roomisode_1:_Behind_the_Dangerdesque");
		else if (location.pathname.substr(0, 12) === "/sadjournal/" && globals.filename != "wonderyears" && globals.filename != "super8")
			this.addHRWikiLink("Strong_Sad's_Lament");
		else if (location.pathname.substr(0,5) === "/vii/" && (globals.filename === "" || globals.filename === "index"))
			this.addHRWikiLink("Viidelectrix");
		else if (globals.filename === "" || globals.filename === "index")
		{
			     if (globals.whichsite === 0)
				this.addHRWikiLink("Index_Page");
			else if (globals.whichsite === 1)
				this.addHRWikiLink("Podstar_Runner");
			else if (globals.whichsite === 2)
				this.addHRWikiLink("Videlectrix");
			//else if (globals.whichsite === 3)
			//	; // this will be a 403 page - do nothing.
		}
		else
			this.addHRWikiLink(globals.filename);
	};
	
	WikiLink.prototype.addHRWikiLink = function addHRWikiLink(pagename, isurl)
	{
		this.linkdiv = document.createElement("div");
		this.linkdiv.style.borderLeft = this.linkdiv.style.borderBottom = '1px solid #666';
		this.linkdiv.style.background = '#EEE';
		this.linkdiv.style.position = "fixed";
		this.linkdiv.style.overflow = 'auto';
		this.linkdiv.style.right = "0px";
		this.linkdiv.style.top = "0px";
		this.linkdiv.style.padding = "3px";
		var link = document.createElement("a");
		if (isurl)
			link.href = pagename;
		else
			link.href = "http://www.hrwiki.org/wiki/" + escape(pagename.replace(/ /g, '_'));
		link.title = "See the HRWiki article for this page";
		link.style.display = "block";
		link.style.textDecoration = "none";
		this.linkdiv.appendChild(link);
		var img=document.createElement("img");
		img.style.border="0px";
		img.style.display="block";
		img.src=globals.images.hrwiki;
		link.appendChild(img);
		this.sublink = document.createElement("a");
		this.sublink.title = "See the HRWiki article for this page's subtitles";
		this.sublink.style.display = "block";
		this.sublink.style.textDecoration = "none";
		this.sublink.style.textAlign = "center";
		this.sublink.style.fontSize = this.sublink.style.lineHeight = "16px";
		this.sublink.style.marginTop = "3px";
		this.linkdiv.appendChild(this.sublink);
		this.sublink.appendChild(document.createTextNode('S'));
		document.body.appendChild(this.linkdiv);
	};
	
	WikiLink.prototype.showWikiLink = function showWikiLink()
	{
		if (this.enabled)
		{
			this.linkdiv.style.display = "block";
			if (globals.modules.subtitles && globals.modules.subtitles.enabled)
			{
				this.sublink.style.display = "block";
				this.sublink.href = "http://www.hrwiki.org/wiki/Subtitles:" + escape(globals.filename.replace(/ /g, '_')) + "/" + escape(globals.modules.subtitles.language);
			}
			else
				this.sublink.style.display = "none";
		}
		else
			this.linkdiv.style.display = "none";
	};

	function NextPrev()
	{
		this.enabled = utils.getPref('prevnext', true);
		this.docheck = utils.getPref('checknext', true);
	}
	NextPrev.prototype.init = function init()
	{
		this.setting_enabled = globals.modules.settingspane.addCheckbox('prevnext', "Show previous/next buttons", "Lets you easily move through SBEmails, TGS, etc", this.enabled);
		this.setting_docheck = globals.modules.settingspane.addCheckbox('checknext', "Check if next exists", 'Doesn\'t add a "next" link on the latest SBEmail, etc', this.docheck, this.setting_enabled);
	
		this.createPrevNext();
		this.showPrevNext();
	};
	NextPrev.prototype.updateSettings = function updateSettings()
	{
		this.enabled = this.setting_enabled.checked;
		utils.setPref("prevnext", this.enabled);
		this.docheck = this.setting_docheck.checked;
		utils.setPref("checknext", this.docheck);
		this.showPrevNext();
	};
	
	NextPrev.prototype.createPrevNext = function createPrevNext()
	{
		// this is coded like this instead of just looking for /(\d+)/ so that it
		// doesn't find pages like commandos3 or xmas04
		var result;
		if ((result = globals.filename.match(/^(sbemail|tgs|answer|bizcasfri|puppetjam|main)(\d+)$/)))
		{
			// sbemail100 and sbemail200 aren't actually sbemails
			if (!(result[1] == "sbemail" && (result[2] == "100" || result[2] == "200")))
				this.addPrevNextlinks(result[1],parseInt(result[2],10));
		}
		else if (globals.filename == "sbemailahundred")
			this.addPrevNextlinks("sbemail", 100);
		else if (globals.filename == "kotpoptoon")
			this.addPrevNextlinks("sbemail", 151);
		else if (globals.filename == "sbemailtwohundred")
			this.addPrevNextlinks("sbemail", 200);
		else if (globals.filename == "hremail3184")
			this.addPrevNextlinks("sbemail", 201);
		else if (globals.filename == "dween_tgs")
			this.addPrevNextlinks("tgs", 6);
	};
	NextPrev.prototype.addPrevNextlinks = function addPrevNextlinks(series, num)
	{
		if (num > 1)
		{
			this.prevlink = document.createElement("a");
			this.prevlink.href = this.makeLink(series, num - 1);
			this.prevlink.style.position="fixed";
			this.prevlink.style.left="0px";
			this.prevlink.style.bottom="0px";
			this.prevlink.style.padding="3px";
			this.prevlink.style.background="white";
			this.prevlink.style.border="1px solid black";
			this.prevlink.style.textDecoration="none";
			this.prevlink.style.display = "none";
			var img = document.createElement("img");
			img.style.border = "none";
			img.src = globals.images.prev;
			this.prevlink.appendChild(img);
			document.body.appendChild(this.prevlink);
		}
	
		this.nextlink = document.createElement("a");
		this.nextlink.href = this.makeLink(series, num + 1);
		this.nextlink.style.position="fixed";
		this.nextlink.style.right="0px";
		this.nextlink.style.bottom="0px";
		this.nextlink.style.padding="3px";
		this.nextlink.style.background="white";
		this.nextlink.style.border="1px solid black";
		this.nextlink.style.textDecoration="none";
		this.nextlink.style.display = "none";
		img = document.createElement("img");
		img.style.border = "none";
		img.src = globals.images.next;
		this.nextlink.appendChild(img);
		document.body.appendChild(this.nextlink);
	
		this.checkedNext = false;
	};
	NextPrev.prototype.makeLink = function makeLink(series, num)
	{
		if (series == "sbemail" && num == 100)
			return "sbemailahundred.html";
		else if (series == "sbemail" && num == 151)
			return "kotpoptoon.html";
		else if (series == "sbemail" && num == 200)
			return "sbemailtwohundred.html";
		else if (series == "sbemail" && num == 201)
			return "hremail3184.html";
		else
			return series + num + ".html";
	};
	
	NextPrev.prototype.showPrevNext = function showPrevNext()
	{
		if (this.enabled)
		{
			if (this.prevlink)
				this.prevlink.style.display = "block";
			if (this.docheck && !this.checkedNext && this.nextlink)
				utils.downloadPage(this.nextlink.href + "?cachedodge=" + GM_getValue('cachedodge', 0), this.onCheckLoad.bind(this), this.onCheckError.bind(this), "HEAD");
			else if (this.nextlink)
				this.nextlink.style.display = "block";
		}
		else
		{
			if (this.prevlink)
				this.prevlink.style.display = "none";
			if (this.nextlink)
				this.nextlink.style.display = "none";
		}
	};
	NextPrev.prototype.onCheckLoad = function onCheckLoad(text, status, statustext, headers)
	{
		if (status == 200 && headers.indexOf("404error.html") < 0)
		{
			this.checkedNext = true;
			this.showPrevNext();
		}
		else if (this.nextlink)
		{
			this.nextlink.parentNode.removeChild(this.nextlink);
			this.nextlink = undefined;
		}
	};
	NextPrev.prototype.onCheckError = function onCheckError()
	{
		this.nextlink.parentNode.removeChild(this.nextlink);
		this.nextlink = undefined;
	};

	function Navbar()
	{
		this.enabled = utils.getPref('navbar', false);
		this.rando = {};
		for (var i in this.SECTIONS)
			this.rando[i] = utils.getPref('rando' + i, true);
	}
	Navbar.prototype.SECTIONS = {
		t: "Big Toons",
		sh: "Shorts",
		ho: "Holday Toons",
		p: "Puppet Stuff",
		teh: "Powered by The Cheat",
		sb: "Strong Bad Emails",
		am: "Marzipan's Answering Machine",
		tgs: "Teen Girl Squad"
	};
	Navbar.prototype.MAIN_COUNT = 26;
	Navbar.prototype.init = function init() {
		utils.addGlobalStyle(
			'#newnavbar\n' +
			'{\n' +
			'\tmargin: 0;\n' +
			'\tpadding: 0;\n' +
			'\ttext-align: center;\n' +
			'\ttext-transform: lowercase;\n' +
			'\theight: 10px;\n' +
			'\tfont: 10px/10px sans-serif;\n' +
			'}\n' +
			'#newnavbar li\n' +
			'{\n' +
			'\tmargin: 0;\n' +
			'\tpadding: 0;\n' +
			'\tdisplay: inline;\n' +
			'}\n' +
			'#newnavbar :link, #newnavbar :visited\n' +
			'{\n' +
			'\tcolor: #666;\n' +
			'\tfont-family: sans-serif;\n' +
			'\ttext-decoration: none;\n' +
			'\tpadding: 0 1em;\n' +
			'}\n' +
			'#newnavbar :link:hover, #newnavbar :visited:hover\n' +
			'{\n' +
			'\tcolor: #999;\n' +
			'}\n' +
			'\n' +
			"/* for overriding podstar's settings: */\n" +
			'#newnavbar :link, #newnavbar :visited\n' +
			'{\n' +
			'\tfont-weight: normal;\n' +
			'}\n' +
			'#newnavbar :link:hover, #newnavbar :visited:hover\n' +
			'{\n' +
			'\tbackground: transparent;\n' +
			'\tfont-weight: normal;\n' +
			'}\n' +
			""
		);
	
		this.setting_enabled = globals.modules.settingspane.addCheckbox('navbar', "Plain HTML navbar", "Replaces the flash navbar with normal links, so you can open in tabs, etc", this.enabled);
		this.setting_rando = {};
		for (var i in this.SECTIONS)
			this.setting_rando[i] = globals.modules.settingspane.addCheckbox('rando' + i, "Include " + this.SECTIONS[i] + " in rando", 'Limit the "rando" function to what you like to watch', this.rando[i], this.setting_enabled);
		
		this.allrandourls = false;
		this.randourls = false;
	
		this.originalnavbar = globals.navbar;
		this.newnavbar = this.buildNavbar(this.originalnavbar);
		this.showNavbar();
	};
	Navbar.prototype.updateSettings = function updateSettings()
	{
		this.enabled = this.setting_enabled.checked;
		utils.setPref("navbar", this.enabled);
		for (var i in this.SECTIONS)
		{
			this.rando[i] = this.setting_rando[i].checked;
			utils.setPref("rando" + i, this.rando[i]);
		}
		this.filterRando();
		this.showNavbar();
	};
	
	Navbar.prototype.showNavbar = function showNavbar()
	{
		if (this.enabled)
		{
			if (this.originalnavbar)
				this.originalnavbar.style.display = "none";
			this.newnavbar.style.display = "";
			this.newnavbar.style.marginTop = (globals.modules.seekbar.enabled ? "0" : "10px");
			globals.navbar = this.newnavbar;
			this.loadRandoXML();
		}
		else
		{
			if (this.originalnavbar)
				this.originalnavbar.style.display = "";
			this.newnavbar.style.display = "none";
			globals.navbar = this.originalnavbar;
		}
		globals.modules.fullscreen.doResize();
	};
	
	Navbar.prototype.buildNavbar = function buildNavbar(where)
	{
		var newnavbar = document.createElement("ul");
		newnavbar.id = "newnavbar";
		if (where)
		{
			while(where.parentNode.tagName.toLowerCase() == "object")
				where = where.parentNode;
			utils.insertAfter(newnavbar, where);
		}
		else
			document.body.appendChild(newnavbar);
	
		this.mainlink = this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/main" + Math.floor(Math.random() * this.MAIN_COUNT + 1) + ".html", "Main");
		// just for fun, re-randomise on each mouse-over (for the status bar)
		this.mainlink.addEventListener("mouseout", this.newMainLink.bind(this), false);
		this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/toons.html", "Toons");
		this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/games.html", "Games");
		this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/characters2.html", "Characters");
		this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/downloads.html", "Downloads");
		this.addnavbarlink(newnavbar, "http://homestarrunner.stores.yahoo.net/", "Store", "storelink");
		this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/sbemail.html", "SB Emails");
		this.addnavbarlink(newnavbar, "http://feeds.feedburner.com/HomestarRunner", "Subscribe");
		this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/email.html", "Contact");
		//this.addnavbarlink(newnavbar, "http://podstar.homestarrunner.com/", "Podcast");
		this.addnavbarlink(newnavbar, "http://www.homestarrunner.com/legal.html", "Legal");
		this.randolink = this.addnavbarlink(newnavbar, "javascript:void(alert('rando.xml not loaded yet... be patient'))", "Rando");
		this.randolink.addEventListener("mouseout", this.newRandoLink.bind(this), false);
	
		return newnavbar;
	};
	Navbar.prototype.addnavbarlink = function addnavbarlink(ul, href, title, extraclass)
	{
		var li = document.createElement("li");
		var link = document.createElement("a");
		link.href = href;
		link.appendChild(document.createTextNode(title));
		if (extraclass)
			link.className = extraclass;
		li.appendChild(link);
		ul.appendChild(li);
		return link;
	};
	
	Navbar.prototype.newMainLink = function newMainLink()
	{
		this.mainlink.href="http://www.homestarrunner.com/main" + Math.floor(Math.random() * this.MAIN_COUNT + 1) + ".html";
	};
	Navbar.prototype.newRandoLink = function newRandoLink()
	{
		if (!this.randourls)
			return;
	
		if (this.randourls.length > 0)
		{
			var r = this.randourls[Math.floor(Math.random() * this.randourls.length)];
			this.randolink.href = r.u;
			this.randolink.title = r.n;
		}
		else
		{
			this.randolink.href = "javascript:void(alert('Nothing to choose from'))";
			this.randolink.title = "Nothing to choose from";
		}
	};
	
	Navbar.prototype.loadRandoXML = function loadRandoXML()
	{
		// Only run this once
		if (this.haveLoadedXML)
			return;
		this.haveLoadedXML = true;
	
		utils.downloadPage(
			"http://www.homestarrunner.com/rando.xml?cachedodge=" + utils.getPref('cachedodge', 0),
			this.randoXMLLoaded.bind(this),
			this.randoXMLError.bind(this)
		);
	};
	Navbar.prototype.randoXMLLoaded = function randoXMLLoaded(responseText)
	{
		var parser = new DOMParser();
		// fix invalid XML...
		// add missing root element
		var doc = responseText.replace(/<\?xml.*?\?>/g, ""); // strip <?xml ?> tag
		doc = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<rando>" + doc + "</rando>";
		// fix bad ampersands
		doc = doc.replace(/&(?!\w*;)/g, "&");
		doc = parser.parseFromString(doc, "application/xml");
		var sbemailcounter = 0;
		this.allrandourls = [];
		for (var i = 0; i < doc.documentElement.childNodes.length; i++)
		{
			var node = doc.documentElement.childNodes[i];
			if (node.nodeType == 1)
			{
				var type = node.nodeName.toLowerCase();
				var u = node.getAttribute('u');
				var n = node.getAttribute('n');
				if (!n) n = "Untitled";
				if (type == "sb")
				{
					sbemailcounter++;
					n = "SBEmail: " + n;
				}
				if (u)
					this.allrandourls.push({u: "http://www.homestarrunner.com/" + u, n: n, type: type});
				else
					this.allrandourls.push({u: "http://www.homestarrunner.com/sbemail" + sbemailcounter + ".html", n: n, type: type});
			}
		}
		this.filterRando();
	};
	Navbar.prototype.randoXMLError = function randoXMLError()
	{
		this.randolink.href = "javascript:void(alert('Error loading rando.xml... try refreshing'))";
	};
	Navbar.prototype.filterRando = function filterRando()
	{
		if (!this.allrandourls)
			return;
		this.randourls = [];
		for (var i in this.allrandourls)
		{
			var r = this.allrandourls[i];
			if (this.rando[r.type] === false) // === false so that it's considered "true" for undefined... if they add a new toon type
				continue;
			this.randourls.push(r);
		}
		this.newRandoLink();
	};

	function Subtitles()
	{
		this.enabled = utils.getPref('subtitles', false);
		this.captions = utils.getPref('captions', true);
		this.colours = utils.getPref('colours', true);
		this.testsubs = utils.getPref('testsubs', false);
		this.language = utils.getPref('language', "en");
		this.testsubsdata = unescape(utils.getPref('testsubsdata', this.DEFAULTXML));
		this.names = utils.getPref('names', 0);
	}
	Subtitles.prototype.DEFAULTXML = escape('<?xml version="1.0" encoding="utf-8"?>\n<transcript xml:lang="en-us">\n<line start="" end="" speaker=""></line>\n</transcript>');
	Subtitles.prototype.NAMES_OPTS = ["Never", "Voiceovers", "Always"];
	Subtitles.prototype.NO_SUBTITLES = document.createComment("");
	Subtitles.prototype.init = function init()
	{
		utils.addGlobalStyle(
			'.subtitles\n' +
			'{\n' +
			'\tbackground: black;\n' +
			'\tcolor: white;\n' +
			'\tfont: 20px/25px sans-serif;\n' +
			'\theight: 100px;\n' +
			'\ttext-align: center;\n' +
			'}\n' +
			'\n' +
			'.subtitle_errors\n' +
			'{\n' +
			'\tbackground: black;\n' +
			'\tcolor: red;\n' +
			'\tfont: 12pt sans-serif;\n' +
			'\ttext-align: left;\n' +
			'\tmargin: 0.5em;\n' +
			'}\n' +
			'\n' +
			'.subtitles .italic\n' +
			'{\n' +
			'\tfont-style: italic;\n' +
			'}\n' +
			'.subtitles .italic em, .subtitles .italic cite, .subtitles .italic i\n' +
			'{\n' +
			'\tfont-style: normal;\n' +
			'}\n' +
			""
		);
		
		this.setting_enabled = globals.modules.settingspane.addCheckbox('subtitles', "Show subtitles", "Shows subtitles or captions below the toon, if any are available", this.enabled);
	
		var settingrow = globals.modules.settingspane.addSettingRow(this.setting_enabled);
		var settinglabel = document.createElement('label');
		settinglabel.htmlFor = "setting_language";
		settinglabel.appendChild(document.createTextNode('Subtitle Language: '));
		settinglabel.title = 'Display subtitles in this language, if any';
		settingrow.appendChild(settinglabel);
		this.setting_language = document.createElement('select');
		this.setting_language.title = 'Display subtitles in this language, if any';
		this.setting_language.id = "setting_language";
		this.setting_language.disabled = true;
		settingrow.appendChild(this.setting_language);
	
		this.language_populated = false;
		this.populateLanguage();
	
		this.setting_captions = globals.modules.settingspane.addCheckbox('captions', "Show captions", "Include sound effects in the subtitles", this.captions, this.setting_enabled);
		this.setting_colours = globals.modules.settingspane.addCheckbox('colours', "Use colours", "Distinguish characters by colour effects (turn off if colourblind)", this.colours, this.setting_enabled);
	
		settingrow = globals.modules.settingspane.addSettingRow(this.setting_enabled);
		settinglabel = document.createElement('label');
		settinglabel.htmlFor = "setting_names";
		settinglabel.appendChild(document.createTextNode('Show speakers\' names: '));
		settinglabel.title = 'Show the speakers\' names before their lines';
		settingrow.appendChild(settinglabel);
		this.setting_names = document.createElement('select');
		this.setting_names.title = 'Show the speakers\' names before their lines';
		this.setting_names.id = "setting_names";
		settingrow.appendChild(this.setting_names);
		for (var i = 0; i < this.NAMES_OPTS.length; i++)
		{
			var option = document.createElement('option');
			option.value = i;
			option.appendChild(document.createTextNode(this.NAMES_OPTS[i]));
			if (this.names == i)
				option.selected = true;
			this.setting_names.appendChild(option);
		}
	
		this.setting_testsubs = globals.modules.settingspane.addCheckbox('testsubs', "Test subtitles script", "Use this to test a subtitles script (copy/paste into a text box)", this.testsubs, this.setting_enabled);
	
		settingrow = globals.modules.settingspane.addSettingRow(this.setting_testsubs);
		this.setting_testsubsdata = document.createElement('textarea');
		this.setting_testsubsdata.title = 'Paste your XML data here';
		this.setting_testsubsdata.id = "setting_testsubsdata";
		this.setting_testsubsdata.style.width = "100%";
		this.setting_testsubsdata.style.height = "10em";
		this.setting_testsubsdata.style.fontSize = "8px";
		this.setting_testsubsdata.style.textAlign = "left";
		this.setting_testsubsdata.appendChild(document.createTextNode(this.testsubsdata));
		settingrow.appendChild(this.setting_testsubsdata);
	
		this.charsready = false;
		this.subsready = false;
	
		this.setupSubtitles();
	
		window.setInterval(this.update.bind(this), 50);
	};
	Subtitles.prototype.updateSettings = function updateSettings()
	{
		this.enabled = this.setting_enabled.checked;
		utils.setPref('subtitles', this.enabled);
		if (this.language_populated)
		{
			this.language = this.setting_language.value;
			utils.setPref('language', this.language);
		}
		this.captions = this.setting_captions.checked;
		utils.setPref('captions', this.captions);
		this.colours = this.setting_colours.checked;
		utils.setPref('colours', this.colours);
		this.names = this.setting_names.value;
		utils.setPref('names', this.names);
		this.testsubs = this.setting_testsubs.checked;
		utils.setPref('testsubs', this.testsubs);
		this.testsubsdata = this.setting_testsubsdata.value;
		utils.setPref('testsubsdata', escape(this.testsubsdata));
	
		this.setupSubtitles();
	};
	
	Subtitles.prototype.populateLanguage = function populateLanguage()
	{
		var option = document.createElement('option');
		option.appendChild(document.createTextNode("Loading..."));
		option.selected = true;
		this.setting_language.appendChild(option);
		utils.downloadWikiXML("Subtitles:Languages", this.languageListDownloaded.bind(this), this.languageListError.bind(this));
	};
	Subtitles.prototype.languageListDownloaded = function languageListDownloaded(xml)
	{
		while (this.setting_language.firstChild)
			this.setting_language.removeChild(this.setting_language.firstChild);
	
		var languages = xml.getElementsByTagName('language');
		for (var i = 0; i < languages.length; i++)
		{
			var node = languages[i];
			// sanity-check the node
			if (node.hasAttribute('xml:lang') && node.firstChild && (node.firstChild.nodeType == xml.TEXT_NODE || node.firstChild.nodeType == xml.CDATA_SECTION_NODE))
			{
				var option = document.createElement('option');
				option.appendChild(document.createTextNode(node.firstChild.nodeValue));
				option.lang = option.value = node.getAttribute('xml:lang');
				if (option.lang == this.language)
					option.selected = true;
				option.dir = "ltr";
				if (node.hasAttribute('dir'))
					option.dir = node.getAttribute('dir');
				this.setting_language.appendChild(option);
			}
		}
		
		this.setting_language.disabled = false;
		this.language_populated = true;
	};
	Subtitles.prototype.languageListError = function languageListError()
	{
		while (this.setting_language.firstChild)
			this.setting_language.removeChild(this.setting_language.firstChild);
		var option = document.createElement('option');
		option.appendChild(document.createTextNode("Error loading languages"));
		option.selected = true;
		this.setting_language.appendChild(option);
	};
	
	Subtitles.prototype.removeSubtitles = function removeSubtitles()
	{
		if (this.subtitleholder)
		{
			this.subtitleholder.parentNode.removeChild(this.subtitleholder);
			this.subtitleholder = undefined;
		}
		if (this.errorsholder)
		{
			this.errorsholder.parentNode.removeChild(this.errorsholder);
			this.errorsholder = undefined;
		}
	
		globals.modules.fullscreen.doResize();
	};
	Subtitles.prototype.createSubtitleHolder = function createSubtitleHolder()
	{
		this.subtitleholder = document.createElement('div');
		this.subtitleholder.className = "subtitles";
		var where = globals.flashmovie;
		if (globals.modules.seekbar && globals.modules.seekbar.seekbar)
			where = globals.modules.seekbar.seekbar;
		while(where.parentNode.tagName.toLowerCase() == "object")
			where = where.parentNode;
		utils.insertAfter(this.subtitleholder, where);
		this.subtitleholder.appendChild(this.NO_SUBTITLES);
		this.currentsubtitles = this.NO_SUBTITLES;
	
		globals.modules.fullscreen.doResize();
	};
	Subtitles.prototype.createErrorsHolder = function createErrorsHolder()
	{
		this.errorsholder = document.createElement('div');
		this.errorsholder.className = "subtitle_errors";
		var where = globals.flashmovie;
		if (globals.modules.seekbar && globals.modules.seekbar.seekbar)
			where = globals.modules.seekbar.seekbar;
		while(where.parentNode.tagName.toLowerCase() == "object")
			where = where.parentNode;
		utils.insertAfter(this.errorsholder, where);
	
		globals.modules.fullscreen.doResize();
	};
	Subtitles.prototype.transcriptError = function transcriptError(message)
	{
		if (!this.errorsholder)
			this.createErrorsHolder();
		var div = document.createElement("div");
		div.appendChild(document.createTextNode(message));
		this.errorsholder.appendChild(div);
	
		globals.modules.fullscreen.doResize();
	};
	
	Subtitles.prototype.setupSubtitles = function setupSubtitles()
	{
		this.removeSubtitles();
	
		if (!this.enabled)
			return;
	
		this.createSubtitleHolder();
		this.setSubtitles(document.createTextNode("Loading subtitles..."));
		
		if (!this.charsready)
			utils.downloadWikiXML('Subtitles:Characters', this.charactersLoaded.bind(this), this.downloadSubsError.bind(this));
		else
			this.reloadSubs();
	};
	Subtitles.prototype.charactersLoaded = function charactersLoaded(xml)
	{
		var speakers = xml.getElementsByTagName("speaker");
		this.characters = {
			sfx: {
				color: "#FFF",
				sfx: true,
				name: {en: ""}
			}
		};
		for (var i = 0; i < speakers.length; i++)
		{
			var speakername = speakers[i].getAttribute("id");
			this.characters[speakername] = {color: speakers[i].getAttribute("color"), sfx: speakers[i].hasAttribute("sfx"), name: {en: ""}};
			var names = speakers[i].getElementsByTagName("name");
			for (var j = 0; j < names.length; j++)
			{
				var lang = names[j].getAttribute("xml:lang");
				if (names[j].firstChild && (names[j].firstChild.nodeType == xml.TEXT_NODE || names[j].firstChild.nodeType == xml.CDATA_SECTION_NODE))
					this.characters[speakername].name[lang] = names[j].firstChild.nodeValue;
			}
		}
		this.charsready = true;
		this.reloadSubs();
	};
	Subtitles.prototype.downloadSubsError = function downloadSubsError(status, statusText)
	{
		this.removeSubtitles();
		if (this.testsubs)
			this.transcriptError(statusText);
	};
	Subtitles.prototype.reloadSubs = function reloadSubs()
	{
		if (!this.charsready)
			return;
		this.subsready = false;
	
		this.removeSubtitles();
		this.createSubtitleHolder();
		this.setSubtitles(document.createTextNode("Loading subtitles..."));
	
		if (!this.testsubs)
			utils.downloadWikiXML('Subtitles:' + globals.filename + '/' + this.language, this.transcriptLoaded.bind(this), this.downloadSubsError.bind(this));
		else
			utils.wikiXMLDownloaded(this.transcriptLoaded.bind(this), this.downloadSubsError.bind(this), this.testsubsdata, 200, "OK");
	};
	
	Subtitles.prototype.transcriptLoaded = function transcriptLoaded(xml)
	{
		// set some defaults
		if (!xml.documentElement.getAttribute("xml:lang")) xml.documentElement.setAttribute("xml:lang", this.language);
		if (!xml.documentElement.getAttribute("dir"))      xml.documentElement.setAttribute("dir",      "ltr");
		// inherit languages to all subnodes
		this.inheritLanguages(xml.documentElement);
		// now parse the lines into divs and get start and end frames
		var lines = xml.getElementsByTagName("line");
		var previousEnd = NaN;
		this.transcript = [];
		for (var i = 0; i < lines.length; i++)
		{
			var line = {};
			// ignore lines with missing start/end values
			// so you can add all the lines and not worry about timing them until later
			if (!lines[i].getAttribute("start") || !lines[i].getAttribute("end"))
				continue;
			line.start = parseInt(lines[i].getAttribute("start"), 10);
			line.end = parseInt(lines[i].getAttribute("end"), 10);
			if (this.testsubs)
			{
				if (isNaN(line.start))
					this.transcriptError("Start value \"" + lines[i].getAttribute("start") + "\" is not a number");
				if (isNaN(line.end))
					this.transcriptError("End value \"" + lines[i].getAttribute("end") + "\" is not a number");
				if (line.end < line.start)
					this.transcriptError("Line beginning frame " + line.start + " ends before it begins.");
				if (line.start < previousEnd)
					this.transcriptError("Line beginning frame " + line.start + " starts before the previous frame ends.");
				previousEnd = line.end;
			}
			line.text = this.importNodes(lines[i]);
			this.transcript.push(line);
		}
		this.subsready = true;
	};
	Subtitles.prototype.inheritLanguages = function inheritLanguages(node)
	{
		for (var i = node.firstChild; i; i = i.nextSibling)
		{
			if (i.nodeType == i.ELEMENT_NODE)
			{
				if (!i.hasAttribute("xml:lang")) i.setAttribute("xml:lang", node.getAttribute("xml:lang"));
				if (!i.hasAttribute("dir"))      i.setAttribute("dir",      node.getAttribute("dir"));
				this.inheritLanguages(i);
			}
		}
	};
	Subtitles.prototype.importNodes = function importNodes(node)
	{
		var name = node.nodeName.toLowerCase();
		if (this.characters[name])
		{
			node.setAttribute("speaker", name);
			name = "speaker";
		}
		if (name == "line" || name == "speaker")
		{
			// format the speaker appropriately as a div
			var speaker = node.getAttribute("speaker");
			if (!this.captions && (speaker == "sfx" || node.hasAttribute("sfx")))
				return document.createComment(""); // return nothing
			newNode = document.createElement("div");
			var char = this.characters[speaker];
			if (!char)
			{
				if (this.testsubs && speaker)
				{
					var line = node;
					while (line && line.nodeName != "line")
						line = line.parentNode;
					if (line)
						this.transcriptError("Line beginning frame " + line.getAttribute("start") + " has an unrecognised speaker name \"" + speaker + '"');
				}
				char = {color: "#FFF", name: {en: ""}};
			}
			if (this.colours)
				newNode.style.color = char.color;
			if (node.hasAttribute("voiceover"))
				newNode.className = "italic";
			if (node.hasAttribute("volume"))
			{
				newNode.style.fontSize = (node.getAttribute("volume") * 100) + "%";
				newNode.style.lineHeight = "1.25em";
			}
			newNode.lang = node.getAttribute("xml:lang");
			newNode.dir = node.getAttribute("dir");
			var hasSpeakerChildren = false;
			for (var i = node.firstChild; i; i = i.nextSibling)
			{
				if (i.nodeType == i.ELEMENT_NODE)
				{
					newNode.appendChild(this.importNodes(i));
					var a = i.nodeName.toLowerCase();
					if (a == "line" || a == "speaker" || this.characters[a])
						hasSpeakerChildren = true;
				}
				else if (i.nodeType == i.TEXT_NODE || i.nodeType == i.CDATA_SECTION_NODE)
					newNode.appendChild(document.importNode(i, true));
			}
			if (!hasSpeakerChildren)
			{
				// this is a normal text node - do some extra text stuff
				if (char.sfx || node.hasAttribute("sfx"))
				{
					newNode.insertBefore(document.createTextNode('('), newNode.firstChild);
					newNode.appendChild(document.createTextNode(')'));
					newNode.className = "italic";
				}
				if (this.names == 2 || (node.hasAttribute("voiceover") && this.names == 1))
				{
					// find the language with the longest prefix match
					// fall back to "en" if none found
					var bestmatch = "en";
					var langbits = node.getAttribute("xml:lang").split("-");
					for (i = langbits.length; i >= 1; i--)
					{
						var lang = langbits.slice(0, i).join("-");
						if (char.name[lang])
						{
							bestmatch = lang;
							break;
						}
					}
					if (char.name[bestmatch] != '')
						newNode.insertBefore(document.createTextNode(char.name[bestmatch] + ": "), newNode.firstChild);
				}
			}
			return newNode;
		}
		else
		{
			// check element blacklist
			if (name == "script" ||
			    name == "style"  ||
			    name == "object" ||
			    name == "param"  ||
			    name == "embed"  ||
			    name == "a"      ||
			    name == "img"    ||
			    name == "applet" ||
			    name == "map"    ||
			    name == "frame"  ||
			    name == "iframe" ||
			    name == "meta"   ||
			    name == "link"   ||
			    name == "form"   ||
			    name == "input")
			{
				if (this.testsubs)
					this.transcriptError("Blacklisted element \"" + name + "\" stripped.");
				return document.createComment(""); // return nothing
			}
			var newNode = document.createElement(name);
			// copy across attributes
			for (i = 0; i < node.attributes.length; i++)
			{
				name = node.attributes[i].nodeName.toLowerCase();
				// check attribute blacklist
				// javascript, and anything that might load stuff from offsite
				if (name != "href" && name != "src" && name.substring(0, 2) != "on")
				{
					if (name == "style")
					{
						// regex taken from MediaWiki Sanitizer.php
						if (!node.attributes[i].value.match(/(expression|tps*:\/\/|url\\s*\()/i))
							newNode.setAttribute("style", node.attributes[i].value);
					}
					else if (name == "xml:lang")
					{
						newNode.lang = node.attributes[i].value;
					}
					else
						newNode.setAttribute(node.attributes[i].nodeName, node.attributes[i].value);
				}
				else if (this.testsubs)
					this.transcriptError("Blacklisted attribute \"" + name + "\" stripped.");
			}
			// copy across children
			for (i = node.firstChild; i; i = i.nextSibling)
			{
				if (i.nodeType == i.ELEMENT_NODE)
					newNode.appendChild(this.importNodes(i));
				else if (i.nodeType == i.TEXT_NODE || i.nodeType == i.CDATA_SECTION_NODE)
					newNode.appendChild(document.importNode(i, true));
			}
			return newNode;
		}
		return document.createComment(""); // fallthrough
	};
	
	Subtitles.prototype.update = function update()
	{
		if (!this.enabled || !this.charsready || !this.subsready || !this.subtitleholder)
			return;
	
		var frame = utils.currentFrame();
		if (frame < 0)
			return;
		frame++; // Make 1-based
		// binary search to find the right transcript line
		var first = 0;
		var last = this.transcript.length;
		while(first < (last - 1))
		{
			var mid = (first + last) >> 1;
			if (frame >= this.transcript[mid].start)
			{
				first = mid;
				if (frame <= this.transcript[mid].end)
					break;
			}
			else
				last = mid;
		}
		// should we actually show the line?
		if(this.transcript[first] && this.transcript[first].start <= frame && this.transcript[first].end >= frame)
			this.setSubtitles(this.transcript[first].text);
		else
			this.setSubtitles(false);
	};
	
	Subtitles.prototype.setSubtitles = function setSubtitles(node)
	{
		if (!this.subtitleholder)
			return;
		if (!node)
			node = this.NO_SUBTITLES;
		if (this.currentsubtitles != node)
		{
			this.subtitleholder.replaceChild(node, this.subtitleholder.firstChild);
			this.currentsubtitles = node;
		}
	};

	// Returned by Special:Getversion
	// <versionstring>4.0.63=http://www.hrwiki.org/w/index.php?title=User:Phlip/Greasemonkey&action=raw&ctype=text/javascript&fakeextension=.user.js</versionstring>
	
	function Updates()
	{
		this.enabled = utils.getPref('updates', true);
	}
	Updates.CURRENT_VERSION = [4, 0, 63];
	Updates.prototype.init = function init()
	{
		// We don't need to do this update checking on Chrome - the Chrome Web Store
		// will handle that for us
		if (!utils.useGMFunctions)
		{
			delete globals.modules.updates;
			return;
		}
	
		this.setting_enabled = globals.modules.settingspane.addCheckbox('updates', "Check for updates", "Regularly check for updates to the All-in-one script", this.enabled);
	
		this.havechecked = false;
		this.doCheck();
	};
	Updates.prototype.updateSettings = function updateSettings()
	{
		this.enabled = this.setting_enabled.checked;
		utils.setPref("updates", this.enabled);
		this.doCheck();
	};
	
	Updates.prototype.doCheck = function doCheck()
	{
		if (this.havechecked || !this.enabled)
			return;
		this.havechecked = true;
	
		if (Date.now() - utils.getPref("lastchecktime", 0) > 86400000)
		{
			utils.downloadPage("http://www.hrwiki.org/wiki/Special:Getversion/User:Phlip/Greasemonkey?cachedodge=" + Math.random(), this.onLoad.bind(this));
		}
		else
			this.handleUpdateString(utils.getPref("lastcheckstring", ""));
	};
	Updates.prototype.onLoad = function onLoad(textcontent)
	{
		utils.setPref("lastchecktime", Date.now());
		utils.setPref("lastcheckstring", textcontent);
		this.handleUpdateString(textcontent);
	};
	Updates.prototype.handleUpdateString = function handleUpdateString(str)
	{
		var parts = str.split("@@");
		for (var i = 0; i < parts.length; i++)
		{
			var matches = parts[i].match(/^(\d+)\.(\d+)\.(\d+)=(.*)$/);
			if (!matches) continue;
			if (matches[1] > Updates.CURRENT_VERSION[0] ||
			    (matches[1] == Updates.CURRENT_VERSION[0] && matches[2] > Updates.CURRENT_VERSION[1]) ||
			    (matches[1] == Updates.CURRENT_VERSION[0] && matches[2] == Updates.CURRENT_VERSION[1] && matches[3] > Updates.CURRENT_VERSION[2]))
			{
				var updatelink = document.createElement('a');
				updatelink.href=matches[4];
				updatelink.style.display = "block";
				updatelink.style.position = 'fixed';
				updatelink.style.left = '0px';
				updatelink.style.top = '0px';
				updatelink.style.border = 'none';
				updatelink.style.zIndex = 1;
				var updatelinkimage = document.createElement('img');
				updatelinkimage.src = globals.images.update;
				var oldversionstr = Updates.CURRENT_VERSION[0] + "." + Updates.CURRENT_VERSION[1] + "." + Updates.CURRENT_VERSION[2];
				var newversionstr = matches[1] + "." + matches[2] + "." + matches[3];
				updatelinkimage.title = "Click here to update from script version " + oldversionstr + " to " + newversionstr;
				updatelinkimage.style.display = "block";
				updatelinkimage.style.border = 'none';
				updatelink.appendChild(updatelinkimage);
				document.body.appendChild(updatelink);
				return;
			}
		}
	};

	// Podstar/Videlectrix (stock IIS), HRWiki and stock Apache error pages, respectively. Don't do anything on those pages.
	if (document.title == "The page cannot be found" || document.title == "Homestar Runner Wiki - 404 Not Found" || document.title == "404 Not Found")
		return;
	
	var utils = new Utils();
	var globals = new Globals();
	globals.initModules();
})();

//
Personal tools