Refactor most pug templates to share a common layout template

This commit is contained in:
Calvin Montgomery 2017-08-22 22:09:48 -07:00
parent 0810591fe3
commit 712a8c228b
14 changed files with 552 additions and 721 deletions

View file

@ -1,103 +1,88 @@
doctype html extends layout.pug
html(lang="en")
head
include head
+head()
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
+navloginlogout()
section#mainpage
.container
if !loggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-danger.messagebox.center
strong Authorization Required
p You must be <a href="/login">logged in</a> to view this page.
else
.col-lg-6.col-md-6
h3 My Channels
if deleteChannelError
.alert.alert-danger.center.messagebox
strong Channel Deletion Failed
p= deleteChannelError
if channels.length == 0
.center
strong You haven't registered any channels
else
table.table.table-bordered
thead
tr
th Channel
tbody
for c in channels
tr
th
form.form-inline.pull-right(action="/account/channels", method="post", onsubmit="return confirm('Are you sure you want to delete " +c.name+ "? This cannot be undone');")
input(type="hidden", name="_csrf", value=csrfToken)
input(type="hidden", name="action", value="delete_channel")
input(type="hidden", name="name", value=c.name)
button.btn.btn-xs.btn-danger(type="submit") Delete
span.glyphicon.glyphicon-trash
a(href=`/${channelPath}/${c.name}`, style="margin-left: 5px")= c.name
.col-lg-6.col-md-6
h3 Register a new channel
if newChannelError
.alert.alert-danger.messagebox.center
strong Channel Registration Failed
p= newChannelError
form(action="/account/channels", method="post")
input(type="hidden", name="_csrf", value=csrfToken)
input(type="hidden", name="action", value="new_channel")
.form-group
label.control-label(for="channelname") Channel URL
.input-group
span.input-group-addon #{baseUrl}/#{channelPath}/
input#channelname.form-control(type="text", name="name", maxlength="30", onkeyup="checkChannel()")
p#validate_channel.text-danger.pull-right
button#register.btn.btn-primary.btn-block(type="submit") Register
include footer block content
+footer() if !loggedIn
script( type='text/javascript'). .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
function checkChannel(){ .alert.alert-danger.messagebox.center
function nameIsInvalid(id){ strong Authorization Required
if(/\s/.test(id)){ p You must be <a href="/login">logged in</a> to view this page.
return 'Channel URL may not contain spaces'; else
} .col-lg-6.col-md-6
if(id === ''){ h3 My Channels
return 'Channel URL must not be empty'; if deleteChannelError
} .alert.alert-danger.center.messagebox
if(!/^[\w-]{1,30}$/.test(id)){ strong Channel Deletion Failed
return 'Channel URL may only consist of a-z, A-Z, 0-9, - and _'; p= deleteChannelError
} if channels.length == 0
return false; .center
strong You haven't registered any channels
else
table.table.table-bordered
thead
tr
th Channel
tbody
for c in channels
tr
th
form.form-inline.pull-right(action="/account/channels", method="post", onsubmit="return confirm('Are you sure you want to delete " +c.name+ "? This cannot be undone');")
input(type="hidden", name="_csrf", value=csrfToken)
input(type="hidden", name="action", value="delete_channel")
input(type="hidden", name="name", value=c.name)
button.btn.btn-xs.btn-danger(type="submit") Delete
span.glyphicon.glyphicon-trash
a(href=`/${channelPath}/${c.name}`, style="margin-left: 5px")= c.name
.col-lg-6.col-md-6
h3 Register a new channel
if newChannelError
.alert.alert-danger.messagebox.center
strong Channel Registration Failed
p= newChannelError
form(action="/account/channels", method="post")
input(type="hidden", name="_csrf", value=csrfToken)
input(type="hidden", name="action", value="new_channel")
.form-group
label.control-label(for="channelname") Channel URL
.input-group
span.input-group-addon #{baseUrl}/#{channelPath}/
input#channelname.form-control(type="text", name="name", maxlength="30", onkeyup="checkChannel()")
p#validate_channel.text-danger.pull-right
button#register.btn.btn-primary.btn-block(type="submit") Register
append footer
script(type='text/javascript').
function checkChannel(){
function nameIsInvalid(id){
if(/\s/.test(id)){
return 'Channel URL may not contain spaces';
} }
if(id === ''){
var box = $("#channelname"); return 'Channel URL must not be empty';
var value = box.val(); }
var lastkey = Date.now(); if(!/^[\w-]{1,30}$/.test(id)){
box.data("lastkey", lastkey); return 'Channel URL may only consist of a-z, A-Z, 0-9, - and _';
}
setTimeout(function () { return false;
if (box.data("lastkey") !== lastkey || box.val() !== value) {
return;
}
if(nameIsInvalid(value)){
$('#validate_channel').text(nameIsInvalid(value))
.parent().addClass('has-error').removeClass('has-success');
$('#register').addClass('disabled');
} else {
$('#validate_channel').text('')
.parent().addClass('has-success').removeClass('has-error');
$('#register').removeClass('disabled');
}
}, 200);
} }
var box = $("#channelname");
var value = box.val();
var lastkey = Date.now();
box.data("lastkey", lastkey);
setTimeout(function () {
if (box.data("lastkey") !== lastkey || box.val() !== value) {
return;
}
if(nameIsInvalid(value)){
$('#validate_channel').text(nameIsInvalid(value))
.parent().addClass('has-error').removeClass('has-success');
$('#register').addClass('disabled');
} else {
$('#validate_channel').text('')
.parent().addClass('has-success').removeClass('has-error');
$('#register').removeClass('disabled');
}
}, 200);
}

