<commit_before>/*
The MIT License (MIT)
Copyright (c) 2013 Calvin Montgomery

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

var uname = readCookie("cytube_uname") || "";
var session = readCookie("cytube_session") || "";
var loggedin = false;

if(uname && session) {
    var data = {
        name: uname,
        session: session
    };
    $.post(WEB_URL + "/api/login?callback=?", data, function (data) {
        if(data.success)
            onLogin();
    }, "jsonp");
}

function onLogin() {
    $("#cpwusername").val(uname);
    $("#ceusername").val(uname);
    $("#accountnav li")[0].innerHTML = "Logged in as " + uname;
    $("#register").hide();
    loggedin = true;
    $("#login").text("Logout");
    createCookie("cytube_uname", uname, 7);
    createCookie("cytube_session", session, 7);
}

function makeTabCallback(tabid, paneid) {
    return function() {
        $("#accountnav li").each(function() {
            $(this).removeClass("active");
        });

        $(tabid).parent().addClass("active");
        $(".span7").each(function() {
            $(this).css("display", "none");
        });
        $(paneid).css("display", "");
    };
}

$("#register").click(makeTabCallback("#register", "#registerpane"));
$("#pwchange").click(makeTabCallback("#pwchange", "#changepwpane"));
$("#pwreset").click(makeTabCallback("#pwreset", "#pwresetpane"));
$("#email").click(makeTabCallback("#email", "#changeemailpane"));
$("#profile").click(makeTabCallback("#profile", "#profilepane"));
$("#profile").click(function() {
    if(uname != "") {
        $.getJSON(WEB_URL+"/api/users/"+uname+"/profile?callback=?",
            function (data) {
            if(data.success) {
                $("#profiletext").val(data.profile_text);
                $("#profileimg").val(data.profile_image);
            }
            else {
                $("<div/>").addClass("alert alert-error")
                    .text("Failed to retrieve profile: " + data.error)
                    .insertBefore($("#profilepane form"));
            }
        });
    }
});
$("#channels").click(makeTabCallback("#channels", "#channelspane"));
$("#channels").click(function () {
    if(!loggedin) {
        var error = $("<div/>").addClass("alert alert-error")
            .text("You must be logged in to view this page")
            .insertBefore($("#channellist"));
        $("<button/>").addClass("close pull-right").click(function () {
            error.remove();
        }).html("×").prependTo(error);
        return;
    }

    var auth = "name=" + encodeURIComponent(uname) + "&session=" +
               encodeURIComponent(session);
    $.getJSON(WEB_URL+"/api/account/mychannels?"+auth+"&callback=?",
        function (data) {
        $("#channellist tbody").remove();
        data.channels.forEach(function (chan) {
            var tr = $("<tr/>").appendTo($("#channellist"));
            var td = $("<td/>").appendTo(tr);
            $("<a/>").attr("href", "./r/" + chan.name)
                .attr("target", "_blank")
                .text(chan.name)
                .appendTo(td);
        });
    });
});

$("#registerbtn").click(function() {
    $("#registerpane").find(".alert-error").remove();
    $("#registerpane").find(".error").removeClass("error");
    var name = $("#regusername").val();
    var pw = $("#regpw").val();
    var pwc = $("#regpwconfirm").val();

    var err = false;
    if(!name.match(/^[a-z0-9_]{1,20}$/i)) {
        $("<div/>").addClass("alert alert-error")
            .text("Usernames must be 1-20 characters long and contain only a-z, 0-9, and underscores")
            .insertAfter($("#regusername").parent().parent());
        err = true;
    }

    if(pw == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Password must not be blank")
            .insertAfter($("#regpw").parent().parent());
        $("#regpw").parent().parent().addClass("error");
        err = true;
    }

    if(pw != pwc) {
        $("<div/>").addClass("alert alert-error")
            .text("Passwords do not match")
            .insertAfter($("#regpwconfirm").parent().parent());
        $("#regpwconfirm").parent().parent().addClass("error");
        err = true;
    }

    if(err) {
        return;
    }

    // Input valid, try registering
    var data = {
        name: name,
        pw: pw
    };

    $.post(WEB_URL + "/api/register?callback=?", data, function (data) {
        if(data.success) {
            uname = name;
            session = data.session;
            onLogin();
            $("<div/>").addClass("alert alert-success")
                .text("Registration successful")
                .insertBefore($("#registerpane form"));
            $("#regpw").val("");
            $("#regusername").val("");
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#registerpane form"));
        }
    }, "jsonp");
});

$("#loginbtn").click(function() {
    $("#loginpane").find(".alert-error").remove();
    $("#loginpane").find(".alert-success").remove();

    if($("#loginpw").val() == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Please provide a password")
            .insertAfter($("#loginpw").parent().parent());
        $("#loginpw").parent().parent().addClass("error");
        return;
    }
    uname = $("#loginusername").val();
    var data = {
        name: uname,
        pw: $("#loginpw").val()
    };
    $.post(WEB_URL+"/api/login?callback=?", data, function(data) {
        if(data.success) {
            session = data.session;
            onLogin();
            $("<div/>").addClass("alert alert-success")
                .text("Login successful")
                .insertBefore($("#loginpane form"));
            $("#loginpw").val("");
            $("#loginusername").val("");
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#loginpane form"));
        }
    }, "jsonp");
});

$("#cpwbtn").click(function() {
    $("#changepwpane").find(".alert-error").remove();
    $("#changepwpane").find(".alert-success").remove();
    $("#changepwpane").find(".error").removeClass("error");
    var name = $("#cpwusername").val();
    var oldpw = $("#cpwoldpw").val();
    var newpw = $("#cpwnewpw").val();
    var newpwc = $("#cpwconfirm").val();

    var err = false;
    if(oldpw == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Password must not be empty")
            .insertAfter($("#cpwoldpw").parent().parent());
        $("#cpwoldpw").parent().parent().addClass("error");
        err = true;
    }

    if(newpw == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Password must not be empty")
            .insertAfter($("#cpwnewpw").parent().parent());
        $("#cpwnewpw").parent().parent().addClass("error");
        err = true;
    }

    if(newpw != newpwc) {
        $("<div/>").addClass("alert alert-error")
            .text("Passwords do not match")
            .insertAfter($("#cpwconfirm").parent().parent());
        $("#cpwconfirm").parent().parent().addClass("error");
        err = true;
    }

    if(err) {
        return;
    }

    // Input valid, try changing password
    var data = {
        name: name,
        oldpw: oldpw,
        newpw: newpw
    };
    $.post(WEB_URL + "/api/account/passwordchange?callback=?", data,
        function (data) {
        if(data.success) {
            $("<div/>").addClass("alert alert-success")
                .text("Password changed.")
                .insertBefore($("#changepwpane form"));
            uname = name;
            session = data.session;
            onLogin();
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#changepwpane form"));
        }
    }, "jsonp");
});

$("#cebtn").click(function() {
    $("#changeemailpane").find(".alert-error").remove();
    $("#changeemailpane").find(".alert-success").remove();
    var name = $("#ceusername").val();
    var pw = $("#cepw").val();
    var email = $("#ceemail").val();
    if(pw == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Please provide a password")
            .insertAfter($("#cepw").parent().parent());
        $("#cepw").parent().parent().addClass("error");
        return;
    }

    if(!email.match(/^[\w_\.]+@[\w_\.]+[a-zA-Z]+$/)) {
        $("<div/>").addClass("alert alert-error")
            .text("Invalid email")
            .insertAfter($("#ceemail").parent().parent());
        $("#ceemail").parent().parent().addClass("error");
        return;
    }

    if(email.match(/.*@(localhost|127\.0\.0\.1)/i)) {
        $("<div/>").addClass("alert alert-error")
            .text("Nice try, but no.")
            .insertAfter($("#ceemail").parent().parent());
        $("#ceemail").parent().parent().addClass("error");
        return;
    }

    var data = {
        name: name,
        pw: pw,
        email: email
    };
    $.post(WEB_URL + "/api/account/email?callback=?", data,
        function (data) {
        if(data.success) {
            $("<div/>").addClass("alert alert-success")
                .text("Email updated")
                .insertBefore($("#changeemailpane form"));
            uname = name;
            session = data.session;
            onLogin();
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#changeemailpane form"));
        }
    }, "jsonp");

});

$("#rpbtn").click(function() {
    $("#rpbtn").text("Sending...");
    $("#pwresetpane").find(".alert-error").remove();
    $("#pwresetpane").find(".alert-success").remove();
    var name = $("#rpusername").val();
    var email = $("#rpemail").val();

    var data = {
        name: name,
        email: email
    };
    $.post(WEB_URL + "/api/account/passwordreset?callback=?", data,
        function (data) {
        $("#rpbtn").text("Send Reset");
        if(data.success) {
            $("<div/>").addClass("alert alert-success")
                .text("Password reset link issued.  Check your email.")
                .insertBefore($("#pwresetpane form"));
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#pwresetpane form"));
        }
    }, "jsonp");

});

$("#profilesave").click(function() {
    $("#profilepane").find(".alert-error").remove();
    $("#profilepane").find(".alert-success").remove();
    var img = $("#profileimg").val();
    var text = $("#profiletext").val();
    var data = {
        name: uname,
        session: session,
        profile_image: img,
        profile_text: text
    };

    $.post(WEB_URL+"/api/account/profile?callback=?", data,
        function (data) {
        if(data.success) {
            $("<div/>").addClass("alert alert-success")
                .text("Profile updated.")
                .insertBefore($("#profilepane form"));
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#profilepane form"));
        }
    }, "jsonp");
});

$("#login").click(function() {
    if(!loggedin) {
        makeTabCallback("#login", "#loginpane")();
    }
    else {
        uname = "";
        session = "";
        eraseCookie("cytube_uname");
        eraseCookie("cytube_session");
        $("#accountnav li")[0].innerHTML = "Not Logged In";
        $("#register").show();
        $("#login").text("Login");
        loggedin = false;
    }
});

function createCookie(name,value,days) {
    if (days) {
        var date = new Date();
        date.setTime(date.getTime()+(days*24*60*60*1000));
        var expires = "; expires="+date.toGMTString();
    }
    else var expires = "";
    document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(";");
    for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==" ") c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}

function eraseCookie(name) {
    createCookie(name,"",-1);
}
<commit_msg>Hack around cross-domain POSTing<commit_after>/*
The MIT License (MIT)
Copyright (c) 2013 Calvin Montgomery

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/*
    So, it turns out that $.post causes Firefox to use a GET request
    on cross-site requests.  What the hell?  I'd understand if they just
    made it error instead, but why give me chicken tenders if I ordered a
    cheeseburger and act like everything's peachy?
*/
function postJSON(url, data, callback) {
    $.ajax(url, {
        method: "POST",
        crossDomain: true,
        data: data,
        success: function (data) {
            try {
                data = data.substring(data.indexOf("{"));
                data = data.substring(0, data.lastIndexOf("}") + 1);
                data = JSON.parse(data);
                callback(data);
            } catch(e) {
                return;
            }
        },
        dataType: "text"
    });
}

