Updates for channel-user-password modal and channel-info input-fields

This commit is contained in:
Kasper Rynning-Tønnesen
2018-04-30 16:05:09 +02:00
parent 902cc9df11
commit b2f51c4682
13 changed files with 485 additions and 537 deletions

73
package-lock.json generated
View File

@@ -700,13 +700,6 @@
"integrity": "sha512-JgSRe4l4UzPwpJuxfcPWEK1SCrL4dxNjp1uqrQLMop3QZUVo+hDU8w9BJKA4JPbulTWI+UzrI2UA3tK12yQ6bg==",
"requires": {
"delayed-stream": "1.0.0"
},
"dependencies": {
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
}
}
},
"component-bind": {
@@ -902,6 +895,11 @@
"isobject": "3.0.1"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"depd": {
"version": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
"integrity": "sha512-Jlk9xvkTDGXwZiIDyoM7+3AsuvJVoyOpRupvEVy9nX3YO3/ieZxhlgh8GpLNZ8AY7HjO6y2YwpMSh1ejhu3uIw=="
@@ -1740,13 +1738,11 @@
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
"requires": {
"delayed-stream": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
},
"dependencies": {
"delayed-stream": {
"version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
}
}
},
"delayed-stream": {
"version": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
}
}
},
@@ -2591,9 +2587,9 @@
}
},
"kareem": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.0.5.tgz",
"integrity": "sha512-dfvpj3mCGJLZuADInhYrKaXkGarJxDqnTEiF91wK6fqwdCRmN+O4aEp8575UjZlQzDkzLI1WDL1uU7vyupURqw=="
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.0.6.tgz",
"integrity": "sha512-/C+l8gABdHsAIfNpykJNWmYodpTnDRyn+JhORkP2VgEf1GgdAc+oTHjVADwISwCJKta031EOIwY6+Hki5z8SpQ=="
},
"kind-of": {
"version": "3.2.2",
@@ -3066,17 +3062,17 @@
}
},
"mongoose": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.0.10.tgz",
"integrity": "sha512-vBfFP6hOHBdsWogc84cLofclWVAiu0+q0/oLxL/y61RUpW4K3BIGH2QhI+7lPBrGpGS1Yk/KfnumndWQI7wZiA==",
"version": "5.0.16",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.0.16.tgz",
"integrity": "sha512-GZqoN85gpk7+MTxZkE+yEVTtvvGfG5//X3UMWfbPQJhNO3nmmF4GFdE1qro73Vsn0PCchxyst3z55JpqZuYAZA==",
"requires": {
"async": "2.1.4",
"bson": "1.0.6",
"kareem": "2.0.5",
"kareem": "2.0.6",
"lodash.get": "4.4.2",
"mongodb": "3.0.4",
"mongodb": "3.0.7",
"mongoose-legacy-pluralize": "1.0.2",
"mpath": "0.3.0",
"mpath": "0.4.1",
"mquery": "3.0.0",
"ms": "2.0.0",
"regexp-clone": "0.0.1",
@@ -3088,30 +3084,35 @@
"resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz",
"integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=",
"requires": {
"lodash": "4.17.5"
"lodash": "4.17.10"
}
},
"lodash": {
"version": "4.17.5",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
"integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"mongodb": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.4.tgz",
"integrity": "sha512-90YIIs7A4ko4kCGafxxXj3foexCAlJBC0YLwwIKgSLoE7Vni2IqUMz6HSsZ3zbXOfR1KWtxfnc0RyAMAY/ViLg==",
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.7.tgz",
"integrity": "sha512-n/14kMJEoARXz1qhpNPhUocqy+z5130jhqgEIX1Tsl8UVpHrndQ8et+VmgC4yPK/I8Tcgc93JEMQCHTekBUnNA==",
"requires": {
"mongodb-core": "3.0.4"
"mongodb-core": "3.0.7"
}
},
"mongodb-core": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.0.4.tgz",
"integrity": "sha512-OTH267FjfwBdEufSnrgd+u8HuLWRuQ6p8DR0XirPl2BdlLEMh4XwjJf1RTlruILp5p2m1w8dDC8rCxibC3W8qQ==",
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.0.7.tgz",
"integrity": "sha512-z6YufO7s40wLiv2ssFshqoLS4+Kf+huhHq6KZ7gDArsKNzXYjAwTMnhEIJ9GQ8fIfBGs5tBLNPfbIDoCKGPmOw==",
"requires": {
"bson": "1.0.6",
"require_optional": "1.0.1"
}
},
"mpath": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.4.1.tgz",
"integrity": "sha512-NNY/MpBkALb9jJmjpBlIi6GRoLveLUM0pJzgbp9vY9F7IQEb/HREC/nxrixechcQwd1NevOhJnWWV8QQQRE+OA=="
}
}
},
@@ -3207,9 +3208,9 @@
"integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA="
},
"nodemailer": {
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.6.3.tgz",
"integrity": "sha512-1AmOpDZJtyPAO+gfUBfT+MWHbYwQ+DZvb1gvYaTxBZV/lUeysZIt4kDq8Dlwt6ViUZGp3cMGR+D1MNQYyYiVUg=="
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.6.4.tgz",
"integrity": "sha512-SD4uuX7NMzZ5f5m1XHDd13J4UC3SmdJk8DsmU1g6Nrs5h3x9LcXr6EBPZIqXRJ3LrF7RdklzGhZRF/TuylTcLg=="
},
"notepack.io": {
"version": "2.1.2",

View File

@@ -46,9 +46,9 @@
"jimp": "^0.2.28",
"mongodb": "^2.2.35",
"mongojs": "^2.5.0",
"mongoose": "^5.0.10",
"mongoose": "^5.0.16",
"mpromise": "^0.5.5",
"nodemailer": "^4.6.3",
"nodemailer": "^4.6.4",
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"redis": "^2.8.0",

View File

@@ -438,13 +438,13 @@ module.exports = function() {
List.skip(list, guid, coll.replace(/ /g,''), offline, socket);
});
socket.on('conf', function(params)
socket.on('conf', function(conf)
{
if(conf.hasOwnProperty("channel") && conf.channel.indexOf("?") > -1){
var _list = conf.channel.substring(0, conf.channel.indexOf("?"));
conf.channel = _list;
}
ListSettings.conf_function(params, coll.replace(/ /g,''), guid, offline, socket);
ListSettings.conf_function(conf, coll.replace(/ /g,''), guid, offline, socket);
});
socket.on('shuffle', function(msg)
@@ -470,10 +470,12 @@ module.exports = function() {
socket.on('change_channel', function(obj)
{
if(obj.hasOwnProperty("channel") && obj.channel.indexOf("?") > -1){
if(obj == undefined && coll != undefined) {
obj.channel = coll;
} else if(obj.hasOwnProperty("channel") && obj.channel.indexOf("?") > -1){
var _list = obj.channel.substring(0, obj.channel.indexOf("?"));
obj.channel = _list;
}
}
if(coll === undefined && obj !== undefined && obj.channel !== undefined){
try {
coll = obj.channel.toLowerCase().replace(/ /g,'');

View File

@@ -1,5 +1,5 @@
function thumbnail(msg, coll, guid, offline, socket) {
if(msg.thumbnail && msg.channel && msg.thumbnail.indexOf("i.imgur.com") > -1){
if(msg.thumbnail != undefined && msg.channel && msg.channel != undefined){
if(typeof(msg.channel) != "string" || typeof(msg.thumbnail) != "string")
{
var result = {
@@ -31,9 +31,10 @@ function thumbnail(msg, coll, guid, offline, socket) {
if(adminpass != "" || msg.adminpass == undefined) {
msg.adminpass = adminpass;
}
msg.thumbnail = msg.thumbnail.replace(/^https?\:\/\//i, "");
if(msg.thumbnail.substring(0,2) != "//") msg.thumbnail = "//" + msg.thumbnail;
if(msg.thumbnail != "") {
msg.thumbnail = msg.thumbnail.replace(/^https?\:\/\//i, "");
if(msg.thumbnail.substring(0,2) != "//") msg.thumbnail = "//" + msg.thumbnail;
}
var channel = msg.channel.toLowerCase();
var hash = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(msg.adminpass),true));
db.collection(channel + "_settings").find({id: "config"}, function(err, docs){

View File

@@ -2117,6 +2117,11 @@ nav ul li:hover, nav ul li.active {
/** settings **/
.admin-information {
padding-right: 1em;
margin-left: 2em;
}
.switch-text{
font-size: 13px !important;
padding: 0 !important;

View File

@@ -3,6 +3,7 @@ var Admin = {
beginning:true,
logged_in: false,
pw: function(msg) {
Admin.logged_in = msg;
if(!msg) return;
@@ -16,13 +17,6 @@ var Admin = {
if(Helper.html(".suggested-badge") != "0" && Helper.html(".suggested-badge") != "") {
Helper.removeClass(".suggested-badge", "hide");
}
if(!Helper.mobilecheck()) {
Helper.tooltip('#chan_thumbnail', {
delay: 5,
position: "left",
html: "imgur link"
});
}
} else {
Admin.hideUserSuggested();
}
@@ -148,6 +142,7 @@ var Admin = {
},
set_conf: function(conf_array) {
conf = conf_array;
music = conf_array.allvideos;
longsongs = conf_array.longsongs;
names = ["vote","addsongs","longsongs","frontpage", "allvideos",

View File

@@ -109,7 +109,7 @@ var Channel = {
dismissible: false
});
M.Modal.init(document.getElementById("user_password"), {
dismissible: false
dismissible: false,
});
Channel.spotify_is_authenticated(spotify_authenticated);
@@ -489,10 +489,22 @@ var Channel = {
onepage_load: function(){
if(changing_to_frontpage) return;
if(user_auth_started) {
Player.stopInterval = true;
user_auth_avoid = true;
if(!Helper.mobilecheck()) {
Helper.tooltip('.castButton', "destroy");
Helper.tooltip("#viewers", "destroy");
//$('.castButton-unactive').tooltip("destroy");
Helper.tooltip("#offline-mode", "destroy");
Helper.tooltip('#admin-lock', "destroy");
}
}
var url_split = window.location.href.split("/");
if(url_split[3].substr(0,1) != "#!" && url_split[3] !== "" && !(url_split.length == 5 && url_split[4].substr(0,1) == "#")){
socket.emit("change_channel");
socket.emit("change_channel", {
channel: channel_before_move
});
Admin.beginning = true;
chan = url_split[3].replace("#", "");
@@ -530,9 +542,6 @@ var Channel = {
Helper.tooltip('.castButton', "destroy");
Helper.tooltip("#viewers", "destroy");
Helper.tooltip("#offline-mode", "destroy");
if(M.Tooltip.getInstance(document.getElementById("chan_thumbnail")) != undefined) {
Helper.tooltip('#chan_thumbnail', "destroy");
}
Helper.tooltip('#fullscreen', "destroy");
if(M.Tooltip.getInstance(document.getElementById("admin-lock")) != undefined) {
Helper.tooltip('#admin-lock', "destroy");
@@ -554,7 +563,7 @@ var Channel = {
}
}
clearTimeout(tap_target_timeout);
before_toast();
//before_toast();
if(Helper.mobilecheck() || user_auth_avoid || client) {
Helper.log(["Removing all listeners"]);
//socket.emit("change_channel");
@@ -563,7 +572,9 @@ var Channel = {
socket.emit("left_channel", {
channel: channel_before_move
});
socket.emit("change_channel");
socket.emit("change_channel", {
channel: channel_before_move
});
chan = "";
socket.removeEventListener("np");
socket.removeEventListener("id");
@@ -609,6 +620,7 @@ var Channel = {
Helper.removeClass("#video-container", "no-opacity");
document.getElementById("main-row").insertAdjacentHTML("afterbegin", "<div id='player_bottom_overlay' class='player player_bottom'></div>");
document.getElementById("player_bottom_overlay").insertAdjacentHTML("afterbegin", "<a id='closePlayer' title='Close Player'>X</a>");
document.getElementById("player_bottom_overlay").setAttribute("data-channel", channel_before_move.toLowerCase());
Helper.removeElement("#playlist");
} else {
try{

View File

@@ -160,62 +160,39 @@ function toast(msg) {
break;
case "faulty_start_end":
if(embed) return;
msg = "You tried to send a faulty start/end value. Try again..";
break;
case "wait_longer":
if(embed) return;
msg = Helper.rnd(["Have you tried to wait longer between commands?!", "Looks like you're clicking that button too much..", "You need to wait longer between clicks.."]);
break;
case "suggested_description":
if(embed) return;
msg = "The description has been suggested!";
break;
case "thumbnail_denied":
if(embed) return;
msg = "The thumbnail will be denied";
break;
case "description_denied":
if(embed) return;
msg = "The description will be denied";
break;
case "addedsong":
if(embed) return;
msg=Helper.rnd(["I added your song", "Your song has been added", "Yay, more songs!", "Thats a cool song!", "I added that song for you", "I see you like adding songs..."]);
break;
case "addedplaylist":
if(embed) return;
msg=Helper.rnd(["I added the playlist", "Your playlist has been added", "Yay, many more songs!", "Thats a cool playlist!", "I added all the songs for you", "I see you like adding songs.."]);
document.getElementById("import").disabled = false;
Helper.addClass("#playlist_loader", "hide");
Helper.removeClass("#import", "hide");
break;
case "savedsettings":
if(embed) return;
msg=Helper.rnd(["I've saved your settings", "I stored all your settings", "Your settings have been stored in a safe place"]);
break;
case "wrongpass":
if(embed) return;
msg=Helper.rnd(["That's not the right password!", "Wrong! Better luck next time...", "You seem to have mistyped the password", "Incorrect. Have you tried meditating?","Nope, wrong password!", "Wrong password. The authorities have been notified."]);
//Crypt.remove_pass(chan.toLowerCase());
Admin.display_logged_out();
Helper.css("#thumbnail_form", "display", "none");
Helper.css("#description_form", "display", "none");
if(!Helper.mobilecheck()) {
Helper.tooltip('#chan_thumbnail', "destroy");
}
w_p = true;
break;
case "deleted_songs":
if(embed) return;
msg="All songs in the channel has been deleted!";
break;
case "shuffled":
if(embed) return;
msg=Helper.rnd(["♫ You stir me right round, baby. ♫","♫ Stir, stir, stir my boat ♫","I vigorously stirred your playlist!", "I hope you like your list stirred, not shaken.", "I shuffled your playlist with the cosmic background radiation as a seed. Enjoy.", "100% randomized, for your listening pleasure!", "I hope you enjoy your fresh playlist!"]);
break;
case "deletesong":
if(embed) return;
msg=Helper.rnd(["Your song is now in a better place...", "You won't be seeing any more of that video...", "EXTERMINATE! EXTERMINATE! EXTERMINATE!", "I killed it with fire", "Thanks for deleting that song. I didn't like it anyways...", "Removed song securely."]);
break;
case "voted":
msg=Helper.rnd(["You voted!", "You vote like a boss", "Voting is the key to democracy", "May you get your song to the very top!", "I love that song! I vouch for you.", "Only you vote that good", "I like the way you vote...", "Up the video goes!", "Voted Zoff for president", "Only 999 more to go!"]);
@@ -225,88 +202,30 @@ function toast(msg) {
break;
case "skip":
if(embed) return;
msg=Helper.rnd(["The song was skipped", "I have skipped a song", "Skipped to the beat", "Skipmaster3000", "They see me skippin', they hatin'"]);
break;
case "listhaspass":
if(embed) return;
if(!tried_again && lastCommand != undefined && lastCommand.length > 0) {
if(Crypt.get_pass() != undefined) {
tried_again = true;
if(lastCommand.length == 1) {
socket.emit(lastCommand[0]);
} else if(lastCommand.length == 2) {
socket.emit(lastCommand[0], lastCommand[1]);
}
lastCommand = [];
return;
}
}
tried_again = false;
msg=Helper.rnd(["I'm sorry, but you have to be an admin to do that!", "Only admins can do that", "You're not allowed to do that, try logging in!", "I can't let you do that", "Please log in to do that"]);
//Crypt.remove_pass(chan.toLowerCase());
Admin.display_logged_out();
Helper.css("#thumbnail_form", "display", "none");
Helper.css("#description_form", "display", "none");
if(!Helper.mobilecheck()) {
Helper.tooltip('#chan_thumbnail', "destroy");
}
w_p = true;
Helper.addClass("#playlist_loader", "hide");
Helper.addClass("#playlist_loader_spotify", "hide");
Helper.removeClass("#import_spotify", "hide");
Helper.removeClass("#import", "hide");
break;
case "noskip":
if(embed) return;
if(!tried_again && lastCommand != undefined && lastCommand.length > 0) {
if(Crypt.get_pass() != undefined) {
tried_again = true;
if(lastCommand.length == 1) {
socket.emit(lastCommand[0]);
} else if(lastCommand.length == 2) {
socket.emit(lastCommand[0], lastCommand[1]);
}
lastCommand = [];
return;
}
}
tried_again = false;
msg=Helper.rnd(["Only Admins can skip songs, peasant!", "You have to log in to skip songs on this channel", "Try clicking the settings icon and logging in before you skip"]);
break;
case "alreadyskip":
if(embed) return;
msg=Helper.rnd(["Skipping is democratic, only one vote per person!", "More people have to vote to skip, not just you!", "Get someone else to skip too! You can't do it on yourself."]);
break;
case "notyetskip":
if(embed) return;
msg="Skipping is disabled the first 10 seconds.";
break;
case "correctpass":
if(embed) return;
tried_again = false;
adminpass = Crypt.get_pass(chan.toLowerCase()) == undefined ? Crypt.tmp_pass : Crypt.get_pass(chan.toLowerCase());
msg="Correct password. You now have access to the sacred realm of The Admin.";
Helper.css("#thumbnail_form", "display", "inline-block");
Helper.css("#description_form", "display", "inline-block");
if(!Helper.mobilecheck()) {
Helper.tooltip('#chan_thumbnail', {
delay: 5,
position: "left",
html: "imgur link"
});
}
break;
case "changedpass":
if(embed) return;
msg="Your password has been changed!";
break;
case "suggested":
if(embed) return;
msg="Your song was suggested!";
break;
case "alreadyplay":
if(embed) return;
msg="Seems the song you want is already playing. No fooling the system!";
break;
}
before_toast();

View File

@@ -740,9 +740,6 @@ function toast(msg) {
Admin.display_logged_out();
Helper.css("#thumbnail_form", "display", "none");
Helper.css("#description_form", "display", "none");
if(!Helper.mobilecheck()) {
Helper.tooltip('#chan_thumbnail', "destroy");
}
w_p = true;
break;
case "deleted_songs":
@@ -789,9 +786,6 @@ function toast(msg) {
Admin.display_logged_out();
Helper.css("#thumbnail_form", "display", "none");
Helper.css("#description_form", "display", "none");
if(!Helper.mobilecheck()) {
Helper.tooltip('#chan_thumbnail', "destroy");
}
w_p = true;
Helper.addClass("#playlist_loader", "hide");
Helper.addClass("#playlist_loader_spotify", "hide");
@@ -868,7 +862,17 @@ function before_toast(){
var toastInstance = toastElement.M_Toast;
toastInstance.remove();
}*/
M.Toast.dismissAll();
var toasts = document.querySelectorAll(".toast");
for(var i = 0; i < toasts.length; i++) {
var instance = M.Toast.getInstance(toasts[i]);
try {
if(instance.timeRemaining > 10) {
instance.dismiss();
}
} catch(e) {
}
}
//M.Toast.dismissAll();
//Materialize.Toast.removeAll();
}

View File

@@ -12,7 +12,6 @@ var list_html = document.querySelectorAll("#list-song-html").length > 0 ? docume
var unseen = false;
var searching = false;
var time_regex = /P((([0-9]*\.?[0-9]*)Y)?(([0-9]*\.?[0-9]*)M)?(([0-9]*\.?[0-9]*)W)?(([0-9]*\.?[0-9]*)D)?)?(T(([0-9]*\.?[0-9]*)H)?(([0-9]*\.?[0-9]*)M)?(([0-9]*\.?[0-9]*)S)?)?/;
var conf = [];
var private_channel = false;
var end_programmatically = false;
var _kWay = "38384040373937396665";
@@ -797,7 +796,8 @@ addListener("click", ".submit-user-password", function(event) {
}
});
addListener("click", ".close-user-password", function() {
addListener("click", "#abort-channel-login", function(event) {
this.preventDefault();
if(user_auth_started) {
Player.stopInterval = true;
user_auth_avoid = true;
@@ -806,11 +806,11 @@ addListener("click", ".close-user-password", function() {
Helper.tooltip("#viewers", "destroy");
//$('.castButton-unactive').tooltip("destroy");
Helper.tooltip("#offline-mode", "destroy");
Helper.tooltip('#chan_thumbnail', "destroy");
Helper.tooltip('#admin-lock', "destroy");
}
window.history.pushState("to the frontpage!", "Title", "/");
Channel.onepage_load();
user_auth_started = false;
} else {
document.getElementById("user-pass-input").value = "";
if(!user_change_password) {
@@ -1438,7 +1438,7 @@ addListener("click", ".brand-logo-navigate", function(event){
addListener("click", "#player_bottom_overlay", function(event){
if(this.target.id == "closePlayer") return;
Frontpage.to_channel(chan.toLowerCase(), false);
Frontpage.to_channel(this.target.getAttribute("data-channel"), false);
});
addListener("click", ".generate-channel-name", function(event) {

View File

@@ -126,6 +126,7 @@ var Player = {
startTime = time - conf.startTime;
song_title = obj.np[0].title;
duration = obj.np[0].duration;
Player.setThumbnail(conf, video_id);
Player.cueVideoById(video_id, duration);
//Player.setBGimage(video_id);
} else if(!paused){
@@ -155,7 +156,7 @@ var Player = {
startTime = time - conf.startTime;
song_title = obj.np[0].title;
duration = obj.np[0].duration;
Player.setThumbnail(conf, video_id);
if(mobile_beginning && Helper.mobilecheck() && seekTo === 0 && !chromecastAvailable) {
seekTo = 1 + Player.np.start;
}
@@ -247,6 +248,12 @@ var Player = {
}
},
setThumbnail: function(conf, video_id) {
if(!conf.hasOwnProperty("thumbnail") || conf.thumbnail == "") {
document.getElementById("thumbnail_image").innerHTML = "<img id='thumbnail_image_channel' src='https://img.youtube.com/vi/"+video_id+"/mqdefault.jpg' alt='thumbnail' />";
}
},
onPlayerStateChange: function(newState) {
Helper.log([
"onPlayerStateChange",

View File

@@ -60,7 +60,7 @@
</form>
</div>
<div class="modal-footer">
<a href="#!" class="modal-action modal-close waves-effect waves-green btn-flat close-user-password">Close</a>
<a href="#!" id="abort-channel-login" class="modal-action modal-close waves-effect waves-green btn-flat close-user-password">Close</a>
<a href="#!" class="waves-effect waves-green btn-flat submit-user-password">Submit</a>
</div>
</div>

View File

@@ -1,401 +1,403 @@
<div class="nav-btn close-settings clickable" title="Close" id="closeSettings">
<i class="material-icons auto-margin">close</i>
</div>
<ul class="collapsible collapsible-accordion settings-collapsible">
<li class="no-padding">
<div class="col s9 collapsible-header bold waves-effect admin-settings">
Channel Settings
<i class="material-icons">tune</i>
</div>
<div class="collapsible-body">
<div class="nav-btn close-settings clickable" title="Close" id="closeSettings">
<i class="material-icons auto-margin">close</i>
</div>
<ul class="collapsible collapsible-accordion settings-collapsible">
<li class="no-padding">
<div class="col s9 collapsible-header bold waves-effect admin-settings">
Channel Settings
<i class="material-icons">tune</i>
</div>
<div class="collapsible-body">
<ul>
<form action="#" id="adminForm" onsubmit="return false;">
<ul>
<li class="white-bg">
<div class="input-field field-settings">
<i id="admin-lock" class="material-icons">lock</i>
<input placeholder="Enter admin password" id="password" type="password" class="validate" />
</div>
</li>
<li>
<span class="switch-text">
Add songs
</span>
<div class="switch">
<label>
<span class="left-span">Anyone</span>
<input name="addsongs" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Admin</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Vote
</span>
<div class="switch">
<label>
<span class="left-span">Anyone</span>
<input name="vote" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Admin</span>
</label>
</div>
</li>
<li class="white-bg">
<div class="input-field field-settings">
<i id="admin-lock" class="material-icons">lock</i>
<input placeholder="Enter admin password" id="password" type="password" class="validate" />
</div>
</li>
<li>
<span class="switch-text">
Add songs
</span>
<div class="switch">
<label>
<span class="left-span">Anyone</span>
<input name="addsongs" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Admin</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Shuffle
</span>
<div class="switch">
<label>
<span class="left-span">Anyone</span>
<input name="shuffle" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Admin</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Vote
</span>
<div class="switch">
<label>
<span class="left-span">Anyone</span>
<input name="vote" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Admin</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Skip
</span>
<div class="switch">
<label>
<span class="left-span">Anyone</span>
<input name="skip" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Admin</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Shuffle
</span>
<div class="switch">
<label>
<span class="left-span">Anyone</span>
<input name="shuffle" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Admin</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Song length
</span>
<div class="switch">
<label>
<span class="left-span">Any</span>
<input name="longsongs" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Short</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Skip
</span>
<div class="switch">
<label>
<span class="left-span">Anyone</span>
<input name="skip" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Admin</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Type
</span>
<div class="switch">
<label>
<span class="left-span">Any</span>
<input name="allvideos" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Song</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Song length
</span>
<div class="switch">
<label>
<span class="left-span">Any</span>
<input name="longsongs" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Short</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Type
</span>
<div class="switch">
<label>
<span class="left-span">Any</span>
<input name="allvideos" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Song</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Frontpage
</span>
<div class="switch">
<label>
<span class="left-span">Hide</span>
<input name="frontpage" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Display</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
Frontpage
</span>
<div class="switch">
<label>
<span class="left-span">Hide</span>
<input name="frontpage" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Display</span>
</label>
</div>
</li>
<li>
<span class="switch-text">
After play
</span>
<div class="switch">
<label>
<span class="left-span">Keep</span>
<input name="removeplay" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Remove</span>
</label>
<li>
<span class="switch-text">
After play
</span>
<div class="switch">
<label>
<span class="left-span">Keep</span>
<input name="removeplay" type="checkbox" class="conf" /><span class="lever"></span>
<span class="right-span">Remove</span>
</label>
</div>
</li>
<li class="user-password-li hide">
<span class="switch-text">
Channel password
</span>
<div class="switch">
<label>
<span class="left-span">No</span>
<input name="userpass" type="checkbox" class="conf password_protected" /><span class="lever"></span>
<span class="right-span">Yes</span>
</label>
</div>
</li>
</form>
<li class="change_user_pass hide">
<a href="#!" class="change_user_pass_btn btn waves-effect blue">Change password</a>
</li>
<li class="delete-all hide">
<a href="#" class="delete-all-songs btn red">Delete all songs</a>
</li>
</ul>
</div>
</li>
<li class="no-padding">
<div class="collapsible-header bold waves-effect">Channel Info
<i class="material-icons">info_outline</i>
</div>
<div class="collapsible-body">
<ul>
<li>
<form id="thumbnail_form" style="display:none;">
<div class="input-field col s12 admin-information">
<input type="text" placeholder="Channel thumbnail" name="chan_thumbnail" id="chan_thumbnail" autocomplete="off" />
</div>
</form>
</li>
<li>
<form id="description_form" style="display:none;">
<div class="input-field col s12 admin-information">
<input type="text" placeholder="Channel description" name="chan_description" id="chan_description" autocomplete="off" maxlength="100" data-length="100" />
</div>
</form>
</li>
<li class="channel_info_container">
<div id="thumbnail_image">
</div>
<div id="description_area">
This channel doesn't have a description yet.
</div>
</li>
</ul>
</div>
</li>
{{#unless client}}
<li class="no-padding remote-panel hide-on-small-only">
<div class="collapsible-header bold waves-effect">Remote Control
<i class="material-icons">settings_remote</i>
</div>
<div class="collapsible-body">
<ul>
<li>
<span class="switch-text">
Enable Remote
</span>
<div class="switch">
<label>
Disabled
<input name="remote_switch" type="checkbox" class="remote_switch_class" checked /><span class="lever"></span>
Enabled
</label>
</div>
<div><a id="code-link" target="_blank">
<img id="code-qr" alt="QR code for control" title="Link to control this Zoff player" src="https://chart.googleapis.com/chart?chs=221x221&amp;cht=qr&amp;choe=UTF-8&amp;chld=L%7C1&amp;chl=http://zoff.me" />
<h4 id="code-text">ABBADUR</h4>
</a></div>
<p>
You can control this Zoff instance from another device by going to <b>https://remote.zoff.me/</b>
</p>
</li>
</ul>
</div>
</li>
<li class="no-padding offline-panel">
<div class="collapsible-header bold waves-effect">Local Mode
<i class="material-icons">visibility_off</i>
</div>
<div class="collapsible-body">
<ul>
<li>
<span class="switch-text">
Local Mode
</span>
<div class="switch">
<label>
Disabled
<input name="offline_switch" type="checkbox" class="offline_switch_class" /><span class="lever"></span>
Enabled
</label>
</div>
<div id="offline-info">
By enabling local mode, you won't receive updates in position of the currently playing song, you'll be able to vote as many times as you please, and you can skip to a specific location in the song.
</div>
</li>
</ul>
</div>
</li>
<li class="no-padding show-only-mobile mobile-remote-panel">
<div class="collapsible-header bold waves-effect import-a">Remote Controller
<i class="material-icons">settings_remote</i>
</div>
<div class="collapsible-body">
<ul id="remote-mobile-container">
<li class="white-bg">
<p id="remote_header">Control another client</p>
<form action="#" class="row" id="remoteform">
<div class="input-field col s12">
<input
class="input-field"
type="text"
id="remote_channel"
name="chan"
autocomplete="off"
spellcheck="false"
maxlength="10"
data-length="10"
placeholder="ID to remotecontroll"
/>
</div>
</form>
<div id="remote-sidebar-buttons-container">
<button id="playbutton_remote" class="remote-button waves-effect btn green" disabled>
<i id="remote_play" class="material-icons">play_arrow</i>
</button>
<button id="pausebutton_remote" class="remote-button waves-effect btn gray" disabled>
<i id="remote_pause" class="material-icons">pause</i>
</button>
<button id="skipbutton_remote" class="remote-button waves-effect btn blue" disabled>
<i id="remote_skip" class="material-icons">skip_next</i>
</button>
</div>
<div class="" id="volume-control-remote" title="Volume"></div>
<i class="material-icons slider-vol-mobile">volume_up</i>
</li>
</ul>
</div>
</li>
<li class="no-padding import-panel">
<div class="collapsible-header bold waves-effect import-a">Import Playlist
<i class="material-icons">keyboard_arrow_down</i>
</div>
<div class="collapsible-body">
<ul>
<li class="white-bg">
<div class="input-field field-settings youtube_unclicked import-buttons">
<a class="waves-effect red btn import-youtube" title="Import from YouTube playlist">
<img src="/assets/images/youtube.png" class="youtube_logo" alt="Youtube Logo" />
</a>
</div>
<div class="input-field field-settings youtube_clicked">
<form action="#" id="listImport">
<i class="material-icons import-icon">playlist_add</i>
<input title="Input YouTube-playlist url here!" placeholder="Enter YouTube-list URL" id="import" type="text" class="validate" autocomplete="off" />
<div class="valign playlist_loader_padding">
<div id="playlist_loader" class="preloader-wrapper small active hide">
{{> spinner}}
</div>
</div>
</li>
<li class="user-password-li hide">
<span class="switch-text">
Channel password
</span>
<div class="switch">
<label>
<span class="left-span">No</span>
<input name="userpass" type="checkbox" class="conf password_protected" /><span class="lever"></span>
<span class="right-span">Yes</span>
</label>
</form>
</div>
</li>
<li class="white-bg">
<div class="input-field field-settings spotify_unauthenticated import-buttons">
<a class="waves-effect green lighten btn import-spotify-auth" title="Import Spotify playlist">
<img src="/assets/images/spotify.png" class="left spotify_logo" alt="Spotify Logo" />Spotify
</a>
</div>
<div class="input-field field-settings spotify_authenticated">
<form action="#" id="listImportSpotify">
<i class="material-icons import-icon">playlist_add</i>
<input title="Input Spotify-playlist url here!" placeholder="Enter Spotify-list url" id="import_spotify" type="text" class="validate" autocomplete="off" />
<div id="playlist_loader_spotify" class="valign playlist_loader_padding hide">
<div class="preloader-wrapper small active">
{{> spinner}}
</div>
</div>
</li>
<li class="change_user_pass hide">
<a href="#!" class="change_user_pass_btn btn waves-effect blue">Change password</a>
</li>
<li class="delete-all hide">
<a href="#" class="delete-all-songs btn red">Delete all songs</a>
</form>
</div>
</li>
<li class="white-bg">
<div class="input-field field-settings import-buttons import-zoff-container">
<a class="waves-effect zoff-color lighten btn import-zoff" title="Import Zoff playlist">
<img src="/assets/images/z.svg" class="left zoff-logo zoff-image-import" alt="Zoff Logo" />Zoff
</a>
</div>
<div class="input-field field-settings zoff_add_field hide">
<form action="#" id="listImportZoff">
<i class="material-icons import-icon">playlist_add</i>
<input title="Input Zoff-playlist name here!" placeholder="Enter Zoff-list" id="import_zoff" type="text" class="validate" autocomplete="off" />
<div id="playlist_loader_zoff" class="valign playlist_loader_padding hide">
<div class="preloader-wrapper small active">
{{> spinner}}
</div>
</div>
</form>
</div>
</li>
<li class="not-imported white-bg hide">
<div class="center-align">Not imported</div>
<ul class="input-field field-settings not-imported-container">
<li class="white-bg not-imported-element">
<div class="extra-add-text truncate"></div>
<a href="#" class="waves-effect red lighten btn right extra-button extra-button-delete">X</a>
<a href="#" class="waves-effect green lighten btn right extra-button extra-button-search">
<i class="material-icons search-extra">search</i>
</a>
</li>
</ul>
</form>
</div>
</li>
<li class="no-padding">
<div class="collapsible-header bold waves-effect">Channel Info
<i class="material-icons">info_outline</i>
</div>
<div class="collapsible-body">
<ul>
<li>
<form id="thumbnail_form" style="display:none;">
<div class="input-field col s12">
<input type="text" placeholder="Thumbnail" name="chan_thumbnail" id="chan_thumbnail" autocomplete="off" />
</div>
</form>
</li>
<li>
<form id="description_form" style="display:none;">
<div class="input-field col s12">
<input type="text" placeholder="Description" name="chan_description" id="chan_description" autocomplete="off" maxlength="100" data-length="100" />
</div>
</form>
</li>
<li class="channel_info_container">
<div id="thumbnail_image">
</div>
<div id="description_area">
This channel doesn't have a description yet..
</div>
</li>
</ul>
</div>
</li>
{{#unless client}}
<li class="no-padding remote-panel hide-on-small-only">
<div class="collapsible-header bold waves-effect">Remote Control
<i class="material-icons">settings_remote</i>
</div>
<div class="collapsible-body">
<ul>
<li>
<span class="switch-text">
Enable Remote
</span>
<div class="switch">
<label>
Disabled
<input name="remote_switch" type="checkbox" class="remote_switch_class" checked /><span class="lever"></span>
Enabled
</label>
</div>
<div><a id="code-link" target="_blank">
<img id="code-qr" alt="QR code for control" title="Link to control this Zoff player" src="https://chart.googleapis.com/chart?chs=221x221&amp;cht=qr&amp;choe=UTF-8&amp;chld=L%7C1&amp;chl=http://zoff.me" />
</li>
</ul>
</div>
</li>
<h4 id="code-text">ABBADUR</h4>
</a></div>
<p>
You can control this Zoff instance from another device by going to <b>https://remote.zoff.me/</b>
</p>
</li>
</ul>
</div>
</li>
<li class="no-padding offline-panel">
<div class="collapsible-header bold waves-effect">Local Mode
<i class="material-icons">visibility_off</i>
</div>
<div class="collapsible-body">
<ul>
<li>
<span class="switch-text">
Local Mode
</span>
<div class="switch">
<label>
Disabled
<input name="offline_switch" type="checkbox" class="offline_switch_class" /><span class="lever"></span>
Enabled
</label>
</div>
<div id="offline-info">
By enabling local mode, you won't receive updates in position of the currently playing song, you'll be able to vote as many times as you please, and you can skip to a specific location in the song.
</div>
</li>
</ul>
</div>
</li>
<li class="no-padding show-only-mobile mobile-remote-panel">
<div class="collapsible-header bold waves-effect import-a">Remote Controller
<i class="material-icons">settings_remote</i>
</div>
<div class="collapsible-body">
<ul id="remote-mobile-container">
<li class="white-bg">
<p id="remote_header">Control another client</p>
<form action="#" class="row" id="remoteform">
<div class="input-field col s12">
<input
class="input-field"
type="text"
id="remote_channel"
name="chan"
autocomplete="off"
spellcheck="false"
maxlength="10"
data-length="10"
placeholder="ID to remotecontroll"
/>
<li class="no-padding export-panel">
<div class="collapsible-header bold waves-effect export-a">Export Playlist
<i class="material-icons">keyboard_arrow_up</i>
</div>
<div class="collapsible-body">
<ul>
<li class="white-bg">
<div class="input-field field-settings youtube_export_button export-buttons">
<a class="waves-effect red btn export-youtube" id="listExport" title="Export to YouTube">
<img src="/assets/images/youtube.png" class="youtube_logo" alt="Youtube Logo" />
</a>
</div>
</li>
<li class="white-bg">
<div class="input-field field-settings spotify_export_button export-buttons">
<a class="waves-effect green lighten btn export-spotify-auth" title="Export Spotify playlist">
<img src="/assets/images/spotify.png" class="left spotify_logo" alt="Spotify Logo" />Spotify
</a>
</div>
</li>
<li class="exported-list-container white-bg hide">
<div class="valign playlist_loader_padding">
<div class="row">
<div class="col s2">
<div id="playlist_loader_export" class="preloader-wrapper small active hide">
{{> spinner}}
</div>
</div>
</form>
<div id="remote-sidebar-buttons-container">
<button id="playbutton_remote" class="remote-button waves-effect btn green" disabled>
<i id="remote_play" class="material-icons">play_arrow</i>
</button>
<button id="pausebutton_remote" class="remote-button waves-effect btn gray" disabled>
<i id="remote_pause" class="material-icons">pause</i>
</button>
<button id="skipbutton_remote" class="remote-button waves-effect btn blue" disabled>
<i id="remote_skip" class="material-icons">skip_next</i>
</button>
<div class="current_number hide col s8 offset-s2">0/0</div>
</div>
<div class="" id="volume-control-remote" title="Volume"></div>
<i class="material-icons slider-vol-mobile">volume_up</i>
</li>
</ul>
</div>
</li>
<li class="no-padding import-panel">
<div class="collapsible-header bold waves-effect import-a">Import Playlist
<i class="material-icons">keyboard_arrow_down</i>
</div>
<div class="collapsible-body">
<ul>
<li class="white-bg">
<div class="input-field field-settings youtube_unclicked import-buttons">
<a class="waves-effect red btn import-youtube" title="Import from YouTube playlist">
<img src="/assets/images/youtube.png" class="youtube_logo" alt="Youtube Logo" />
</a>
</div>
<div class="input-field field-settings youtube_clicked">
<form action="#" id="listImport">
<i class="material-icons import-icon">playlist_add</i>
<input title="Input YouTube-playlist url here!" placeholder="Enter YouTube-list URL" id="import" type="text" class="validate" autocomplete="off" />
<div class="valign playlist_loader_padding">
<div id="playlist_loader" class="preloader-wrapper small active hide">
{{> spinner}}
</div>
</li>
<li class="white-bg not-exported-height">
<div class="exported-list input-field field-settings export-buttons">
</div>
<ul>
<li class="not-exported white-bg hide">
<div class="center-align">Not exported songs</div>
<ul class="input-field field-settings not-exported-container">
<li class="white-bg not-exported-element">
<div id="extra-export-container-text">
<input class="extra-add-text truncate" readonly />
</div>
</div>
</form>
</div>
</li>
<li class="white-bg">
<div class="input-field field-settings spotify_unauthenticated import-buttons">
<a class="waves-effect green lighten btn import-spotify-auth" title="Import Spotify playlist">
<img src="/assets/images/spotify.png" class="left spotify_logo" alt="Spotify Logo" />Spotify
</a>
</div>
<div class="input-field field-settings spotify_authenticated">
<form action="#" id="listImportSpotify">
<i class="material-icons import-icon">playlist_add</i>
<input title="Input Spotify-playlist url here!" placeholder="Enter Spotify-list url" id="import_spotify" type="text" class="validate" autocomplete="off" />
<div id="playlist_loader_spotify" class="valign playlist_loader_padding hide">
<div class="preloader-wrapper small active">
{{> spinner}}
</div>
</div>
</form>
</div>
</li>
<li class="white-bg">
<div class="input-field field-settings import-buttons import-zoff-container">
<a class="waves-effect zoff-color lighten btn import-zoff" title="Import Zoff playlist">
<img src="/assets/images/z.svg" class="left zoff-logo zoff-image-import" alt="Zoff Logo" />Zoff
</a>
</div>
<div class="input-field field-settings zoff_add_field hide">
<form action="#" id="listImportZoff">
<i class="material-icons import-icon">playlist_add</i>
<input title="Input Zoff-playlist name here!" placeholder="Enter Zoff-list" id="import_zoff" type="text" class="validate" autocomplete="off" />
<div id="playlist_loader_zoff" class="valign playlist_loader_padding hide">
<div class="preloader-wrapper small active">
{{> spinner}}
</div>
</div>
</form>
</div>
</li>
<li class="not-imported white-bg hide">
<div class="center-align">Not imported</div>
<ul class="input-field field-settings not-imported-container">
<li class="white-bg not-imported-element">
<div class="extra-add-text truncate"></div>
<a href="#" class="waves-effect red lighten btn right extra-button extra-button-delete">X</a>
<a href="#" class="waves-effect green lighten btn right extra-button extra-button-search">
<i class="material-icons search-extra">search</i>
</a>
</li>
</ul>
</li>
</ul>
</div>
</li>
<li class="no-padding export-panel">
<div class="collapsible-header bold waves-effect export-a">Export Playlist
<i class="material-icons">keyboard_arrow_up</i>
</div>
<div class="collapsible-body">
<ul>
<li class="white-bg">
<div class="input-field field-settings youtube_export_button export-buttons">
<a class="waves-effect red btn export-youtube" id="listExport" title="Export to YouTube">
<img src="/assets/images/youtube.png" class="youtube_logo" alt="Youtube Logo" />
</a>
</div>
</li>
<li class="white-bg">
<div class="input-field field-settings spotify_export_button export-buttons">
<a class="waves-effect green lighten btn export-spotify-auth" title="Export Spotify playlist">
<img src="/assets/images/spotify.png" class="left spotify_logo" alt="Spotify Logo" />Spotify
</a>
</div>
</li>
<li class="exported-list-container white-bg hide">
<div class="valign playlist_loader_padding">
<div class="row">
<div class="col s2">
<div id="playlist_loader_export" class="preloader-wrapper small active hide">
{{> spinner}}
</div>
</div>
<div class="current_number hide col s8 offset-s2">0/0</div>
</div>
</div>
</li>
<li class="white-bg not-exported-height">
<div class="exported-list input-field field-settings export-buttons">
</div>
<ul>
<li class="not-exported white-bg hide">
<div class="center-align">Not exported songs</div>
<ul class="input-field field-settings not-exported-container">
<li class="white-bg not-exported-element">
<div id="extra-export-container-text">
<input class="extra-add-text truncate" readonly />
</div>
<a href="#" class="waves-effect red lighten btn right extra-button extra-button-delete">X</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</li>
{{/unless}}
<a href="#" class="waves-effect red lighten btn right extra-button extra-button-delete">X</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</li>
{{/unless}}
</ul>