View file

@ -1,96 +1,83 @@
doctype html extends layout.pug
html(lang="en")
head
include head
+head()
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
+navloginlogout()
section#mainpage
.container
if !loggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-danger.messagebox.center
strong Authorization Required
p You must be <a href="/login">logged in</a> to view this page.
else
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
if successMessage
.alert.alert-success.center
p= successMessage
else if errorMessage
.alert.alert-danger.center
p= errorMessage
h3 Change Password
form(action="/account/edit", method="post", onsubmit="return validatePasswordChange()")
input(type="hidden", name="_csrf", value=csrfToken)
input(type="hidden", name="action", value="change_password")
.form-group
label.control-label(for="username") Username
input#username.form-control(type="text", name="name", value=loginName, disabled=true)
.form-group
label.control-label(for="oldpassword") Current Password
input#oldpassword.form-control(type="password", name="oldpassword")
.form-group
label.control-label(for="newpassword") New Password
input#newpassword.form-control(type="password", name="newpassword")
.form-group
label.control-label(for="newpassword_confirm") Confirm New Password
input#newpassword_confirm.form-control(type="password", name="newpassword_confirm")
button#changepassbtn.btn.btn-danger.btn-block(type="submit") Change Password
hr
h3 Change Email
form(action="/account/edit", method="post", onsubmit="return submitEmail()")
input(type="hidden", name="_csrf", value=csrfToken)
input(type="hidden", name="action", value="change_email")
.form-group
label.control-label(for="username2") Username
input#username2.form-control(type="text", name="name", value=loginName, disabled=true)
.form-group
label.control-label(for="password2") Password
input#password2.form-control(type="password", name="password")
.form-group
label.control-label(for="email") New Email
input#email.form-control(type="email", name="email")
button#changeemailbtn.btn.btn-danger.btn-block(type="submit") Change Email
include footer
+footer()
script(type="text/javascript").
function validatePasswordChange() {
var pw = $("#newpassword").val();
var pwc = $("#newpassword_confirm").val();
$("#passwordempty").remove();
$("#passwordmismatch").remove();
if (pw === '') { block content
if !loggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-danger.messagebox.center
strong Authorization Required
p You must be <a href="/login">logged in</a> to view this page.
else
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
if successMessage
.alert.alert-success.center
p= successMessage
else if errorMessage
.alert.alert-danger.center
p= errorMessage
h3 Change Password
form(action="/account/edit", method="post", onsubmit="return validatePasswordChange()")
input(type="hidden", name="_csrf", value=csrfToken)
input(type="hidden", name="action", value="change_password")
.form-group
label.control-label(for="username") Username
input#username.form-control(type="text", name="name", value=loginName, disabled=true)
.form-group
label.control-label(for="oldpassword") Current Password
input#oldpassword.form-control(type="password", name="oldpassword")
.form-group
label.control-label(for="newpassword") New Password
input#newpassword.form-control(type="password", name="newpassword")
.form-group
label.control-label(for="newpassword_confirm") Confirm New Password
input#newpassword_confirm.form-control(type="password", name="newpassword_confirm")
button#changepassbtn.btn.btn-danger.btn-block(type="submit") Change Password
hr
h3 Change Email
form(action="/account/edit", method="post", onsubmit="return submitEmail()")
input(type="hidden", name="_csrf", value=csrfToken)
input(type="hidden", name="action", value="change_email")
.form-group
label.control-label(for="username2") Username
input#username2.form-control(type="text", name="name", value=loginName, disabled=true)
.form-group
label.control-label(for="password2") Password
input#password2.form-control(type="password", name="password")
.form-group
label.control-label(for="email") New Email
input#email.form-control(type="email", name="email")
button#changeemailbtn.btn.btn-danger.btn-block(type="submit") Change Email
append footer
script(type="text/javascript").
function validatePasswordChange() {
var pw = $("#newpassword").val();
var pwc = $("#newpassword_confirm").val();
$("#passwordempty").remove();
$("#passwordmismatch").remove();
if (pw === '') {
$("#newpassword").parent().addClass("has-error");
$("<p/>").addClass("text-danger")
.attr("id", "passwordempty")
.text("Password must not be empty")
.insertAfter($("#newpassword"));
return false;
} else {
if (pw !== pwc) {
$("#newpassword_confirm").parent().addClass("has-error");
$("#newpassword").parent().addClass("has-error"); $("#newpassword").parent().addClass("has-error");
$("<p/>").addClass("text-danger") $("<p/>").addClass("text-danger")
.attr("id", "passwordempty") .attr("id", "passwordmismatch")
.text("Password must not be empty") .text("Passwords do not match")
.insertAfter($("#newpassword")); .insertAfter($("#newpassword_confirm"));
return false; return false;
} else { } else {
if (pw !== pwc) { $("#username").attr("disabled", false);
$("#newpassword_confirm").parent().addClass("has-error"); return true;
$("#newpassword").parent().addClass("has-error");
$("<p/>").addClass("text-danger")
.attr("id", "passwordmismatch")
.text("Passwords do not match")
.insertAfter($("#newpassword_confirm"));
return false;
} else {
$("#username").attr("disabled", false);
return true;
}
} }
} }
function submitEmail() { }
$("#username2").attr("disabled", false); function submitEmail() {
return true; $("#username2").attr("disabled", false);
} return true;
}

