// ==UserScript==
// @namespace http://mrob.com/time/scripts-beta
// @name newpix-converter for OTT
// @description Converts phpBB dates into the One Time Unit: the newpix. For this to work your date format must be: "D M d, Y g:i:s a e" without the quotes.
// @author Mrob27, Pikrass, and Smithers
// @version 36562.57
// @downloadURL http://mrob.com/time/scripts-beta/newpix-converter.user.js.txt
// @grant GM_getValue
// @grant GM_setValue
// @include http://forums.xkcd.com/viewtopic.php*
// @include http://forums3.xkcd.com/viewtopic.php*
// @include http://fora.xkcd.com/viewtopic.php*
// @include http://echochamber.me/viewtopic.php*
// @include http://forums.xkcd.com/posting.php*
// @include http://forums3.xkcd.com/posting.php*
// @include http://fora.xkcd.com/posting.php*
// @include http://echochamber.me/posting.php*
// @include http://1190.bicyclesonthemoon.info/ott/view*
// @run-at document-end
// ==/UserScript==
// REVISION HISTORY
// np1050.12 Original version, posted in OTT:506:24|#p3345722
// np1050.78 Fix 'NaN.oo B.T.' which happened when date format was "D M d, Y g:i a e"
// np1814 Version that mrob27 used as the basis for this
//
// np4200 Display date as both Heretical and Newpix
// np7990.29 Color the 'Last edited by' various shades of pink/red based on how much time has passed between the original post and the most recent edit
// np8251.51 Add options checkboxes for 'keep heretical date' and 'color Last edited notices'
// np8251.68 Fix title of options
// np8549.01 Go back to '1190' method of determining if we're in OTT
// np9035.92 Add option to act in all threads (OTTers sometimes post elesewhere)
// np9117.43 Use cross-platform functions to load and save options settings.
// np10966.67 Work on the balthamirror (1190.bicyclesonthemoon.dnsd.info)
// np14757.49 Remove 'dnsd' from bicyclesonthemoon hostname
// np21083.61 Add @grant and @run-at requests
// np26836.20 Clean up syntax (semicolons, == vs. ===, etc.)
// np27650.25 Make sure the script runs at least once
// np36562.57 Add "forums3.xkcd.com"
console.info("Newpix Converter: A 'GM_getValue is not supported' message following this one is benign and results from a runtime compatibility test.");
var needrun = 1;
var newpixConverter = {
// Change this according to your preference
decimals: 2,
o_keep_heret : { val: "0" },
o_spurler : { val: "0" },
o_all_threads : { val: "0" },
/* Chrome provides a bogus nonfunctional GM_getValue function,
so we have to do this stupid two-step test */
isGM: ((typeof GM_getValue != 'undefined') && (typeof GM_getValue('a', 'b') != 'undefined')),
/* Use this instead of GM_getValue */
vget: function(key, def) {
var rv;
if (this.isGM) {
rv = GM_getValue(key, def);
} else {
rv = localStorage[key];
}
if (typeof rv === "undefined") {
rv = def;
}
return rv;
},
/* Use this instead of GM_setValue */
vstr: function(key, val) {
if (this.isGM) {
GM_setValue(key, val);
} else {
localStorage[key] = val;
}
},
/* Use this instead of JSON.stringify */
jsenc: function(obj) {
if (typeof obj === "undefined") {
return "";
}
try {
return JSON.stringify(obj);
} catch(e) {
return JSON.encode(obj);
}
},
/* Use this instead of JSON.parse */
jsdec: function(str) {
if (typeof str === "undefined") {
return 0;
}
try {
return JSON.parse(str);
} catch(e) {
return JSON.decode(str);
}
},
// Set a checkbox to be on or off
setChkVal: function(chk, val) {
if (val === 0) {
chk.checked=false;
} else {
chk.checked=true;
}
},
/* opt_action will set or clear an option. */
opt_action: function(chk, optobj, nam) {
// If the option was just initialized, it will now become set.
// This makes sense becuse if they've just installed the script
// and click on the checkbox, they want to set the checkbox.
if (optobj.val === 0) {
optobj.val = 1;
} else {
optobj.val = 0;
}
this.setChkVal(chk, optobj.val);
chk.disabled = false;
/* Save the user's work in a way that will persist across page loads.
* see http://wiki.greasespot.net/GM_setValue */
this.vstr(nam, this.jsenc(optobj.val));
},
make_checkbox: function(nam, title, optobj, pdiv) {
// Make the checkbox for 'reveal light text'
var chk = document.createElement('input');
chk.type = 'checkbox';
chk.value = 'npcv-' + nam;
chk.id = nam;
var lbl = document.createElement('label');
lbl.htmlFor = nam;
var lab_text = document.createTextNode(title);
lbl.appendChild(lab_text);
this.setChkVal(chk, optobj.val);
chk.addEventListener('click',
this.opt_action.bind(this, chk, optobj, nam));
pdiv.appendChild(chk); pdiv.appendChild(lbl);
},
/* create_checkboxen will add a checkbox that changes an option */
create_checkboxen: function() {
var container = document.createElement('div');
var preDiv = document.createElement('div');
preDiv.style.marginTop = '1px';
preDiv.style.fontSize = '1.0em';
preDiv.style.fontWeight = 'bold';
preDiv.style.color = '#0B7';
var optstitle = document.createTextNode("Newpix-Converter Options:");
preDiv.appendChild(optstitle);
var opts_div = document.createElement('div');
opts_div.style.textAlign = 'left';
// Make the checkbox for 'Keep Heretical Date'
opts_div.appendChild(document.createTextNode(" "));
this.make_checkbox('o_keep_heret', 'Keep Heretical Date',
this.o_keep_heret, opts_div);
// Make the checkbox for 'Color "last edited" Notices'
opts_div.appendChild(document.createTextNode(" "));
this.make_checkbox('o_spurler', 'Color "Last edited" notices',
this.o_spurler, opts_div);
// Make the checkbox for 'Act on All Threads' option
opts_div.appendChild(document.createTextNode(" "));
this.make_checkbox('o_all_threads', 'Act in All Threads',
this.o_all_threads, opts_div);
container.appendChild(preDiv);
container.appendChild(opts_div);
return(container);
},
getUtcOffset: function() {
var offset = {np: 0, fnp: 0};
var rights = document.getElementsByClassName('rightside');
var text = rights[rights.length-1].innerHTML;
var reg = /UTC (-|\+) (\d+)(?::(\d{2}))? hour/;
var m = text.match(reg);
if (m !== null) {
offset.np = (m[1] == '+' ? parseInt(m[2]) : -parseInt(m[2]));
if(m[3] !== undefined) {
offset.fnp = (m[1] == '+' ? parseInt(m[3]) : -parseInt(m[3]));
}
}
if(text.indexOf('DST') != -1) {
offset.np++;
}
return offset;
},
dateToNewpix: function(date) {
var oneTrueBeginningOfTime = new Date('Mon Mar 25, 2013 4:00 am UTC');
var np = (date - oneTrueBeginningOfTime) / 1800000;
// Old newpix -> new newpix
if(np >= 240) {
np = 240 + (np - 240)/2;
}
if(np >= 0) {
return np + 1;
} else {
return np - 1;
}
},
hereticToNewpix: function(hereticString) {
var hereticDate = new Date(hereticString);
var off = this.getUtcOffset();
hereticDate.setHours(hereticDate.getHours() - off.np);
hereticDate.setMinutes(hereticDate.getMinutes() - off.fnp);
var realDate = this.dateToNewpix(hereticDate);
return realDate;
},
npToString: function(np) {
var str = np.toString();
var dot = str.indexOf('.');
var dec;
if(dot == -1) {
str += '.';
dec = 0;
} else {
dec = str.length - dot - 1;
}
while(dec < this.decimals) {
str += '0';
dec++;
}
str = str.substr(0, str.length - (dec - this.decimals));
return str;
},
NewpixToString: function(realDate) {
if (realDate >= 0) {
return 'newpix ' + this.npToString(realDate);
} else {
return 'newpix ' + this.npToString(-realDate) + ' B.T.';
}
},
convert: function() {
if (needrun === 0) {
return(0);
}
needrun = 0;
// Initialize options, create the checkboxes.
this.o_keep_heret = { val: this.jsdec(this.vget('o_keep_heret', '0')) };
if (typeof this.o_keep_heret == 'undefined') {
this.o_keep_heret = { val: "0" };
this.o_keep_heret.val = this.jsdec(this.vget('o_keep_heret', '0'));
}
this.o_spurler = { val: this.jsdec(this.vget('o_spurler', '0')) };
if (typeof this.o_spurler == 'undefined') {
this.o_spurler = { val: "0" };
this.o_spurler.val = this.jsdec(this.vget('o_spurler', '0'));
}
this.o_all_threads = {val: this.jsdec(this.vget('o_all_threads', '0')) };
if (typeof this.o_all_threads == 'undefined') {
this.o_all_threads = { val: "0" };
this.o_all_threads.val = this.jsdec(this.vget('o_all_threads', '0'));
}
var footer = document.getElementById("page-footer");
var ft_par = footer.parentNode;
ft_par.insertBefore(this.create_checkboxen(), footer);
// Check if we're on the One True Thread or if the act on all threads
// option is selected.
if (this.o_all_threads.val === 0) {
if ( (location.href.indexOf('t=101043') == -1)
&& (document.title.indexOf('1190') == -1) )
{
return;
}
}
var i, j, npd;
var newpix_dates = []; // was "new Array();"
var postnodes = [];
// Convert original post times.
var authors = document.getElementsByClassName('author');
for (i = 0; i < authors.length; i++) {
npd = this.hereticToNewpix(authors[i].lastChild.data.substr(3));
if (this.o_keep_heret.val === 0) {
authors[i].lastChild.data = ' ';
} else {
authors[i].lastChild.data += ' = ';
}
authors[i].lastChild.data += this.NewpixToString(npd);
newpix_dates[i] = npd;
postnodes[i] = authors[i].parentNode;
}
// Convert "last edited" times.
var edits = document.getElementsByClassName('notice');
for (i = 0; i < edits.length; i++) {
var str = edits[i].lastChild.data;
var edpar = edits[i].parentNode;
var comma = str.lastIndexOf(',');
npd = this.hereticToNewpix(str.substr(4, comma-4));
edits[i].lastChild.data = ' at ';
if (this.o_keep_heret.val) {
edits[i].lastChild.data += (str.substr(4, comma-4) + ' = ');
}
edits[i].lastChild.data += (this.NewpixToString(npd) + str.substr(comma));
if (this.o_spurler.val) {
// Change the color of the edited message based on how much later
// it was edited.
for(j=0; j
Joined:'))
{
npd = this.hereticToNewpix(node.lastChild.data.substr(1));
node.lastChild.data = ' ' + this.NewpixToString(npd);
break;
}
}
}
},
};
window.addEventListener('DOMContentLoaded',
newpixConverter.convert.bind(newpixConverter));
// make sure it runs at least once
newpixConverter.convert(newpixConverter);