Neopets - Dailies

Collect all dailies on one page for easy usage

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Neopets - Dailies
// @namespace    http://www.neopets.com/dailies
// @version      0.70.0762
// @description  Collect all dailies on one page for easy usage
// @author       Anon
// @match        http://www.neopets.com/dailies
// @grant        none
// @require      https://cdnjs.cloudflare.com/ajax/libs/js-cookie/2.1.3/js.cookie.min.js
// ==/UserScript==

// ==================== Options ====================

// ===== Debug Settings =====

/*
 * set the level of verbosity for debug messages
 * 0: none
 * 1: small notifications, starting, finishing, progress
 * 2: debug dump outputs
 * 3: lemme think about it
 * 4: all
 * default: 4
 */
const DEBUGLEVEL = 4;

// ===== Debug End

// ===== Misc Settings =====

/*
 * use cookies to remember timer starts and maybe other stuff
 * not implemented yet
 * default: true
 */
const ALLOWCOOKIES = true;

/*
 * Show my dev notes in an extra section
 * default: false
 */
const ALLOW_NOTES = false;
/*
 * show a test for pet preview swfs
 * default: false
 */
const ALLOW_PET_TESTS = false;

/*
 * the minimized & expanded icons
 * default: "\u25C4"
 * default: "\u25BC"
 */
const CHAR_MINI_CLOSED = "\u25C4";
const CHAR_MINI_OPENED = "\u25BC";

/*
 * hide swf dailies on creation so they only start loading when you expand them
 * default: true
 */
const START_FLASH_HIDDEN = true;

// ===== Misc End



// ===== Account Safety

/*
 * whether to disallow most of the page to accounts other than your main
 * to prevent accidental dailies usage on a side
 * Not implemented yet
 * default: false
 */
const MAIN_CHECK = false;

/*
 * your main acccount name, for the main check, write the name EXACTLY, including all capitalization, numbers, and symbols
 * default: a blank string ""
 */
const MAIN_NAME = "";

// ===== Account End



// ===== Lab Safety

/*
 * show the secret laboratory, disable if you don't want the chance of accidentally zapping your pets
 * default: false
 */
const LAB_ALLOW_SECRET = false;

/*
 * pets that are allowed to be zapped, write the name EXACTLY, including all capitalization, numbers, and symbols
 * if you don't want a pet to be able to be zapped at all, DO NOT WRITE THEIR NAME, simple as that
 * the list is empty at the start for a reason, I only want it to be possible to zap a pet if you give your permission to the page explicitly
 * default: an empty list []
 */
const LAB_RATS_SECRET = [
    "",
    "",
    "",
    "",
    ""
];

/*
 * show the petpet laboratory, disable if you don't want the chance of accidentally zapping your petpets
 * default: false
 */
const LAB_ALLOW_PETPET = false;

/*
 * pets whose petpets are allowed to be zapped, write the name EXACTLY, including all capitalization, numbers, and symbols
 * if you don't want a pet's petpet to be able to be zapped at all, DO NOT WRITE THEIR NAME, simple as that
 * the list is empty at the start for a reason, I only want it to be possible to zap a petpet if you give your permission to the page explicitly
 * default: an empty list []
 */
const LAB_RATS_PETPET = [
    "",
    "",
    "",
    "",
    ""
];

// ===== Lab End



// ===== Wheel Settings =====

/*
 * mask out the excess content at the edges of the wheels
 * default: true
 */
const WHEEL_ENABLE_MASK = true;

/*
 * show the wheel of extravagance, disable if you don't want the chance of accidentally spending 100k
 * default: false
 */
const WHEEL_ALLOW_EXT = false;

/*
 * show the wheel of misfortune
 * default: true
 */
const WHEEL_ALLOW_MIS = true;

/*
 * show the mute button on the wheel of monotony
 * default: true
 */
const WHEEL_SHOW_MON_MUTE = true;

// ===== Wheel End

// ==================== Options End



// ==================== Includes/Data ====================

// ===== Stylesheet =====
var DAILYSTYLE = `
/* ===== Reset Neopets Settings ===== */

.sectionFrame .sidebarHeader,
.dailyFrame .sidebarHeader
{
height: auto;
overflow: auto;
position: relative;
margin: 0;
padding: 0;
padding-left: 0;
}

/* ===== Header Rules ===== */
.sectionFrame .sidebarHeader,
.dailyFrame .sidebarHeader
{
padding: 0.5em 0.25em 0.5em 0.25em;
}

.frameHeader>*
{
position: relative;
display: inline-table;
margin: 0 0.25em 0 0.25em;
}

.frameHeader .minimize
{
width: 1.25em;
height: 1.25em;
float: right;
}

.frameHeader .minimize .charspan
{
position: absolute;
left: 0;
top: -1.4px;
}

/* ===== Section Rules General ===== */
.sectionFrame,
.sectionFrame .sectionHeader
{
margin-bottom: 0.5em;
}

.sectionContent
{
padding-right: 0.5em;
}

/* ===== Daily Rules General ===== */
.dailyFrame
{
display: inline-table;
margin-left: 0.5em;
margin-bottom: 0.5em;
}

.dailyFrame .dailyHeader
{
margin-bottom: 0.5em;
}

.dailyContent
{
position: relative;
width: inherit;
height: 300px;
overflow: hidden;
margin: 0.5em;
}

.dailyFrame .widthbar
{
margin: 0 0.5em 0 0.5em;
}

.dailyFrame .dailyContent button
{
display: inline-block;
}



/* ===== Daily Rules Justify ===== */
.dailyFrame .justifyContainer
{
width: inherit;
height: inherit;
}

.dailyFrame.justify .dailyHeader
{
margin-bottom: 0.5em;
}

.dailyFrame.justify .widthbar
{
width: inherit;
}

.dailyFrame.justify .justifyContainer
{
display: table-cell;
vertical-align: middle;
}

.dailyFrame.justify .justifyContainer>*
{
text-align: justify;
font-size: 0.01px;
}

.dailyFrame.justify .justifyContainer>*>* {
display: inline-block;
}

.dailyFrame.justify .justifyContainer>*:after {
content: '';
width: 100%;
display: inline-block;
}

/* ===== Centers ===== */
.dailyFrame.centers .dailyContent
{
text-align: center;
}

.dailyFrame.centers .dailyContent>*
{
display: table-cell;
vertical-align: middle;
}

.dailyFrame.centers table {
display: inline-table;
margin: 0.25em 0.25em 0 0.25em;
}

/* ===== Error ===== */
.dailyFrame.error .dailyContent
{
height: auto;
}

/* ===== Thin ===== */
.dailyFrame.thin .dailyContent,
.dailyFrame.thin .widthbar
{
width: 222px;
}

/* ===== Wide ===== */
.dailyFrame.wide .dailyContent,
.dailyFrame.wide .widthbar
{
width: 621px;
}

/* ===== Half ===== */
.dailyFrame.half .dailyContent
{
height: 150px;
}

/* ===== Large ===== */
.dailyFrame.large .dailyContent
{
width: 621px;
height: auto;
}

.dailyFrame.large .widthbar
{
width: 621px;
}

/* ===== Box ===== */
.dailyFrame.box .dailyContent,
.dailyFrame.box .widthbar
{
width: 300px;
}

.dailyFrame.box .dailyContent .mask
{
display: initial;
position: absolute;
left: 0%;
top: 0%;
width: 100%;
height: 100%;
}

/* ===== Scrolling ===== */
.dailyFrame.scrolling .dailyContent
{
height: 300px;
overflow-y: scroll;
}

/* ===== Wheel Rules =====*/
.wheel .dailyContent
{
overflow: hidden;
}

.wheel .dailyContent .mask
{
display: none;
pointer-events: none;
}

/* ===== trudy Rules ===== */
.trudy .dailyContent,
.trudy iframe,
.trudy html
{
overflow: hidden;
}

.trudy .dailyContent>div
{
transform-origin: 0% 0%;
transform: scale(0.5) translateX(-108px);
}

/* ===== Shoptill Rules ===== */
.till .dailyContent
{
position: relative;
overflow: hidden;
}

.till .dailyContent>div
{
overflow: hidden;
}
/*
.till .dailyContent>div
{
position: absolute;
left: -438px;
top: -530px;
}
*/

/* ===== Lists ===== */
.dailyContent ol,
.dailyContent ul,
.dailyContent li
{
margin: 0;
}

.dailyContent ul li,
.dailyContent ol li
{
margin 0.25em 0 0 0;
font-weight: bold;
}

.dailyContent ul li *,
.dailyContent ol li *
{
font-weight: normal;
}

/* ===== Crop Button ===== */
.cropButton {
display: inline-table;
position: relative;
vertical-align: middle;
}

.cropButton div {
width: 80px; //wd;
height: 80px; //ht;
//666, 333 center;
//scale: 0.16;
position: relative;
overflow: hidden;
margin: 0;
}

.cropButton div img {
position: relative;
}

/* ===== Extras ===== */
button embed
{
pointer-events: none;
}
`;
// what? what do you mean your shitty browser doesn't support template literals?

// ===== Stylesheet End