View file

@ -1,28 +1,13 @@
doctype html extends layout.pug
html(lang="en")
head block content
include head .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
+head() h3 Recover Password
body if recovered
#wrap .alert.alert-success.center.messagebox
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") strong Your password has been changed
include nav p Your account has been assigned the temporary password <code>#{recoverPw}</code>. You may now use this password to log in and choose a new password by visiting the <a href="/account/edit">change password/email</a> page.
+navheader() else
#nav-collapsible.collapse.navbar-collapse .alert.alert-danger.center.messagebox
ul.nav.navbar-nav strong Password recovery failed
+navdefaultlinks() p= recoverErr
+navloginlogout()
section#mainpage
.container
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
h3 Recover Password
if recovered
.alert.alert-success.center.messagebox
strong Your password has been changed
p Your account has been assigned the temporary password <code>#{recoverPw}</code>. You may now use this password to log in and choose a new password by visiting the <a href="/account/edit">change password/email</a> page.
else
.alert.alert-danger.center.messagebox
strong Password recovery failed
p= recoverErr
include footer
+footer()

View file

@ -1,38 +1,22 @@
doctype html extends layout.pug
html(lang="en")
head
include head
+head()
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
+navloginlogout()
section#mainpage
.container
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
h3 Reset Password
if reset
.alert.alert-success.center.messagebox
strong Password reset request sent
p Please check #{resetEmail} for your recovery link.
else if resetErr
.alert.alert-danger.center.messagebox
strong Error
p= resetErr
form(action="/account/passwordreset", method="post", role="form")
input(type="hidden", name="_csrf", value=csrfToken)
.form-group
label.control-label(for="username") Username
input#username.form-control(type="text", name="name")
.form-group
label.control-label(for="email") Email address
input#email.form-control(type="email", name="email")
button.btn.btn-primary.btn-block(type="submit") Send reset request
include footer block content
+footer() .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
h3 Reset Password
if reset
.alert.alert-success.center.messagebox
strong Password reset request sent
p Please check #{resetEmail} for your recovery link.
else if resetErr
.alert.alert-danger.center.messagebox
strong Error
p= resetErr
form(action="/account/passwordreset", method="post", role="form")
input(type="hidden", name="_csrf", value=csrfToken)
.form-group
label.control-label(for="username") Username
input#username.form-control(type="text", name="name")
.form-group
label.control-label(for="email") Email address
input#email.form-control(type="email", name="email")
button.btn.btn-primary.btn-block(type="submit") Send reset request

View file

