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
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
extends layout.pug
include footer
+footer()
script( type='text/javascript').
function checkChannel(){
function nameIsInvalid(id){
if(/\s/.test(id)){
return 'Channel URL may not contain spaces';
}
if(id === ''){
return 'Channel URL must not be empty';
}
if(!/^[\w-]{1,30}$/.test(id)){
return 'Channel URL may only consist of a-z, A-Z, 0-9, - and _';
}
return false;
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-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
append footer
script(type='text/javascript').
function checkChannel(){
function nameIsInvalid(id){
if(/\s/.test(id)){
return 'Channel URL may not contain spaces';
}
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);
if(id === ''){
return 'Channel URL must not be empty';
}
if(!/^[\w-]{1,30}$/.test(id)){
return 'Channel URL may only consist of a-z, A-Z, 0-9, - and _';
}
return false;
}
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
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();
extends layout.pug
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");
$("<p/>").addClass("text-danger")
.attr("id", "passwordempty")
.text("Password must not be empty")
.insertAfter($("#newpassword"));
.attr("id", "passwordmismatch")
.text("Passwords do not match")
.insertAfter($("#newpassword_confirm"));
return false;
} else {
if (pw !== pwc) {
$("#newpassword_confirm").parent().addClass("has-error");
$("#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;
}
$("#username").attr("disabled", false);
return true;
}
}
function submitEmail() {
$("#username2").attr("disabled", false);
return true;
}
}
function submitEmail() {
$("#username2").attr("disabled", false);
return true;
}

View file

@ -1,28 +1,13 @@
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
.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()
extends layout.pug
block content
.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

View file

@ -1,38 +1,22 @@
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
.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
extends layout.pug
include footer
+footer()
block content
.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
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
extends layout.pug
include footer
+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();
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
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
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) {
validateImage();
if (hasError) {
event.preventDefault();
}
});
$("form").submit(function (event) {
validateImage();
if (hasError) {
event.preventDefault();
}
});

View file

@ -1,47 +1,34 @@
extends layout.pug
mixin email(e, k)
button.btn.btn-xs.btn-default(onclick="showEmail(this, '"+e+"', '"+k+"')") Show Email
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
.col-md-8.col-md-offset-2
h1 Contact
h3 Email
if contacts.length == 0
p No contacts listed.
else
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();
block content
.col-md-8.col-md-offset-2
h1 Contact
h3 Email
if contacts.length == 0
p No contacts listed.
else
each contact in contacts
strong= contact.name
p.text-muted= contact.title
+email(contact.email, contact.emkey)
br
hr
append 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();
}

View file

@ -1,31 +1,15 @@
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()
extends layout.pug
section#mainpage.container
.col-md-12
.alert.alert-danger
h1 Invalid Session
p Your browser attempted to submit form data to <code>#{path}</code> with an invalid authentication token. This may be because:
ul
li Your session has expired
li Your request was missing the authentication token
li A malicious user has attempted to tamper with your session
li Your browser does not support cookies, or they are not enabled
| If the problem persists, please contact an administrator.
if referer
a(href=referer) Return to previous page
include footer
+footer()
block content
.col-md-12
.alert.alert-danger
h1 Invalid Session
p Your browser attempted to submit form data to <code>#{path}</code> with an invalid authentication token. This may be because:
ul
li Your session has expired
li Your request was missing the authentication token
li A malicious user has attempted to tamper with your session
li Your browser does not support cookies, or they are not enabled
| If the problem persists, please contact an administrator.
if referer
a(href=referer) Return to previous page

View file

@ -1,60 +1,44 @@
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
.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>.
extends layout.pug
include footer
+footer()
block content
.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()
h1 Not Found
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()
h1 Oops
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
.col-md-12
.alert.alert-danger
if status == 404
+notfound()
else if status == 403
+forbidden()
else
+genericerror()
include footer
+footer()
block content
.col-md-12
.alert.alert-danger
if status == 404
+notfound()
else if status == 403
+forbidden()
else
+genericerror()

View file

@ -1,42 +1,26 @@
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()
+navsuperadmin(false)
+navloginlogout()
section#mainpage
.container
.col-lg-9.col-md-9
h3 Public Channels
table.table.table-bordered.table-striped
thead
th Channel
th # Connected
th Now Playing
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();
}
});
extends layout.pug
block content
.col-lg-9.col-md-9
h3 Public Channels
table.table.table-bordered.table-striped
thead
th Channel
th # Connected
th Now Playing
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.
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
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 wasAlreadyLoggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-info.messagebox.center
h3(style="margin: 5px auto") Logged in as #{loginName}
else if !loggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
if loginError
.alert.alert-danger.messagebox.center
strong Login Failed
p= loginError
h2 Login
form(role="form", action="/login", method="post")
input(type="hidden", name="_csrf", value=csrfToken)
if redirect
input(type="hidden", name="dest", value=redirect)
.form-group
label(for="username") Username
input#username.form-control(type="text", name="name")
.form-group
label(for="password") Password
input#password.form-control(type="password", name="password")
a(href="/account/passwordreset") Forgot password?
.form-group
.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()
extends layout.pug
block content
if wasAlreadyLoggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-info.messagebox.center
h3(style="margin: 5px auto") Logged in as #{loginName}
else if !loggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
if loginError
.alert.alert-danger.messagebox.center
strong Login Failed
p= loginError
h2 Login
form(role="form", action="/login", method="post")
input(type="hidden", name="_csrf", value=csrfToken)
if redirect
input(type="hidden", name="dest", value=redirect)
.form-group
label(for="username") Username
input#username.form-control(type="text", name="name")
.form-group
label(for="password") Password
input#password.form-control(type="password", name="password")
a(href="/account/passwordreset") Forgot password?
.form-group
.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

View file

@ -1,23 +1,9 @@
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()
+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()
extends layout.pug
block content
.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

View file

@ -1,127 +1,114 @@
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()
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();
extends layout.pug
$("#validate_password").text('');
$("#validate_confirm").text('');
if (pw === "") {
stateError('Password must not be empty', target, 'validate_password')
block content
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#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;
} else {
target.parent()
target2.parent()
.removeClass("has-error")
.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();
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;
}
function checkEmail() {
var email = $("#register-email").val();
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;
}