diff options
| author | Alon Levy <alon@pobox.com> | 2014-12-05 20:17:46 +0200 |
|---|---|---|
| committer | Alon Levy <alon@pobox.com> | 2014-12-05 20:39:53 +0200 |
| commit | c53dce9f564b3d8bcb712f3047d48a700ad2f82d (patch) | |
| tree | 9af17877f2f6e45654b4e04e2372f5990f96a764 /src | |
| parent | f5569e0b4d0fa21999e1be6334cd25dd67f5d86b (diff) | |
replace jquery.autocomplete with our own autocompletion
Missing:
- handling dropdown in the middle of the sentence correctly (almost
there, extra space added)
- clicking changes focus (transition event related?) and .focus() doesn't
work - could not find how to fix it.
Diffstat (limited to 'src')
| -rw-r--r-- | src/textanalysis.js | 37 | ||||
| -rw-r--r-- | src/textanalysis.ui.js | 99 | ||||
| -rw-r--r-- | src/view/completer.js | 167 | ||||
| -rw-r--r-- | src/view/node_info.js | 4 |
4 files changed, 209 insertions, 98 deletions
diff --git a/src/textanalysis.js b/src/textanalysis.js index 98ca8aea..f2d5f555 100644 --- a/src/textanalysis.js +++ b/src/textanalysis.js @@ -9,7 +9,8 @@ var typeStack = []; var lastnode; -var sugg = {}; // suggestions for autocompletion of node names +var sugg = {}, // suggestions for autocompletion of node names + suggestions_options = new Bacon.Bus(); // TODO: Property: same as bus, but with initial value var ANALYSIS_NODE_START = 'ANALYSIS_NODE_START'; var ANALYSIS_NODE = 'ANALYSIS_NODE' @@ -24,36 +25,7 @@ function autoSuggestAddName(name) { /* note that name can contain spaces - this is ok. We might want to limit this though? */ sugg[name] = 1; -} - -function unquoted(name) -{ - var start = 0, - end = name.length; - - if (name.length >= 1) { - if (name.charAt(0) == '"') { - start = 1; - if (name.length > 1 && name.charAt(name.length - 1) == '"') { - end = name.length - 1; - } - } - return name.substring(start, end); - } - return name; -} - -function autocompleteCallback(request, response_callback) -{ - var ret = []; - if (request.term === "" || request.term) { - for (var name in sugg) { - if (name.toLowerCase().indexOf(unquoted(request.term.toLowerCase())) === 0) { - ret.push(name); - } - } - } - response_callback(ret); + suggestions_options.push(sugg); } /* up_to_two_renames: @@ -502,9 +474,8 @@ function init(graph) return { init:init, - autocompleteCallback:autocompleteCallback, textAnalyser:textAnalyser, - autoSuggestAddName:autoSuggestAddName, + suggestions_options: suggestions_options, ANALYSIS_NODE_START:ANALYSIS_NODE_START, ANALYSIS_NODE: ANALYSIS_NODE, ANALYSIS_LINK:ANALYSIS_LINK, diff --git a/src/textanalysis.ui.js b/src/textanalysis.ui.js index b7863271..0b2a9bc3 100644 --- a/src/textanalysis.ui.js +++ b/src/textanalysis.ui.js @@ -1,12 +1,12 @@ "use strict" -define(['jquery', 'Bacon', 'consts', 'rz_bus', 'autocomplete', 'rz_core', 'textanalysis'], -function($, Bacon, consts, rz_bus, autocomplete, rz_core, textanalysis) { +define(['jquery', 'Bacon', 'consts', 'rz_bus', 'rz_core', 'textanalysis', 'view/completer'], +function($, Bacon, consts, rz_bus, rz_core, textanalysis, completer) { var text = "", // Last text of sentence element_name = '#textanalyser', element = $(element_name), - suggestionChange, + element_raw = element[0], plus_button = $('.add-button'), description = consts.description; @@ -37,6 +37,8 @@ var typeselection = function TypeSelectionDialog() { return typeselection; }(); +var analysisCompleter = completer(element, $('.suggestion')); + function analyzeSentence(sentence, finalize) { var ret = textanalysis.textAnalyser(sentence, finalize); @@ -55,9 +57,7 @@ function analyzeSentence(sentence, finalize) if (finalize || sentence.length == 0) { typeselection.hide(); - $('span.ui-helper-hidden-accessible').hide(); - } else { - $('span.ui-helper-hidden-accessible').show(); + analysisCompleter.hide(); } } @@ -106,21 +106,7 @@ return { return; } - element.autocompleteTrigger({ - triggerStart: '#', - triggerEnd: '', - source: textanalysis.autocompleteCallback, - response: function(element, ui) { - ui.content.forEach(function (x) { - if (x.value.search(' ') != -1) { - x.value = '"' + x.value + '"'; - } - }); - }, - open: function() { - $('.ui-autocomplete').css('width', '10px'); - }, - }); + analysisCompleter.options.plug(textanalysis.suggestions_options); var document_keydown = new Bacon.Bus(); rz_bus.ui_key.plug(document_keydown); @@ -129,6 +115,12 @@ return { var ret = undefined; switch (e.keyCode) { + case 13: + if (!analysisCompleter.handleEnter()) { + submitNewSentence(); + } + ret = false; + break; case 9: //TAB if (textanalysis.lastnode()) { e.preventDefault(); @@ -136,32 +128,37 @@ return { ret = false; } break; - case 37: //UP - $('html, body').scrollLeft(0); - break; - case 39: //DOWN - $('html, body').scrollLeft(0); + case 38: //UP + case 40: //DOWN + ret = false; break; + } + document_keydown.push({where: consts.KEYSTROKE_WHERE_TEXTANALYSIS, keys: [e.keyCode]}); + return ret; + }); + element.keyup(function(e) { + var ret = undefined; + switch (e.keyCode) { case 38: //UP - suggestionChange = true; + analysisCompleter.prev_option(); + ret = false; break; case 40: //DOWN - suggestionChange = true; + analysisCompleter.next_option(); + ret = false; break; + default: + analysisCompleter.oninput(element_raw.value, element_raw.selectionStart); + analysisCompleter.show(); } - document_keydown.push({where: consts.KEYSTROKE_WHERE_DOCUMENT, keys: [e.keyCode]}); return ret; }); - function maybeSubmitNewSentence() { - if(!suggestionChange) { - text = element.val(); - element.val(""); - analyzeSentence(text, true); - text = ""; - } else { - suggestionChange = false; - } + function submitNewSentence() { + text = element.val(); + element.val(""); + analyzeSentence(text, true); + text = ""; } // Click is required to prevent the default action - this is a form so that's a post, @@ -171,34 +168,10 @@ return { // http://stackoverflow.com/questions/15786891/browser-sometimes-ignores-a-jquery-click-event-during-a-css3-transform plus_button.bind("click mousedown", function(e) { console.dir(e); - maybeSubmitNewSentence(); + submitNewSentence(); e.preventDefault(); }); - var element_keydown = new Bacon.Bus(); - rz_bus.ui_key.plug(element_keydown); - element.keydown(function(e) { - var ret = undefined; - switch (e.which) { - case 13: - maybeSubmitNewSentence(); - ret = false; - break; - case 37: //RIGHT - $('body').scrollLeft(0); - e.stopPropagation(); - ret = false; - break; - case 39: //LEFT - $('body').scrollLeft(0); - e.stopPropagation(); - ret = false; - break; - } - element_keydown.push({where: consts.KEYSTROKE_WHERE_TEXTANALYSIS, keys:[e.which]}); - return ret; - }); - var input = new Bacon.Bus(); rz_bus.ui_input.plug(input); if ('oninput' in document.documentElement) { diff --git a/src/view/completer.js b/src/view/completer.js new file mode 100644 index 00000000..ce848eff --- /dev/null +++ b/src/view/completer.js @@ -0,0 +1,167 @@ +define( +['jquery', 'Bacon'], +function($, Bacon) { + +function unquoted(name) +{ + var start = 0, + end = name.length; + + if (name.length >= 1) { + if (name.charAt(0) == '"') { + start = 1; + if (name.length > 1 && name.charAt(name.length - 1) == '"') { + end = name.length - 1; + } + } + return name.substring(start, end); + } + return name; +} + +function setCaret(e, num) +{ + e.selectionStart = e.selectionEnd = num; +} + +var completer = (function (input_element, dropdown) { + var triggerStart = '#', + triggerEnd = '', + dropdown_raw = dropdown[0], + options_bus = new Bacon.Bus(), + options = [], + selected_index = -1, + input_element_raw = input_element[0], + completion_start = 0, + completion_end = 0; + + // turn off the browser's autocomplete + input_element.attr('autocomplete', 'off'); + + //$('.ui-autocomplete').css('width', '10px'); + options_bus.onValue(function update_options(new_options) { + options = new_options; + }); + + function completions(text) + { + var ret = [], + noquotes = unquoted(text); + + for (var name in options) { + if (name.toLowerCase().indexOf(noquotes) === 0) { + ret.push(name); + } + } + return ret; + } + + function oninput(text, cursor) { + // #this is a # + // ^ + // + // #this is a #t + // ^ + // + // #this and #that then #he + // ^ + console.log(text); + console.log(cursor); + var hash = text.slice(0, cursor).lastIndexOf('#'); + // TODO check if current completion has been invalidated + _invalidateSelection(); + dropdown_raw.innerHTML = ""; // remove all elements + if (hash == -1) { + return; + } + var space = text.slice(hash).indexOf(' '); + space = space == -1 ? text.length : space; + if (space < cursor) { + return; + } + // [hash] [cursor] [space/end] + completions(text.slice(hash + 1, space)).forEach(function(name) { + dropdown.append($('<div>' + name + '</div>')); + dropdown.children().each(function (index, elem) { + elem.onclick = function() { _applySuggestion(index); }; + input_element.focus(); + }); + }); + completion_start = hash + 1; + completion_end = space; + } + + function _invalidateSelection() { + update_highlighting(-1); + } + + function move_option(change, default_value) { + var next, + n = dropdown.children().length; + + if (n == 0) { + return; + } + if (selected_index == -1) { + next = default_value; + } else { + next = (selected_index + change) % n; + } + update_highlighting(next); + } + function next_option() { + move_option(1, 0); + } + function prev_option() { + move_option(-1, dropdown.children().length); + } + function _get_option(index) { + return dropdown.children()[index].innerHTML; + } + function _choice(i) { + return dropdown.children().eq(i); + } + function update_highlighting(new_index) { + if (selected_index != -1) { + _choice(selected_index).removeClass('selected'); + } + if (new_index != -1) { + _choice(new_index).addClass('selected'); + } + selected_index = new_index; + } + function _applySuggestion(index) { + var cur = input_element.val(), + start = cur.slice(0, completion_start) + _get_option(index) + ' '; + input_element.val(start + cur.slice(completion_end)); + setCaret(input_element, start.length); + oninput('', 0); + } + function handleEnter() { + if (selected_index == -1) { + return false; + } + _applySuggestion(selected_index); + return true; + } + + return { + options: options_bus, + hide: function() { + console.log('hide'); + dropdown.hide(); + }, + show: function() { + console.log('show'); + dropdown.show(); + }, + oninput: oninput, + next_option: next_option, + prev_option: prev_option, + handleEnter: handleEnter, + }; +}); + +return completer; + +}); diff --git a/src/view/node_info.js b/src/view/node_info.js index f798b387..55d7ac72 100644 --- a/src/view/node_info.js +++ b/src/view/node_info.js @@ -1,5 +1,5 @@ -define(['jquery', 'view/helpers', 'view/internal'], -function($, view_helpers, internal) { +define(['jquery', 'datepicker', 'view/helpers', 'view/internal'], +function($, _unused_datepicker, view_helpers, internal) { function show(d) { var editURL = ''; |
