Teambuilder folder feature

In addition to format folders, we now support regular folders that
behave as you'd expect. These folders even map to actual
directories in the downloadable client!

Folders are delimited by `/` in team names in all storage formats
(including export, and packed saving), and are stored in the
`.folder` field of team objects.

Because of this, `/` is no longer an acceptable character in team
names. This improves interoperability with teams and filesystems,
so we should probably have done this anyway.

I also added back a friendly message to the top of the teambuilder.
This commit is contained in:
Guangcong Luo
2015-12-16 22:09:16 -05:00
parent 486a272417
commit 42ddd81c68
7 changed files with 379 additions and 98 deletions

View File

@@ -33,7 +33,8 @@ module.exports = {
"Popup": false, "ForfeitPopup": false, "BracketPopup": false, "LoginPasswordPopup": false, "UserPopup": false, "TeamPopup": false,
"AvatarsPopup": false, "BattleListPopup": false, "CreditsPopup": false, "FormatPopup": false, "FormattingPopup": false, "LoginPopup": false,
"MovePopup": false, "SoundsPopup": false, "OptionsPopup": false, "PromptPopup": false, "ProxyPopup": false, "ReconnectPopup": false,
"RegisterPopup": false, "ReplayUploadedPopup": false, "RulesPopup": false, "TabListPopup": false, "TournamentBox": false
"RegisterPopup": false, "ReplayUploadedPopup": false, "RulesPopup": false, "TabListPopup": false, "TournamentBox": false,
"CustomBackgroundPopup": false
},
"rules": {
"comma-dangle": [2, "never"],

View File

@@ -334,6 +334,10 @@ var Tools = {
tracker.callbacks[i][0].call(tracker.callbacks[i][1], value);
}
};
tracker.unload = function () {
if (!tracker.isLoaded) return;
tracker.isLoaded = false;
};
return tracker;
},

View File

@@ -42,11 +42,10 @@
}
buf += '<div class="menugroup"><p><button class="button mainmenu2" name="joinRoom" value="teambuilder">Teambuilder</button></p>';
buf += '<p><button class="button mainmenu3" name="joinRoom" value="ladder">Ladder</button></p>';
buf += '<p><button class="button mainmenu4" name="credits">Credits</button></p></div></div>';
buf += '<p><button class="button mainmenu3" name="joinRoom" value="ladder">Ladder</button></p></div>';
buf += '<div class="menugroup"><p><button class="button mainmenu5 onlineonly disabled" name="roomlist">Watch a battle</button></p>';
buf += '<p><button class="button mainmenu6 onlineonly disabled" name="finduser">Find a user</button></p></div>';
buf += '<div class="menugroup"><p><button class="button mainmenu4 onlineonly disabled" name="roomlist">Watch a battle</button></p>';
buf += '<p><button class="button mainmenu5 onlineonly disabled" name="finduser">Find a user</button></p></div>';
this.$('.mainmenu').html(buf);

View File