// ===== Circle Mask =====
var MASKCIRCLE =
    "data:image/gif;base64,R0lGODlhLAEsAYABAP///wAAASH5BAEKAAEALAAAAAAsASwBAAL+hI+py+0Po5y02ouz3rz7D4ZHQJbmiZbiyrbuC8PpTNdzjOf6zk/" +
    "2DwyaesSi8UgRKpdKpPMJdTGnVGH0is06qtxuUAsOF73kMlCMTofM7HZNDY9L3PR6So7P2/d8Uv4f1icoCFjoNIhIaLiYk+iIyBi58kgJKXmpUalpidkZsQnK6TmK" +
    "EGoqSop5uoqaasgK2+oqF1srO4tmq6uIm7b7y9ubBUwcLPxUnMx3jKzsvMds9DxtF81DjV1tHZPdTbct5S3uBi4yft5W/oHObqa+0R7v/m4hb09Gn3S/z5X/yQ+Qi" +
    "r8GAQtWGZjAoMIpCA0sfLgEIcSJVvJRvHjmHcb+jTbUcfxIAxzIkXeikTx5ghnKlSp6sXwZABdMmK5mziRl06annDlV8ewZ6edPRkKFLio6tBBSo3+WFtXj9GmcqE" +
    "unUpXq6yrSrFq3Burq9CtYr8PGhsViliratGehsFXb7G3UuHKrIql79QherdL2wu3hly/gwH91EBbc6DBiborz4mjc9TFkxy8mR65smTKLzJc3c9a85vNiEKJHeyj" +
    "deR1q0BxWp27tmnWG2K9n05Z94TbuCrpN1+tdODfw3f+Gz7VtvO3v5MeXM7fL+3nw4tKh+6iunDp2stq3M53j3fqD8M3HkxdP8Dz3Leqxsm/vngH89Qvmx69vP2n6" +
    "/Pr+8fPn+d5/Ou0n4E3yFQigfwgaqMCCQDXoIIMJRUgThBRWWMqFGI6g4UsWdojShyCSJOKIIE1oYogcpnhShiySuOKLJ8YoI0cu1mgjjThedOOOPOroI0RABikkA" +
    "ETmeORGDiX5I5NNOjnRklA+JOWUClVpZUFYZgnQllzu4+WX9oQpZjxklsnOmWieo+aa4rTpZjdwxonNnHROY+edzuSpZzJ89knMn4D+Iuigugxp6J6IJurnoowG6u" +
    "ijhEYq6aGUVlpLj5gWo+mmwHTq6S4ohjopqKTCUuKpsaSqKiustnrKq7CGouCssdZqK6245rrJgbya4uuvoBAoLCUBFvv+yLHIJqLssoOY5+wtpkb7DbTU9gHBtc9" +
    "mqy003XXLBnjgVsvtuOSIa24Z+qSr7nXs4rPuuwdFJ++88dbLhHD4RqTvvhX1629HyAUsMAYEZzTwwSjApnBJ8DScUgcQR8zwxDGdZjFpGWvcsDkQT6KwZwe3MDLJ" +
    "BGO2rwz+MlavZC27zK5h8u7w7mDjEpHuGOD21a1e2h5CLV3LuuXsFUMbjWxZv4KxNNO8ipErV6rC0apVp9JCKlSbArK1UpIe9TXYiUoy9iWD7qQnTnTW5KZMaApTp" +
    "kpcWpOlSFB6xCQ9SfpDpEQ7NmRkjYAXat/g0yJouKwCJr5rgYwHi/jjxOZ4J3m5lFduOXyYu3ve5vSG5znAz4WecHKkP2zc6RIPp7pqtLXuMWqwi/zZ7OFYZjvLh+" +
    "Uus1+82/zW7zyPJTzQvhWvc3bIE53g8mJ56DzVLUavtZLUvxLl9WZrqX0q8nRvkpzga9To+ICvan7oyaa/vBfsvw+/BQUAADs=";
// ===== Mask End



// ==================== Includes End



// ==================== Utility Functions ====================

/*
 * Turns a htmlstring into a documentfragment, in the real way
 */
function fragmentize(htmlstring){
    var frag = document.createDocumentFragment();
    var tdiv = document.createElement("div");
    tdiv.innerHTML = htmlstring;
    while (tdiv.firstChild) {
        frag.appendChild(tdiv.firstChild);
    }
    return frag;
}

/*
 * Copies properties to target if they both share the property
 * mostly made so i dont have to write this over again
 */
function copySharedProperties(o, target) {
    for (var p in o) {
        if (p in target) target[p] = o[p];
    }
    return target;
}

/*
 * Allows the collapsing and expanding of the daily divs
 */
function hideContent(caller) {
    try {
        var target = caller.parentNode.parentNode.content;
        target.style.display = "none";
        caller.span.innerText = CHAR_MINI_CLOSED;
    } catch(e) {}
}

/*
 * Allows the collapsing and expanding of the daily divs
 */
function showContent(caller) {
    try {
        var target = caller.parentNode.parentNode.content;
        target.style.display = "";
        caller.span.innerText = CHAR_MINI_OPENED;
    } catch(e) {}
}

/*
 * toggles the collapsing and expanding of the daily divs
 */
window.toggleContent = function(caller) {
    if (caller.span.innerText === CHAR_MINI_OPENED) {
        //collapse
        hideContent(caller);
    } else {
        //expand
        showContent(caller);
    }
};

/*
 * I'm sick of people telling me to do things when they just end up being more work
 */
createXMLHttpRequest = function() {
    var request = null;

    if (window.XMLHttpRequest) { // Mozilla, Safari, ...
        request = new XMLHttpRequest();
    } else if (window.ActiveXObject) { // IE
        try {
            request = new ActiveXObject('Msxml2.XMLHTTP');
        } catch (e) {
            try {
                request = new ActiveXObject('Microsoft.XMLHTTP');
            } catch (er) {}
        }
    }
    return request;
};

/*
 * I wish people would stop telling me to use fetch when it's really not what they keep saying it is
 * you need to do so much more fucking bullshit to make it work the exact same as the xhr
 * and I can't find the fucking docs anywhere (cause im too lazy)
 */
window.getPage = function(url) {
    console.log("getting",url);
    return new Promise(function(resolve, reject){
        var request = createXMLHttpRequest();
        request.open('GET', url, true);
        request.onload = function(e) {
            if (request.status >= 200 && request.status < 400) {
                // Success!
                resolve(request.responseText);
            } else {
                // We reached our target server, but it returned an error
                reject(request.statusText);
            }
        };
        request.onerror = function() {
            // There was a connection error of some sort
            reject("Network Error, Content could not be loaded.");
        };
        request.send();
    });
};

/*
 * sets up a cascading then chain from a list of promises
 *
 * in between, it attempts to append the completed daily to the page
 */
function prepAppends(ary, mount) {
    return ary.reduce(function(prev, curr, next){
        if (curr.constructor.name !== "Promise") throw new TypeError("Invalid Object, expected Promise");
        return curr.then(function(r){try{mount.appendChild(r);} catch(e){}}).then(next);
    }, Promise.resolve());
}

// ========== Preparation ==========

/*
 * Prepare the dailies object from the returned pet lookup page
 */
function parsePets(r) {
    console.log("pets_parse",!!(r));
    var result = /<table id='nav'[^]*?<\/table>/igm.exec(r);
    var frag = document.createDocumentFragment();
    window.bod = document.createElement("body");
    bod.innerHTML = result;
    frag.appendChild(bod);

    window.pets_parse = Array.prototype.slice.call(frag.querySelectorAll(".pet_toggler img"));

    if (typeof(pets_parse) !== "undefined" && pets_parse !== null) {
        pets_parse.forEach((item, i) => {
            var name = item.getAttribute("title");
            var id = item.getAttribute("style").match(/\/cp\/(\w+)\//)[1];
            window.dailies.pets[i] = {
                "name": name,
                "id": id,
                "swf": window.dailies.createPetPreview(name)
            };
        });
    }
}

/*
 * a copy of the required functions for trudy's daily, straight from the scripts on the trudy page
 */
function prepareTrudy() {

    // ===== Init Cbox Script =====
    window.initCbox = function(baseClass) {
        $('#colorbox').attr('class', baseClass);
        $('#cboxWrapper').css('width', ($('#cboxTopLeft').width() + $('#cboxTopCenter').width() + $('#cboxTopRight').width()) + 'px');
        $('#cboxContent').css('width', $('#cboxWrapper').width() - ($('#cboxMiddleLeft').width() + $('#cboxMiddleRight').width()) + 'px');
        $('#cboxCurrent').css('display', 'none');
        $('#cboxLoadedContent').css('overflow', 'visible');
        $.colorbox.resize();
        $('#cboxWrapper').css('width', ($('#cboxTopLeft').width() + $('#cboxTopCenter').width() + $('#cboxTopRight').width()) + 'px');
        $('#cboxContent').css('width', $('#cboxWrapper').width() - ($('#cboxMiddleLeft').width() + $('#cboxMiddleRight').width()) + 'px');
        $('#cboxLoadedContent').width($('#cboxContent').width());

        try {
            eval("initCbox_" + baseClass + "()");
        } catch (e) {

        }
    };

    window.defaultColorbox = {
        opacity: 0.70,
        fixed: true,
        scrolling: false,
        transition: 'none',
        onComplete: function(type) {
            initCbox(type);
        }
    };

    // ===== Init Trudy's Script =====
    window.CloseSlotsGame = function() {
        $('#tempcontent').css('display', 'none');
    };

    window.trudyspopup = function(href, version) {
        if (typeof version == 'undefined') {
            version = 1;
        }

        var height = '450px';
        var innerHeight = '343px';
        var adjust = 463;

        $.colorbox({
            width: '625px',
            height: height,
            innerWidth: '270px',
            innerHeight: innerHeight,
            opacity: 0.70,
            fixed: true,
            scrolling: false,
            inline: true,
            transition: 'none',
            href: href,
            onComplete: function() {
                initCbox('ac2014nc');
                $('#cboxLoadedContent').css('width', $('#cboxContent').css('width'));
            }
        });
    };

    window.ShowDailyPrizes = function(prize, type) {
        console.log("ShowDailyPrizes");

        $('#newPrizeMessage').css('display', 'none');
        $('#unluckyMessage').css('display', 'none');
        $('#prize1').css('display', 'none');
        $('#prize2').css('display', 'none');

        if(type == 'badluck') $('#unluckyMessage').css('display', 'block');
        else $('#newPrizeMessage').css('display', 'block');

        for(var i=0;i<prize.length;i++){
            var k = i+1;

            $('#prize' + k).css('display', 'block');
            $('#prizePopupImage' + k).css('background', 'url(' + prize[i].url + ') no-repeat');
            $('#prizePopupName' + k).html('<b>' + prize[i].name + '</b>');
        }

        trudyspopup('#prizePopup', 5);
        try {console.log("restarting trudy timer");document.querySelector("#trudyTimer").restart();}
        catch (e) {}
    };

    window.ShowDailySlotsHelp = function() {
        //$('#helpmsg').css('display', 'none');
        trudyspopup('#slotsHelpPopup', 5);
    };

}

// ========== Preparation End

// ========== Element Creation ==========

/*
 * creates a minimize button and returns the node reference to it
 */
function createMinimizeButton() {
    var button = document.createElement("button");
    button.setAttribute("class", "minimize");
    button.setAttribute("type", "button");
    button.setAttribute("onclick", "toggleContent(this)");



    var span = document.createElement("span");
    span.setAttribute("class", "charspan");
    span.appendChild(document.createTextNode(CHAR_MINI_OPENED));

    button.appendChild(span);
    button.span = span;

    return button;
}

/*
 * creates the timer element for a daily frame
 */
function createDailyTimer(p) {
    if (p.type === "none") return null;
    var time = document.createElement("time");
    time.setAttribute("class", "dailyTimer");

    return time;
}

/*
 * Creates a single input from a parameters object
 */
function createInput(p) {
    var input;
    // otherwise, create the input like normal

    // figure out if element is select type
    var isselect = false;
    if (p.element !== undefined && p.element !== "") {
        input = document.createElement(p.element);
        if (p.element.toLowerCase() === "select") isselect = true;
        if (input.constructor.name === "HTMLUnknownElement") throw new Exception("Element is not valid HTML element");
    } else {
        // no input element given, create normal input
        input = document.createElement("input");
        if (p.type !== undefined && p.type.toLowerCase() === "select") isselect = true;
    }

    copySharedProperties(p, input);
    //                console.debug(inputs[i], inputs[i] instanceof Function , input);
    input.classList.add("dailyInput");

    if (isselect && p.values !== undefined && p.values.length > 0) {
        // if it's a select type, add all of the menu options
        p.values.forEach((j, indexj)=>{
            var option = document.createElement("option");
            option.setAttribute("value", j);
            option.appendChild(document.createTextNode(j));
            input.appendChild(option);
        });
    } else {
        // otherwise, just set the value
        // though the value should have already been set by the property copier
    }

    return input;
}

/**
 * 1 argument: parameter object
 * any property a form has can be put inside
 * additional properties:
 * inputs array:
 *	input = {
 *      (any property an input can have, suggested properties: )
 * 		content: {}, // special, optional, but overrides everything else
 *      values: [], // special, required for select input type, a list of values to select from
 *		className: "", //optional
 * 		id: "", // optional
 *		element: "", // optional
 *		type: "", //required if no element property
 *  	name: "", //required, probably
 *  	value: "" //required
 *	}
 */
function createDailyForm(p) {
    var frag = document.createDocumentFragment();

    var div = document.createElement("div");
    div.classList.add("justifyContainer");

    var form = document.createElement("form");
    copySharedProperties(p, form);
    form.classList.add("dailyForm");
    // target default
    if (form.target === "") form.target = "_blank";
    // inputs
    var inputs = p.inputs;
    try {
        if (inputs === undefined || inputs === null) throw "no inputs";
        inputs.forEach((i, index)=>{
            var input;

            if (i.content !== undefined && i.content !== null) {
                // allow custom content to override input element
                input = i.content;
            } else {
                input = createInput(i);
            }

            form.appendChild(input);
            form.appendChild(document.createTextNode(" "));
        });
    } catch (e) {
        if (e === "no inputs") {
            console.trace("No inputs, skipping");
        } else {
            console.trace("broken inputs?", e);
        }

    }

    if (inputs === undefined && p.button === undefined) {
        var buton = document.createElement("button");
        buton.setAttribute("type", "submit");
        buton.appendChild(document.createTextNode("Submit"));
        form.appendChild(buton);
    }

    if (p.button !== undefined) {
        form.appendChild(p.button);
    }

    div.appendChild(form);
    frag.appendChild(div);

    return frag;
}

/*
 * creates a daily frame and returns the node reference to it
 * Arguments:
 * titlename: the title to put in the header
 * idname: the unique id for this daily frame
 * classname: a css class string for further specification of properties
 * nodecontent: (optional) a node that can be appended to the content of the daily frame
 * timercontent: {optional) a timer node that can be appended to the header
 */
function createDailyFrame(p) {
    var type;
    if (p.type !== undefined && (p.type === "article" || p.type === "section")) type = p.type;
    else type = "article";
    var frame = document.createElement(type);

    copySharedProperties(p, frame);
    frame.classList.add("sidebarTable");
    if (type === "section") frame.classList.add("sectionFrame");
    if (type === "article") frame.classList.add("dailyFrame");


    var header = document.createElement("header");
    header.classList.add("sidebarHeader", "frameHeader");
    if (type === "section") header.classList.add("sectionHeader");
    if (type === "article") header.classList.add("dailyHeader");

    var title = document.createElement("h3");
    title.classList.add("title");
    var titletext = document.createTextNode(p.titleText);
    if (p.titleUrl !== undefined) {
        var anchor = document.createElement("a");
        anchor.href = p.titleUrl;
        anchor.target = "_blank";
        anchor.appendChild(titletext);
        title.appendChild(anchor);
    } else {
        title.appendChild(titletext);
    }

    // timer stuff goes here

    var button = createMinimizeButton();


    var content = document.createElement("div");
    content.classList.add("frameContent", "collapsing");
    if (type === "section") content.classList.add("sectionContent");
    if (type === "article") content.classList.add("dailyContent");
    content.onmousedown = function(e) {
        //console.log("clickdown", this, e);
    };
    content.onmouseup = function(e) {
        //console.log("clickup", this, e);
    };


    var widthbar = document.createElement("footer");
    widthbar.classList.add("widthbar");


    header.appendChild(title);
    frame.label = title;
    // append timer
    //
    header.appendChild(button);
    frame.collapse = button;

    frame.appendChild(header);
    frame.header = header;
    frame.appendChild(content);
    frame.content = content;
    frame.appendChild(widthbar);
    frame.widthbar = widthbar;

    if (p.nodeContent !== undefined && p.nodeContent !== null) frame.content.appendChild(p.nodeContent);

    return frame;
}

/*
 * Driver function to create a section easier
 */
function createDailySection(p) {
    p.type = "section";

    return createDailyFrame(p);
}

function createCropButton(p) {
    var btn = document.createElement("button");
    copySharedProperties(p, btn);
    btn.classList.add("cropButton");

    var width = 80; if (p.width !== undefined) width = p.width;
    var height = 80; if (p.height !== undefined) height = p.height;
    var div = document.createElement("div");
    div.style.width = width + "px";
    div.style.height = height + "px";

    // prepare image
    var img = document.createElement("img");
    // start loading the image
    var url = ""; if (p.url !== undefined) url = p.url;
    img.src = url;
    var scale = 1.0; if (p.scale !== undefined) scale = p.scale;
    img.onload = function () {
        //console.log(url, this);
        this.width = Math.ceil(scale * this.naturalWidth);
        this.height = Math.ceil(scale * this.naturalHeight);
    };
    var xcenter = 0; if (p.xcenter !== undefined) xcenter = p.xcenter;
    var ycenter = 0; if (p.ycenter !== undefined) ycenter = p.ycenter;
    img.style.transform =
        "translate(" +
        -Math.round(scale * xcenter - (width / 2)) + "px" + "," +
        -Math.round(scale * ycenter - (height / 2)) + "px" +
        ")";

    div.appendChild(img);
    btn.appendChild(div);

    if (p.message !== undefined) {
        var para = document.createElement("p");
        para.appendChild(document.createTextNode(p.message));
        btn.appendChild(para);
    }

    return btn;

}

/*
 * Create an embed of a neopian wheel with proper offset and scale to fit the box
 */
function createWheel(url, wheelsize, xcen, ycen, id) {
    var frag = document.createDocumentFragment();

    var embed = document.createElement("embed");
    //attributes
    embed.setAttribute("src", url);
    if (arguments.length >= 4) {
        embed.setAttribute("id", id);
        embed.setAttribute("name", id);
    }

    var scalar = 300 / wheelsize;
    var width = Math.ceil(scalar * 800);
    var height = Math.ceil(scalar * 500);
    embed.setAttribute("width", width);
    embed.setAttribute("height", height);

    embed.setAttribute("quality", "high");
    embed.setAttribute("scale", "exactfit");
    embed.setAttribute("menu", "false");
    embed.setAttribute("allowscriptaccess", "never");
    embed.setAttribute("swliveconnect", "true");
    embed.setAttribute("wmode", "transparent");
    //flashvars
    embed.setAttribute(
        "flashvars",
        "host_url=" + encodeURIComponent("www.neopets.com") + "&" +
        "lang=en"
    );
    //translate
    var xmid = Math.round(scalar * xcen) - 150;
    var xoff = "-" + xmid + "px";
    var ymid = Math.round(scalar * ycen) - 150;
    var yoff = "-" + ymid + "px";
    embed.style.transform =
        "translate(" +
        xoff + ", " +
        yoff + ")";
    frag.appendChild(embed);

    if (WHEEL_ENABLE_MASK) {
        var image = document.createElement("img");
        image.setAttribute("src", MASKCIRCLE);
        image.setAttribute("class", "mask");
        frag.appendChild(image);
    }
    return frag;
}

// ========== Creation End

// ==================== Utility End



// ==================== Food Club Utilities ====================

// ===== String Utilities =====

String.prototype.textNode = function() {
    return document.createTextNode(this);
};

String.prototype.bold = function() {
    var b = document.createElement("b");
    b.appendChild(this.textNode());
    return b;
};

String.prototype.toTD = function() {
    var td = document.createElement("td");
    td.setAttribute("align", "center");
    td.appendChild(this.textNode());
    return td;
};

String.prototype.toBoldTD = function() {
    var td = document.createElement("td");
    td.setAttribute("align", "center");
    td.appendChild(this.bold());
    return td;
};


// ===== BetWrap =====

/*
 * Expose BetWrap to the developer console so i can test shit
 */
window.createBetWrap = function(table) {
    return new BetWrap(table);
};

/*
 * BetWrap Object, holds your bets and information about the set as a whole
 */
function BetWrap(table) {
    // handle missing new operator to avoid globals
    var cstr = BetWrap;
    var args = Array.prototype.slice.call(arguments);
    if (!(this instanceof cstr)) {
        args.unshift("");
        return new(Function.bind.apply(cstr, args));
    }

    // object initialization
    this.round = 0;
    this.numBets = 0;
    this.bets = [];
    this.maxAmount = 0;
    this.totalAmount = 0;
    this.totalOdds = 0;
    this.totalWinnings = 0;

    // use the parameters if there are any
    if (arguments.length > 0 && table !== "undefined") {
        this.parseBets(table);
    }

    this.updateInfo();

    return this;
}

/*
 * parse bets into this from a html string table
 * as long as the bets you're following copy their table right from
 * the food club page, this should work
 *
 * might update it for neodaq's thing too, no promises though
 */
BetWrap.prototype.parseBets = function(table) {
    var frag = fragmentize(table);

    var rows = Array.prototype.slice.call(frag.querySelectorAll("tr"));
    // rows [0] and [1] are the header and labels rows
    rows.shift();
    rows.shift();
    // rows [length] is the footer row
    rows.pop();

    while (rows[0] !== undefined) {
        this.bets.push(new Bet(rows.shift()));
    }

    return this;
};

/*
 * change the maximum np per bet to the given value
 * if a bet goes over a million, it will cap at just barely a million
 * so you only lose an odds worth of np, rather than some silly amount
 * (if your odds were 30:1, you lose some amount less than 30(in this case it's 20 np))
 */
BetWrap.prototype.changeNP = function(newnp) {
    try {
        for (var i in this.bets) if (this.bets[i] instanceof Bet) {
            var odds = parseInt(this.bets[i].odds);
            if (odds !== odds) throw new TypeError("odds NaN");
            //clamp the winnings to a million
            var result = parseInt(newnp);
            if (result * odds >= 1000000 + odds) {
                result = Math.ceil(1000000 / odds);
            }
            this.bets[i].amount = result;
            this.bets[i].winnings = Math.min(result * odds, 1000000);
        }
        this.maxAmount = parseInt(newnp);
    } catch (e) {
        console.error("Something went wrong setting the new np value:", e);
    }
    this.updateInfo();

    return this;
};

/*
 * fix all of the properties of this betwrap to accurately reflect the bets
 */
BetWrap.prototype.updateInfo = function() {
    var tnum = 0;
    var tamt = 0;
    var todd = 0;
    var twin = 0;

    for (var i in this.bets)
        if (this.bets[i] !== undefined && this.bets[i] instanceof Bet) {
            tnum++;
            tamt += this.bets[i].amount;
            todd += this.bets[i].odds;
            twin += this.bets[i].winnings;
        }

    if (this.bets[0] !== undefined) this.round = this.bets[0].round;
    this.numBets = tnum;
    this.totalAmount = tamt;
    this.totalOdds = todd;
    this.totalWinnings = twin;

    return this;
};

/*
 * turn the bets back into a table so you can put em somewhere
 * I might expand the functionality of this "bet editor" in the future but no promises
 */
BetWrap.prototype.toTable = function() {
    var table = document.createElement("table");
    table.setAttribute("border", "1");
    table.setAttribute("class", "bets_table");
    table.setAttribute("cellpadding", "4");
    table.setAttribute("cellspacing", "2");
    table.setAttribute("width", "500");
    var tbody = document.createElement("tbody");

    // header row (rows[0])
    var thead = document.createElement("tr");
    var thcontent = document.createElement("td");
    thcontent.setAttribute("align", "center");
    thcontent.setAttribute("colspan", "5");
    var thfont = document.createElement("font");
    thfont.setAttribute("size", "4");
    thfont.setAttribute("color", "black");
    thfont.appendChild("Current Bets".bold());
    thcontent.appendChild(thfont);
    thead.appendChild(thcontent);
    tbody.appendChild(thead);

    // labels row (rows[1])
    var tlabel = document.createElement("tr");
    var labels = ["Round", "Bet Info", "Amount", "Odds", "Winnings"];
    for (var i in labels) if (labels[i].constructor.name === "String") {
        tlabel.appendChild(labels[i].toBoldTD());
    }
    tbody.appendChild(tlabel);

    // bet rows (potential up to rows[11])
    for (var b in this.bets) if (this.bets[b] instanceof Bet) {
        tbody.appendChild(this.bets[b].toTableRow());
    }

    // footer row (rows[rows.length-1])
    var tfoot = document.createElement("tr");
    if (this.bets.length !== 0) {
        var winlabel = document.createElement("td");
        winlabel.setAttribute("align", "right");
        winlabel.setAttribute("colspan", "4");
        winlabel.appendChild("Total Possible Winnings".bold());
        tfoot.appendChild(winlabel);
        tfoot.appendChild((this.totalWinnings + " NP").toBoldTD());
    } else {
        var errmsg = "You do not have any bets placed for this round!".toTD();
        errmsg.setAttribute("colspan", "5");
        tfoot.appendChild(errmsg);
    }
    tbody.appendChild(tfoot);

    table.appendChild(tbody);
    return table;
};

BetWrap.prototype.toButtonTable = function() {
    var table = document.createElement("table");
    table.setAttribute("border", "1");
    table.setAttribute("class", "bets_table");
    table.setAttribute("cellpadding", "4");
    table.setAttribute("cellspacing", "2");
    table.setAttribute("width", "55");
    var tbody = document.createElement("tbody");

    // header row (rows[0])
    var thead = document.createElement("tr");
    var thcontent = document.createElement("td");
    thcontent.setAttribute("align", "center");
    var thfont = document.createElement("font");
    thfont.setAttribute("size", "4");
    thfont.setAttribute("color", "black");
    thfont.appendChild("Submit".bold());
    thcontent.appendChild(thfont);
    thead.appendChild(thcontent);
    tbody.appendChild(thead);

    // labels row (rows[1])
    var tlabel = document.createElement("tr");
    tlabel.appendChild(" ... ".toBoldTD());
    tbody.appendChild(tlabel);

    // bet rows (potential up to rows[11])
    for (var i in this.bets) if (this.bets[i] instanceof Bet) {
        var tr = document.createElement("tr");
        var td = document.createElement("td");
        td.setAttribute("align", "center");
        var tinputs = this.bets[i].toInputsArray(parseInt(i) + 1);
        td.appendChild(createDailyForm({
            method: "POST",
            action: "http://www.neopets.com/pirates/process_foodclub.phtml",
            inputs: tinputs
        }));
        //"POST", "http://www.neopets.com/pirates/process_foodclub.phtml", this.bets[i].toInputsArray(parseInt(i) + 1);
        tr.appendChild(td);
        tbody.appendChild(tr);
    }

    // footer row (rows[rows.length-1])
    var tfoot = document.createElement("tr");
    if (this.bets.length !== 0) {
        var winlabel = " ... ".toTD();
        tfoot.appendChild(winlabel);
    } else {
        var errmsg = "No Bets.".toTD();
        tfoot.appendChild(errmsg);
    }
    tbody.appendChild(tfoot);

    table.appendChild(tbody);
    return table;
};

// ===== Bet =====

/*
 * Bet Object, holds information about a single bet
 */
function Bet(row) {
    // handle missing new operator to avoid globals
    var cstr = Bet;
    var args = Array.prototype.slice.call(arguments);
    if (!(this instanceof cstr)) {
        args.unshift("");
        return new(Function.bind.apply(cstr, args));
    }

    // object initialization
    this.round = 0;
    this.betInfo = {
        "Shipwreck": "",
        "Lagoon": "",
        "Treasure Island": "",
        "Hidden Cove": "",
        "Harpoon Harry's": ""
    };
    this.betInfoIds = {
        1: 0,
        2: 0,
        3: 0,
        4: 0,
        5: 0
    };
    this.amount = 0;
    this.odds = 0;
    this.winnings = 0;

    // use the parameters if there are any
    if (arguments.length > 0 && typeof row !== "undefined") {
        this.parseRow(row);
    }

    return this;
}

/*
 * Parse a single table row into information for this bet
 */
Bet.prototype.parseRow = function(row) {
    // Round
    this.round = parseInt(row.childNodes[0].textContent);
    // Bet Info
    this.parsePirates(row);
    // Amount
    this.amount = parseInt(row.childNodes[2].textContent);
    // Odds
    this.odds = parseInt(row.childNodes[3].textContent);
    // Winnings
    this.winnings = parseInt(row.childNodes[4].textContent);

    return this;
};

/*
 * take the string from the table div and extract the pirate and arena info
 * this is a subroutine of parseRow
 */
Bet.prototype.parsePirates = function(row) {
    var str = row.childNodes[1].innerHTML;
    str = str.replace(/<[^]*?>/igm, " ");
    str = str.trim();
    var ary = str.split("  ");

    for (var i in ary) if (ary[i].constructor.name === "String") {
        var pair = ary[i].split(" : ");
        this.betInfo[pair[0]] = pair[1];
        this.betInfoIds[dailies.arenas.indexOf(pair[0])] = dailies.pirates.indexOf(pair[1]);
    }

    return this;
};

/*
 * turns the information for this bet into an input array to be used in createDailyForm
 */
Bet.prototype.toInputsArray = function(n) {
    var ary = [];
    ary.push({
        type: "hidden",
        name: "type",
        value: "bet"
    }, {
        type: "hidden",
        name: "bet_amount",
        value: String(this.amount)
    }, {
        type: "hidden",
        name: "total_odds",
        value: String(this.odds)
    }, {
        type: "hidden",
        name: "winnings",
        value: String(this.winnings)
    });

    for (var i in this.betInfoIds) {
        if (this.betInfoIds[i] !== 0) {
            ary.push({
                type: "hidden",
                name: "matches[]",
                value: i
            }, {
                type: "hidden",
                name: "winner" + i,
                value: this.betInfoIds[i]
            });
        }
    }

    ary.push({
        content: (function() {
            var btn = document.createElement("button");
            btn.setAttribute("type", "submit");
            btn.appendChild(("Submit #" + n).textNode());
            return btn;
        })()
    });

    return ary;
};

/*
 * turns this bet back into a table row so it can be put back
 * into the big table with all of the bets
 */
Bet.prototype.toTableRow = function() {
    var row = document.createElement("tr");
    // Round
    row.appendChild(String(this.round).toBoldTD());
    // Bet Info
    row.appendChild(this.getBetInfo());
    // Amount
    row.appendChild((this.amount + " NP").toTD());
    // Odds
    row.appendChild((this.odds + ":1").toTD());
    // Winnings
    row.appendChild((this.winnings + " NP").toTD());

    return row;
};

/*
 * builds a table div out of the arena and pirate information
 * subroutine of toTableRow
 */
Bet.prototype.getBetInfo = function() {
    var tdbet = document.createElement("td");
    for (var i in this.betInfoIds) {
        if (this.betInfoIds[i] !== 0) {
            var arena = dailies.arenas[i];
            var pirate = dailies.pirates[this.betInfoIds[i]];
            // <b>Arena's Name</b>
            tdbet.appendChild(arena.bold());
            // ": Pirate's Name"
            tdbet.appendChild(document.createTextNode(": " + pirate));
            // <br>
            tdbet.appendChild(document.createElement("br"));
        }
    }

    return tdbet;
};

// ==================== Food Club Functions End



// ==================== Initialization ====================

//change the page title so it's not an ugly "neopets 404"
window.setTimeout(function(){document.title = "Neopets - Dailies";}, 1000);

/*
window.onbeforeunload = function(e) {
    e.returnValue = "Page redirected, was this what you wanted?";
    return dialogText;
};
*/

//create the page's extra style
var injectstyle = document.createElement("style");
injectstyle.setAttribute("type", "text/css");
injectstyle.setAttribute("id", "dailies_style");
injectstyle.appendChild(document.createTextNode(DAILYSTYLE));

document.head.appendChild(injectstyle);

/**
template = document.createElement("template");
article = document.createElement("article");
*/


if (!window.pets_parse) window.pets_parse = {};

if (!window.dailies) window.dailies = {
    pets: [
        //gets filled later
    ],

    // id: http://pets.neopets.com/cp/[id]/[mood]/[size].png
    // name: http://pets.neopets.com/cpn/[name]/[mood]/[size].png

    moods: {
        "ihavenomouthandimustscream":   "0",
        "happy":                        "1",
        "sad":                          "2",
        "angry":                        "3",
        "sick":                         "4",
    },

    sizes: {
        "face50":                       "1",
        "full150":                      "2",
        "bust80":                       "3",
        "full300":                      "4",
        "full500":                      "5",
        "face150":                      "6",
        "full640":                      "7"
    },

    arenas: [
        // 0 isn't valid
        "\0",
        // 1:
        "Shipwreck",
        // 2:
        "Lagoon",
        // 3:
        "Treasure Island",
        // 4:
        "Hidden Cove",
        // 5:
        "Harpoon Harry's",
    ],

    pirates: [
        // 0 isn't valid
        "\0",
        // 1:
        "Scurvy Dan the Blade",
        // 2:
        "Young Sproggie",
        // 3:
        "Orvinn the First Mate",
        // 4:
        "Lucky McKyriggan",
        // 5:
        "Sir Edmund Ogletree",
        // 6:
        "Peg Leg Percival",
        // 7:
        "Bonnie Pip Culliford",
        // 8:
        "Puffo the Waister",
        // 9:
        "Stuff-A-Roo",
        // 10:
        "Squire Venable",
        // 11:
        "Captain Crossblades",
        // 12:
        "Ol' Stripey",
        // 13:
        "Ned the Skipper",
        // 14:
        "Fairfax the Deckhand",
        // 15:
        "Gooblah the Grarrl",
        // 16:
        "Franchisco Corvallio",
        // 17:
        "Federismo Corvallio",
        // 18:
        "Admiral Blackbeard",
        // 19:
        "Buck Cutlass",
        // 20:
        "The Tailhook Kid"
    ],

    createPetImageUrl: function(id, mood, size, petname) {
        var url =
            "http://pets.neopets.com/cp" +
            "/" + id +
            "/" + dailies.moods[mood] +
            "/" + dailies.sizes[size] +
            ".png";

        return url;
    },

    createPetImage: function(id, mood, size, petname) {
        var img = document.createElement("img");

        img.setAttribute("src", dailies.createPetImageUrl(id, mood, size, petname));
        img.setAttribute("alt", petname);
        img.setAttribute("title", petname);

        return img;
    },

    createPetPreview: function(pet, size) {
        var tsize = "300";
        if (arguments.length >= 2) tsize = String(size);
        var embed = document.createElement("embed");
        embed.setAttribute("type", "application/x-shockwave-flash");
        embed.setAttribute("src", "http://images.neopets.com/customise/customNeopetViewer_v35.swf");
        embed.setAttribute("class", "CustomNeopetView");
        embed.setAttribute("name", "CustomNeopetView");
        embed.setAttribute("title", pet);
        embed.setAttribute("width", tsize);
        embed.setAttribute("height", tsize);
        embed.setAttribute("bgcolor", "white");
        embed.setAttribute("quality", "high");
        embed.setAttribute("scale", "showall");
        embed.setAttribute("menu", "false");
        embed.setAttribute("allowscriptaccess", "always");
        embed.setAttribute("swliveconnect", "true");
        embed.setAttribute("wmode", "transparent");

        embed.setAttribute(
            "FlashVars",
            "webServer="    + encodeURIComponent("http://www.neopets.com")                      + "&" +
            "imageServer="  + encodeURIComponent("http://images.neopets.com")                   + "&" +
            "gatewayURL="   + encodeURIComponent("http://www.neopets.com/amfphp/gateway.php")   + "&" +
            "pet_name="     + pet + "&" +
            "lang=en"             + "&" +
            "pet_slot="
        );

        return embed;
    }


};

// ==================== Init End



// ==================== Main Program ====================

// Neopets' main content table, we'll fill this up
var TableContent = document.querySelector("td.content");
// baleet all the shit inside so we can replace it
try { while (TableContent.firstChild) {
    TableContent.removeChild(TableContent.firstChild);
}}
catch(e) {
    // I can't remember why this is in a try catch block, but I think I wanted to avoid it erroring if there were no children
}

var frag = document.createDocumentFragment();

var DailiesSection = createDailySection({titleText: "Dailies", id: "DailiesSection"});
var DailiesContent = DailiesSection.content;
frag.appendChild(DailiesSection);

var EmbedsSection = createDailySection({titleText: "Embeds & iFrames", id: "EmbedsSection"});
var EmbedsContent = EmbedsSection.content;
frag.appendChild(EmbedsSection);

var UtilsSection = createDailySection({titleText: "Utilities", id: "UtilsSection"});
var UtilsContent = UtilsSection.content;
frag.appendChild(UtilsSection);

if (ALLOW_NOTES) {
    var NotesSection = createDailySection({titleText: "Notes", id: "NotesSection", className: "startCollapsed"});
    var NotesContent = NotesSection.content;
    frag.appendChild(NotesSection);
}

if (ALLOW_PET_TESTS) {
    var PetsSection = createDailySection({titleText: "Pets", id: "PetsSection", className: "startCollapsed"});
    var PetsContent = PetsSection.content;
    frag.appendChild(PetsSection);
}

TableContent.appendChild(frag);


window.setTimeout(function(){
    document.querySelectorAll(".startCollapsed").forEach(r=>{hideContent(r.collapse);});
}, 1000);



getPage("http://www.neopets.com/quickref.phtml").then(
    function(result) {
        parsePets(result);
    },
    function(result){
        PetsContent.appendChild(document.createTextNode("An Error Occurred getting pet information, some dailies may not work"));
        Promise.resolve(result);
    })
    .then(function(){
    /*
    // ========================================
    // ========== Create The Dailies ==========
    // ========================================
    */

    // ===== Dailies =====
    prepAppends([

        promiseAnchorMgmt(),

        promiseAppleBobbing(),

        promiseColtzanShrine(),

        promiseForgottenShore(),

        promiseFruitMachine(),

        promiseHealingSprings()



    ], DailiesContent);
    // ===== Dailies End

    // ===== Utilities =====
    prepAppends([

        promiseShopTill(),

        promiseFishing(),

        promiseParseBets(),

        promiseQuickLodge()

    ], UtilsContent);
    // ===== Utilities End

    // ===== Embeds and iFrames =====
    prepAppends([
        // ===== Wheels =====
        //Excitement
        promiseWheelExcitement(),
        //Extravagance
        promiseWheelExtravagance(),
        //Knowledge
        promiseWheelKnowledge(),
        //Mediocrity
        promiseWheelMediocrity(),
        //Misfortune
        promiseWheelMisfortune(),
        //Monotony
        promiseWheelMonotony(),
        // ===== Wheels End

        // ===== Qasalan Expellibox =====
        promiseExpellibox(),

        // ===== Trudy's surprise
        promiseTrudyDaily()

    ], EmbedsContent)
        .then(function(){
        if (START_FLASH_HIDDEN)
            EmbedsContent.querySelectorAll(".flash").forEach(r=>{hideContent(r.collapse);});
    });
    // ===== Embeds End

    // ===== Notes =====
    if (ALLOW_NOTES) prepAppends([
        // Brainstorming
        promiseNotesBrain(),

        // Dailies
        promiseNotesDailies()

    ], NotesContent);
    // ===== Notes End

    // ===== Pets Tests =====
    if (ALLOW_PET_TESTS) prepAppends([

        promisePetTests()

    ], PetsContent);
    // ===== Pets End

});

// ===== Daily Promise Functions =====

// TODO: move all daily creation content here, it's ugly where it is
// also here I can put nice descriptions on

// ===== [Dailies] =====

/*
 * daily for anchor management
 * it needs to getpage to find the unique id, for some reason
 * if it fails to get it, it will create a different button image
 * if there's a different error, it should make an error box
 */
function promiseAnchorMgmt() {
    return (
        getPage("http://www.neopets.com/pirates/anchormanagement.phtml")
        .then(function(r){
            var uid;
            try {
                uid = /<form method=["']post["'] id=["']form-fire-cannon["']>[^]*?<input[^]*?value=["']([\w\d]+)["'][^]*?><\/form>/igm.exec(r)[1];
            } catch(e) {
                return Promise.reject("already");
            }
            var inputs = [
                {
                    type: "hidden",
                    name: "action",
                    value: uid
                },
                {
                    content: createCropButton({
                        url: "http://images.neopets.com/pirates/disappearance/anchor/h43y27.jpg",
                        scale: (80/150),
                        xcenter: 235,
                        ycenter: 187,
                        width: 160,
                        height: 80,
                        message: "Fire!!!"
                    })
                }
            ];

            return createDailyFrame({
                titleText: "Anchor Management",
                titleUrl: "http://www.neopets.com/pirates/anchormanagement.phtml",
                id: "anchormgmt",
                className: "daily box half centers",
                nodeContent: createDailyForm({
                    method: "POST",
                    action: "http://www.neopets.com/pirates/anchormanagement.phtml",
                    inputs: inputs,
                    id: "anchorman_form"
                })
            });
        }).catch(function(r){
            if (r === "already") {
                var inputs = [
                    {
                        content: createCropButton({
                            url: "http://images.neopets.com/pirates/disappearance/anchor/ju6b7n.jpg",
                            scale: (160/636),
                            xcenter: 318,
                            ycenter: 175,
                            width: 160,
                            height: 80,
                            message: "Already Fired..."
                        })
                    }
                ];
                return createDailyFrame({
                    titleText: "Anchor Management",
                    titleUrl: "http://www.neopets.com/pirates/anchormanagement.phtml",
                    id: "anchormgmt",
                    className: "daily box half centers",
                    nodeContent: createDailyForm({
                        method: "GET",
                        action: "",
                        inputs: inputs,
                        id: "anchorman_form"
                    })
                });
            } else {
                return createDailyFrame({
                    titleText: "Anchor Management",
                    titleUrl: "http://www.neopets.com/pirates/anchormanagement.phtml",
                    id: "anchormgmt",
                    className: "daily box half centers",
                    nodeContent: fragmentize("There was an unknown error")
                });
            }
        })
    );
}

/*
 *
 */
function promiseAppleBobbing() {
    return (
        Promise.resolve()
        .then(function(){
            return (
                createDailyFrame({
                    titleText: "Apple Bobbing",
                    titleUrl: "http://www.neopets.com/halloween/applebobbing.phtml",
                    id: "apple_bob",
                    className: "daily box half centers",
                    nodeContent: createDailyForm({
                        // method is get by default
                        action: "http://www.neopets.com/halloween/applebobbing.phtml",
                        inputs: [
                            {
                                type: "hidden",
                                name: "bobbing",
                                value: "1"
                            }
                        ],
                        // inputs end
                        button: createCropButton({
                            type: "submit",
                            url: "http://images.neopets.com/halloween/applebob/bg.jpg",
                            scale: (80 / 130),
                            xcenter: 150,
                            ycenter: 111,
                            width: 160,
                            height: 80,
                            message: "Bob for Apples"
                        })
                        // button end
                    })
                })
            );
            // return end
        })
        // then end
    );
}

function promiseColtzanShrine() {
    return (
        Promise.resolve()
        .then(function(){
            var button = createCropButton({
                url: "http://images.neopets.com/desert/shrine_scene.gif",
                scale: (80/280),
                xcenter: 313,
                ycenter: 146,
                width: 160,
                type: "submit",
                name: "type",
                value: "approach",
                message: "Approach the Shrine"
            });
            var form = createDailyForm({
                method: "POST",
                action: "http://www.neopets.com/desert/shrine.phtml",
                button: button
            });
            return (
                createDailyFrame({
                    titleText: "Coltzan's Shrine",
                    titleUrl: "http://www.neopets.com/desert/shrine.phtml",
                    id: "czan_shrine",
                    className: "daily box half centers",
                    nodeContent: form
                })
            );
        })
    );
}

function promiseForgottenShore() {
    return (
        getPage("http://www.neopets.com/pirates/forgottenshore.phtml")
        .then(function(r){
            try {
                var ref_ck = /_ref_ck=([\w\d]*)/igm.exec(r);

                if (ref_ck === null) {
                    // was not able to find the id

                    // Located about 80 miles south of Mystery Island, Krawk Island was originally a cluster of smaller islands...
                    if (/Mystery Island/igm.exec(r) !== null)
                        return Promise.reject("nomap");
                    // A deserted shore stretches along in front of you, but there's nothing of interest to be found today.
                    else if (/nothing of interest/igm.exec(r) !== null)
                        return Promise.reject("none");
                    // You've already searched the coast for treasure today. Perhaps you should try again tomorrow.
                    else if (/already searched the coast/igm.exec(r) !== null)
                        return Promise.reject("already");
                    // unknown error
                    else
                        return Promise.reject("DEFAULT");
                } else {
                    // was able to find the id

                    var uid = ref_ck[1];
                    var inputs = [
                        {
                            type: "hidden",
                            name: "confirm",
                            value: "1"
                        },
                        {
                            type: "hidden",
                            name: "_ref_ck",
                            value: uid
                        }
                    ];

                    var buttons = {
                        // default scale of 1 and height of 80 for all
                        shore_back: {
                            xcenter: 300,
                            ycenter: 350,
                            width: 160
                        },
                        shore_np: {
                            xcenter: 300,
                            ycenter: 254,
                            width: 160,
                            message: "Found NeoPoints!"
                        },
                        shore_chest: {
                            xcenter: 498,
                            ycenter: 230,
                            width: 160,
                            message: "Found Chest!"
                        },
                        shore_sand: {
                            xcenter: 383,
                            ycenter: 189,
                            width: 160,
                            message: "Found Treasure!"
                        },
                        shore_shiny: {
                            xcenter: 93,
                            ycenter: 261,
                            width: 160,
                            message: "Found Jewelery!"
                        },
                        shore_coin: {
                            xcenter: 148,
                            ycenter: 215,
                            width: 160,
                            message: "Found Coin!"
                        }
                    };
                    var re = /#(shore_\w*) {[^]*?background: url\(([^]*?)\)[^]*?}/igm;
                    var str = r;
                    var item;
                    while ((item = re.exec(r)) !== null) {
                        buttons[item[1]].url = item[2];
                    }
                    console.log("buttons:", buttons);
                    var current = /id=["'](shore_(?!back)\w*)["']/igm.exec(r)[1];
                    console.log("current:", current);

                    var button = createCropButton(buttons[current]);
                    copySharedProperties(
                        {
                            backgroundImage: "url(" + buttons.shore_back.url + ")",
                            backgroundRepeat: "no-repeat",
                            backgroundPositionX: -((buttons[current].xcenter) - (160 / 2)) + "px",
                            backgroundPositionY: -((buttons[current].ycenter) - (80 / 2)) + "px",
                            backgroundSizeX: "600px",
                            backgroundSizeY: "500px"
                        },
                        button.firstChild.style
                    );
                    console.log(button);
                    return Promise.resolve(createDailyForm({
                        // method get
                        action: "http://www.neopets.com/pirates/forgottenshore.phtml",
                        inputs: inputs,
                        button: button
                    }));
                }

            } catch (e) {
                // some weird shit happened anyway
                return Promise.reject("DEFAULT");
            }
        })
        .catch(function(r){
            var button;
            if (r === "nomap") {
                return Promise.resolve(fragmentize("You do not have the map! (buy it, it's like under 5000 np for the whole set)"));
            } else if (r === "none") {
                button = createCropButton({
                    type: "submit",
                    url: "http://images.neopets.com/pirates/forgottenshore/hc4u1s.jpg",
                    scale: (160/600),
                    xcenter: 300,
                    ycenter: 350,
                    width: 160,
                    height: 80,
                    message: "Nothing of Interest..."
                });
                return Promise.resolve(
                    createDailyForm({
                        // method get by default
                        action: "http://www.neopets.com/pirates/forgottenshore.phtml",
                        button: button
                    })
                );
            } else if (r === "already") {
                button = createCropButton({
                    type: "submit",
                    url: "http://images.neopets.com/pirates/forgottenshore/hc4u1s.jpg",
                    scale: (160/600),
                    xcenter: 300,
                    ycenter: 350,
                    width: 160,
                    height: 80,
                    message: "Already Checked..."
                });
                return Promise.resolve(
                    createDailyForm({
                        // method get by default
                        action: "http://www.neopets.com/pirates/forgottenshore.phtml",
                        button: button
                    })
                );
            } else {
                return Promise.resolve(fragmentize("There was an unknown error."));
            }
        })
        .then(function(r){
            return createDailyFrame({
                titleText: "Forgotten Shore",
                titleUrl: "http://www.neopets.com/pirates/forgottenshore.phtml",
                id: "forgotshore",
                className: "daily box half centers",
                nodeContent: r
            });
        })
    );
}

function promiseFruitMachine() {
    var buttons = {
        aisha: {
            type: "submit",
            url: "http://images.neopets.com/desert/fruit/bg.jpg",
            scale: 1.0,
            xcenter: 104,
            ycenter: 117,
            width: 160
        },
        kau: {
            type: "submit",
            url: "http://images.neopets.com/desert/fruit/bg.jpg",
            scale: (80/90),
            xcenter: 281,
            ycenter: 174,
            width: 160
        },
        lupe: {
            type: "submit",
            url: "http://images.neopets.com/desert/fruit/bg.jpg",
            scale: 1.0,
            xcenter: 484,
            ycenter: 129,
            width: 160
        }
    };
    return (
        getPage("http://www.neopets.com/desert/fruit/index.phtml")
        .then(function(r){
            try {
                var value = /name=['"]ck['"] value=['"]([\w\d]*?)['"]/igm.exec(r)[1];
                console.debug(value);
                var submit = buttons.lupe;
                submit.message = "Spin, spin, spin!";
                var form = createDailyForm({
                    method: "POST",
                    action: "http://www.neopets.com/desert/fruit/index.phtml",
                    inputs: [
                        {
                            type: "hidden",
                            name: "spin",
                            value: "1"
                        },
                        {
                            type: "hidden",
                            name: "ck",
                            value: value
                        }
                    ],
                    button: createCropButton(buttons.lupe)
                });
                return Promise.resolve(form);
            } catch (e) {
                return Promise.reject("already");
            }
        })
        .catch(function(r){
            if (r === "already") {
                var err = buttons.kau;
                err.message = "Already Spun...";
                return Promise.resolve(createDailyForm({
                    action: "http://www.neopets.com/desert/fruit/index.phtml",
                    button: createCropButton(buttons.kau)
                }));
            } else {
                return Promise.resolve(fragmentize("There was an unknown error."));
            }

        })
        .then(function(r){
            return createDailyFrame({
                titleText: "Fruit Machine",
                titleUrl: "http://www.neopets.com/desert/fruit/index.phtml",
                id: "fruitmachine",
                className: "daily box half centers",
                nodeContent: r
            });
        })
    );
}

function promiseHealingSprings() {
    return (
        Promise.resolve()
        .then(function(){
            return (
                createDailyFrame({
                    titleText: "Healing Springs",
                    titleUrl: "http://www.neopets.com/faerieland/springs.phtml",
                    id: "h_springs",
                    className: "box half justify",
                    // content
                    nodeContent: createDailyForm({
                        method: "POST",
                        action: "http://www.neopets.com/faerieland/springs.phtml",
                        inputs: [
                            /*
                                I wrapped the following functions like (...)() so that I could just insert them inline here
                                it's exactly the same format as this: [ { content: nodeobject }, { content: nodeobject } ]
                                */

                            // Heal My Pets!
                            {
                                content: createCropButton({
                                    type: "submit",
                                    name: "type",
                                    value: "heal",
                                    url: "http://images.neopets.com/faerieland/springs1.gif",
                                    scale: (80 / 150), //scale
                                    xcenter: 154,
                                    ycenter: 81,
                                    // default width and height of 80
                                    message: "Heal Pets"
                                })
                            },
                            // See what is for sale
                            {
                                content: createCropButton({
                                    type: "submit",
                                    name: "type",
                                    value: "purchase",
                                    url: "http://images.neopets.com/faerieland/springs3.gif",
                                    // default scale of 1.0
                                    xcenter: 260,
                                    ycenter: 45,
                                    // default width and height of 80
                                    message: "Browse Shop"
                                })
                            }
                        ],
                        id: "h_springs_form"
                    })
                    // content end
                })
            );
        })
    );
}

// ===== [Utilities] =====

/*
 * TODO: fix this horrible MESS
 */
function promiseShopTill() {

    return (
        getPage("http://www.neopets.com/market.phtml?type=till")
        .then(function(r){

            if (/You don't have your own shop yet/igm.exec(r) !== null) return Promise.reject("noshop");
            var nps;
            try {
                nps = /You currently have <b>(\d*?) NP<\/b> in your till./.exec(r)[1];
            } catch(e) {
                return promise.reject();
            }
            /*
            var frag = document.createDocumentFragment();

            // Create the two input boxes
            var amt = createInput({
                type: "text",
                name: "amount",
                id: "amt_field",
                size: "9",
                maxlength: "10",
                placeholder: "Amount"
            });
            copySharedProperties(
                {
                    textAlign: "right",
                    paddingRight: "0.5em"
                },
                amt.style
            );

            var pin = createInput({
                type: "password",
                name: "pin",
                id: "pin_field",
                size: "2",
                maxlength: "4",
                placeholder: "PIN"
            });
            copySharedProperties(
                {
                    textAlign: "center"
                },
                pin.style
            );
            // input boxzes end

            var inputs = [
                {
                    element: "p",
                    type: "",
                    innerHTML: "You currently have <b>" + nps + " NP</b> in your till."
                },
                {
                    content: document.createElement("br")
                },
                {
                    type: "hidden",
                    name: "type",
                    value: "withdraw"
                },
                {
                    content: amt
                },
                {
                    content: pin
                },
                {
                    content: document.createElement("br")
                },
                {
                    element: "button",
                    type: "button",
                    innerHTML: "Collect Till"
                }
            ];

            var form = createDailyForm({
                method: "POST",
                action: "http://www.neopets.com/process_market.phtml",
                target: "ShopTill",
                inputs: inputs
                /*
                button: createInput({
                    element: "button",
                    type: "button",
                    name: "amount",
                    value: nps,
                    innerHTML: "Collect All"
                })

            });

            form.querySelectorAll("button").forEach((item,i) => {

                item.onclick = function(){
                    window.wind = window.open("http://www.neopets.com/market.phtml?type=till", "ShopTill");
                    var myform = this.form;
                    var mycon = console;
                    console.debug("Windywind:", wind);
                    wind.onload = ((i)=>{
                        console.log("loaded");
                        var form = window.wind.document.querySelector("form");
                        console.log(form).bind(mycon);
                        form.amt_field = form.querySelector("input[name='pwd']");
                        form.amt_field.value = myform.amt_field.value;
                        //form.pin_field = form.pin_field;
                        form.pin_field.value = myform.pin_field.value;

                        form.submit();
                    });
                    /*
                    console.log(this.form);
                    wind.form = this.form.cloneNode(true);
                    wind.form.target = "_self";
                    console.log(wind.form);
                    window.setTimeout(((i)=>{this.form.submit();}).bind(wind), 500);

                };
            });
*/
            var frag = document.createDocumentFragment();
            var form = document.createElement("iframe");
            var div = document.createElement("div");
            form.src = "http://www.neopets.com/market.phtml?type=till";
            var xpos;
            var ypos;
            form.onload = function(v) {
                var iform = this.contentDocument.querySelector("form");

                iform.scrollIntoView();


                this.contentDocument.body.style.position = "absolute";
                this.contentDocument.body.style.left = "-438px";
                

            }.bind(form, [nps]);
            form.scrolling = "no";
            var p = document.createElement("p");
            form.style.border = "0";

            div.appendChild(form);
            frag.appendChild(div);


            p.innerHTML = "You currently have <b>" + nps + " NP</b> in your till.";
            p.style.position = "absolute";
            p.style.left = "0px";
            p.style.top = "0px";
            p.style.margin = "0";
            frag.appendChild(p);
            //frag.appendChild(document.createElement("br"));

            return Promise.resolve(frag);
        })
        .catch(function(r){
            var result;
            if (r === "noshop") {
                result = "You don't have a shop!";
            } else {
                result = "There was an unknown error...";
            }
            return (
                Promise.resolve(fragmentize(result))
            );
        })
        .then(function(r){
            return createDailyFrame({
                titleText: "Shop Till",
                titleUrl: "http://www.neopets.com/market.phtml?type=till",
                id: "shoptill",
                className: "util box half centers till",
                nodeContent: r
            });
        })
    );
}

/*
 * Food Club Parse
 * this daily has a textbox that you paste a html table of someone's bets into
 * it parses the bets and sets them to your maximum bet amount automatically
 * then it puts the table up with submit buttons to let you make the bets too
 */
function promiseParseBets() {
    return (
        // ===== Food Club Parse =====
        getPage("http://www.neopets.com/pirates/foodclub.phtml?type=bet")
        .then(function(r) {
            try {
                var mymaxnp = /You can only place up to <b>(\d*?)<\/b> NeoPoints per bet/.exec(r)[1];
            } catch(e) {
                return Promise.reject("closed");
            }
            var inputs = [
                {
                    element: "textarea",
                    id: "strip"
                },
                {
                    content: document.createElement("br")
                },
                {
                    content: (function(){
                        var btn = document.createElement("button");
                        btn.setAttribute("type", "button");
                        btn.onclick = function() {
                            var mybets = createBetWrap(this.form.strip.value);
                            mybets.changeNP(mymaxnp);
                            this.form.parentNode.parentNode.MyBets = mybets;
                            this.form.parentNode.parentNode.updateTables();
                        };
                        btn.appendChild(document.createTextNode("Parse this Table"));

                        return btn;
                    })()
                },
                {
                    content: (function(){
                        var div = document.createElement("div");
                        div.setAttribute("class", "fc_tables");
                        return div;
                    })()
                }
            ];
            var form = createDailyForm({
                method: "GET",
                inputs: inputs,
                id: "parse_form"
            });
            var fc = createDailyFrame({
                titleText: "Quick Food Club",
                id: "parse_bets",
                className: "daily wide large centers",
                nodeContent: form
            });
            fc.content.updateTables = function() {
                var target = this.querySelector(".fc_tables");
                if (target !== undefined && target !== null)
                    while(target.firstChild !== undefined && target.firstChild !== null)
                        target.removeChild(target.firstChild);
                target.appendChild(this.MyBets.toTable());
                target.appendChild(this.MyBets.toButtonTable());
            };

            return fc;
        })
        .catch(function(r){
            var msg;
            if (r === "closed") {
                msg = fragmentize("The betting gates are closed. Come back later and refresh to bet again.");
            } else {
                msg = fragmentize("An Unknown error occurred");
            }
            return (
                createDailyFrame({
                    titleText: "Quick Food Club",
                    titleUrl: "http://www.neopets.com/pirates/foodclub.phtml?type=bet",
                    id: "parse_bets",
                    className: "daily wide large centers",
                    nodeContent: msg
                })
            );
        })
    );
}

/*
 * creates a daily with buttons for each pet
 * it changes to that pet and pulls their line in 1/2 second
 * sometimes it doesn't work because of slow connection
 * but it's even slower if I make it wait until the response has fully loaded :(
 * so sometimes you might have to fish the same pet again because it didn't work the first time
 */
function promiseFishing() {
    return (
        // ===== Fishing
        Promise.resolve()
        .then(function(r){
            var frag = document.createDocumentFragment();

            var div = document.createElement("div");
            div.setAttribute("class", "justifyContainer");

            var form = document.createElement("form");
            form.setAttribute("action", "http://www.neopets.com/water/fishing.phtml");
            form.setAttribute("method", "POST");
            form.setAttribute("target", "_blank");

            var input = document.createElement("input");
            input.setAttribute("type", "hidden");
            input.setAttribute("name", "go_fish");
            input.setAttribute("value", "1");
            form.appendChild(input);

            div.appendChild(form);
            frag.appendChild(div);

            dailies.pets.forEach((pet, i) => {
                //create button
                var buton = document.createElement("button");
                buton.setAttribute("title", pet.name);
                //button chitlens
                buton.appendChild(window.dailies.createPetImage(pet.id, "happy", "bust80", pet.name));
                var pg = document.createElement("p");
                pg.appendChild(document.createTextNode("Go Fish!"));
                buton.appendChild(pg);
                //set button attribs
                buton.setAttribute("type", "button");
                buton.setAttribute("class", "go_fish " + pet.name + " fish_" + pet.name);
                buton.dataset.index = i;
                buton.onclick = function() {
                    var context = this;
                    getPage("http://www.neopets.com/process_changepet.phtml?new_active_pet=" + window.dailies.pets[this.dataset.index].name
                           ).then(window.setTimeout(function(){context.form.submit();}, 500));
                };

                form.appendChild(buton);
                form.appendChild(document.createTextNode(" "));
            });
            var fishing = createDailyFrame({
                titleText: "Ye Olde Fishing Vortex",
                titleUrl: "http://www.neopets.com/water/fishing.phtml",
                id: "fishing",
                className: "daily wide half justify",
                nodeContent: frag
            });

            return fishing;
        })
        // ===== Fishing End
    );
}

/*
 * Creates a daily with buttons for each of your pets
 * automatically books your pet for the maximum length stay for the minimum amount of money
 * (5np cockroach towers * 28 days)
 */
function promiseQuickLodge() {
    return (
        // ===== Neolodge
        Promise.resolve()
        .then(function(r) {
            var inputs = [];
            inputs.push.apply(inputs, [
                {
                    type: "hidden",
                    name: "hotel_rate",
                    value: "5"
                },
                {
                    type: "hidden",
                    name: "nights",
                    value: "28"
                }
            ]);
            dailies.pets.forEach((pet, i) => {
                // create button
                var buton = document.createElement("button");
                buton.setAttribute("title", pet.name);
                //button chitlens
                buton.appendChild(window.dailies.createPetImage(pet.id, "happy", "bust80", pet.name));
                var pg = document.createElement("p");
                pg.appendChild(document.createTextNode("Book Stay!"));
                buton.appendChild(pg);
                //button attribs
                buton.setAttribute("type", "submit");
                buton.setAttribute("name", "pet_name");
                buton.setAttribute("value", pet.name);

                inputs.push({
                    content: buton
                });
            });
            var form = createDailyForm({
                method: "POST",
                action: "http://www.neopets.com/book_neolodge.phtml",
                inputs: inputs,
                id: "lodge_form"
            });
            return createDailyFrame({
                titleText: "Quick Neolodge",
                titleUrl: "http://www.neopets.com/neolodge.phtml",
                id: "lodge",
                className: "daily wide half justify",
                nodeContent: form
            });
        })
        // ===== Neolodge End
    );
}

// ===== [Wheels] =====
function promiseWheelExcitement() {
    return (
        // Excitement
        Promise.resolve(createWheel("http://images.neopets.com/wheels/wheel_of_excitement_v3_831fbec8f8.swf?r=564743181", 352, 558, 258, "flash_32139696264"))
        .then(function(r){
            return createDailyFrame({
                titleText: "Wheel of Excitement",
                titleUrl: "http://www.neopets.com/faerieland/wheel.phtml",
                id: "wheel_exc",
                className: "daily box wheel flash" + (START_FLASH_HIDDEN ? " startCollapsed" : ""),
                nodeContent: r
            });
        })
    );
}

function promiseWheelExtravagance() {
    return (
        // Extravagance
        (WHEEL_ALLOW_EXT ? // This wheel is expensive and dangerous, I've given the option to disable it
         Promise.resolve(createWheel("http://images.neopets.com/wheels/wheel_of_extravagance_v1_5dd2d07006.swf?r=892962206", 325, 540, 240, "flash_15132567672"))
         .then(function(r){
            return createDailyFrame({
                titleText: "Wheel of Extravagance",
                titleUrl: "http://www.neopets.com/desert/extravagance.phtml",
                id: "wheel_ext",
                className: "daily box wheel flash" + (START_FLASH_HIDDEN ? " startCollapsed" : ""),
                nodeContent: r
            });
        })
         : Promise.resolve())
    );
}

function promiseWheelKnowledge() {
    return (
        // Knowledge
        Promise.resolve(createWheel("http://images.neopets.com/wheels/wheel_of_knowledge_v2_731eafc8f8.swf?r=1495789846", 372, 559, 262, "flash_45622675376"))
        .then(function(r){
            return createDailyFrame({
                titleText: "Wheel of Knowledge",
                titleUrl: "http://www.neopets.com/medieval/knowledge.phtml",
                id: "wheel_kno",
                className: "daily box wheel flash" + (START_FLASH_HIDDEN ? " startCollapsed" : ""),
                nodeContent: r
            });
        })
    );
}

function promiseWheelMediocrity() {
    return (
        // Mediocrity
        Promise.resolve(createWheel("http://images.neopets.com/wheels/wheel_of_mediocrity_v2_c4ed41eb31.swf", 425, 548, 238, "flash_77760435077"))
        .then(function(r){
            return createDailyFrame({
                titleText: "Wheel of Mediocrity",
                titleUrl: "http://www.neopets.com/prehistoric/mediocrity.phtml",
                id: "wheel_med",
                className: "daily box wheel flash" + (START_FLASH_HIDDEN ? " startCollapsed" : ""),
                nodeContent: r
            });
        })
    );
}

function promiseWheelMisfortune() {
    return (
        // Misfortune
        (WHEEL_ALLOW_MIS ? // people don't like this wheel because a lot of the time it's not worth it, so you can disable it
         Promise.resolve(createWheel("http://images.neopets.com/wheels/wheel_of_misfortune_v2_3075ced020.swf", 395, 555, 247, "flash_91615379415"))
         .then(function(r){
            return createDailyFrame({
                titleText: "Wheel of Misfortune",
                titleUrl: "http://www.neopets.com/halloween/wheel/index.phtml",
                id: "wheel_mis",
                className: "daily box wheel flash" + (START_FLASH_HIDDEN ? " startCollapsed" : ""),
                nodeContent: r
            });
        })
         : Promise.resolve())
    );
}

function promiseWheelMonotony() {
    return (
        // Monotony
        //actual size, xcen, ycen: 315, 570, 212
        Promise.resolve(createWheel("http://images.neopets.com/wheels/wheel_of_monotony_v2_380e3dbdad.swf?r=1802226625",
                                    (WHEEL_SHOW_MON_MUTE ? 450 : 315), // size
                                    (WHEEL_SHOW_MON_MUTE ? 626 : 570), // xcen
                                    (WHEEL_SHOW_MON_MUTE ? 302 : 212), // ycen
                                    "flash_30609553018"))
        .then(function(r){
            return createDailyFrame({
                titleText: "Wheel of Monotony",
                titleUrl: "http://www.neopets.com/prehistoric/monotony/monotony.phtml",
                id: "wheel_mon",
                className: "daily box wheel flash" + (START_FLASH_HIDDEN ? " startCollapsed" : ""),
                nodeContent: r
            });
        })
    );
}
// ===== Wheels End

function promiseExpellibox() {
    return (
        Promise.resolve()
        .then(function(r) {
            var embed = document.createElement("embed");
            embed.setAttribute("src", "http://images.neopets.com/games/g905_v3_99390.swf");
            embed.setAttribute("width", "300");
            embed.setAttribute("height", "300");
            embed.setAttribute(
                "flashvars",
                "lang=en" + "&" +
                "baseurl=" + encodeURIComponent("origin.ncmall.neopets.com") + "&" +
                "neocash=1"
            );

            return createDailyFrame({
                titleText: "Qasalan Expellibox",
                titleUrl: "http://ncmall.neopets.com/mall/shop.phtml?page=giveaway",
                id: "expellibox",
                className: "daily box expeli flash" + (START_FLASH_HIDDEN ? " startCollapsed" : ""),
                nodeContent: embed
            });
        })
    );
}

function promiseTrudyDaily() {
    return(
        getPage("http://www.neopets.com/trudys_surprise.phtml")
        .then(function(result){
            prepareTrudy();
            var temp = fragmentize(result);
            var content = document.createDocumentFragment();
            content.appendChild(temp.querySelectorAll("td.content>div")[1]);
            var scripts = content.querySelectorAll("script");
            //scripts[1].textContent
            // /onClosed: function\(\)\s+{\s+(.+)\s+}/igm replace onClosed with new
            var trudy = createDailyFrame({
                titleText: "Trudy's Surprise",
                titleUrl: "http://www.neopets.com/trudys_surprise.phtml",
                id: "trudy_surprise",
                className: "daily box trudy",
                nodeContent: content
            });
            return trudy;
        })
    );
}

// ===== [MISC] =====
function promiseNotesBrain() {
    return (
        getPage("https://dl.dropboxusercontent.com/u/5061840/HTML%2C%20Coding%2C%20and%20Design/Neodailies/Notes-Brainstorming.html")
        .then(function(r){
            return createDailyFrame({
                titleText: "Brainstorming", id: "notes-bst", className: "wide notes scrolling",
                nodeContent: fragmentize(r)
            });
        })
        .catch(function(r){
            return createDailyFrame({
                titleText: "Brainstorming", id: "notes-bst", className: "wide error",
                nodeContent: fragmentize(r)
            });
        })
    );
}

function promiseNotesDailies() {
    return (
        getPage("https://dl.dropboxusercontent.com/u/5061840/HTML%2C%20Coding%2C%20and%20Design/Neodailies/Notes-Dailies.html")
        .then(function(r){
            return createDailyFrame({
                titleText: "Dailies", id: "notes-dls", className: "wide notes scrolling",
                nodeContent: fragmentize(r)
            });
        })
        .catch(function(r){
            return createDailyFrame({
                titleText: "Dailies", id: "notes-dls", className: "wide error",
                codeContent: fragmentize(r)
            });
        })
    );
}

function promisePetTests() {
    return (
        Promise.resolve()
        .then(function(r){
            var frag = document.createDocumentFragment();
            var swfs = document.createDocumentFragment();
            var pics = document.createDocumentFragment();
            var i;

            for (i = 0; i < pets_parse.length; ++i) {
                swfs.appendChild(dailies.pets[i].swf);
                pics.appendChild(dailies.createPetImage(dailies.pets[i].id, "happy", "face150", dailies.pets[i].name));
            }

            frag.appendChild(swfs);
            frag.appendChild(pics);

            return createDailyFrame({
                titleText: "Pet Tests",
                id: "ptests",
                className: "wide large flash",
                nodeContent: frag
            });
        })
    );
}

// ===== Daily Promise End =====



// =====

// ==================== Main End