var uname = readCookie("cytube_uname") || "";
var session = readCookie("cytube_session") || "";
var loggedin = false;

if(uname && session) {
    var data = {
        name: uname,
        session: session
    };
    postJSON(WEB_URL + "/api/login?callback=?", data, function (data) {
        if(data.success)
            onLogin();
    }, "jsonp");
}

function onLogin() {
    $("#cpwusername").val(uname);
    $("#ceusername").val(uname);
    $("#accountnav li")[0].innerHTML = "Logged in as " + uname;
    $("#register").hide();
    loggedin = true;
    $("#login").text("Logout");
    createCookie("cytube_uname", uname, 7);
    createCookie("cytube_session", session, 7);
}

function makeTabCallback(tabid, paneid) {
    return function() {
        $("#accountnav li").each(function() {
            $(this).removeClass("active");
        });

        $(tabid).parent().addClass("active");
        $(".span7").each(function() {
            $(this).css("display", "none");
        });
        $(paneid).css("display", "");
    };
}

$("#register").click(makeTabCallback("#register", "#registerpane"));
$("#pwchange").click(makeTabCallback("#pwchange", "#changepwpane"));
$("#pwreset").click(makeTabCallback("#pwreset", "#pwresetpane"));
$("#email").click(makeTabCallback("#email", "#changeemailpane"));
$("#profile").click(makeTabCallback("#profile", "#profilepane"));
$("#profile").click(function() {
    if(uname != "") {
        $.getJSON(WEB_URL+"/api/users/"+uname+"/profile?callback=?",
            function (data) {
            if(data.success) {
                $("#profiletext").val(data.profile_text);
                $("#profileimg").val(data.profile_image);
            }
            else {
                $("<div/>").addClass("alert alert-error")
                    .text("Failed to retrieve profile: " + data.error)
                    .insertBefore($("#profilepane form"));
            }
        });
    }
});
$("#channels").click(makeTabCallback("#channels", "#channelspane"));
$("#channels").click(function () {
    if(!loggedin) {
        var error = $("<div/>").addClass("alert alert-error")
            .text("You must be logged in to view this page")
            .insertBefore($("#channellist"));
        $("<button/>").addClass("close pull-right").click(function () {
            error.remove();
        }).html("×").prependTo(error);
        return;
    }

    var auth = "name=" + encodeURIComponent(uname) + "&session=" +
               encodeURIComponent(session);
    $.getJSON(WEB_URL+"/api/account/mychannels?"+auth+"&callback=?",
        function (data) {
        $("#channellist tbody").remove();
        data.channels.forEach(function (chan) {
            var tr = $("<tr/>").appendTo($("#channellist"));
            var td = $("<td/>").appendTo(tr);
            $("<a/>").attr("href", "./r/" + chan.name)
                .attr("target", "_blank")
                .text(chan.name)
                .appendTo(td);
        });
    });
});

$("#registerbtn").click(function() {
    $("#registerpane").find(".alert-error").remove();
    $("#registerpane").find(".error").removeClass("error");
    var name = $("#regusername").val();
    var pw = $("#regpw").val();
    var pwc = $("#regpwconfirm").val();

    var err = false;
    if(!name.match(/^[a-z0-9_]{1,20}$/i)) {
        $("<div/>").addClass("alert alert-error")
            .text("Usernames must be 1-20 characters long and contain only a-z, 0-9, and underscores")
            .insertAfter($("#regusername").parent().parent());
        err = true;
    }

    if(pw == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Password must not be blank")
            .insertAfter($("#regpw").parent().parent());
        $("#regpw").parent().parent().addClass("error");
        err = true;
    }

    if(pw != pwc) {
        $("<div/>").addClass("alert alert-error")
            .text("Passwords do not match")
            .insertAfter($("#regpwconfirm").parent().parent());
        $("#regpwconfirm").parent().parent().addClass("error");
        err = true;
    }

    if(err) {
        return;
    }

    // Input valid, try registering
    var data = {
        name: name,
        pw: pw
    };

    postJSON(WEB_URL + "/api/register?callback=?", data, function (data) {
        if(data.success) {
            uname = name;
            session = data.session;
            onLogin();
            $("<div/>").addClass("alert alert-success")
                .text("Registration successful")
                .insertBefore($("#registerpane form"));
            $("#regpw").val("");
            $("#regusername").val("");
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#registerpane form"));
        }
    }, "jsonp");
});

$("#loginbtn").click(function() {
    $("#loginpane").find(".alert-error").remove();
    $("#loginpane").find(".alert-success").remove();

    if($("#loginpw").val() == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Please provide a password")
            .insertAfter($("#loginpw").parent().parent());
        $("#loginpw").parent().parent().addClass("error");
        return;
    }
    uname = $("#loginusername").val();
    var data = {
        name: uname,
        pw: $("#loginpw").val()
    };

    postJSON(WEB_URL+"/api/login?callback=?", data, function(data) {
        if(data.success) {
            session = data.session;
            onLogin();
            $("<div/>").addClass("alert alert-success")
                .text("Login successful")
                .insertBefore($("#loginpane form"));
            $("#loginpw").val("");
            $("#loginusername").val("");
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#loginpane form"));
        }
    }, "jsonp");
});

$("#cpwbtn").click(function() {
    $("#changepwpane").find(".alert-error").remove();
    $("#changepwpane").find(".alert-success").remove();
    $("#changepwpane").find(".error").removeClass("error");
    var name = $("#cpwusername").val();
    var oldpw = $("#cpwoldpw").val();
    var newpw = $("#cpwnewpw").val();
    var newpwc = $("#cpwconfirm").val();

    var err = false;
    if(oldpw == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Password must not be empty")
            .insertAfter($("#cpwoldpw").parent().parent());
        $("#cpwoldpw").parent().parent().addClass("error");
        err = true;
    }

    if(newpw == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Password must not be empty")
            .insertAfter($("#cpwnewpw").parent().parent());
        $("#cpwnewpw").parent().parent().addClass("error");
        err = true;
    }

    if(newpw != newpwc) {
        $("<div/>").addClass("alert alert-error")
            .text("Passwords do not match")
            .insertAfter($("#cpwconfirm").parent().parent());
        $("#cpwconfirm").parent().parent().addClass("error");
        err = true;
    }

    if(err) {
        return;
    }

    // Input valid, try changing password
    var data = {
        name: name,
        oldpw: oldpw,
        newpw: newpw
    };
    postJSON(WEB_URL + "/api/account/passwordchange?callback=?", data,
        function (data) {
        if(data.success) {
            $("<div/>").addClass("alert alert-success")
                .text("Password changed.")
                .insertBefore($("#changepwpane form"));
            uname = name;
            session = data.session;
            onLogin();
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#changepwpane form"));
        }
    }, "jsonp");
});

$("#cebtn").click(function() {
    $("#changeemailpane").find(".alert-error").remove();
    $("#changeemailpane").find(".alert-success").remove();
    var name = $("#ceusername").val();
    var pw = $("#cepw").val();
    var email = $("#ceemail").val();
    if(pw == "") {
        $("<div/>").addClass("alert alert-error")
            .text("Please provide a password")
            .insertAfter($("#cepw").parent().parent());
        $("#cepw").parent().parent().addClass("error");
        return;
    }

    if(!email.match(/^[\w_\.]+@[\w_\.]+[a-zA-Z]+$/)) {
        $("<div/>").addClass("alert alert-error")
            .text("Invalid email")
            .insertAfter($("#ceemail").parent().parent());
        $("#ceemail").parent().parent().addClass("error");
        return;
    }

    if(email.match(/.*@(localhost|127\.0\.0\.1)/i)) {
        $("<div/>").addClass("alert alert-error")
            .text("Nice try, but no.")
            .insertAfter($("#ceemail").parent().parent());
        $("#ceemail").parent().parent().addClass("error");
        return;
    }

    var data = {
        name: name,
        pw: pw,
        email: email
    };
    postJSON(WEB_URL + "/api/account/email?callback=?", data,
        function (data) {
        if(data.success) {
            $("<div/>").addClass("alert alert-success")
                .text("Email updated")
                .insertBefore($("#changeemailpane form"));
            uname = name;
            session = data.session;
            onLogin();
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#changeemailpane form"));
        }
    }, "jsonp");

});

$("#rpbtn").click(function() {
    $("#rpbtn").text("Sending...");
    $("#pwresetpane").find(".alert-error").remove();
    $("#pwresetpane").find(".alert-success").remove();
    var name = $("#rpusername").val();
    var email = $("#rpemail").val();

    var data = {
        name: name,
        email: email
    };
    postJSON(WEB_URL + "/api/account/passwordreset?callback=?", data,
        function (data) {
        $("#rpbtn").text("Send Reset");
        if(data.success) {
            $("<div/>").addClass("alert alert-success")
                .text("Password reset link issued.  Check your email.")
                .insertBefore($("#pwresetpane form"));
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#pwresetpane form"));
        }
    }, "jsonp");

});

$("#profilesave").click(function() {
    $("#profilepane").find(".alert-error").remove();
    $("#profilepane").find(".alert-success").remove();
    var img = $("#profileimg").val();
    var text = $("#profiletext").val();
    var data = {
        name: uname,
        session: session,
        profile_image: img,
        profile_text: text
    };

    postJSON(WEB_URL+"/api/account/profile?callback=?", data,
        function (data) {
        if(data.success) {
            $("<div/>").addClass("alert alert-success")
                .text("Profile updated.")
                .insertBefore($("#profilepane form"));
        }
        else {
            $("<div/>").addClass("alert alert-error")
                .text(data.error)
                .insertBefore($("#profilepane form"));
        }
    }, "jsonp");
});

$("#login").click(function() {
    if(!loggedin) {
        makeTabCallback("#login", "#loginpane")();
    }
    else {
        uname = "";
        session = "";
        eraseCookie("cytube_uname");
        eraseCookie("cytube_session");
        $("#accountnav li")[0].innerHTML = "Not Logged In";
        $("#register").show();
        $("#login").text("Login");
        loggedin = false;
    }
});

function createCookie(name,value,days) {
    if (days) {
        var date = new Date();
        date.setTime(date.getTime()+(days*24*60*60*1000));
        var expires = "; expires="+date.toGMTString();
    }
    else var expires = "";
    document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(";");
    for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==" ") c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}

function eraseCookie(name) {
    createCookie(name,"",-1);
}
<|endoftext|>


<commit_before>/*@flow*/
/*:: const define = () => {}; */
define([
    '/api/config'
], function (Config) { /*::});module.exports = (function() {
    const Config = (undefined:any);
    */

    var module = { exports: {} };
    var key = Config.requireConf.urlArgs;
    var localStorage = {};
    try {
        localStorage = window.localStorage || {};
        if (localStorage['LESS_CACHE'] !== key) {
            Object.keys(localStorage).forEach(function (k) {
                if (k.indexOf('LESS_CACHE|') !== 0) { return; }
                delete localStorage[k];
            });
            localStorage['LESS_CACHE'] = key;
        }
    } catch (e) {
        console.error(e);
        localStorage = {};
    }

    var cacheGet = function (k, cb) {
        if (window.cryptpadCache) { return void window.cryptpadCache.get(k, cb); }
        setTimeout(function () { cb(localStorage['LESS_CACHE|' + key + '|' + k]); });
    };
    var cachePut = function (k, v, cb) {
        if (window.cryptpadCache) { return void window.cryptpadCache.put(k, v, cb); }
        cb = cb || function () { };
        setTimeout(function () { localStorage['LESS_CACHE|' + key + '|' + k] = v; cb(); });
    };

    var fixURL = function (url, parent) {
        // data: blob: etc
        if (/^[a-zA-Z0-9]*:/.test(url)) { return url; }
        var ua = url.split('#');
        var mark = (ua[0].indexOf('?') !== -1) ? '&' : '?';
        ua[0] = ua[0] + mark + key;
        if (ua[0].indexOf(':') === -1 && ua[0].indexOf('/') && parent) {
            ua[0] = parent.replace(/\/[^\/]*$/, '/') + ua[0];
        }
        var out = ua.join('#');
        //console.log(url + "  -->  " + out);
        return out;
    };

    var inject = function (cssText, url) {
        var curStyle = document.createElement('style');
        curStyle.setAttribute('data-original-src', url);
        curStyle.type = 'text/css';
        curStyle.appendChild(document.createTextNode(cssText));
        if (!document.head) { throw new Error(); }
        document.head.appendChild(curStyle);
    };

    var fixAllURLs = function (source, parent) {
        var urlRegEx = /@import\s*("([^"]*)"|'([^']*)')|url\s*\(\s*(\s*"([^"]*)"|'([^']*)'|[^\)]*\s*)\s*\)/ig;
        var result, url;

        while (!!(result = urlRegEx.exec(source))) {
            url = result[3] || result[2] || result[5] || result[6] || result[4];
            var newUrl = fixURL(url, parent);
            var quoteLen = result[5] || result[6] ? 1 : 0;
            source = source.substr(0, urlRegEx.lastIndex - url.length - quoteLen - 1)
               + newUrl + source.substr(urlRegEx.lastIndex - quoteLen - 1);
            urlRegEx.lastIndex = urlRegEx.lastIndex + (newUrl.length - url.length);
        }

        return source;
    };

    var loadCSS = function (url, cb) {
        var xhr = new window.XMLHttpRequest();
        xhr.open("GET", fixURL(url), true);
        xhr.responseType = 'text';
        xhr.onload = function () {
            if (/^4/.test('' + this.status)) { return cb("error loading " + url); }
            cb(undefined, xhr.response);
        };
        xhr.send(null);
    };

    var lessEngine;
    var getLessEngine = function (cb) {
        if (lessEngine) {
            cb(lessEngine);
        } else {
            require(['/bower_components/less/dist/less.min.js'], function (Less) {
                lessEngine = Less;
                var doXHR = lessEngine.FileManager.prototype.doXHR;
                lessEngine.FileManager.prototype.doXHR = function (url, type, callback, errback) {
                    url = fixURL(url);
                    //console.log("xhr: " + url);
                    return doXHR(url, type, callback, errback);
                };
                cb(lessEngine);
            });
        }
    };

    var loadLess = function (url, cb) {
        getLessEngine(function (less) {
            less.render('@import (multiple) "' + url + '";', {}, function(err, css) {
                if (err) { return void cb(err); }
                cb(undefined, css.css);
            }, window.less);
        });
    };

    module.exports.load = function (url /*:string*/, cb /*:()=>void*/) {
        cacheGet(url, function (css) {
            if (css) {
                inject(css, url);
                return void cb();
            }
            console.log('CACHE MISS ' + url);
            ((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) {
                if (err) { console.error(err); }
                var output = fixAllURLs(css, url);
                cachePut(url, output);
                inject(output, url);
                cb();
            });
        });
    };

    return module.exports;
})/*::()*/;
<commit_msg>Improve the LessLoader check to hopefully stop the message saying cookies were blocked.<commit_after>/*@flow*/
/*:: const define = () => {}; */
define([
    '/api/config'
], function (Config) { /*::});module.exports = (function() {
    const Config = (undefined:any);
    */

    var module = { exports: {} };
    var key = Config.requireConf.urlArgs;
    var localStorage = {};
    if (!window.cryptpadCache) {
        try {
            localStorage = window.localStorage || {};
            if (localStorage['LESS_CACHE'] !== key) {
                Object.keys(localStorage).forEach(function (k) {
                    if (k.indexOf('LESS_CACHE|') !== 0) { return; }
                    delete localStorage[k];
                });
                localStorage['LESS_CACHE'] = key;
            }
        } catch (e) {
            console.error(e);
            localStorage = {};
        }
    }

    var cacheGet = function (k, cb) {
        if (window.cryptpadCache) { return void window.cryptpadCache.get(k, cb); }
        setTimeout(function () { cb(localStorage['LESS_CACHE|' + key + '|' + k]); });
    };
    var cachePut = function (k, v, cb) {
        if (window.cryptpadCache) { return void window.cryptpadCache.put(k, v, cb); }
        cb = cb || function () { };
        setTimeout(function () { localStorage['LESS_CACHE|' + key + '|' + k] = v; cb(); });
    };

    var fixURL = function (url, parent) {
        // data: blob: etc
        if (/^[a-zA-Z0-9]*:/.test(url)) { return url; }
        var ua = url.split('#');
        var mark = (ua[0].indexOf('?') !== -1) ? '&' : '?';
        ua[0] = ua[0] + mark + key;
        if (ua[0].indexOf(':') === -1 && ua[0].indexOf('/') && parent) {
            ua[0] = parent.replace(/\/[^\/]*$/, '/') + ua[0];
        }
        var out = ua.join('#');
        //console.log(url + "  -->  " + out);
        return out;
    };

    var inject = function (cssText, url) {
        var curStyle = document.createElement('style');
        curStyle.setAttribute('data-original-src', url);
        curStyle.type = 'text/css';
        curStyle.appendChild(document.createTextNode(cssText));
        if (!document.head) { throw new Error(); }
        document.head.appendChild(curStyle);
    };

    var fixAllURLs = function (source, parent) {
        var urlRegEx = /@import\s*("([^"]*)"|'([^']*)')|url\s*\(\s*(\s*"([^"]*)"|'([^']*)'|[^\)]*\s*)\s*\)/ig;
        var result, url;

        while (!!(result = urlRegEx.exec(source))) {
            url = result[3] || result[2] || result[5] || result[6] || result[4];
            var newUrl = fixURL(url, parent);
            var quoteLen = result[5] || result[6] ? 1 : 0;
            source = source.substr(0, urlRegEx.lastIndex - url.length - quoteLen - 1)
               + newUrl + source.substr(urlRegEx.lastIndex - quoteLen - 1);
            urlRegEx.lastIndex = urlRegEx.lastIndex + (newUrl.length - url.length);
        }

        return source;
    };

    var loadCSS = function (url, cb) {
        var xhr = new window.XMLHttpRequest();
        xhr.open("GET", fixURL(url), true);
        xhr.responseType = 'text';
        xhr.onload = function () {
            if (/^4/.test('' + this.status)) { return cb("error loading " + url); }
            cb(undefined, xhr.response);
        };
        xhr.send(null);
    };

    var lessEngine;
    var getLessEngine = function (cb) {
        if (lessEngine) {
            cb(lessEngine);
        } else {
            require(['/bower_components/less/dist/less.min.js'], function (Less) {
                lessEngine = Less;
                var doXHR = lessEngine.FileManager.prototype.doXHR;
                lessEngine.FileManager.prototype.doXHR = function (url, type, callback, errback) {
                    url = fixURL(url);
                    //console.log("xhr: " + url);
                    return doXHR(url, type, callback, errback);
                };
                cb(lessEngine);
            });
        }
    };

    var loadLess = function (url, cb) {
        getLessEngine(function (less) {
            less.render('@import (multiple) "' + url + '";', {}, function(err, css) {
                if (err) { return void cb(err); }
                cb(undefined, css.css);
            }, window.less);
        });
    };

    module.exports.load = function (url /*:string*/, cb /*:()=>void*/) {
        cacheGet(url, function (css) {
            if (css) {
                inject(css, url);
                return void cb();
            }
            console.log('CACHE MISS ' + url);
            ((/\.less([\?\#].*)?$/.test(url)) ? loadLess : loadCSS)(url, function (err, css) {
                if (err) { console.error(err); }
                var output = fixAllURLs(css, url);
                cachePut(url, output);
                inject(output, url);
                cb();
            });
        });
    };

    return module.exports;
})/*::()*/;
<|endoftext|>


<commit_before>// @flow

import React, { Component } from 'react';
import type { Node, ElementProps } from 'react';

export type TooltipState = {
    isHidden: boolean,
};

export type LabelProps = ElementProps<'*'> & TooltipState;
export type OverlayProps = ElementProps<'*'> & TooltipState;

export type TooltipProps = ElementProps<'div'> & {
    label: LabelProps => Node,
    overlay: OverlayProps => Node,
};

let counter = 0;

class Tooltip extends Component<TooltipProps, TooltipState> {
    constructor(props: TooltipProps) {
        super(props);
        this.identifier = `react-accessible-tooltip-${counter}`;
        counter += 1;
    }

    state = {
        isHidden: true,
    };

    onBlur({ relatedTarget, currentTarget }: SyntheticFocusEvent<HTMLElement>) {
        // The idea of this logic is that we should only close the tooltip if focus has shifted from the tooltip AND all of its descendents.
        if (!(relatedTarget && relatedTarget instanceof HTMLElement)) {
            this.hide();
        } else if (!currentTarget.contains(relatedTarget)) {
            this.hide();
        }
    }

    hide() {
        this.setState({ isHidden: true });
    }

    show() {
        this.setState({ isHidden: false });
    }

    toggle() {
        this.setState({ isHidden: !this.state.isHidden });
    }

    node: ?HTMLDivElement;
    identifier: string;

    render() {
        const { label: Label, overlay: Overlay, ...rest } = this.props;
        return (
            <div
                {...rest}
                onBlur={e => this.onBlur(e)}
                ref={node => {
                    this.node = node;
                }}
            >
                <Label
                    {...this.state}
                    labelAttributes={{
                        role: 'tooltip',
                        tabIndex: '0',
                        'aria-describedby': `#${this.identifier}`,
                        onFocus: () => this.show(),
                    }}
                    requestHide={() => this.hide()}
                    requestShow={() => this.show()}
                    requestToggle={() => this.toggle()}
                />
                <Overlay
                    {...this.state}
                    overlayAttributes={{
                        tabIndex: '-1',
                        id: this.identifier,
                        'aria-hidden': this.state.isHidden,
                    }}
                    requestHide={() => this.hide()}
                    requestShow={() => this.show()}
                    requestToggle={() => this.toggle()}
                />
            </div>
        );
    }
}

export default Tooltip;
<commit_msg>Update typing<commit_after>// @flow

import React, { Component } from 'react';
import type { ElementProps, ComponentType } from 'react';

export type TooltipState = {
    isHidden: boolean,
};

export type LabelProps = TooltipState & {
    labelAttributes: {
        role: 'tooltip',
        tabIndex: '0',
        'aria-describedby': string,
        onFocus: () => {},
    },
    requestHide: () => {},
    requestShow: () => {},
    requestToggle: () => {},
};

export type OverlayProps = TooltipState & {
    overlayAttributes: {
        tabIndex: '-1',
        'aria-describedby': string,
        onFocus: () => {},
    },
    requestHide: () => {},
    requestShow: () => {},
    requestToggle: () => {},
};

export type TooltipProps = ElementProps<'div'> & {
    label: ComponentType<LabelProps>,
    overlay: ComponentType<OverlayProps>,
};

let counter = 0;

class Tooltip extends Component<TooltipProps, TooltipState> {
    constructor(props: TooltipProps) {
        super(props);
        this.identifier = `react-accessible-tooltip-${counter}`;
        counter += 1;
    }

    state = {
        isHidden: true,
    };

    onBlur({ relatedTarget, currentTarget }: SyntheticFocusEvent<HTMLElement>) {
        // The idea of this logic is that we should only close the tooltip if focus has shifted from the tooltip AND all of its descendents.
        if (!(relatedTarget && relatedTarget instanceof HTMLElement)) {
            this.hide();
        } else if (!currentTarget.contains(relatedTarget)) {
            this.hide();
        }
    }

    hide() {
        this.setState({ isHidden: true });
    }

    show() {
        this.setState({ isHidden: false });
    }

    toggle() {
        this.setState({ isHidden: !this.state.isHidden });
    }

    node: ?HTMLDivElement;
    identifier: string;

    render() {
        const { label: Label, overlay: Overlay, ...rest } = this.props;
        return (
            <div
                {...rest}
                onBlur={e => this.onBlur(e)}
                ref={node => {
                    this.node = node;
                }}
            >
                <Label
                    {...this.state}
                    labelAttributes={{
                        role: 'tooltip',
                        tabIndex: '0',
                        'aria-describedby': `#${this.identifier}`,
                        onFocus: () => this.show(),
                    }}
                    requestHide={() => this.hide()}
                    requestShow={() => this.show()}
                    requestToggle={() => this.toggle()}
                />
                <Overlay
                    {...this.state}
                    overlayAttributes={{
                        tabIndex: '-1',
                        id: this.identifier,
                        'aria-hidden': this.state.isHidden,
                    }}
                    requestHide={() => this.hide()}
                    requestShow={() => this.show()}
                    requestToggle={() => this.toggle()}
                />
            </div>
        );
    }
}

export default Tooltip;
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

import React from 'react';
import globalize from 'random-global';
import createPlayer from './lib/createPlayer';

/**
 * Create a new `YouTube` component.
 */

class YouTube extends React.Component {

  /**
   * @param {Object} props
   */

  constructor(props) {
    super(props);

    this._internalPlayer = null;
    this._playerReadyHandle = null;
    this._stateChangeHandle = null;

    this._handlePlayerReady = this._handlePlayerReady.bind(this);
    this._handlePlayerStateChange = this._handlePlayerStateChange.bind(this);
  }

  /**
   * @param {Object} nextProps
   * @returns {Boolean}
   */

  shouldComponentUpdate(nextProps) {
    return nextProps.url !== this.props.url;
  }

  componentDidMount() {
    this._createPlayer();
  }

  componentDidUpdate() {
    this._createPlayer();
  }

  componentWillUnmount() {
    this._destroyPlayer();
  }

  /**
   * @returns Object
   */

  render() {
    return (

    );
  }

  /**
   * Create a new `internalPlayer`.
   *
   * This is called on every render, which is only triggered after
   * `props.url` has changed. Setting or changing any other props
   * will *not* cause a new player to be loaded.
   */

  _createPlayer() {
    this._destroyPlayer();

    createPlayer(this.props, (player) => {
      this._setupPlayer(player);
    }.bind(this));
  }

  /**
   * Destroy the currently embedded player/iframe and remove any event listeners
   * bound to it.
   */

  _destroyPlayer() {
    if (this._internalPlayer) {
      this._unbindEvents();
      this._destroyGlobalEventHandlers();
      this._internalPlayer.destroy();
    }
  }

  /**
   * Integrate a newly created `player` with the rest of the component.
   *
   * @param {Object} player
   */

  _setupPlayer(player) {
    this._internalPlayer = player;
    this._globalizeEventHandlers();
    this._bindEvents();
  }

  /**
   * When the player is all loaded up, load the url
   * passed via `props.url` and notify anybody listening.
   *
   * Is exposed in the global namespace under a random
   * name, see `_globalizeEventHandlers`
   */

  _handlePlayerReady() {
    this.props.onReady();
  }

  /**
   * Respond to player events
   *
   * Event definitions at https://developers.google.com/youtube/js_api_reference#Events
   *
   * Is exposed in the global namespace under a random
   * name, see `_globalizeEventHandlers`
   *
   * @param {Object} event
   */

  _handlePlayerStateChange(event) {
    switch(event.data) {

      case window.YT.PlayerState.ENDED:
        this.props.onEnd();
        break;

      case window.YT.PlayerState.PLAYING:
        this.props.onPlay();
        break;

      case window.YT.PlayerState.PAUSED:
        this.props.onPause();
        break;

      default:
        return;
    }
  }

  /**
   * Expose our player event handlers onto the global namespace
   * under random handles, then store those handles into `state`.
   *
   * The YouTube API requires a `player`s event handlers to be
   * exposed in the global namespace, so this is unfortunate but necessary.
   */

  _globalizeEventHandlers() {
    this._playerReadyHandle = globalize(this._handlePlayerReady);
    this._stateChangeHandle = globalize(this._handlePlayerStateChange);
  }

  /**
   * Clean up the ickyness of globalness.
   */

  _destroyGlobalEventHandlers() {
    delete window[this._playerReadyHandle];
    delete window[this._stateChangeHandle];
  }

  /**
   * Listen for events coming from `player`.
   */

  _bindEvents() {
    this._internalPlayer.addEventListener('onReady', this._playerReadyHandle);
    this._internalPlayer.addEventListener('onStateChange', this._stateChangeHandle);
  }

  /**
   * Remove all event bindings.
   */

  _unbindEvents() {
    this._internalPlayer.removeEventListener('onReady', this._playerReadyHandle);
    this._internalPlayer.removeEventListener('onStateChange', this._stateChangeHandle);
  }
}

YouTube.propTypes = {
  // url to play. It's kept in sync, changing it will
  // cause the player to refresh and play the new url.
  url: React.PropTypes.string.isRequired,

  // custom ID for player element
  id: React.PropTypes.string,

  // Options passed to a new `YT.Player` instance
  // https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player
  //
  // NOTE: Do not include event listeners in here, they will be ignored.
  //
  opts: React.PropTypes.object,

  // event subscriptions
  onReady: React.PropTypes.func,
  onPlay: React.PropTypes.func,
  onPause: React.PropTypes.func,
  onEnd: React.PropTypes.func
};

YouTube.defaultProps = {
  id: 'react-yt-player',
  opts: {},
  onReady: () => {},
  onPlay: () => {},
  onPause: () => {},
  onEnd: () => {}
};

/**
 * Expose `YouTube`
 */

export default YouTube;
<commit_msg>use static getters for `defaultProps` & `propTypes`<commit_after>/**
 * Module dependencies
 */

import React from 'react';
import globalize from 'random-global';
import createPlayer from './lib/createPlayer';

/**
 * Create a new `YouTube` component.
 */

class YouTube extends React.Component {

  /**
   * @returns {Object}
   */

  static get defaultProps() {
    return {
      id: 'react-yt-player',
      opts: {},
      onReady: () => {},
      onPlay: () => {},
      onPause: () => {},
      onEnd: () => {}
    };
  }

  /**
   * @returns {Object}
   */

  static get propTypes() {
    return {
      // url to play. It's kept in sync, changing it will
      // cause the player to refresh and play the new url.
      url: React.PropTypes.string.isRequired,

      // custom ID for player element
      id: React.PropTypes.string,

      // Options passed to a new `YT.Player` instance
      // https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player
      //
      // NOTE: Do not include event listeners in here, they will be ignored.
      //
      opts: React.PropTypes.object,

      // event subscriptions
      onReady: React.PropTypes.func,
      onPlay: React.PropTypes.func,
      onPause: React.PropTypes.func,
      onEnd: React.PropTypes.func
    };
  }

  /**
   * @param {Object} props
   */

  constructor(props) {
    super(props);

    this._internalPlayer = null;
    this._playerReadyHandle = null;
    this._stateChangeHandle = null;

    this._handlePlayerReady = this._handlePlayerReady.bind(this);
    this._handlePlayerStateChange = this._handlePlayerStateChange.bind(this);
  }

  /**
   * @param {Object} nextProps
   * @returns {Boolean}
   */

  shouldComponentUpdate(nextProps) {
    return nextProps.url !== this.props.url;
  }

  componentDidMount() {
    this._createPlayer();
  }

  componentDidUpdate() {
    this._createPlayer();
  }

  componentWillUnmount() {
    this._destroyPlayer();
  }

  /**
   * @returns Object
   */

  render() {
    return (

    );
  }

  /**
   * Create a new `internalPlayer`.
   *
   * This is called on every render, which is only triggered after
   * `props.url` has changed. Setting or changing any other props
   * will *not* cause a new player to be loaded.
   */

  _createPlayer() {
    this._destroyPlayer();

    createPlayer(this.props, (player) => {
      this._setupPlayer(player);
    }.bind(this));
  }

  /**
   * Destroy the currently embedded player/iframe and remove any event listeners
   * bound to it.
   */

  _destroyPlayer() {
    if (this._internalPlayer) {
      this._unbindEvents();
      this._destroyGlobalEventHandlers();
      this._internalPlayer.destroy();
    }
  }

  /**
   * Integrate a newly created `player` with the rest of the component.
   *
   * @param {Object} player
   */

  _setupPlayer(player) {
    this._internalPlayer = player;
    this._globalizeEventHandlers();
    this._bindEvents();
  }

  /**
   * When the player is all loaded up, load the url
   * passed via `props.url` and notify anybody listening.
   *
   * Is exposed in the global namespace under a random
   * name, see `_globalizeEventHandlers`
   */

  _handlePlayerReady() {
    this.props.onReady();
  }

  /**
   * Respond to player events
   *
   * Event definitions at https://developers.google.com/youtube/js_api_reference#Events
   *
   * Is exposed in the global namespace under a random
   * name, see `_globalizeEventHandlers`
   *
   * @param {Object} event
   */

  _handlePlayerStateChange(event) {
    switch(event.data) {

      case window.YT.PlayerState.ENDED:
        this.props.onEnd();
        break;

      case window.YT.PlayerState.PLAYING:
        this.props.onPlay();
        break;

      case window.YT.PlayerState.PAUSED:
        this.props.onPause();
        break;

      default:
        return;
    }
  }

  /**
   * Expose our player event handlers onto the global namespace
   * under random handles, then store those handles into `state`.
   *
   * The YouTube API requires a `player`s event handlers to be
   * exposed in the global namespace, so this is unfortunate but necessary.
   */

  _globalizeEventHandlers() {
    this._playerReadyHandle = globalize(this._handlePlayerReady);
    this._stateChangeHandle = globalize(this._handlePlayerStateChange);
  }

  /**
   * Clean up the ickyness of globalness.
   */

  _destroyGlobalEventHandlers() {
    delete window[this._playerReadyHandle];
    delete window[this._stateChangeHandle];
  }

  /**
   * Listen for events coming from `player`.
   */

  _bindEvents() {
    this._internalPlayer.addEventListener('onReady', this._playerReadyHandle);
    this._internalPlayer.addEventListener('onStateChange', this._stateChangeHandle);
  }

  /**
   * Remove all event bindings.
   */

  _unbindEvents() {
    this._internalPlayer.removeEventListener('onReady', this._playerReadyHandle);
    this._internalPlayer.removeEventListener('onStateChange', this._stateChangeHandle);
  }
}

/**
 * Expose `YouTube`
 */

export default YouTube;
<|endoftext|>


<commit_before>Bones.utils.until = function(url, callback) {
    $.ajax({
        url: url,
        success: callback,
        error: function() { setTimeout(function() {
            Bones.utils.until(url, callback);
        }, 500); }
    });
};

Bones.utils.serial = function (steps, callback) {
    (_(steps).reduceRight(_.wrap, callback))();
};

Bones.utils.form = function(form, model, options) {
    var parseOptions = function (o) {
        return _(o.match(/([\d\w-]*)\=(\"[^\"]*\"|[^\s]*)/g)).reduce(function(memo,pair) {
            pair = pair.replace(/"|'/g, '').split('=');
            memo[pair[0]] = pair[1];
            return memo;
        }, {});
    };
    var attr = _($('input[name],textarea[name],select[name],.slider',form)).reduce(function(memo, el) {
        el = $(el);
        if (el.hasClass('slider')) return model.deepSet(
            el.data('key'),
            el.hasClass('range') ? el.slider('values') : el.slider('value'),
            { memo:memo });
        if (el.attr('type') === 'checkbox') return model.deepSet(
            el.attr('name'),
            el.is(':checked'),
            { memo:memo });
        return model.deepSet(
            el.attr('name'),
            el.hasClass('parsable') ? parseOptions(el.val()) : el.val(),
            { memo:memo });
    }, {});
    return attr;
};

Bones.utils.sliders = function(el, model, options) {
    options = options || {};
    var up = function(ev, ui) {
        function num(num) {
            num = num || 0;
            if (num >= 1e6) {
                return (num / 1e6).toFixed(1) + 'm';
            } else if (num >= 1e3) {
                return (num / 1e3).toFixed(1) + 'k';
            } else if (num >= 100) {
                return num.toFixed(0);
            } else {
                return num;
            }
        };
        if ($(ev.target).hasClass('range')) {
            ui.values = ui.values || $(ev.target).slider('values');
            $('.ui-slider-handle:first', ev.target).text(num(ui.values[0]));
            $('.ui-slider-handle:last', ev.target).text(num(ui.values[1]));
        } else {
            ui.value = ui.value || $(ev.target).slider('value');
            $('.ui-slider-handle', ev.target).text(num(ui.value));
        }
    };
    var set = function(ev, ui) {
        up(ev, ui);
        var key = $(ev.target).data('key');
        var val = $(ev.target).hasClass('range')
            ? $(ev.target).slider('values')
            : $(ev.target).slider('value');
        model.deepSet(key, val);
    };
    $(el).each(function() {
        $(this).slider(_({
            min:$(this).data('min') || 0,
            max:$(this).data('max') || 0,
            step:$(this).data('step') || 1,
            values: $(this).hasClass('range')
                ? model.deepGet($(this).data('key')) || [
                    $(this).data('min') || 0,
                    $(this).data('max') || 0 ]
                : undefined,
            value: !$(this).hasClass('range')
                ? model.deepGet($(this).data('key')) || 0
                : undefined,
            range: $(this).hasClass('range') ? true : 'min',
            change: set,
            create: up,
            slide: up
        }).extend(options));
    });
};

view = Backbone.View.extend();

view.prototype.events = {
    'click .bleed a': 'unload',
    'click #popup a[href=#close], #popup input.cancel': 'popupClose',
    'click a.popup': 'popupOpen',
    'click #drawer a[href=#close]': 'drawerClose',
    'click a.drawer': 'drawerOpen',
    'click .button.dropdown': 'dropdown',
    'click .toggler a': 'toggler',
    'click a.restart': 'restart',
    'keydown': 'keydown'
};

view.prototype.initialize = function() {
    _(this).bindAll(
        'unload',
        'popupOpen',
        'popupClose',
        'drawerOpen',
        'drawerClose',
        'toggler',
        'keydown',
        'dropdown'
    );
};

view.prototype.unload = function() {
    return !window.onbeforeunload || window.onbeforeunload() !== false;
};

view.prototype.popupOpen = function(ev) {
    var target = $(ev.currentTarget);
    var title = target.attr('title') || target.text();

    $(this.el).addClass('overlay');
    this.$('#popup').addClass('active');
    this.$('#popup > .title').text(title);
    return false;
};

view.prototype.popupClose = function(ev) {
    $(this.el).removeClass('overlay');
    this.$('#popup')
        .removeClass('active')
        .html(templates.Pane());
    return false;
};

view.prototype.drawerOpen = function(ev) {
    var target = $(ev.currentTarget);

    // Close drawers when the target is active.
    if (target.is('.active')) return this.drawerClose();

    var title = target.text() || target.attr('title');
    this.$('a.drawer.active').removeClass('active');
    target.addClass('active');
    this.$('#drawer')[target.hasClass('mini') ? 'addClass' : 'removeClass']('mini');
    this.$('#drawer').addClass('active')
    this.$('#drawer > .title').text(title);
    return false;
};

view.prototype.drawerClose = function(ev) {
    this.$('a.drawer.active').removeClass('active');
    this.$('#drawer')
        .removeClass('active')
        .html(templates.Pane());
    return false;
};

view.prototype.toggler = function(ev) {
    var link = $(ev.currentTarget);
    var parent = link.parents('.toggler');
    var target = link.attr('href').split('#').pop();
    if (link.hasClass('disabled')) return false;

    $('a', parent).removeClass('active');
    this.$('.' + target).siblings('.active').removeClass('active');

    link.addClass('active');
    this.$('.' + target).addClass('active');
    return false;
};

view.prototype.keydown = function(ev) {
    // Escape
    if (ev.which == 27 && (!ev.ctrlKey && !ev.metaKey && !ev.altKey)) {
        // @TODO for some reason a function bound from the Modal view
        // to a keydown event is not fired. Probably related to
        // event delegation/bubbling?
        if (this.$('#modal.active').size()) {
            if (!$('#popup.active').size()) $('body').removeClass('overlay');
            this.$('#modal.active').removeClass('active');
        } else if (this.$('#popup.active').size()) {
            this.popupClose();
        } else if (this.$('#drawer.active').size()) {
            this.drawerClose();
        }
        return false;
    }
    // Ctrl + S
    // The keydown event is only passed to a specific view if an form
    // element in that view has focus. When no form element has focus
    // the top most view takes precedence and the event is *not* bubbled
    // down (http://api.jquery.com/keydown). This code conceptually belongs
    // in `Project.bones` but is handled here as the App is the one to
    // receive the event.
    if (ev.which == 83 &&
        ((ev.ctrlKey || ev.metaKey) && !ev.altKey)) {
        this.$('.actions a[href=#save]').click();
        return false;
    }
};

view.prototype.dropdown = function(ev) {
    var target = $(ev.currentTarget);
    if (!target.is('.dropdown')) target = target.parents('.button.dropdown');
    var app = this.el;
    if (!target.hasClass('active')) {
        target.addClass('active');
        $(app).bind('click', collapse);
        target.children('.menu').bind('click', collapse);
    }
    function collapse(ev) {
        target.removeClass('active');
        $(app).unbind('click', collapse);
        target.children('.menu').bind('click', collapse);
    }
};

view.prototype.restart = function(ev) {
    var target = $(ev.currentTarget);
    var parent = target.parents('.restartable');
    if (parent.hasClass('restarting')) return false;

    target.addClass('active');
    parent.addClass('restarting');
    $.ajax({
        url: 'http://'+window.abilities.tileUrl+'/restart',
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({'bones.token':Backbone.csrf('/restart')}),
        dataType: 'json',
        processData: false,
        success: function() {
            Bones.utils.until('http://'+window.abilities.tileUrl+'/status', function() {
                target.removeClass('active');
                parent
                    .removeClass('loading')
                    .removeClass('restarting')
                    .removeClass('restartable');
                if (parent.is('#drawer')) $('a[href=#close]',parent).click();
            });
        },
        error: function(err) {
            target.removeClass('active');
            parent
                .removeClass('loading')
                .removeClass('restarting')
                .removeClass('restartable');
            if (parent.is('#drawer')) $('a[href=#close]',parent).click();
            new views.Modal(err);
        }
    });
    return false;
};

<commit_msg>[ui] Fix "*" link not working in carto reference (see mapbox/tilemill#1693)<commit_after>Bones.utils.until = function(url, callback) {
    $.ajax({
        url: url,
        success: callback,
        error: function() { setTimeout(function() {
            Bones.utils.until(url, callback);
        }, 500); }
    });
};

Bones.utils.serial = function (steps, callback) {
    (_(steps).reduceRight(_.wrap, callback))();
};

Bones.utils.form = function(form, model, options) {
    var parseOptions = function (o) {
        return _(o.match(/([\d\w-]*)\=(\"[^\"]*\"|[^\s]*)/g)).reduce(function(memo,pair) {
            pair = pair.replace(/"|'/g, '').split('=');
            memo[pair[0]] = pair[1];
            return memo;
        }, {});
    };
    var attr = _($('input[name],textarea[name],select[name],.slider',form)).reduce(function(memo, el) {
        el = $(el);
        if (el.hasClass('slider')) return model.deepSet(
            el.data('key'),
            el.hasClass('range') ? el.slider('values') : el.slider('value'),
            { memo:memo });
        if (el.attr('type') === 'checkbox') return model.deepSet(
            el.attr('name'),
            el.is(':checked'),
            { memo:memo });
        return model.deepSet(
            el.attr('name'),
            el.hasClass('parsable') ? parseOptions(el.val()) : el.val(),
            { memo:memo });
    }, {});
    return attr;
};

Bones.utils.sliders = function(el, model, options) {
    options = options || {};
    var up = function(ev, ui) {
        function num(num) {
            num = num || 0;
            if (num >= 1e6) {
                return (num / 1e6).toFixed(1) + 'm';
            } else if (num >= 1e3) {
                return (num / 1e3).toFixed(1) + 'k';
            } else if (num >= 100) {
                return num.toFixed(0);
            } else {
                return num;
            }
        };
        if ($(ev.target).hasClass('range')) {
            ui.values = ui.values || $(ev.target).slider('values');
            $('.ui-slider-handle:first', ev.target).text(num(ui.values[0]));
            $('.ui-slider-handle:last', ev.target).text(num(ui.values[1]));
        } else {
            ui.value = ui.value || $(ev.target).slider('value');
            $('.ui-slider-handle', ev.target).text(num(ui.value));
        }
    };
    var set = function(ev, ui) {
        up(ev, ui);
        var key = $(ev.target).data('key');
        var val = $(ev.target).hasClass('range')
            ? $(ev.target).slider('values')
            : $(ev.target).slider('value');
        model.deepSet(key, val);
    };
    $(el).each(function() {
        $(this).slider(_({
            min:$(this).data('min') || 0,
            max:$(this).data('max') || 0,
            step:$(this).data('step') || 1,
            values: $(this).hasClass('range')
                ? model.deepGet($(this).data('key')) || [
                    $(this).data('min') || 0,
                    $(this).data('max') || 0 ]
                : undefined,
            value: !$(this).hasClass('range')
                ? model.deepGet($(this).data('key')) || 0
                : undefined,
            range: $(this).hasClass('range') ? true : 'min',
            change: set,
            create: up,
            slide: up
        }).extend(options));
    });
};

view = Backbone.View.extend();

view.prototype.events = {
    'click .bleed a': 'unload',
    'click #popup a[href=#close], #popup input.cancel': 'popupClose',
    'click a.popup': 'popupOpen',
    'click #drawer a[href=#close]': 'drawerClose',
    'click a.drawer': 'drawerOpen',
    'click .button.dropdown': 'dropdown',
    'click .toggler a': 'toggler',
    'click a.restart': 'restart',
    'keydown': 'keydown'
};

view.prototype.initialize = function() {
    _(this).bindAll(
        'unload',
        'popupOpen',
        'popupClose',
        'drawerOpen',
        'drawerClose',
        'toggler',
        'keydown',
        'dropdown'
    );
};

view.prototype.unload = function() {
    return !window.onbeforeunload || window.onbeforeunload() !== false;
};

view.prototype.popupOpen = function(ev) {
    var target = $(ev.currentTarget);
    var title = target.attr('title') || target.text();

    $(this.el).addClass('overlay');
    this.$('#popup').addClass('active');
    this.$('#popup > .title').text(title);
    return false;
};

view.prototype.popupClose = function(ev) {
    $(this.el).removeClass('overlay');
    this.$('#popup')
        .removeClass('active')
        .html(templates.Pane());
    return false;
};

view.prototype.drawerOpen = function(ev) {
    var target = $(ev.currentTarget);

    // Close drawers when the target is active.
    if (target.is('.active')) return this.drawerClose();

    var title = target.text() || target.attr('title');
    this.$('a.drawer.active').removeClass('active');
    target.addClass('active');
    this.$('#drawer')[target.hasClass('mini') ? 'addClass' : 'removeClass']('mini');
    this.$('#drawer').addClass('active')
    this.$('#drawer > .title').text(title);
    return false;
};

view.prototype.drawerClose = function(ev) {
    this.$('a.drawer.active').removeClass('active');
    this.$('#drawer')
        .removeClass('active')
        .html(templates.Pane());
    return false;
};

view.prototype.toggler = function(ev) {
    var link = $(ev.currentTarget);
    var parent = link.parents('.toggler');
    var target = link.attr('href').split('#').pop();
    var targetSelector = '.' + target;
    // mapnik-reference comes with a "*" simbolizer which ends in
    // a "section-*" class name, but "*" is a metacharacter for jQuery
    targetSelector = targetSelector.replace('*', '\\*');
    if (link.hasClass('disabled')) return false;

    $('a', parent).removeClass('active');
    this.$(targetSelector).siblings('.active').removeClass('active');

    link.addClass('active');
    this.$(targetSelector).addClass('active');
    return false;
};

view.prototype.keydown = function(ev) {
    // Escape
    if (ev.which == 27 && (!ev.ctrlKey && !ev.metaKey && !ev.altKey)) {
        // @TODO for some reason a function bound from the Modal view
        // to a keydown event is not fired. Probably related to
        // event delegation/bubbling?
        if (this.$('#modal.active').size()) {
            if (!$('#popup.active').size()) $('body').removeClass('overlay');
            this.$('#modal.active').removeClass('active');
        } else if (this.$('#popup.active').size()) {
            this.popupClose();
        } else if (this.$('#drawer.active').size()) {
            this.drawerClose();
        }
        return false;
    }
    // Ctrl + S
    // The keydown event is only passed to a specific view if an form
    // element in that view has focus. When no form element has focus
    // the top most view takes precedence and the event is *not* bubbled
    // down (http://api.jquery.com/keydown). This code conceptually belongs
    // in `Project.bones` but is handled here as the App is the one to
    // receive the event.
    if (ev.which == 83 &&
        ((ev.ctrlKey || ev.metaKey) && !ev.altKey)) {
        this.$('.actions a[href=#save]').click();
        return false;
    }
};

view.prototype.dropdown = function(ev) {
    var target = $(ev.currentTarget);
    if (!target.is('.dropdown')) target = target.parents('.button.dropdown');
    var app = this.el;
    if (!target.hasClass('active')) {
        target.addClass('active');
        $(app).bind('click', collapse);
        target.children('.menu').bind('click', collapse);
    }
    function collapse(ev) {
        target.removeClass('active');
        $(app).unbind('click', collapse);
        target.children('.menu').bind('click', collapse);
    }
};

view.prototype.restart = function(ev) {
    var target = $(ev.currentTarget);
    var parent = target.parents('.restartable');
    if (parent.hasClass('restarting')) return false;

    target.addClass('active');
    parent.addClass('restarting');
    $.ajax({
        url: 'http://'+window.abilities.tileUrl+'/restart',
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify({'bones.token':Backbone.csrf('/restart')}),
        dataType: 'json',
        processData: false,
        success: function() {
            Bones.utils.until('http://'+window.abilities.tileUrl+'/status', function() {
                target.removeClass('active');
                parent
                    .removeClass('loading')
                    .removeClass('restarting')
                    .removeClass('restartable');
                if (parent.is('#drawer')) $('a[href=#close]',parent).click();
            });
        },
        error: function(err) {
            target.removeClass('active');
            parent
                .removeClass('loading')
                .removeClass('restarting')
                .removeClass('restartable');
            if (parent.is('#drawer')) $('a[href=#close]',parent).click();
            new views.Modal(err);
        }
    });
    return false;
};

<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2014

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
 */
var screenOrientation = {},
    Orientations = [
        'portrait-primary',
        // The orientation is in the primary portrait mode.
        'portrait-secondary',
        // The orientation is in the secondary portrait mode.
        'landscape-primary',
        // The orientation is in the primary landscape mode.
        'landscape-secondary',
        // The orientation is in the secondary landscape mode.
        'portrait',
        // The orientation is either portrait-primary or portrait-secondary.
        'landscape'
        // The orientation is either landscape-primary or landscape-secondary.
    ];

screenOrientation.Orientations = Orientations;
screenOrientation.currOrientation = 'unlocked';

screenOrientation.setOrientation = function(orientation) {
    //platform specific files override this function
    console.log('setOrientation not supported on device');
};

function addScreenOrientationApi(obj) {
    if (obj.unlockOrientation || obj.lockOrientation) {
        return;
    }

    obj.lockOrientation = function(orientation) {
        if (Orientations.indexOf(orientation) == -1) {
            console.log('INVALID ORIENTATION', orientation);
            return;
        }
        screenOrientation.currOrientation = orientation;
        screen.orientation = screenOrientation.currOrientation;
        screenOrientation.setOrientation(orientation);
    };

    obj.unlockOrientation = function() {
        screenOrientation.currOrientation = 'unlocked';
        screen.orientation = screenOrientation.currOrientation;
        screenOrientation.setOrientation('unlocked');
    };
}

addScreenOrientationApi(screen);
orientationChange();

function orientationChange() {
    var orientation;

    switch (window.orientation) {
        case 0:
             orientation = 'portrait-primary';
             break;
        case 90:
            orientation = 'landscape-primary';
            break;
        case 180:
            orientation = 'portrait-secondary';
            break;
        case -90:
            orientation = 'landscape-secondary';
            break;
        default:
            orientation = 'unknown';
    }

    screen.orientation = orientation;
}

window.addEventListener("orientationchange", orientationChange, true);

module.exports = screenOrientation;<commit_msg>Removed direct reference to screen in addScreenOrientationApi function<commit_after>/*
The MIT License (MIT)

Copyright (c) 2014

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
 */
var screenOrientation = {},
    Orientations = [
        'portrait-primary',
        // The orientation is in the primary portrait mode.
        'portrait-secondary',
        // The orientation is in the secondary portrait mode.
        'landscape-primary',
        // The orientation is in the primary landscape mode.
        'landscape-secondary',
        // The orientation is in the secondary landscape mode.
        'portrait',
        // The orientation is either portrait-primary or portrait-secondary.
        'landscape'
        // The orientation is either landscape-primary or landscape-secondary.
    ];

screenOrientation.Orientations = Orientations;
screenOrientation.currOrientation = 'unlocked';

screenOrientation.setOrientation = function(orientation) {
    //platform specific files override this function
    console.log('setOrientation not supported on device');
};

function addScreenOrientationApi(screenObject) {
    if (screenObject.unlockOrientation || screenObject.lockOrientation) {
        return;
    }

    screenObject.lockOrientation = function(orientation) {
        if (Orientations.indexOf(orientation) == -1) {
            console.log('INVALID ORIENTATION', orientation);
            return;
        }
        screenOrientation.currOrientation = screenObject.orientation = orientation;
        screenOrientation.setOrientation(orientation);
    };

    screenObject.unlockOrientation = function() {
        screenOrientation.currOrientation = screenObject.orientation = 'unlocked';
        screenOrientation.setOrientation('unlocked');
    };
}

addScreenOrientationApi(screen);
orientationChange();

function orientationChange() {
    var orientation;

    switch (window.orientation) {
        case 0:
             orientation = 'portrait-primary';
             break;
        case 90:
            orientation = 'landscape-primary';
            break;
        case 180:
            orientation = 'portrait-secondary';
            break;
        case -90:
            orientation = 'landscape-secondary';
            break;
        default:
            orientation = 'unknown';
    }

    screen.orientation = orientation;
}

window.addEventListener("orientationchange", orientationChange, true);

module.exports = screenOrientation;<|endoftext|>


<commit_before>import {
  AUTHENTICATE,
  AUTHENTICATE_FAILED,
  AUTHENTICATE_SUCCEEDED,
  FETCH,
  INITIALIZE,
  INVALIDATE_SESSION,
  RESTORE,
  RESTORE_FAILED
} from './actionTypes'

export const authenticate = (authenticator, payload) => ({
  type: AUTHENTICATE,
  meta: { authenticator },
  payload
})

export const authenticateSucceeded = (authenticator, payload) => ({
  type: AUTHENTICATE_SUCCEEDED,
  meta: { authenticator },
  payload
})

export const authenticateFailed = () => ({
  type: AUTHENTICATE_FAILED
})

export const fetch = (url, options) => ({
  type: FETCH,
  payload: { url, options }
})

export const invalidateSession = () => ({
  type: INVALIDATE_SESSION
})

export const restore = payload => ({
  type: RESTORE,
  payload
})

export const restoreFailed = () => ({
  type: RESTORE_FAILED
})

export const initialize = payload => ({
  type: INITIALIZE,
  payload
})
<commit_msg>Allow payload for authenticateFailed<commit_after>import {
  AUTHENTICATE,
  AUTHENTICATE_FAILED,
  AUTHENTICATE_SUCCEEDED,
  FETCH,
  INITIALIZE,
  INVALIDATE_SESSION,
  RESTORE,
  RESTORE_FAILED
} from './actionTypes'

export const authenticate = (authenticator, payload) => ({
  type: AUTHENTICATE,
  meta: { authenticator },
  payload
})

export const authenticateSucceeded = (authenticator, payload) => ({
  type: AUTHENTICATE_SUCCEEDED,
  meta: { authenticator },
  payload
})

export const authenticateFailed = payload => ({
  type: AUTHENTICATE_FAILED,
  payload
})

export const fetch = (url, options) => ({
  type: FETCH,
  payload: { url, options }
})

export const invalidateSession = () => ({
  type: INVALIDATE_SESSION
})

export const restore = payload => ({
  type: RESTORE,
  payload
})

export const restoreFailed = () => ({
  type: RESTORE_FAILED
})

export const initialize = payload => ({
  type: INITIALIZE,
  payload
})
<|endoftext|>


<commit_before>import { isArray, map } from 'lodash'
import { createAction } from 'cape-redux'
import { insertFields, updateFields, requireIdType, buildTriple } from './helpers'

export const ENTITY_DEL = 'graph/ENTITY_DEL'
export const entityDel = createAction(ENTITY_DEL, requireIdType)

export const ENTITY_PUT = 'graph/ENTITY_PUT'
export const entityPut = createAction(ENTITY_PUT, insertFields)

export const ENTITY_PUTALL = 'graph/ENTITY_PUTALL'
export const entityPutAll = createAction(ENTITY_PUTALL, values => {
  if (!isArray(values)) throw new Error('Must send putAll an array.')
  return map(values, insertFields)
})

export const ENTITY_UPDATE = 'graph/ENTITY_UPDATE'
export const entityUpdate = createAction(ENTITY_UPDATE, updateFields)

export const TRIPLE_PUT = 'graph/TRIPLE_PUT'
// You send it an object with an id, object, subject and predicate properties.
// This is only used when there a multiple predicate refs.
export const triplePut = createAction(TRIPLE_PUT, buildTriple)
<commit_msg>entityPutRaw<commit_after>import { isArray, map } from 'lodash'
import { createAction } from 'cape-redux'
import { insertFields, updateFields, requireIdType, buildTriple } from './helpers'

export const ENTITY_DEL = 'graph/ENTITY_DEL'
export const entityDel = createAction(ENTITY_DEL, requireIdType)

export const ENTITY_PUT = 'graph/ENTITY_PUT'
export const entityPut = createAction(ENTITY_PUT, insertFields)
export const entityPutRaw = createAction(ENTITY_PUT)

export const ENTITY_PUTALL = 'graph/ENTITY_PUTALL'
export const entityPutAll = createAction(ENTITY_PUTALL, values => {
  if (!isArray(values)) throw new Error('Must send putAll an array.')
  return map(values, insertFields)
})

export const ENTITY_UPDATE = 'graph/ENTITY_UPDATE'
export const entityUpdate = createAction(ENTITY_UPDATE, updateFields)

export const TRIPLE_PUT = 'graph/TRIPLE_PUT'
// You send it an object with an id, object, subject and predicate properties.
// This is only used when there a multiple predicate refs.
export const triplePut = createAction(TRIPLE_PUT, buildTriple)
<|endoftext|>


<commit_before>import { isPage, getCurrentPage, getMaxPages } from './index';

const parseMarkdownUrl = (date, rawUrl) => (
  `/${date}/${rawUrl.match(/_posts[/](.*).md/)[1]}/`
);

const parseUrl = (date, rawUrl) => (
  `/${date}/${rawUrl}/`
);

const minusOnePage = (currentPage) => {
  if (currentPage - 1 >= 0) {
    return currentPage - 1;
  }

  return -1;
};

const addOnePage = (currentPage) => {
  const maxPages = getMaxPages();

  if (currentPage + 1 <= maxPages) {
    return currentPage + 1;
  }
  return -1;
};

const parsePageUrl = (index) => {
  if (index > 0) {
    return `/page/${index}`;
  } else if (index === 0) {
    return '/';
  }
  return -1;
};

const handlePreviousPage = () => {
  const index = minusOnePage(getCurrentPage());

  return parsePageUrl(index);
};

const handleNextPage = () => {
  const index = addOnePage(getCurrentPage());

  return parsePageUrl(index);
};

export default {
  parseMarkdownUrl,
  parseUrl,
  handlePreviousPage,
  handleNextPage,
};
<commit_msg>Fix about page bug<commit_after>import { getCurrentPage, getMaxPages } from './index';

const parseMarkdownUrl = (date, rawUrl) => (
  `/${date}/${rawUrl.match(/_posts[/](.*).md/)[1]}/`
);

const parseUrl = (date, rawUrl) => {
  if (rawUrl === 'about') {
    return '/about/';
  }
  return `/${date}/${rawUrl}/`;
};

const minusOnePage = (currentPage) => {
  if (currentPage - 1 >= 0) {
    return currentPage - 1;
  }

  return -1;
};

const addOnePage = (currentPage) => {
  const maxPages = getMaxPages();

  if (currentPage + 1 <= maxPages) {
    return currentPage + 1;
  }
  return -1;
};

const parsePageUrl = (index) => {
  if (index > 0) {
    return `/page/${index}`;
  } else if (index === 0) {
    return '/';
  }
  return -1;
};

const handlePreviousPage = () => {
  const index = minusOnePage(getCurrentPage());

  return parsePageUrl(index);
};

const handleNextPage = () => {
  const index = addOnePage(getCurrentPage());

  return parsePageUrl(index);
};

export default {
  parseMarkdownUrl,
  parseUrl,
  handlePreviousPage,
  handleNextPage,
};
<|endoftext|>