@@ -29,7 +29,6 @@
this.saveFlag = false;
app.user.trigger('saveteams');
}
this.curFormatKeep = '';
},
events: {
// team changes
@@ -112,7 +111,17 @@
curTeamLoc: 0,
curSet: null,
curSetLoc: 0,
curFormat: '',
// curFolder will have '/' at the end if it's a folder, but
// it will be alphanumeric (so guaranteed no '/') if it's a
// format
// Special values:
// '' - show all
// 'gen6' - show teams with no format
// '/' - show teams with no folder
curFolder: '',
curFolderKeep: '',
exportMode: false,
update: function () {
teams = Storage.teams;
@@ -169,15 +178,27 @@
updateFolderList: function () {
var buf = '<div class="folderlist"><div class="folderlistbefore"></div>';
buf += '<div class="folder' + (!this.curFormat ? ' cur"><div class="folderhack3"><div class="folderhack1"></div><div class="folderhack2"></div>' : '">') + '<div class="selectFolder" data-value="all">(all)</div></div>' + (!this.curFormat ? '</div>' : '');
buf += '<div class="folder' + (!this.curFolder ? ' cur"><div class="folderhack3"><div class="folderhack1"></div><div class="folderhack2"></div>' : '">') + '<div class="selectFolder" data-value="all"><em>(all)</em></div></div>' + (!this.curFolder ? '</div>' : '');
var folderTable = {};
var folders = [];
if (Storage.teams) for (var i = -2; i < Storage.teams.length; i++) {
if (i >= 0) {
var folder = Storage.teams[i].folder;
if (folder && !((folder + '/') in folderTable)) {
folders.push('Z' + folder);
folderTable[folder + '/'] = 1;
if (!('/' in folderTable)) {
folders.push('Z~');
folderTable['/'] = 1;
}
}
}
var format;
if (i === -2) {
format = this.curFormatKeep;
format = this.curFolderKeep;
} else if (i === -1) {
format = this.curFormat;
format = this.curFolder;
} else {
format = Storage.teams[i].format;
if (!format) format = 'gen6';
@@ -185,36 +206,66 @@
if (!format) continue;
if (format in folderTable) continue;
folderTable[format] = 1;
if (format.slice(-1) === '/') {
folders.push('Z' + (format.slice(0, -1) || '~'));
if (!('/' in folderTable)) {
folders.push('Z~');
folderTable['/'] = 1;
}
continue;
}
if (format === 'gen6') {
folders.push('A~');
continue;
}
switch (format.slice(0, 4)) {
case 'gen1': format = 'Z' + format.slice(4); break;
case 'gen2': format = 'X' + format.slice(4); break;
case 'gen3': format = 'Y' + format.slice(4); break;
case 'gen4': format = 'W' + format.slice(4); break;
case 'gen5': format = 'V' + format.slice(4); break;
case 'gen1': format = 'F' + format.slice(4); break;
case 'gen2': format = 'E' + format.slice(4); break;
case 'gen3': format = 'D' + format.slice(4); break;
case 'gen4': format = 'C' + format.slice(4); break;
case 'gen5': format = 'B' + format.slice(4); break;
default: format = 'A' + format; break;
}
folders.push(format);
}
folders.sort();
var gen = '';
var formatFolderBuf = '<div class="foldersep"></div>';
formatFolderBuf += '<div class="folder"><div class="selectFolder" data-value="+"><i class="fa fa-plus"></i><em>(add format folder)</em></div></div>';
for (var i = 0; i < folders.length; i++) {
var format = folders[i];
var newGen;
switch (format.charAt(0)) {
case 'Z': newGen = '1'; break;
case 'Y': newGen = '2'; break;
case 'X': newGen = '3'; break;
case 'W': newGen = '4'; break;
case 'V': newGen = '5'; break;
case 'F': newGen = '1'; break;
case 'E': newGen = '2'; break;
case 'D': newGen = '3'; break;
case 'C': newGen = '4'; break;
case 'B': newGen = '5'; break;
case 'A': newGen = '6'; break;
case 'Z': newGen = '/'; break;
}
if (gen !== newGen) {
gen = newGen;
buf += '<div class="folder"><h3>Gen ' + gen + '</h3></div>';
if (gen === '/') {
buf += formatFolderBuf;
formatFolderBuf = '';
buf += '<div class="foldersep"></div>';
buf += '<div class="folder"><h3>Folders</h3></div>';
} else {
buf += '<div class="folder"><h3>Gen ' + gen + '</h3></div>';
}
}
if (gen === '/') {
formatName = format.slice(1);
format = formatName + '/';
if (formatName === '~') {
formatName = '(uncategorized)';
format = '/';
} else {
formatName = Tools.escapeHTML(formatName);
}
buf += '<div class="folder' + (this.curFolder === format ? ' cur"><div class="folderhack3"><div class="folderhack1"></div><div class="folderhack2"></div>' : '">') + '<div class="selectFolder" data-value="' + format + '"><i class="fa ' + (this.curFolder === format ? 'fa-folder-open' : 'fa-folder') + (format === '/' ? '-o' : '') + '"></i>' + formatName + '</div></div>' + (this.curFolder === format ? '</div>' : '');
continue;
}
var formatName = format.slice(1);
if (formatName === '~') formatName = '';
@@ -226,11 +277,11 @@
if (format === 'gen6') formatName = '(uncategorized)';
// folders are <div>s rather than <button>s because in theory it has
// less weird interactions with HTML5 drag-and-drop
buf += '<div class="folder' + (this.curFormat === format ? ' cur"><div class="folderhack3"><div class="folderhack1"></div><div class="folderhack2"></div>' : '">') + '<div class="selectFolder" data-value="' + format + '"><i class="fa ' + (this.curFormat === format ? 'fa-folder-open-o' : 'fa-folder-o') + '"></i>' + formatName + '</div></div>' + (this.curFormat === format ? '</div>' : '');
buf += '<div class="folder' + (this.curFolder === format ? ' cur"><div class="folderhack3"><div class="folderhack1"></div><div class="folderhack2"></div>' : '">') + '<div class="selectFolder" data-value="' + format + '"><i class="fa ' + (this.curFolder === format ? 'fa-folder-open-o' : 'fa-folder-o') + '"></i>' + formatName + '</div></div>' + (this.curFolder === format ? '</div>' : '');
}
buf += '<div class="folder"><h3></h3></div>';
buf += '<div class="folder"><div class="selectFolder" data-value="+"><i class="fa fa-plus"></i>(New format folder)</div></div>';
buf += formatFolderBuf;
buf += '<div class="foldersep"></div>';
buf += '<div class="folder"><div class="selectFolder" data-value="++"><i class="fa fa-plus"></i><em>(add folder)</em></div></div>';
buf += '<div class="folderlistafter"></div></div>';
@@ -243,27 +294,50 @@
// teampane
buf += this.clipboardHTML();
if (!this.curFormat) {
var filterFormat = '';
var filterFolder = undefined;
if (!this.curFolder) {
buf += '<h2>Hi</h2>';
buf += '<p>Did you have a good day?</p>';
buf += '<p><button class="button" name="greeting" value="Y"><i class="fa fa-smile-o"></i> Yes, my day was pretty good</button> <button class="button" name="greeting" value="N"><i class="fa fa-frown-o"></i> No, it wasn\'t great</button></p>';
buf += '<h2>All teams</h2>';
} else {
buf += '<h2>' + this.curFormat + '</h2>';
if (this.curFolder.slice(-1) === '/') {
filterFolder = this.curFolder.slice(0, -1);
if (filterFolder) {
buf += '<h2><i class="fa fa-folder-open"></i> ' + filterFolder + ' <button class="button small" style="margin-left:5px" name="renameFolder"><i class="fa fa-pencil"></i> Rename</button></h2>';
} else {
buf += '<h2><i class="fa fa-folder-open-o"></i> Teams not in any folders</h2>';
}
} else {
filterFormat = this.curFolder;
buf += '<h2><i class="fa fa-folder-open-o"></i> ' + filterFormat + '</h2>';
}
}
var newButtonText = "New Team";
if (filterFolder) newButtonText = "New Team in folder";
if (filterFormat && filterFormat !== 'gen6') {
newButtonText = "New " + Tools.escapeFormat(filterFormat) + " Team";
}
buf += '<p><button name="newTop" class="button big"><i class="fa fa-plus-circle"></i> ' + newButtonText + '</button></p>';
buf += '<ul class="teamlist">';
var atLeastOne = false;
if (!window.localStorage && !window.nodewebkit) buf += '<li>== CAN\'T SAVE ==<br /><small>Your browser doesn\'t support <code>localStorage</code> and can\'t save teams! Update to a newer browser.</small></li>';
if (Storage.cantSave) buf += '<li>== CAN\'T SAVE ==<br /><small>You hit your browser\'s limit for team storage! Please backup them and delete some of them. Your teams won\'t be saved until you\'re under the limit again.</small></li>';
if (!teams.length) {
if (this.deletedTeamLoc >= 0) {
buf += '<ul class="teamlist"><li><button name="undoDelete"><i class="fa fa-undo"></i> Undo Delete</button></li></ul>';
buf += '<li><button name="undoDelete"><i class="fa fa-undo"></i> Undo Delete</button></li>';
}
buf += '<p><em>you don\'t have any teams lol</em></p>';
buf += '<li><p><em>you don\'t have any teams lol</em></p></li>';
} else {
var atLeastOne = false;
for (var i = 0; i < teams.length + 1; i++) {
if (i === this.deletedTeamLoc) {
if (!atLeastOne) {
buf += '<p><button name="newTop" class="button big"><i class="fa fa-plus-circle"></i> New' + (this.curFormat && this.curFormat !== 'gen6' ? ' ' + this.curFormat : '') + ' team</button></p><ul class="teamlist">';
atLeastOne = true;
}
if (!atLeastOne) atLeastOne = true;
buf += '<li><button name="undoDelete"><i class="fa fa-undo"></i> Undo Delete</button></li>';
}
if (i >= teams.length) break;
@@ -281,16 +355,15 @@
continue;
}
if (this.curFormat && this.curFormat !== (team.format || 'gen6')) continue;
if (filterFormat && filterFormat !== (team.format || 'gen6')) continue;
if (filterFolder !== undefined && filterFolder !== team.folder) continue;
if (!atLeastOne) {
buf += '<p><button name="newTop" class="button big"><i class="fa fa-plus-circle"></i> New' + (this.curFormat && this.curFormat !== 'gen6' ? ' ' + this.curFormat : '') + ' team</button></p><ul class="teamlist">';
atLeastOne = true;
}
if (!atLeastOne) atLeastOne = true;
var formatText = '';
if (team.format) {
formatText = '[' + team.format + '] ';
}
if (team.folder) formatText += team.folder + '/';
// teams are <div>s rather than <button>s because Firefox doesn't
// support dragging and dropping buttons.
@@ -299,24 +372,66 @@
buf += '</small></div><button name="edit" value="' + i + '"><i class="fa fa-pencil"></i>Edit</button><button name="delete" value="' + i + '"><i class="fa fa-trash"></i>Delete</button></li>';
}
if (!atLeastOne) {
buf += '<ul class="teamlist"><li><em>you don\'t have any ' + this.curFormat + ' teams lol</em></li>';
if (filterFolder) {
buf += '<li><p><em>you don\'t have any teams in this folder lol</em></p></li>';
} else {
buf += '<li><p><em>you don\'t have any ' + this.curFolder + ' teams lol</em></p></li>';
}
}
buf += '</ul>';
}
buf += '<p><button name="new" class="button big"><i class="fa fa-plus-circle"></i> New' + (this.curFormat && this.curFormat !== 'gen6' ? ' ' + this.curFormat : '') + ' team</button></p>';
buf += '</ul>';
if (atLeastOne) {
buf += '<p><button name="new" class="button"><i class="fa fa-plus-circle"></i> ' + newButtonText + '</button></p>';
}
if (window.nodewebkit) {
buf += '<button name="revealFolder" class="button"><i class="fa fa-folder-open"></i> Reveal teams folder</button> <button name="reloadTeamsFolder" class="button"><i class="fa fa-refresh"></i> Reload teams files</button> <button name="backup" class="button"><i class="fa fa-upload"></i> Backup/Restore all teams</button>';
} else {
} else if (atLeastOne) {
buf += '<p><strong>Clearing your cookies (specifically, <code>localStorage</code>) will delete your teams.</strong></p>';
buf += '<button name="backup" class="button"><i class="fa fa-upload"></i> Backup/Restore all teams</button>';
buf += '<p>If you want to clear your cookies or <code>localStorage</code>, you can use the Backup/Restore feature to save your teams as text first.</p>';
} else {
buf += '<button name="backup" class="button"><i class="fa fa-upload"></i> Restore teams from backup</button>';
}
var $pane = this.$('.teampane');
$pane.html(buf);
if (resetScroll) $pane.scrollTop(0);
},
greeting: function (answer, button) {
var buf = '<p><strong>' + $(button).html() + '</p></strong>';
if (answer === 'N') {
buf += '<p>Aww, that\'s too bad. :( I hope playing on Pok&eacute;mon Showdown today can help cheer you up!</p>';
} else if (answer === 'Y') {
buf += '<p>Cool! I just added some pretty cool teambuilder features, so I\'m pretty happy, too. Did you know you can drag and drop teams to different format-folders? You can also drag and drop them to and from your computer (works best in Chrome).</p>';
buf += '<p><button class="button" name="greeting" value="W"><i class="fa fa-question-circle"></i> Wait, who are you? Talking to a teambuilder is weird.</button></p>';
} else if (answer === 'W') {
buf += '<p>Oh, I\'m Zarel! I made a Credits button for this...</p>';
buf += '<div class="menugroup"><p><button class="button mainmenu4" name="credits"><i class="fa fa-info-circle"></i> Credits</button></p></div>';
buf += '<p>Isn\'t it pretty? Matches your background and everything. It used to be in the Main Menu but we had to get rid of it to save space.</p>';
buf += '<p>Speaking of, you should try <button class="button" name="background"><i class="fa fa-picture-o"></i> changing your background</button>.';
buf += '<p><button class="button" name="greeting" value="B"><i class="fa fa-hand-pointer-o"></i> You might be having too much fun with these buttons and icons</button></p>';
} else if (answer === 'B') {
buf += '<p>I paid good money for those icons! I need to get my money\'s worth!</p>';
buf += '<p><button class="button" name="greeting" value="WR"><i class="fa fa-exclamation-triangle"></i> Wait, really?</button></p>';
} else if (answer === 'WR') {
buf += '<p>No, they were free. That just makes it easier to get my money\'s worth. Let\'s play rock paper scissors!</p>';
buf += '<p><button class="button" name="greeting" value="RR"><i class="fa fa-hand-rock-o"></i> Rock</button> <button class="button" name="greeting" value="RP"><i class="fa fa-hand-rock-o"></i> Paper</button> <button class="button" name="greeting" value="RS"><i class="fa fa-hand-scissors-o"></i> Scissors</button> <button class="button" name="greeting" value="RL"><i class="fa fa-hand-lizard-o"></i> Lizard</button> <button class="button" name="greeting" value="RP"><i class="fa fa-hand-spock-o"></i> Spock</button></p>';
} else if (answer[0] === 'R') {
buf += '<p>I play laser, I win. <i class="fa fa-hand-o-left"></i></p>';
buf += '<p><button class="button" name="greeting" value="YC"><i class="fa fa-thumbs-o-down"></i> You can\'t do that!</button></p>';
} else if (answer === 'YC') {
buf += '<p>Okay, then I play peace sign <i class="fa fa-hand-peace-o"></i>, everyone signs a peace treaty, ending the war and ushering in a new era of prosperity.</p>';
}
$(button).parent().replaceWith(buf);
},
credits: function () {
app.addPopup(CreditsPopup);
},
background: function () {
app.addPopup(CustomBackgroundPopup);
},
selectFolder: function (format) {
if (format && format.currentTarget) {
var e = format;
@@ -330,13 +445,53 @@
}});
return;
}
if (format === '++') {
e.stopImmediatePropagation();
var self = this;
// app.addPopupPrompt("Folder name:", "Create folder", function (newFormat) {
// self.selectFolder(newFormat + '/');
// });
app.addPopup(PromptPopup, {message: "Folder name:", button: "Create folder", sourceEl: e.currentTarget, callback: function (name) {
name = $.trim(name);
if (name.indexOf('/') >= 0 || name.indexOf('\\') >= 0) {
app.addPopupMessage("Names can't contain slashes, since they're used as a folder separator.");
name = name.replace(/[\\\/]/g, '');
}
if (!name) return;
self.selectFolder(name + '/');
}});
return;
}
} else {
this.curFormatKeep = format;
this.curFolderKeep = format;
}
this.curFormat = (format === 'all' ? '' : format);
this.curFolder = (format === 'all' ? '' : format);
this.updateFolderList();
this.updateTeamList(true);
},
renameFolder: function () {
if (!this.curFolder) return;
if (this.curFolder.slice(-1) !== '/') return;
var oldFolder = this.curFolder.slice(0, -1);
var self = this;
app.addPopup(PromptPopup, {message: "Folder name:", button: "Rename folder", value: oldFolder, callback: function (name) {
name = $.trim(name);
if (name.indexOf('/') >= 0 || name.indexOf('\\') >= 0) {
app.addPopupMessage("Names can't contain slashes, since they're used as a folder separator.");
name = name.replace(/[\\\/]/g, '');
}
if (!name) return;
if (name === oldFolder) return;
for (var i = 0; i < Storage.teams.length; i++) {
var team = Storage.teams[i];
if (team.folder !== oldFolder) continue;
team.folder = name;
if (window.nodewebkit) Storage.saveTeam(team);
}
if (!window.nodewebkit) Storage.saveTeam(team);
self.selectFolder(name + '/');
}});
},
show: function () {
Room.prototype.show.apply(this, arguments);
var $teamwrapper = this.$('.teamwrapper');
@@ -410,6 +565,7 @@
}
},
saveBackup: function () {
Storage.deleteAllTeams();
Storage.importTeam(this.$('.teamedit textarea').val(), true);
teams = Storage.teams;
Storage.saveAllTeams();
@@ -422,20 +578,34 @@
this.back();
},
"new": function () {
var format = this.curFolder;
var folder = '';
if (format && format.charAt(format.length - 1) === '/') {
folder = format.slice(0, -1);
format = '';
}
var newTeam = {
name: 'Untitled ' + (teams.length + 1),
format: (this.curFormat || ''),
format: format,
team: '',
folder: folder,
iconCache: ''
};
teams.push(newTeam);
this.edit(teams.length - 1);
},
newTop: function () {
var format = this.curFolder;
var folder = '';
if (format && format.charAt(format.length - 1) === '/') {
folder = format.slice(0, -1);
format = '';
}
var newTeam = {
name: 'Untitled ' + (teams.length + 1),
format: (this.curFormat || ''),
format: format,
team: '',
folder: folder,
iconCache: ''
};
teams.unshift(newTeam);
@@ -546,7 +716,11 @@
if (app.draggingFolder) {
var format = app.draggingFolder.dataset.value;
app.draggingFolder = null;
team.format = format;
if (format.slice(-1) === '/') {
team.folder = format.slice(0, -1);
} else {
team.format = format;
}
this.selectFolder(format);
edited = true;
} else {
@@ -554,7 +728,7 @@
}
if (edited) {
Storage.saveTeams();
Storage.saveTeam(team);
app.user.trigger('saveteams');
}
@@ -621,7 +795,7 @@
return;
}
var format = e.currentTarget.dataset.value;
if (format === '+' || format === 'all' || format === this.curFormat) {
if (format === '+' || format === '++' || format === 'all' || format === this.curFolder) {
return;
}
if (parseInt(app.dragging.dataset.value, 10) >= Storage.teams.length) {
@@ -691,6 +865,7 @@
name: name,
format: format,
team: team,
folder: '',
iconCache: ''
});
self.finishDrop();
@@ -890,8 +1065,13 @@
app.send('/vtm ' + format);
},
teamNameChange: function (e) {
this.curTeam.name = ($.trim(e.currentTarget.value) || 'Untitled ' + (this.curTeamLoc + 1));
e.currentTarget.value = this.curTeam.name;
var name = ($.trim(e.currentTarget.value) || 'Untitled ' + (this.curTeamLoc + 1));
if (name.indexOf('/') >= 0 || name.indexOf('\\') >= 0) {
app.addPopupMessage("Names can't contain slashes, since they're used as a folder separator.");
name = name.replace(/[\\\/]/g, '');
}
this.curTeam.name = name;
e.currentTarget.value = name;
this.save();
},
format: function (format, button) {
@@ -912,9 +1092,9 @@
},
nicknameChange: function (e) {
var i = +$(e.currentTarget).closest('li').attr('value');
var team = this.curSetList[i];
var name = $.trim(e.currentTarget.value) || team.species;
e.currentTarget.value = team.name = name;
var set = this.curSetList[i];
var name = $.trim(e.currentTarget.value) || set.species;
e.currentTarget.value = set.name = name;
this.save();
},