@ -1,76 +1,62 @@
doctype html extends layout.pug
html(lang="en")
head
include head
+head()
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
+navloginlogout()
section#mainpage
.container
if !loggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-danger.messagebox.center
strong Authorization Required
p You must be <a href="/login">logged in</a> to view this page.
else
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
h3 Profile
if profileError
.alert.alert-danger.center.messagebox
strong Profile Error
p= profileError
.profile-box.linewrap(style="position: inherit; z-index: auto;")
img.profile-image(src=profileImage)
strong= loginName
p= profileText
h3 Edit Profile
form(action="/account/profile", method="post", role="form")
input(type="hidden", name="_csrf", value=csrfToken)
.form-group
label.control-label(for="profileimage") Image
input#profileimage.form-control(type="text", name="image", maxlength="255")
.form-group
label.control-label(for="profiletext") Text
textarea#profiletext.form-control(cols="10", name="text", maxlength="255")= profileText
button.btn.btn-primary.btn-block(type="submit") Save
include footer block content
+footer() if !loggedIn
script(type="text/javascript"). .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
var $profileImage = $("#profileimage"); .alert.alert-danger.messagebox.center
$profileImage.val("#{profileImage}"); strong Authorization Required
var hasError = false; p You must be <a href="/login">logged in</a> to view this page.
function validateImage() { else
var value = $profileImage.val().trim(); .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
$profileImage.val(value); h3 Profile
if (!/^$|^https:/.test(value)) { if profileError
hasError = true; .alert.alert-danger.center.messagebox
$profileImage.parent().addClass("has-error"); strong Profile Error
var $error = $("#profileimage-error"); p= profileError
if ($error.length === 0) { .profile-box.linewrap(style="position: inherit; z-index: auto;")
$error = $("<p/>") img.profile-image(src=profileImage)
.attr({ id: "profileimage-error" }) strong= loginName
.addClass("text-danger") p= profileText
.html("Profile image must be a URL beginning with <code>https://</code>") h3 Edit Profile
.insertAfter($profileImage); form(action="/account/profile", method="post", role="form")
} input(type="hidden", name="_csrf", value=csrfToken)
} else { .form-group
hasError = false; label.control-label(for="profileimage") Image
$profileImage.parent().removeClass("has-error"); input#profileimage.form-control(type="text", name="image", maxlength="255")
$("#profileimage-error").remove(); .form-group
label.control-label(for="profiletext") Text
textarea#profiletext.form-control(cols="10", name="text", maxlength="255")= profileText
button.btn.btn-primary.btn-block(type="submit") Save
append footer
script(type="text/javascript").
var $profileImage = $("#profileimage");
$profileImage.val("#{profileImage}");
var hasError = false;
function validateImage() {
var value = $profileImage.val().trim();
$profileImage.val(value);
if (!/^$|^https:/.test(value)) {
hasError = true;
$profileImage.parent().addClass("has-error");
var $error = $("#profileimage-error");
if ($error.length === 0) {
$error = $("<p/>")
.attr({ id: "profileimage-error" })
.addClass("text-danger")
.html("Profile image must be a URL beginning with <code>https://</code>")
.insertAfter($profileImage);
} }
} else {
hasError = false;
$profileImage.parent().removeClass("has-error");
$("#profileimage-error").remove();
} }
}
$("form").submit(function (event) { $("form").submit(function (event) {
validateImage(); validateImage();
if (hasError) { if (hasError) {
event.preventDefault(); event.preventDefault();
} }
}); });

View file

@ -1,47 +1,34 @@
extends layout.pug
mixin email(e, k) mixin email(e, k)
button.btn.btn-xs.btn-default(onclick="showEmail(this, '"+e+"', '"+k+"')") Show Email button.btn.btn-xs.btn-default(onclick="showEmail(this, '"+e+"', '"+k+"')") Show Email
doctype html block content
html(lang="en") .col-md-8.col-md-offset-2
head h1 Contact
include head h3 Email
+head() if contacts.length == 0
body p No contacts listed.
#wrap else
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") each contact in contacts
include nav strong= contact.name
+navheader() p.text-muted= contact.title
#nav-collapsible.collapse.navbar-collapse +email(contact.email, contact.emkey)
ul.nav.navbar-nav br
+navdefaultlinks() hr
+navloginlogout()
section#mainpage append footer
.container script(type="text/javascript").
.col-md-8.col-md-offset-2 function showEmail(btn, email, key) {
h1 Contact email = unescape(email);
h3 Email key = unescape(key);
if contacts.length == 0 var dest = new Array(email.length);
p No contacts listed. for (var i = 0; i < email.length; i++) {
else dest[i] = String.fromCharCode(email.charCodeAt(i) ^ key.charCodeAt(i % key.length));
each contact in contacts
strong= contact.name
p.text-muted= contact.title
+email(contact.email, contact.emkey)
br
hr
include footer
+footer()
script(type="text/javascript").
function showEmail(btn, email, key) {
email = unescape(email);
key = unescape(key);
var dest = new Array(email.length);
for (var i = 0; i < email.length; i++) {
dest[i] = String.fromCharCode(email.charCodeAt(i) ^ key.charCodeAt(i % key.length));
}
email = dest.join("");
$("<a/>").attr("href", "mailto:" + email)
.text(email)
.insertBefore(btn);
$(btn).remove();
} }
email = dest.join("");
$("<a/>").attr("href", "mailto:" + email)
.text(email)
.insertBefore(btn);
$(btn).remove();
}

