Commit ed502faa authored by joshua-gould's avatar joshua-gould

autocomplete in middle of string

parent ffa39a5b
...@@ -428,12 +428,26 @@ morpheus.Util.autosuggest = function(options) { ...@@ -428,12 +428,26 @@ morpheus.Util.autosuggest = function(options) {
&& $(this).data('ui-autocomplete').menu.active) { && $(this).data('ui-autocomplete').menu.active) {
event.preventDefault(); event.preventDefault();
} }
}).autocomplete({ })
minLength : 1, .autocomplete(
{
minLength : 0,
delay : options.delay, delay : options.delay,
source : function(request, response) { source : function(request, response) {
// delegate back to autocomplete, but extract the last term // delegate back to autocomplete, but extract the
var terms = morpheus.Util.getAutocompleteTokens(request.term); // autocomplete term
var terms = morpheus.Util
.getAutocompleteTokens(
request.term,
{
trim : false,
selectionStart : options.$el[0].selectionStart
});
if (terms.selectionStartIndex === undefined
|| terms.selectionStartIndex === -1) {
terms.selectionStartIndex = terms.length - 1;
}
if (options.suggestWhenEmpty || terms.length > 0) { if (options.suggestWhenEmpty || terms.length > 0) {
options.filter(terms, response); options.filter(terms, response);
} }
...@@ -443,28 +457,55 @@ morpheus.Util.autosuggest = function(options) { ...@@ -443,28 +457,55 @@ morpheus.Util.autosuggest = function(options) {
return false; return false;
}, },
select : function(event, ui) { select : function(event, ui) {
if (ui.item.skip) {
return false;
}
if (options.multi) { if (options.multi) {
var terms = morpheus.Util.getAutocompleteTokens(this.value); var terms = morpheus.Util
// remove the current input .getAutocompleteTokens(
terms.pop(); this.value,
// add the selected item {
var val = ui.item.value; trim : false,
// if ((val.indexOf(' ') > 0 && val[0] !== '"')) { selectionStart : options.$el[0].selectionStart
// val = '"' + val + '"'; // quote });
// } // quote value if needed
// val = val.replace(fieldRegExp, '\\:'); // TODO escape field var value = (ui.item.value[0] !== '"'
// separators && ui.item.value.indexOf(' ') > 0 ? ('"'
terms.push(val); + ui.item.value + '"')
: ui.item.value);
var show = ui.item.show; // || (ui.item.space
// &&
// options.suggestWhenEmpty);
// replace the current input
if (terms.length === 0) {
terms.push(value);
} else {
terms[terms.selectionStartIndex === -1
|| terms.selectionStartIndex === undefined ? terms.length - 1
: terms.selectionStartIndex] = value;
}
// add the selected item
this.value = terms.join(' '); this.value = terms.join(' ');
if (show) { // did
// we
// select
// just a
// field name?
setTimeout(function() {
options.$el.autocomplete('search',
options.$el.val());
}, 20);
}
if (options.select) { if (options.select) {
options.select(); options.select();
} }
$(this).autocomplete('close');
return false; return false;
} else if (options.select) { }
if (options.select) {
options.select(); options.select();
$(this).autocomplete('close');
} }
if (event.which === 13) { if (event.which === 13) {
event.stopImmediatePropagation(); event.stopImmediatePropagation();
...@@ -473,15 +514,15 @@ morpheus.Util.autosuggest = function(options) { ...@@ -473,15 +514,15 @@ morpheus.Util.autosuggest = function(options) {
}); });
// use html for label instead of default text // use html for label instead of default text
options.$el.autocomplete('instance')._renderItem = function(ul, item) { var instance = options.$el.autocomplete('instance');
return $('<li>').html(item.label).appendTo(ul); instance._renderItem = function(ul, item) {
return $('<li class="ui-menu-item">').html(
item.render ? item.render() : item.label).appendTo(ul);
}; };
if (options.suggestWhenEmpty) { if (options.suggestWhenEmpty) {
options.$el.on('focus', function() { options.$el.on('focus', function() {
if ($.trim(options.$el.val()) === '') { options.$el.autocomplete('search', options.$el.val());
options.$el.autocomplete('search', ' ');
}
}); });
} }
...@@ -490,7 +531,7 @@ morpheus.Util.autosuggest = function(options) { ...@@ -490,7 +531,7 @@ morpheus.Util.autosuggest = function(options) {
options.$el.autocomplete('close'); options.$el.autocomplete('close');
} else if (options.suggestWhenEmpty) { } else if (options.suggestWhenEmpty) {
if (options.$el.val() === '') { if (options.$el.val() === '') {
options.$el.autocomplete('search', ' '); options.$el.autocomplete('search', '');
} }
} }
...@@ -498,6 +539,74 @@ morpheus.Util.autosuggest = function(options) { ...@@ -498,6 +539,74 @@ morpheus.Util.autosuggest = function(options) {
}; };
morpheus.Util.getAutocompleteTokens = function(text, options) {
options = $.extend({}, {
trim : true
}, options);
if (options.trim) {
text = $.trim(text);
}
if (text === '') {
return [];
}
var inQuote = false;
var inSelectionStart = false;
var tokens = [];
var currentToken = [];
for (var i = 0, n = text.length; i < n; i++) {
var c = text[i];
if (c === '"') {
inQuote = !inQuote;
currentToken.push(c);
} else {
if ((c === ' ' || c === '\t') && !inQuote) {
tokens.push({
s : currentToken.join(''),
inSelectionStart : currentToken.inSelectionStart
});
currentToken = []; // start new token
} else { // add to current token
currentToken.push(c);
}
}
if (i === options.selectionStart - 1) {
currentToken.inSelectionStart = true;
}
}
tokens.push({
s : currentToken.join(''),
inSelectionStart : currentToken.inSelectionStart
});
// add trailing token
if (!options.trim && !inQuote && text[text.length - 1] === ' ') {
tokens.push({
s : ' ',
inSelectionStart : false
});
}
// remove empty tokens
// keep spaces at end of input "field:value" for next autocomplete
var filteredTokens = [];
var selectionStartIndex = -1;
for (var i = 0, ntokens = tokens.length; i < ntokens; i++) {
var token = tokens[i];
var s = token.s;
if (options.trim || i < (ntokens - 1)) {
s = $.trim(s);
}
if (s !== '') {
if (token.inSelectionStart) {
selectionStartIndex = filteredTokens.length;
}
filteredTokens.push(s);
}
}
filteredTokens.selectionStartIndex = selectionStartIndex;
return filteredTokens;
};
/** /**
* @deprecated * @deprecated
*/ */
...@@ -870,63 +979,37 @@ morpheus.Util.escapeRegex = function(value) { ...@@ -870,63 +979,37 @@ morpheus.Util.escapeRegex = function(value) {
.replace(/[-[\]{}()+?,\\^$|#\s]/g, '\\$&'); .replace(/[-[\]{}()+?,\\^$|#\s]/g, '\\$&');
}; };
morpheus.Util.getAutocompleteTokens = function(text) {
text = $.trim(text);
if (text === '') {
return [];
}
var inQuote = false;
var tokens = [];
var currentToken = [];
for (var i = 0, length = text.length; i < length; i++) {
var c = text[i];
if (c === '"') {
inQuote = !inQuote;
currentToken.push(c);
} else {
if ((c === ' ' || c === '\t') && !inQuote) {
tokens.push(currentToken.join(''));
currentToken = [];
} else {
currentToken.push(c);
}
}
}
tokens.push(currentToken.join(''));
tokens = tokens.map(function(t) {
return $.trim(t);
});
tokens = tokens.filter(function(t) {
return t !== '';
});
return tokens;
};
morpheus.Util.createSearchPredicates = function(options) { morpheus.Util.createSearchPredicates = function(options) {
options = $.extend({}, {
validateFieldNames : true
}, options);
var tokens = options.tokens; var tokens = options.tokens;
if (tokens == null) {
return [];
}
var availableFields = options.fields; var availableFields = options.fields;
var disableFieldSearch = availableFields == null var validateFieldNames = options.validateFieldNames;
|| availableFields.length === 0; var fieldSearchEnabled = !validateFieldNames
|| (availableFields != null && availableFields.length > 0);
var fieldRegExp = /\\:/g; var fieldRegExp = /\\:/g;
var predicates = []; var predicates = [];
var defaultIsExactMatch = options.defaultMatchMode === 'exact'; var defaultIsExactMatch = options.defaultMatchMode === 'exact';
_
.each( tokens
tokens, .forEach(function(token) {
function(token) {
var field = null; var field = null;
var semi = token.indexOf(':'); var semi = token.indexOf(':');
if (semi > 0) { // field search? if (semi > 0) { // field search?
if (disableFieldSearch if (!fieldSearchEnabled
|| token.charCodeAt(semi - 1) === 92) { // \: || token.charCodeAt(semi - 1) === 92) { // \:
token = token.replace(fieldRegExp, ':'); token = token.replace(fieldRegExp, ':');
} else { // only a field search if field matches } else { // only a field search if field matches
// one of available fields // one of available fields
var possibleToken = $.trim(token var possibleToken = $.trim(token.substring(semi + 1));
.substring(semi + 1));
// check for "field":"val" and "field:val" // check for "field":"val" and "field:val"
var possibleField = $.trim(token.substring(0, var possibleField = $.trim(token.substring(0, semi)); // split
semi)); // split on : // on :
if (possibleField.length > 0 if (possibleField.length > 0
&& possibleField[0] === '"' && possibleField[0] === '"'
&& possibleField[possibleField.length - 1] === '"') { && possibleField[possibleField.length - 1] === '"') {
...@@ -941,7 +1024,8 @@ morpheus.Util.createSearchPredicates = function(options) { ...@@ -941,7 +1024,8 @@ morpheus.Util.createSearchPredicates = function(options) {
possibleToken = '"' + possibleToken; possibleToken = '"' + possibleToken;
} }
if (availableFields.indexOf(possibleField) !== -1) { if (!validateFieldNames
|| availableFields.indexOf(possibleField) !== -1) {
token = possibleToken; token = possibleToken;
field = possibleField; field = possibleField;
} }
...@@ -953,48 +1037,81 @@ morpheus.Util.createSearchPredicates = function(options) { ...@@ -953,48 +1037,81 @@ morpheus.Util.createSearchPredicates = function(options) {
isNot = true; isNot = true;
} }
var predicate; var predicate;
var range = token.indexOf('..'); var rangeIndex = -1;
if (range !== -1) { // range query var rangeToken = null;
var min = parseFloat(token.substring(0, range)); var rangeIndicators = [ '..', '>=', '>', '<=', '<', '=' ];
var max = parseFloat(token.substring(range + 2)); for (var i = 0; i < rangeIndicators.length; i++) {
rangeIndex = token.indexOf(rangeIndicators[i]);
if (rangeIndex !== -1) {
rangeToken = rangeIndicators[i];
break;
}
}
if (rangeIndex !== -1) { // range query
if (rangeToken === '..') {
var start = parseFloat(token.substring(0, rangeIndex));
var end = parseFloat(token.substring(rangeIndex + 2));
if (!isNaN(start) && !isNaN(end)) {
predicate = new morpheus.Util.NumberRangePredicate( predicate = new morpheus.Util.NumberRangePredicate(
field, min, max); field, start, end);
} else if (token[0] === '"' }
&& token[token.length - 1] === '"') { // exact } else if (rangeToken === '>') {
var val = parseFloat(token.substring(rangeIndex + 1));
if (!isNaN(val)) {
predicate = new morpheus.Util.GreaterThanPredicate(
field, val);
}
} else if (rangeToken === '>=') {
var val = parseFloat(token.substring(rangeIndex + 2));
if (!isNaN(val)) {
predicate = new morpheus.Util.GreaterThanOrEqualPredicate(
field, val);
}
} else if (rangeToken === '<') {
var val = parseFloat(token.substring(rangeIndex + 1));
if (!isNaN(val)) {
predicate = new morpheus.Util.LessThanPredicate(
field, val);
}
} else if (rangeToken === '<=') {
var val = parseFloat(token.substring(rangeIndex + 2));
if (!isNaN(val)) {
predicate = new morpheus.Util.LessThanOrEqualPredicate(
field, val);
}
} else if (rangeToken === '=') {
var val = parseFloat(token.substring(rangeIndex + 1));
if (!isNaN(val)) {
predicate = new morpheus.Util.EqualsPredicate(
field, val);
}
} else {
predicate = defaultIsExactMatch ? new morpheus.Util.ExactTermPredicate(
field, token)
: new morpheus.Util.RegexPredicate(field, token);
}
} else if (token[0] === '"' && token[token.length - 1] === '"') { // exact
// match // match
token = token.substring(1, token.length - 1); token = token.substring(1, token.length - 1);
predicate = new morpheus.Util.ExactTermPredicate( predicate = new morpheus.Util.ExactTermPredicate(field,
field, token);
} else if (token.indexOf('*') !== -1) { // contains
predicate = new morpheus.Util.RegexPredicate(field,
token); token);
} else if (token.indexOf('*') !== -1) { // contains
predicate = new morpheus.Util.RegexPredicate(field, token);
} else { } else {
predicate = defaultIsExactMatch ? new morpheus.Util.ExactTermPredicate( predicate = defaultIsExactMatch ? new morpheus.Util.ExactTermPredicate(
field, token) field, token)
: new morpheus.Util.RegexPredicate(field, : new morpheus.Util.RegexPredicate(field, token);
token);
} }
if (predicate != null) {
predicates.push(isNot ? new morpheus.Util.NotPredicate( predicates.push(isNot ? new morpheus.Util.NotPredicate(
predicate) : predicate); predicate) : predicate);
}
}); });
return predicates; return predicates;
}; };
// morpheus.Util.getAutocompleteTokens = function(searchText) {
// searchText = $.trim(searchText);
// var tokens = searchText.match(/"[^"]*"|[^\s,"]+/g);
// if (tokens === null) {
// return [];
// }
// tokens = tokens.map(function(t) {
// var t = $.trim(t);
// return t;
// });
// tokens = tokens.filter(function(e) {
// return e != '' && e != undefined;
// });
// return tokens;
// };
morpheus.Util.createRegExpStringToMatchText = function(text) { morpheus.Util.createRegExpStringToMatchText = function(text) {
var tokens = morpheus.Util.getAutocompleteTokens(text); var tokens = morpheus.Util.getAutocompleteTokens(text);
...@@ -1158,9 +1275,6 @@ morpheus.Util.ContainsPredicate.prototype = { ...@@ -1158,9 +1275,6 @@ morpheus.Util.ContainsPredicate.prototype = {
return value.toLowerCase return value.toLowerCase
&& value.toLowerCase().indexOf(this.text) !== -1; && value.toLowerCase().indexOf(this.text) !== -1;
}, },
getText : function() {
return this.text;
},
getField : function() { getField : function() {
return this.field; return this.field;
}, },
...@@ -1183,9 +1297,6 @@ morpheus.Util.ExactTermPredicate.prototype = { ...@@ -1183,9 +1297,6 @@ morpheus.Util.ExactTermPredicate.prototype = {
getField : function() { getField : function() {
return this.field; return this.field;
}, },
getText : function() {
return this.text;
},
isNumber : function() { isNumber : function() {
return false; return false;
}, },
...@@ -1205,9 +1316,6 @@ morpheus.Util.RegexPredicate.prototype = { ...@@ -1205,9 +1316,6 @@ morpheus.Util.RegexPredicate.prototype = {
getField : function() { getField : function() {
return this.field; return this.field;
}, },
getText : function() {
return this.text;
},
isNumber : function() { isNumber : function() {
return false; return false;
}, },
...@@ -1227,9 +1335,6 @@ morpheus.Util.NumberRangePredicate.prototype = { ...@@ -1227,9 +1335,6 @@ morpheus.Util.NumberRangePredicate.prototype = {
getField : function() { getField : function() {
return this.field; return this.field;
}, },
getText : function() {
return this.min + '..' + this.max;
},
isNumber : function() { isNumber : function() {
return true; return true;
}, },
...@@ -1238,6 +1343,83 @@ morpheus.Util.NumberRangePredicate.prototype = { ...@@ -1238,6 +1343,83 @@ morpheus.Util.NumberRangePredicate.prototype = {
+ this.max; + this.max;
} }
}; };
morpheus.Util.GreaterThanPredicate = function(field, val) {
this.field = field;
this.val = val;
};
morpheus.Util.GreaterThanPredicate.prototype = {
accept : function(value) {
return value > this.val;
},
getField : function() {
return this.field;
},
isNumber : function() {
return true;
}
};
morpheus.Util.GreaterThanOrEqualPredicate = function(field, val) {
this.field = field;
this.val = val;
};
morpheus.Util.GreaterThanOrEqualPredicate.prototype = {
accept : function(value) {
return value >= this.val;
},
getField : function() {
return this.field;
},
isNumber : function() {
return true;
}
};
morpheus.Util.LessThanPredicate = function(field, val) {
this.field = field;
this.val = val;
};
morpheus.Util.LessThanPredicate.prototype = {
accept : function(value) {
return value < this.val;
},
getField : function() {
return this.field;
},
isNumber : function() {
return true;
}
};
morpheus.Util.LessThanOrEqualPredicate = function(field, val) {
this.field = field;
this.val = val;
};
morpheus.Util.LessThanOrEqualPredicate.prototype = {
accept : function(value) {
return value <= this.val;
},
getField : function() {
return this.field;
},
isNumber : function() {
return true;
}
};
morpheus.Util.EqualsPredicate = function(field, val) {
this.field = field;
this.val = val;
};
morpheus.Util.EqualsPredicate.prototype = {
accept : function(value) {
return value === this.val;
},
getField : function() {
return this.field;
},
isNumber : function() {
return true;
}
};
morpheus.Util.NotPredicate = function(p) { morpheus.Util.NotPredicate = function(p) {
this.p = p; this.p = p;
}; };
...@@ -1248,9 +1430,6 @@ morpheus.Util.NotPredicate.prototype = { ...@@ -1248,9 +1430,6 @@ morpheus.Util.NotPredicate.prototype = {
getField : function() { getField : function() {
return this.p.getField(); return this.p.getField();
}, },
getText : function() {
return this.p.getText();
},
isNumber : function() { isNumber : function() {
return this.p.isNumber(); return this.p.isNumber();
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment