Integrate new tab completion methods
There is now an option to choose which tab completion method to use. Also, emotes can be tab completed.
This commit is contained in:
parent
e1ad7c63af
commit
27e168ba8b
|
@ -2,7 +2,7 @@
|
||||||
"author": "Calvin Montgomery",
|
"author": "Calvin Montgomery",
|
||||||
"name": "CyTube",
|
"name": "CyTube",
|
||||||
"description": "Online media synchronizer and chat",
|
"description": "Online media synchronizer and chat",
|
||||||
"version": "3.27.1",
|
"version": "3.28.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "http://github.com/calzoneman/sync"
|
"url": "http://github.com/calzoneman/sync"
|
||||||
},
|
},
|
||||||
|
|
|
@ -240,6 +240,7 @@ html(lang="en")
|
||||||
script(id="socketio-js", src=sioSource)
|
script(id="socketio-js", src=sioSource)
|
||||||
script(src="/js/data.js")
|
script(src="/js/data.js")
|
||||||
script(src="/js/util.js")
|
script(src="/js/util.js")
|
||||||
|
script(src="/js/tabcomplete.js")
|
||||||
script(src="/js/player.js")
|
script(src="/js/player.js")
|
||||||
script(src="/js/paginator.js")
|
script(src="/js/paginator.js")
|
||||||
script(src="/js/ui.js")
|
script(src="/js/ui.js")
|
||||||
|
|
|
@ -114,6 +114,12 @@ mixin us-chat
|
||||||
+rcheckbox("us-sendbtn", "Add a send button to chat")
|
+rcheckbox("us-sendbtn", "Add a send button to chat")
|
||||||
+rcheckbox("us-no-emotes", "Disable chat emotes")
|
+rcheckbox("us-no-emotes", "Disable chat emotes")
|
||||||
+rcheckbox("us-strip-image", "Remove images from chat")
|
+rcheckbox("us-strip-image", "Remove images from chat")
|
||||||
|
.form-group
|
||||||
|
label.control-label.col-sm-4(for="#us-chat-tab-method") Tab completion method
|
||||||
|
.col-sm-8
|
||||||
|
select#us-chat-tab-method.form-control
|
||||||
|
option(value="Cycle options") Cycle options
|
||||||
|
option(value="Longest unique match") Longest unique match
|
||||||
|
|
||||||
mixin us-mod
|
mixin us-mod
|
||||||
#us-mod.tab-pane
|
#us-mod.tab-pane
|
||||||
|
|
|
@ -4,7 +4,7 @@ require('../../www/js/tabcomplete');
|
||||||
|
|
||||||
|
|
||||||
describe('CyTube.tabCompletionMethods', () => {
|
describe('CyTube.tabCompletionMethods', () => {
|
||||||
describe('"Longest unique prefix"', () => {
|
describe('"Longest unique match"', () => {
|
||||||
const testcases = [
|
const testcases = [
|
||||||
{
|
{
|
||||||
input: 'and his name is j',
|
input: 'and his name is j',
|
||||||
|
@ -26,6 +26,16 @@ describe('CyTube.tabCompletionMethods', () => {
|
||||||
},
|
},
|
||||||
description: 'completes a unique match'
|
description: 'completes a unique match'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: 'johnc',
|
||||||
|
position: 5,
|
||||||
|
options: ['johncena', 'johnstamos', 'johto'],
|
||||||
|
output: {
|
||||||
|
text: 'johncena ',
|
||||||
|
newPosition: 9
|
||||||
|
},
|
||||||
|
description: 'completes a unique match at the beginning of the string'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: 'and his name is johnc',
|
input: 'and his name is johnc',
|
||||||
position: 21,
|
position: 21,
|
||||||
|
@ -70,7 +80,7 @@ describe('CyTube.tabCompletionMethods', () => {
|
||||||
testcases.forEach(test => {
|
testcases.forEach(test => {
|
||||||
it(test.description, () => {
|
it(test.description, () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
CyTube.tabCompleteMethods['Longest unique prefix'](
|
CyTube.tabCompleteMethods['Longest unique match'](
|
||||||
test.input,
|
test.input,
|
||||||
test.position,
|
test.position,
|
||||||
test.options,
|
test.options,
|
||||||
|
@ -108,6 +118,30 @@ describe('CyTube.tabCompletionMethods', () => {
|
||||||
],
|
],
|
||||||
description: 'cycles through options correctly'
|
description: 'cycles through options correctly'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: 'c',
|
||||||
|
position: 1,
|
||||||
|
options: ['COBOL', 'Carlos', 'carl', 'john', 'joseph', ''],
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
text: 'carl ',
|
||||||
|
newPosition: 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Carlos ',
|
||||||
|
newPosition: 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'COBOL ',
|
||||||
|
newPosition: 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'carl ',
|
||||||
|
newPosition: 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
description: 'cycles through options correctly at the beginning of the string'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: 'hey ',
|
input: 'hey ',
|
||||||
position: 5,
|
position: 5,
|
||||||
|
|
|
@ -123,7 +123,8 @@ var USEROPTS = {
|
||||||
show_shadowchat : getOrDefault("show_shadowchat", false),
|
show_shadowchat : getOrDefault("show_shadowchat", false),
|
||||||
emotelist_sort : getOrDefault("emotelist_sort", true),
|
emotelist_sort : getOrDefault("emotelist_sort", true),
|
||||||
no_emotes : getOrDefault("no_emotes", false),
|
no_emotes : getOrDefault("no_emotes", false),
|
||||||
strip_image : getOrDefault("strip_image", false)
|
strip_image : getOrDefault("strip_image", false),
|
||||||
|
chat_tab_method : getOrDefault("chat_tab_method", "Cycle options")
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Backwards compatibility check */
|
/* Backwards compatibility check */
|
||||||
|
|
|
@ -2,7 +2,7 @@ CyTube.tabCompleteMethods = {};
|
||||||
|
|
||||||
// Bash-style completion
|
// Bash-style completion
|
||||||
// Only completes as far as it is possible to maintain uniqueness of the completion.
|
// Only completes as far as it is possible to maintain uniqueness of the completion.
|
||||||
CyTube.tabCompleteMethods['Longest unique prefix'] = function (input, position, options, context) {
|
CyTube.tabCompleteMethods['Longest unique match'] = function (input, position, options, context) {
|
||||||
var lower = input.toLowerCase();
|
var lower = input.toLowerCase();
|
||||||
// First, backtrack to the nearest whitespace to find the
|
// First, backtrack to the nearest whitespace to find the
|
||||||
// incomplete string that should be completed.
|
// incomplete string that should be completed.
|
||||||
|
@ -10,12 +10,12 @@ CyTube.tabCompleteMethods['Longest unique prefix'] = function (input, position,
|
||||||
var incomplete = '';
|
var incomplete = '';
|
||||||
for (start = position - 1; start >= 0; start--) {
|
for (start = position - 1; start >= 0; start--) {
|
||||||
if (/\s/.test(lower[start])) {
|
if (/\s/.test(lower[start])) {
|
||||||
start++;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
incomplete = lower[start] + incomplete;
|
incomplete = lower[start] + incomplete;
|
||||||
}
|
}
|
||||||
|
start++;
|
||||||
|
|
||||||
// Nothing to complete
|
// Nothing to complete
|
||||||
if (!incomplete.length) {
|
if (!incomplete.length) {
|
||||||
|
@ -101,12 +101,12 @@ CyTube.tabCompleteMethods['Cycle options'] = function (input, position, options,
|
||||||
var incomplete = '';
|
var incomplete = '';
|
||||||
for (start = position - 1; start >= 0; start--) {
|
for (start = position - 1; start >= 0; start--) {
|
||||||
if (/\s/.test(lower[start])) {
|
if (/\s/.test(lower[start])) {
|
||||||
start++;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
incomplete = lower[start] + incomplete;
|
incomplete = lower[start] + incomplete;
|
||||||
}
|
}
|
||||||
|
start++;
|
||||||
|
|
||||||
// Nothing to complete
|
// Nothing to complete
|
||||||
if (!incomplete.length) {
|
if (!incomplete.length) {
|
||||||
|
|
58
www/js/ui.js
58
www/js/ui.js
|
@ -111,7 +111,11 @@ $("#guestname").keydown(function (ev) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function chatTabComplete() {
|
|
||||||
|
/**
|
||||||
|
* TODO: Remove this post-deployment
|
||||||
|
*/
|
||||||
|
function oldChatTabComplete() {
|
||||||
var words = $("#chatline").val().split(" ");
|
var words = $("#chatline").val().split(" ");
|
||||||
var current = words[words.length - 1].toLowerCase();
|
var current = words[words.length - 1].toLowerCase();
|
||||||
if (!current.match(/^[\w-]{1,20}$/)) {
|
if (!current.match(/^[\w-]{1,20}$/)) {
|
||||||
|
@ -186,6 +190,54 @@ function chatTabComplete() {
|
||||||
$("#chatline").val(words.join(" "));
|
$("#chatline").val(words.join(" "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CyTube.chatTabCompleteData = {
|
||||||
|
context: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
function chatTabComplete() {
|
||||||
|
if (!CyTube.tabCompleteMethods) {
|
||||||
|
oldChatTabComplete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var chatline = document.getElementById("chatline");
|
||||||
|
var currentText = chatline.value;
|
||||||
|
var currentPosition = chatline.selectionEnd;
|
||||||
|
if (typeof currentPosition !== 'number' || !chatline.setSelectionRange) {
|
||||||
|
// Bail, we're on IE8 or something similarly dysfunctional
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var firstWord = !/\s/.test(currentText.trim());
|
||||||
|
var options = [];
|
||||||
|
var userlistElems = document.getElementById("userlist").children;
|
||||||
|
for (var i = 0; i < userlistElems.length; i++) {
|
||||||
|
var username = userlistElems[i].children[1].textContent;
|
||||||
|
if (firstWord) {
|
||||||
|
username += ':';
|
||||||
|
}
|
||||||
|
options.push(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
CHANNEL.emotes.forEach(function (emote) {
|
||||||
|
options.push(emote.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
var method = USEROPTS.chat_tab_method;
|
||||||
|
if (!CyTube.tabCompleteMethods[method]) {
|
||||||
|
console.error("Unknown chat tab completion method '" + method + "', using default");
|
||||||
|
method = "Cycle options";
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = CyTube.tabCompleteMethods[method](
|
||||||
|
currentText,
|
||||||
|
currentPosition,
|
||||||
|
options,
|
||||||
|
CyTube.chatTabCompleteData.context
|
||||||
|
);
|
||||||
|
|
||||||
|
chatline.value = result.text;
|
||||||
|
chatline.setSelectionRange(result.newPosition, result.newPosition);
|
||||||
|
}
|
||||||
|
|
||||||
$("#chatline").keydown(function(ev) {
|
$("#chatline").keydown(function(ev) {
|
||||||
// Enter/return
|
// Enter/return
|
||||||
if(ev.keyCode == 13) {
|
if(ev.keyCode == 13) {
|
||||||
|
@ -218,7 +270,11 @@ $("#chatline").keydown(function(ev) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if(ev.keyCode == 9) { // Tab completion
|
else if(ev.keyCode == 9) { // Tab completion
|
||||||
|
try {
|
||||||
chatTabComplete();
|
chatTabComplete();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -643,6 +643,7 @@ function showUserOptions() {
|
||||||
$("#us-sendbtn").prop("checked", USEROPTS.chatbtn);
|
$("#us-sendbtn").prop("checked", USEROPTS.chatbtn);
|
||||||
$("#us-no-emotes").prop("checked", USEROPTS.no_emotes);
|
$("#us-no-emotes").prop("checked", USEROPTS.no_emotes);
|
||||||
$("#us-strip-image").prop("checked", USEROPTS.strip_image);
|
$("#us-strip-image").prop("checked", USEROPTS.strip_image);
|
||||||
|
$("#us-chat-tab-method").val(USEROPTS.chat_tab_method);
|
||||||
|
|
||||||
$("#us-modflair").prop("checked", USEROPTS.modhat);
|
$("#us-modflair").prop("checked", USEROPTS.modhat);
|
||||||
$("#us-shadowchat").prop("checked", USEROPTS.show_shadowchat);
|
$("#us-shadowchat").prop("checked", USEROPTS.show_shadowchat);
|
||||||
|
@ -677,6 +678,7 @@ function saveUserOptions() {
|
||||||
USEROPTS.chatbtn = $("#us-sendbtn").prop("checked");
|
USEROPTS.chatbtn = $("#us-sendbtn").prop("checked");
|
||||||
USEROPTS.no_emotes = $("#us-no-emotes").prop("checked");
|
USEROPTS.no_emotes = $("#us-no-emotes").prop("checked");
|
||||||
USEROPTS.strip_image = $("#us-strip-image").prop("checked");
|
USEROPTS.strip_image = $("#us-strip-image").prop("checked");
|
||||||
|
USEROPTS.chat_tab_method = $("#us-chat-tab-method").val();
|
||||||
|
|
||||||
if (CLIENT.rank >= 2) {
|
if (CLIENT.rank >= 2) {
|
||||||
USEROPTS.modhat = $("#us-modflair").prop("checked");
|
USEROPTS.modhat = $("#us-modflair").prop("checked");
|
||||||
|
|
Loading…
Reference in a new issue