View file

@ -1,31 +1,15 @@
doctype html extends layout.pug
html(lang="en")
head
include head
+head()
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
+navloginlogout()
section#mainpage.container block content
.col-md-12 .col-md-12
.alert.alert-danger .alert.alert-danger
h1 Invalid Session h1 Invalid Session
p Your browser attempted to submit form data to <code>#{path}</code> with an invalid authentication token. This may be because: p Your browser attempted to submit form data to <code>#{path}</code> with an invalid authentication token. This may be because:
ul ul
li Your session has expired li Your session has expired
li Your request was missing the authentication token li Your request was missing the authentication token
li A malicious user has attempted to tamper with your session li A malicious user has attempted to tamper with your session
li Your browser does not support cookies, or they are not enabled li Your browser does not support cookies, or they are not enabled
| If the problem persists, please contact an administrator. | If the problem persists, please contact an administrator.
if referer if referer
a(href=referer) Return to previous page a(href=referer) Return to previous page
include footer
+footer()

View file

@ -1,60 +1,44 @@
doctype html extends layout.pug
html(lang="en")
head
include head
+head()
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
+navloginlogout()
section#mainpage
.container
.col-md-8.col-md-offset-2
h1 Google Drive Userscript
h2 Why?
p.
Since Google Drive support was launched in early 2014, it has broken
at least 4-5 times, requiring increasing effort to get it working again
and disrupting many channels. This is because there is no official API
for it like there is for YouTube videos, which means support for it
relies on undocumented tricks. In August 2016, the decision was made
to phase out the native support for Google Drive and instead require
users to install a userscript, which allows to bypass certain browser
restrictions and make the code easier, simpler, and less prone to failure
(it could still break due to future Google Drive changes, but is less
likely to be difficult to fix).
h2 How It Works
p.
The userscript is a short script that you can install using a browser
extension such as Greasemonkey or Tampermonkey that runs on the page
and provides additional functionality needed to play Google Drive
videos.
h2 Installation
ul
li
strong Chrome
| &mdash;Install <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">Tampermonkey</a>.
li
strong Firefox
| &mdash;Install <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Tampermonkey</a>
| or <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/" target="_blank">Greasemonkey</a>.
li
strong Other Browsers
| &mdash;Install the appropriate userscript plugin for your browser.
| Tampermonkey supports many browsers besides Chrome.
p.
Once you have installed the userscript manager addon for your browser,
you can <a href="/js/cytube-google-drive.user.js" target="_blank">
install the userscript</a>. If this link 404s, it means the administrator
of this server hasn't generated it yet.
p.
You can find a guide with screenshots of the installation process
<a href="https://github.com/calzoneman/sync/wiki/Google-Drive-Userscript-Installation-Guide" target="_blank">on GitHub</a>.
include footer block content
+footer() .col-md-8.col-md-offset-2
h1 Google Drive Userscript
h2 Why?
p.
Since Google Drive support was launched in early 2014, it has broken
at least 4-5 times, requiring increasing effort to get it working again
and disrupting many channels. This is because there is no official API
for it like there is for YouTube videos, which means support for it
relies on undocumented tricks. In August 2016, the decision was made
to phase out the native support for Google Drive and instead require
users to install a userscript, which allows to bypass certain browser
restrictions and make the code easier, simpler, and less prone to failure
(it could still break due to future Google Drive changes, but is less
likely to be difficult to fix).
h2 How It Works
p.
The userscript is a short script that you can install using a browser
extension such as Greasemonkey or Tampermonkey that runs on the page
and provides additional functionality needed to play Google Drive
videos.
h2 Installation
ul
li
strong Chrome
| &mdash;Install <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo" target="_blank">Tampermonkey</a>.
li
strong Firefox
| &mdash;Install <a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" target="_blank">Tampermonkey</a>
| or <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/" target="_blank">Greasemonkey</a>.
li
strong Other Browsers
| &mdash;Install the appropriate userscript plugin for your browser.
| Tampermonkey supports many browsers besides Chrome.
p.
Once you have installed the userscript manager addon for your browser,
you can <a href="/js/cytube-google-drive.user.js" target="_blank">
install the userscript</a>. If this link 404s, it means the administrator
of this server hasn't generated it yet.
p.
You can find a guide with screenshots of the installation process
<a href="https://github.com/calzoneman/sync/wiki/Google-Drive-Userscript-Installation-Guide" target="_blank">on GitHub</a>.

View file

@ -1,3 +1,5 @@
extends layout.pug
mixin notfound() mixin notfound()
h1 Not Found h1 Not Found
p The page you were looking for doesn't seem to exist. Please check that you typed the URL correctly. p The page you were looking for doesn't seem to exist. Please check that you typed the URL correctly.
@ -9,30 +11,13 @@ mixin forbidden()
mixin genericerror() mixin genericerror()
h1 Oops h1 Oops
p Your request could not be processed. Status code: <code>#{status}</code>, message: <code>#{message}</code> p Your request could not be processed. Status code: <code>#{status}</code>, message: <code>#{message}</code>
doctype html
html(lang="en")
head
include head
+head()
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
+navloginlogout()
section#mainpage.container block content
.col-md-12 .col-md-12
.alert.alert-danger .alert.alert-danger
if status == 404 if status == 404
+notfound() +notfound()
else if status == 403 else if status == 403
+forbidden() +forbidden()
else else
+genericerror() +genericerror()
include footer
+footer()

View file

@ -1,42 +1,26 @@
doctype html extends layout.pug
html(lang="en")
head block content
include head .col-lg-9.col-md-9
+head() h3 Public Channels
body table.table.table-bordered.table-striped
#wrap thead
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") th Channel
include nav th # Connected
+navheader() th Now Playing
#nav-collapsible.collapse.navbar-collapse tbody
ul.nav.navbar-nav each chan in channels
+navdefaultlinks() tr
+navsuperadmin(false) td: a(href=`/${channelPath}/${chan.name}`) #{chan.pagetitle} (#{chan.name})
+navloginlogout() td= chan.usercount
section#mainpage td= chan.mediatitle
.container .col-lg-3.col-md-3
.col-lg-9.col-md-9 h3 Enter Channel
h3 Public Channels input#channelname.form-control(type="text", placeholder="Channel Name")
table.table.table-bordered.table-striped p.text-muted New channels can be registered from the <a href="/account/channels">My Channels</a> page.
thead script(type="text/javascript").
th Channel $("#channelname").keydown(function (ev) {
th # Connected if (ev.keyCode === 13) {
th Now Playing location.href = "/#{channelPath}/" + $("#channelname").val();
tbody }
each chan in channels });
tr
td: a(href=`/${channelPath}/${chan.name}`) #{chan.pagetitle} (#{chan.name})
td= chan.usercount
td= chan.mediatitle
.col-lg-3.col-md-3
h3 Enter Channel
input#channelname.form-control(type="text", placeholder="Channel Name")
p.text-muted New channels can be registered from the <a href="/account/channels">My Channels</a> page.
include footer
+footer()
script(type="text/javascript").
$("#channelname").keydown(function (ev) {
if (ev.keyCode === 13) {
location.href = "/#{channelPath}/" + $("#channelname").val();
}
});

22
templates/layout.pug Normal file
View file

@ -0,0 +1,22 @@
doctype html
html(lang="en")
head
block head
include head
+head()
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
+navsuperadmin(false)
+navloginlogout()
section#mainpage
.container
block content
block footer
include footer
+footer()

View file

@ -1,54 +1,39 @@
doctype html extends layout.pug
html(lang="en")
head block content
include head if wasAlreadyLoggedIn
+head() .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
body .alert.alert-info.messagebox.center
#wrap h3(style="margin: 5px auto") Logged in as #{loginName}
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") else if !loggedIn
include nav .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
+navheader() if loginError
#nav-collapsible.collapse.navbar-collapse .alert.alert-danger.messagebox.center
ul.nav.navbar-nav strong Login Failed
+navdefaultlinks() p= loginError
if loggedIn h2 Login
+navlogoutform() form(role="form", action="/login", method="post")
section#mainpage.container input(type="hidden", name="_csrf", value=csrfToken)
if wasAlreadyLoggedIn if redirect
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 input(type="hidden", name="dest", value=redirect)
.alert.alert-info.messagebox.center .form-group
h3(style="margin: 5px auto") Logged in as #{loginName} label(for="username") Username
else if !loggedIn input#username.form-control(type="text", name="name")
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3 .form-group
if loginError label(for="password") Password
.alert.alert-danger.messagebox.center input#password.form-control(type="password", name="password")
strong Login Failed a(href="/account/passwordreset") Forgot password?
p= loginError .form-group
h2 Login .checkbox
form(role="form", action="/login", method="post") label
input(type="hidden", name="_csrf", value=csrfToken) input(type="checkbox", name="remember")
if redirect | Remember me
input(type="hidden", name="dest", value=redirect) button.btn.btn-success.btn-block(type="submit") Login
.form-group else
label(for="username") Username .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
input#username.form-control(type="text", name="name") .alert.alert-success.messagebox.center
.form-group strong Login Successful
label(for="password") Password p Logged in as #{loginName}
input#password.form-control(type="password", name="password") if redirect
a(href="/account/passwordreset") Forgot password? br
.form-group a(href=redirect) Return to previous page
.checkbox
label
input(type="checkbox", name="remember")
| Remember me
button.btn.btn-success.btn-block(type="submit") Login
else
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-success.messagebox.center
strong Login Successful
p Logged in as #{loginName}
if redirect
br
a(href=redirect) Return to previous page
include footer
+footer()

View file

@ -1,23 +1,9 @@
doctype html extends layout.pug
html(lang="en")
head block content
include head .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
+head() .alert.alert-info.center.messagebox
body strong Logged out
#wrap p
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation") if redirect
include nav a(href=redirect) Return to previous page
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
+navloginform("/")
section#mainpage.container
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-info.center.messagebox
strong Logged out
p
if redirect
a(href=redirect) Return to previous page
include footer
+footer()

View file