View File

@@ -2229,14 +2229,13 @@
});
var PromptPopup = this.PromptPopup = Popup.extend({
type: 'semimodal',
initialize: function (data) {
if (!data || !data.message || typeof data.callback !== "function") return;
this.callback = data.callback;
var buf = '<form>';
buf += '<p><label class="label">' + data.message;
buf += '<input class="textbox autofocus" type="text" name="data" /></label></p>';
buf += '<input class="textbox autofocus" type="text" name="data" value="' + Tools.escapeHTML(data.value || '') + '" /></label></p>';
buf += '<p class="buttonbar"><button type="submit"><strong>' + data.button + '</strong></button> <button name="close">Cancel</button></p>';
buf += '</form>';

View File

@@ -114,12 +114,12 @@ Storage.bg = {
loadHues: function (hues) {
$('#mainmenubuttoncolors').remove();
var cssBuf = '';
for (var i = 0; i < 6; i++) {
for (var i = 0; i < 5; i++) {
var n = i + 1;
var hs = hues[i];
cssBuf += '.mainmenuwrapper .button.mainmenu' + n + ' { background: linear-gradient(to bottom, hsl(' + hs + ',72%), hsl(' + hs + ',52%)); border-color: hsl(' + hs + ',40%); }\n';
cssBuf += '.mainmenuwrapper .button.mainmenu' + n + ':hover { background: linear-gradient(to bottom, hsl(' + hs + ',62%), hsl(' + hs + ',42%)); border-color: hsl(' + hs + ',21%); }\n';
cssBuf += '.mainmenuwrapper .button.mainmenu' + n + ':active { background: linear-gradient(to bottom, hsl(' + hs + ',42%), hsl(' + hs + ',58%)); border-color: hsl(' + hs + ',21%); }\n';
cssBuf += 'body .button.mainmenu' + n + ' { background: linear-gradient(to bottom, hsl(' + hs + ',72%), hsl(' + hs + ',52%)); border-color: hsl(' + hs + ',40%); }\n';
cssBuf += 'body .button.mainmenu' + n + ':hover { background: linear-gradient(to bottom, hsl(' + hs + ',62%), hsl(' + hs + ',42%)); border-color: hsl(' + hs + ',21%); }\n';
cssBuf += 'body .button.mainmenu' + n + ':active { background: linear-gradient(to bottom, hsl(' + hs + ',42%), hsl(' + hs + ',58%)); border-color: hsl(' + hs + ',21%); }\n';
}
$('head').append('<style id="mainmenubuttoncolors">' + cssBuf + '</style>');
},
@@ -132,13 +132,13 @@ Storage.bg = {
// or localStorage throws
try {
var colorThief = new ColorThief();
var colors = colorThief.getPalette(img, 6);
var colors = colorThief.getPalette(img, 5);
var hues = [];
if (!colors) {
hues = ['0, 0%', '0, 0%', '0, 0%', '0, 0%', '0, 0%', '0, 0%'];
hues = ['0, 0%', '0, 0%', '0, 0%', '0, 0%', '0, 0%'];
} else {
for (var i = 0; i < 6; i++) {
for (var i = 0; i < 5; i++) {
var color = colors[i];
var hs = Storage.bg.getHueSat(color[0] / 255, color[1] / 255, color[2] / 255);
hues.unshift(hs);
@@ -179,7 +179,7 @@ try {
var bg = localStorage.getItem('showdown_bg').split('\n');
if (bg.length >= 2) {
Storage.bg.load(bg[0], bg[1]);
if (bg.length === 8) Storage.bg.loadHues(bg.slice(2));
if (bg.length >= 7) Storage.bg.loadHues(bg.slice(2));
}
} catch (e) {}
@@ -473,6 +473,8 @@ Storage.saveAllTeams = function () {
this.saveTeams();
};
Storage.deleteAllTeams = function () {};
/*********************************************************
* Team importing and exporting
*********************************************************/
@@ -487,6 +489,7 @@ Storage.unpackAllTeams = function (buffer) {
name: oldTeam.name || '',
format: oldTeam.format || '',
team: Storage.packTeam(oldTeam.team),
folder: '',
iconCache: ''
};
});
@@ -497,10 +500,13 @@ Storage.unpackAllTeams = function (buffer) {
if (pipeIndex < 0) return;
var bracketIndex = line.indexOf(']');
if (bracketIndex > pipeIndex) bracketIndex = -1;
var slashIndex = line.lastIndexOf('/', pipeIndex);
if (slashIndex < 0) slashIndex = bracketIndex; // line.slice(slashIndex + 1, pipeIndex) will be ''
return {
name: line.slice(bracketIndex + 1, pipeIndex),
name: line.slice(slashIndex + 1, pipeIndex),
format: bracketIndex > 0 ? line.slice(0, bracketIndex) : '',
team: line.slice(pipeIndex + 1),
folder: line.slice(bracketIndex + 1, slashIndex > 0 ? slashIndex : bracketIndex + 1),
iconCache: ''
};
}).filter(function (v) { return v; });
@@ -508,7 +514,7 @@ Storage.unpackAllTeams = function (buffer) {
Storage.packAllTeams = function (teams) {
return teams.map(function (team) {
return (team.format ? '' + team.format + ']' : '') + team.name + '|' + Storage.getPackedTeam(team);
return (team.format ? '' + team.format + ']' : '') + (team.folder ? '' + team.folder + '/' : '') + team.name + '|' + Storage.getPackedTeam(team);
}).join('\n');
};
@@ -917,10 +923,17 @@ Storage.importTeam = function (text, teams) {
if (teams.length) {
teams[teams.length - 1].team = Storage.packTeam(teams[teams.length - 1].team);
}
var slashIndex = line.lastIndexOf('/');
var folder = '';
if (slashIndex > 0) {
folder = line.slice(0, slashIndex);
line = line.slice(slashIndex + 1);
}
teams.push({
name: line,
format: format,
team: team,
folder: folder,
iconCache: ''
});
} else if (!curSet) {
@@ -1029,7 +1042,7 @@ Storage.exportAllTeams = function () {
var buf = '';
for (var i = 0, len = Storage.teams.length; i < len; i++) {
var team = Storage.teams[i];
buf += '=== ' + (team.format ? '[' + team.format + '] ' : '') + team.name + ' ===\n\n';
buf += '=== ' + (team.format ? '[' + team.format + '] ' : '') + (team.folder ? '' + team.folder + '/' : '') + team.name + ' ===\n\n';
buf += Storage.exportTeam(team.team);
buf += '\n';
}
@@ -1243,6 +1256,7 @@ Storage.initDirectory2 = function () {
// load teams
self.nwLoadTeams();
self.saveAllTeams = self.nwSaveAllTeams;
self.deleteAllTeams = self.nwDeleteAllTeams;
self.saveTeam = self.nwSaveTeam;
self.deleteTeam = self.nwDeleteTeam;
@@ -1258,12 +1272,36 @@ Storage.revealFolder = function () {
gui.Shell.openItem(this.dir);
};
Storage.nwFindTextFilesRecursive = function (dir, done) {
var results = [];
fs.readdir(dir, function (err, list) {
if (err) return done(err);
var pending = list.length;
if (!pending) return done(null, results);
list.forEach(function (file) {
file = dir + '/' + file;
fs.stat(file, function (err, stat) {
if (stat && stat.isDirectory()) {
Storage.nwFindTextFilesRecursive(file, function (err, res) {
results = results.concat(res);
if (!--pending) done(null, results);
});
} else {
if (file.slice(-4).toLowerCase() === '.txt') results.push(file);
if (!--pending) done(null, results);
}
});
});
});
};
// teams
Storage.nwLoadTeams = function () {
var self = this;
var localApp = window.app;
fs.readdir(this.dir + 'Teams', function (err, files) {
var dirOffset = this.dir.length + 6;
Storage.nwFindTextFilesRecursive(this.dir + 'Teams', function (err, files) {
if (err) return;
self.teams = [];
self.nwTeamsLeft = files.length;
@@ -1271,7 +1309,7 @@ Storage.nwLoadTeams = function () {
self.nwFinishedLoadingTeams(localApp);
}
for (var i = 0; i < files.length; i++) {
self.nwLoadTeamFile(files[i], localApp);
self.nwLoadTeamFile(files[i].slice(dirOffset), localApp);
}
});
};
@@ -1279,12 +1317,11 @@ Storage.nwLoadTeams = function () {
Storage.nwLoadTeamFile = function (filename, localApp) {
var self = this;
var line = filename;
if (line.substr(line.length - 4).toLowerCase() === '.txt') {
line = line.substr(0, line.length - 4);
if (line.slice(-4).toLowerCase() === '.txt') {
line = line.slice(0, -4);
} else {
// not a team file
self.nwTeamsLeft--;
if (!self.nwTeamsLeft) {
if (!--self.nwTeamsLeft) {
self.nwFinishedLoadingTeams(localApp);
}
return;
@@ -1292,8 +1329,22 @@ Storage.nwLoadTeamFile = function (filename, localApp) {
var format = '';
var bracketIndex = line.indexOf(']');
if (bracketIndex >= 0) {
format = line.substr(1, bracketIndex - 1);
line = $.trim(line.substr(bracketIndex + 1));
format = line.slice(1, bracketIndex);
line = $.trim(line.slice(bracketIndex + 1));
}
var slashIndex = line.indexOf('/');
var folder = '';
if (slashIndex >= 0) {
folder = line.slice(0, slashIndex);
line = $.trim(line.slice(slashIndex + 1));
}
slashIndex = line.indexOf('/');
if (slashIndex >= 0) {
// very nested, not currently supported, skip
if (!--self.nwTeamsLeft) {
self.nwFinishedLoadingTeams(localApp);
}
return;
}
fs.readFile(this.dir + 'Teams/' + filename, function (err, data) {
if (!err) {
@@ -1301,11 +1352,11 @@ Storage.nwLoadTeamFile = function (filename, localApp) {
name: line,
format: format,
team: Storage.packTeam(Storage.importTeam('' + data)),
folder: folder,
iconCache: '',
filename: filename
});
self.nwTeamsLeft--;
if (!self.nwTeamsLeft) {
if (!--self.nwTeamsLeft) {
self.nwFinishedLoadingTeams(localApp);
}
}
@@ -1323,19 +1374,27 @@ Storage.teamCompare = function (a, b) {
return 0;
};
Storage.fsReady = Tools.makeLoadTracker();
Storage.fsReady.load();
Storage.nwDeleteAllTeams = function (callback) {
var self = this;
fs.readdir(this.dir + 'Teams', function (err, files) {
if (err) return;
self.nwTeamsLeft = files.length;
if (!self.nwTeamsLeft) {
callback();
return;
// only delete teams we've opened
var deleteFilenames = [];
for (var i = 0; i < this.teams.length; i++) {
if (this.teams[i].filename) {
deleteFilenames.push(this.teams[i].filename);
delete this.teams[i].filename;
}
for (var i = 0; i < files.length; i++) {
self.nwDeleteTeamFile(files[i], callback);
}
});
}
if (!deleteFilenames.length) {
if (callback) callback();
return;
}
Storage.fsReady.unload();
this.nwTeamsLeft = deleteFilenames.length;
for (var i = 0; i < deleteFilenames.length; i++) {
this.nwDeleteTeamFile(deleteFilenames[i], callback);
}
};
Storage.nwDeleteTeamFile = function (filename, callback) {
@@ -1346,12 +1405,21 @@ Storage.nwDeleteTeamFile = function (filename, callback) {
} else {
// not a team file
self.nwTeamsLeft--;
if (!self.nwTeamsLeft) callback();
if (!self.nwTeamsLeft) {
if (callback) callback();
Storage.fsReady.load();
}
return;
}
fs.unlink(this.dir + 'Teams/' + filename, function (err) {
var directory = filename.split('/').slice(0, -1).join('/');
fs.rmdir(directory, function () {});
self.nwTeamsLeft--;
if (!self.nwTeamsLeft) callback();
if (!self.nwTeamsLeft) {
if (callback) callback();
Storage.fsReady.load();
}
});
};
@@ -1359,10 +1427,19 @@ Storage.nwSaveTeam = function (team) {
if (!team) return;
var filename = team.name + '.txt';
if (team.format) filename = '[' + team.format + '] ' + filename;
filename = $.trim(filename).replace(/[\\\/]+/g, '');
filename = filename.trim().replace(/[\\\/]+/g, '');
if (team.folder) filename = '' + team.folder.replace(/[\\\/]+/g, '') + '/' + filename;
// not too hard to support saving to nested directories, but loading is a whole other issue
var splitFilename = filename.split('/');
var folder = splitFilename.slice(0, -1).join('');
var filename = folder + '/' + splitFilename[splitFilename.length - 1];
try {
fs.mkdirSync(this.dir + 'Teams/' + folder);
} catch (e) {}
if (team.filename && filename !== team.filename) {
fs.unlink(this.dir + 'Teams/' + team.filename, function () {});
this.nwDeleteTeam();
}
team.filename = filename;
fs.writeFile(this.dir + 'Teams/' + filename, Storage.exportTeam(team.team).replace(/\n/g, '\r\n'));
@@ -1370,14 +1447,18 @@ Storage.nwSaveTeam = function (team) {
Storage.nwDeleteTeam = function (team) {
if (team.filename) {
fs.unlink(this.dir + 'Teams/' + team.filename, function () {});
delete team.filename;
var oldFilename = team.filename;
var oldDirectory = oldFilename.split('/').slice(0, -1).join('/');
if (oldDirectory) oldDirectory = this.dir + 'Teams/' + oldDirectory;
fs.unlink(this.dir + 'Teams/' + oldFilename, function () {
if (oldDirectory) fs.rmdir(oldDirectory, function () {});
});
}
};
Storage.nwSaveAllTeams = function () {
var self = this;
this.nwDeleteAllTeams(function () {
Storage.fsReady(function () {
self.nwDoSaveAllTeams();
});
};

View File

@@ -837,7 +837,8 @@ p.or:after {
.menugroup p {
margin: 10px 0;
}
.menugroup .button {
.menugroup .button,
.menugroup button.button {
box-sizing: border-box;
font-size: 12pt;
@@ -1999,13 +2000,24 @@ a.ilink.yours {
padding: 0;
border: 0;
}
.folderlist .folder {
.folderlist .folder,
.folderlist .foldersep {
display: table-row;
margin: 0;
padding: 0;
border: 0;
height: 30px;
}
.folderlist .foldersep {
height: 15px;
}
.folderlist .foldersep:before {
display: table-cell;
height: 15px;
background: #d7e3ec;
border-right: 1px solid #888888;
content: '';
}
.folderlistafter {
display: table-row;
margin: 0;
@@ -2123,6 +2135,11 @@ a.ilink.yours {
font-size: 12pt;
padding: 5px 10px;
}
.teampane .button.small,
.teamwrapper .button.small {
font-size: 8pt;
padding: 2px 5px;
}
.teamlist {
list-style: none;