@ -1,127 +1,114 @@
doctype html extends layout.pug
html(lang="en")
head
include head
+head()
body
#wrap
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
include nav
+navheader()
#nav-collapsible.collapse.navbar-collapse
ul.nav.navbar-nav
+navdefaultlinks()
if loggedIn
+navlogoutform()
section#mainpage.container
if loggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-danger.messagebox.center
strong Already logged in
p.
You are already logged in. If you intend to register a new account, please <a href="/logout?redirect=/register">Logout</a> first.
// TODO Link to My Account page
else if !registered
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
if registerError
.alert.alert-danger.messagebox.center
strong Registration Failed
p= registerError
h2 Register
form(role="form", action="/register", method="post", onsubmit="return verify()")
input(type="hidden", name="_csrf", value=csrfToken)
.form-group
label.control-label(for="username") Username
input#username.form-control(type="text", name="name", onkeyup="checkUsername()", maxlength="20")
p#validate_username.text-danger.pull-right
.form-group
label.control-label(for="password") Password
input#password.form-control(type="password", name="password", onkeyup="checkPasswords()")
p#validate_password.text-danger.pull-right
.form-group
label.control-label(for="password_confirm") Confirm Password
input#password_confirm.form-control(type="password", onkeyup="checkPasswords()")
p#validate_confirm.text-danger.pull-right
.form-group
label.control-label(for="email") Email (optional)
input#email.form-control(type="email", name="email")
p#validate_email.text-danger.pull-right
p
| Providing an email address is optional and will allow you to recover your account via email if you forget your password.
strong &nbsp;&nbsp;If you do not provide an email address, you will not be able to recover a lost account!
button#registerbtn.btn.btn-success.btn-block(type="submit") Register
else
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-success.messagebox.center
strong Registration Successful
p Thanks for registering, #{registerName}! Now you can <a href="/login">Login</a> to use your account.
include footer
+footer()
script(type="text/javascript").
function verify() {
var valid = checkUsername();
valid = checkPasswords() && valid;
valid = checkEmail() && valid;
return valid;
}
function checkUsername() {
function stateError(text){
target.parent()
.addClass("has-error")
.removeClass("has-success");
$("#validate_username").text(text);
}
var target = $("#username");
var name = target.val();
if (name === "") {
stateError('Username must not be empty')
return false;
} else if (!(/^[-\w\u00c0-\u00ff]{1,20}$/).test(name)) {
stateError("Username must consist of 1-20 characters" +
" a-Z, A-Z, 0-9, -, or _.");
return false;
} else {
target.parent()
.removeClass("has-error")
.addClass("has-success");
$("#validate_username").text('');
}
}
function checkPasswords() {
function stateError(text, target, validator){
target.parent()
.addClass("has-error")
.removeClass("has-success");
$(`#${validator}`).text(text);
}
var target = $("#password");
var target2 = $("#password_confirm");
var pw = target.val();
var pwc = target2.val();
$("#validate_password").text(''); block content
$("#validate_confirm").text(''); if loggedIn
if (pw === "") { .col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
stateError('Password must not be empty', target, 'validate_password') .alert.alert-danger.messagebox.center
strong Already logged in
p.
You are already logged in. If you intend to register a new account, please <a href="/logout?redirect=/register">Logout</a> first.
// TODO Link to My Account page
else if !registered
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
if registerError
.alert.alert-danger.messagebox.center
strong Registration Failed
p= registerError
h2 Register
form(role="form", action="/register", method="post", onsubmit="return verify()")
input(type="hidden", name="_csrf", value=csrfToken)
.form-group
label.control-label(for="username") Username
input#register-username.form-control(type="text", name="name", onkeyup="checkUsername()", maxlength="20")
p#validate_username.text-danger.pull-right
.form-group
label.control-label(for="password") Password
input#register-password.form-control(type="password", name="password", onkeyup="checkPasswords()")
p#validate_password.text-danger.pull-right
.form-group
label.control-label(for="password_confirm") Confirm Password
input#register-password-confirm.form-control(type="password", onkeyup="checkPasswords()")
p#validate_confirm.text-danger.pull-right
.form-group
label.control-label(for="email") Email (optional)
input#register-email.form-control(type="email", name="email")
p#validate_email.text-danger.pull-right
p
| Providing an email address is optional and will allow you to recover your account via email if you forget your password.
strong &nbsp;&nbsp;If you do not provide an email address, you will not be able to recover a lost account!
button#registerbtn.btn.btn-success.btn-block(type="submit") Register
else
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-success.messagebox.center
strong Registration Successful
p Thanks for registering, #{registerName}! Now you can <a href="/login">Login</a> to use your account.
append footer
script(type="text/javascript").
function verify() {
var valid = checkUsername();
valid = checkPasswords() && valid;
valid = checkEmail() && valid;
return valid;
}
function checkUsername() {
function stateError(text){
target.parent()
.addClass("has-error")
.removeClass("has-success");
$("#validate_username").text(text);
}
var target = $("#register-username");
var name = target.val();
if (name === "") {
stateError('Username must not be empty')
return false;
} else if (!(/^[-\w\u00c0-\u00ff]{1,20}$/).test(name)) {
stateError("Username must consist of 1-20 characters" +
" a-Z, A-Z, 0-9, -, or _.");
return false;
} else {
target.parent()
.removeClass("has-error")
.addClass("has-success");
$("#validate_username").text('');
}
}
function checkPasswords() {
function stateError(text, target, validator){
target.parent()
.addClass("has-error")
.removeClass("has-success");
$(`#${validator}`).text(text);
}
var target = $("#register-password");
var target2 = $("#register-password-confirm");
var pw = target.val();
var pwc = target2.val();
$("#validate_password").text('');
$("#validate_confirm").text('');
if (pw === "") {
stateError('Password must not be empty', target, 'validate_password')
return false;
} else {
target.parent()
.removeClass("has-error")
.addClass("has-success");
if (pw !== pwc) {
stateError('Passwords do not match', target2, 'validate_confirm')
return false; return false;
} else { } else {
target.parent() target2.parent()
.removeClass("has-error") .removeClass("has-error")
.addClass("has-success"); .addClass("has-success");
if (pw !== pwc) {
stateError('Passwords do not match', target2, 'validate_confirm')
return false;
} else {
target2.parent()
.removeClass("has-error")
.addClass("has-success");
}
} }
} }
function checkEmail() { }
var email = $("#email").val(); function checkEmail() {
if (email.trim() === "") { var email = $("#register-email").val();
return confirm("Are you sure you want to register without setting a recovery email address? If you lose the password, or if your account is compromised, you WILL NOT be able to recover it."); if (email.trim() === "") {
} return confirm("Are you sure you want to register without setting a recovery email address? If you lose the password, or if your account is compromised, you WILL NOT be able to recover it.");
return true;
} }
return true;
}