Skip to content

Commit

Permalink
fix: suggestions break when containing html entities
Browse files Browse the repository at this point in the history
fixes #3
  • Loading branch information
gr2m committed Aug 19, 2014
1 parent 5daae0a commit 4b06b4c
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 32 deletions.
37 changes: 28 additions & 9 deletions bootstrap-contenteditable-autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,17 @@

//http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
var regexEscapeLatters = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;
var trailingCommaRegex = /[,\s]*$/;
var splitWordsWithWhitespaceRegex = /\s*,\s*/;
var regexTrailingComma = /[,\s]*$/;
var regexSplitWordsWithWhitespace = /\s*,\s*/;


//
function handleNewSuggestions (suggestions) {
var html = '';
var search = currentValue.replace(regexEscapeLatters, '\\$&');
var regex = new RegExp('('+search+')', 'i');

currentValues = currentValue.trim().split(splitWordsWithWhitespaceRegex);
currentValues = currentValue.trim().split(regexSplitWordsWithWhitespace);
currentSuggestions = suggestions.map( normalizeSuggestion ).filter(newSuggestionsOnly);

if (currentSuggestions.length === 0) {
Expand All @@ -171,6 +172,8 @@

if (! label) return;

label = htmlEscape(label);

// select first result per default
html += '<div' + highlight + '>';
html += label.replace(regex, '<strong>$1</strong>');
Expand Down Expand Up @@ -228,11 +231,12 @@
//
function selectSuggestionByElement($element) {
var selected = currentSuggestions[ $element.index() ];
var value = selected.value;
if (isMultiple) {
replaceCurrentWordWith(selected.value);
replaceCurrentWordWith(value);
} else {
$input.text(selected.value).focus();
setCursorAt(selected.value.length);
$input.text(value).focus();
setCursorAt(value.length);
}

$input.trigger('autocomplete:select', [selected]);
Expand Down Expand Up @@ -295,7 +299,7 @@
if (charCount + word.length >= cursorAt) {
beforeQuery = currentValue.substring(0, charCount).trim();
afterQuery = currentValue.substring(cursorAt);
$input.html(beforeQuery + ' ' + newWord + ',&nbsp;' + afterQuery);
$input.html(htmlEscape(beforeQuery + ' ' + newWord) + ',&nbsp;' + htmlEscape(afterQuery));
setCursorAt( (beforeQuery + ' ' + newWord + ', ').length );
return;
}
Expand Down Expand Up @@ -327,14 +331,29 @@
var currentValue = $input.text();

if (currentValue) {
$input.val(currentValue.replace(trailingCommaRegex, ', '));
$input.val(currentValue.replace(regexTrailingComma, ', '));
}
}

//
function removeTrailingComma () {
var currentValue = $input.text();
$input.val(currentValue.replace(trailingCommaRegex, ''));
$input.val(currentValue.replace(regexTrailingComma, ''));
}

//
var regexAmpersands = /&/g;
var regexSingleQuotes = /'/g;
var regexDoubleQuotes = /"/g;
var regexLessThanSigns = /</g;
var regexGreaterThanSigns = />/g;
function htmlEscape(string) {
return string
.replace(regexAmpersands, '&amp;')
.replace(regexSingleQuotes, '&#39;')
.replace(regexDoubleQuotes, '&quot;')
.replace(regexLessThanSigns, '&lt;')
.replace(regexGreaterThanSigns, '&gt;');
}

initialize();
Expand Down
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@
],
"devDependencies": {
"bootstrap": "~3.1.0",
"bootstrap-expandable-input": "~0.0.6"
"bootstrap-expandable-input": "~0.0.14"
}
}
13 changes: 6 additions & 7 deletions bower_components/bootstrap-expandable-input/.bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bootstrap-expandable-input",
"version": "0.0.6",
"version": "0.0.14",
"authors": [
"Gregor Martynus <gregor@martynus.net>"
],
Expand All @@ -26,14 +26,13 @@
"bootstrap": "~3.1.0"
},
"homepage": "https://github.com/gr2m/bootstrap-expandable-input",
"_release": "0.0.6",
"_release": "0.0.14",
"_resolution": {
"type": "version",
"tag": "0.0.6",
"commit": "a05aaeee3b5f72a8638a3517c0c68da258a2a86a"
"tag": "0.0.14",
"commit": "269f5495d6cada052a4460e09fc3eea0d3f987b7"
},
"_source": "git://github.com/gr2m/bootstrap-expandable-input.git",
"_target": "~0.0.6",
"_originalSource": "bootstrap-expandable-input",
"_direct": true
"_target": "~0.0.14",
"_originalSource": "bootstrap-expandable-input"
}
6 changes: 3 additions & 3 deletions bower_components/bootstrap-expandable-input/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ Usage
<link rel="stylesheet" type="text/css" href="bootstrap.css">
<script src="bootstrap.js"></script>

<!-- load editable-table assets -->
<link rel="stylesheet" type="text/css" href="bootstrap-expandable-inputs.css">
<script src="bootstrap-expandable-inputs.js"></script>
<!-- load expandable-input assets -->
<link rel="stylesheet" type="text/css" href="bootstrap-expandable-input.css">
<script src="bootstrap-expandable-input.js"></script>

<!-- The behaviour is initialzied on first interaction -->
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ html body [contenteditable]:empty:before {
content: attr(placeholder);
color: #a9a9a9;
}
html body [contenteditable=false]:empty:before {
visibility: hidden !important;
}
@media print {
html body [contenteditable]:empty:before {
visibility: hidden !important;
}
}

html body [contenteditable].contenteditable-inline {
display: inline-block;
Expand All @@ -28,4 +36,7 @@ html body [contenteditable].contenteditable-inline br {
html body [contenteditable].contenteditable-inline:after {
content: ' ';
font-size: 0;
display: inline-block;
width: 0;
height: 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,21 +126,54 @@
//
// implements $('[contenteditable]').val()
//
var regexNewLines = /\n/g;
var regexAmpersands = /&/g;
var regexLessThanSigns = /</g;
var regexGreaterThanSigns = />/g;
var regexWhiteSpaces = /\s+$/g;
var regexBr = /<br>/gi;
var regexDivCloseAndOpen = /<div>\s*<\/div>/gi;
var regexLineBreakElements = /(<div>|<\/div>)/gi;
var regexTags = /<[^>]+>/gi;
var regexSpacesNotPrecedByWordBreaks = /\B /g;
var regexMultipleSpaces = / +/g;
var regexEntity = /&([^;]+);/g;
function patchJQueryVal () {
var origVal = $.fn.val;
$.fn.val = function(text) {
if ($(this).is('[contenteditable]')) {
if (typeof text === 'undefined') {
return $(this).text();
if (this.is('[contenteditable]')) {
if (arguments.length === 0) {
// .textContent removes all line breaks, which is why we
// have to use .innerHTML and manually remove all HTML tags
// We could use .innerText, but that is not supported in Firefox
return this[0].innerHTML
.replace(regexBr, '\n')
.replace(regexDivCloseAndOpen, '\n')
.replace(regexLineBreakElements, '\n')
.replace(regexTags, '')
.replace(regexMultipleSpaces, ' ')
.replace(regexEntity, replaceEntity)
.trim();
}

text = text.replace(/\s+$/g, '&nbsp;');
return $(this).html( text );
text = String(text || '');
text = text.replace(regexAmpersands, '&amp;');
text = text.replace(regexLessThanSigns, '&lt;');
text = text.replace(regexGreaterThanSigns, '&gt;');
text = text.replace(regexWhiteSpaces, '&nbsp;');
text = text.replace(regexNewLines, '<br>');
text = text.replace(regexSpacesNotPrecedByWordBreaks, '&nbsp;');
return this.html( text );
}
return origVal.apply(this, arguments);
};
}

var ENTITIES = {'quot':'"','amp':'&','apos':'\'','lt':'<','gt':'>','nbsp':' ','iexcl':'¡','cent':'¢','pound':'£','curren':'¤','yen':'¥','brvbar':'¦','sect':'§','uml':'¨','copy':'©','ordf':'ª','laquo':'«','not':'¬','shy':' ','reg':'®','macr':'¯','deg':'°','plusmn':'±','sup2':'²','sup3':'³','acute':'´','micro':'µ','para':'¶','middot':'·','cedil':'¸','sup1':'¹','ordm':'º','raquo':'»','frac14':'¼','frac12':'½','frac34':'¾','iquest':'¿','Agrave':'À','Aacute':'Á','Acirc':'Â','Atilde':'Ã','Auml':'Ä','Aring':'Å','AElig':'Æ','Ccedil':'Ç','Egrave':'È','Eacute':'É','Ecirc':'Ê','Euml':'Ë','Igrave':'Ì','Iacute':'Í','Icirc':'Î','Iuml':'Ï','ETH':'Ð','Ntilde':'Ñ','Ograve':'Ò','Oacute':'Ó','Ocirc':'Ô','Otilde':'Õ','Ouml':'Ö','times':'×','Oslash':'Ø','Ugrave':'Ù','Uacute':'Ú','Ucirc':'Û','Uuml':'Ü','Yacute':'Ý','THORN':'Þ','szlig':'ß','agrave':'à','aacute':'á','acirc':'â','atilde':'ã','auml':'ä','aring':'å','aelig':'æ','ccedil':'ç','egrave':'è','eacute':'é','ecirc':'ê','euml':'ë','igrave':'ì','iacute':'í','icirc':'î','iuml':'ï','eth':'ð','ntilde':'ñ','ograve':'ò','oacute':'ó','ocirc':'ô','otilde':'õ','ouml':'ö','divide':'÷','oslash':'ø','ugrave':'ù','uacute':'ú','ucirc':'û','uuml':'ü','yacute':'ý','thorn':'þ','yuml':'ÿ','OElig':'Œ','oelig':'œ','Scaron':'Š','scaron':'š','Yuml':'Ÿ','fnof':'ƒ','circ':'ˆ','tilde':'˜','Alpha':'Α','Beta':'Β','Gamma':'Γ','Delta':'Δ','Epsilon':'Ε','Zeta':'Ζ','Eta':'Η','Theta':'Θ','Iota':'Ι','Kappa':'Κ','Lambda':'Λ','Mu':'Μ','Nu':'Ν','Xi':'Ξ','Omicron':'Ο','Pi':'Π','Rho':'Ρ','Sigma':'Σ','Tau':'Τ','Upsilon':'Υ','Phi':'Φ','Chi':'Χ','Psi':'Ψ','Omega':'Ω','alpha':'α','beta':'β','gamma':'γ','delta':'δ','epsilon':'ε','zeta':'ζ','eta':'η','theta':'θ','iota':'ι','kappa':'κ','lambda':'λ','mu':'μ','nu':'ν','xi':'ξ','omicron':'ο','pi':'π','rho':'ρ','sigmaf':'ς','sigma':'σ','tau':'τ','upsilon':'υ','phi':'φ','chi':'χ','psi':'ψ','omega':'ω','thetasym':'ϑ','upsih':'ϒ','piv':'ϖ','ensp':' ','emsp':' ','thinsp':'','zwnj':' ','zwj':' ','lrm':' ','rlm':' ','ndash':'–','mdash':'—','lsquo':'‘','rsquo':'’','sbquo':'‚','ldquo':'“','rdquo':'”','bdquo':'„','dagger':'†','Dagger':'‡','bull':'•','hellip':'…','permil':'‰','prime':'′','Prime':'″','lsaquo':'‹','rsaquo':'›','oline':'‾','frasl':'⁄','euro':'€','image':'ℑ','weierp':'℘','real':'ℜ','trade':'™','alefsym':'ℵ','larr':'←','uarr':'↑','rarr':'→','darr':'↓','harr':'↔','crarr':'↵','lArr':'⇐','uArr':'⇑','rArr':'⇒','dArr':'⇓','hArr':'⇔','forall':'∀','part':'∂','exist':'∃','empty':'∅','nabla':'∇','isin':'∈','notin':'∉','ni':'∋','prod':'∏','sum':'∑','minus':'−','lowast':'∗','radic':'√','prop':'∝','infin':'∞','ang':'∠','and':'∧','or':'∨','cap':'∩','cup':'∪','int':'∫','there4':'∴','sim':'∼','cong':'≅','asymp':'≈','ne':'≠','equiv':'≡','le':'≤','ge':'≥','sub':'⊂','sup':'⊃','nsub':'⊄','sube':'⊆','supe':'⊇','oplus':'⊕','otimes':'⊗','perp':'⊥','sdot':'⋅','vellip':'⋮','lceil':'⌈','rceil':'⌉','lfloor':'⌊','rfloor':'⌋','lang':'〈','rang':'〉','loz':'◊','spades':'♠','clubs':'♣','hearts':'♥','diams':'♦'};
function replaceEntity(match, entity) {
return ENTITIES[entity] || '';
}

//
// implements $('[contenteditable]').select()
//
Expand All @@ -162,21 +195,18 @@
// EXPANDABLE INPUT DATA-API
// =========================

$(document.body).on('focus.bs.expandableInput.data-api', '[contenteditable]', function(event) {
$(document).on('focus.bs.expandableInput.data-api', '[contenteditable]', function(event) {
var $input = $(event.currentTarget);

// already initialized? Stop here.
if ($input.data('bs.expandableInput')) return;
if ($input.data('bs.expandableInput')) return true;

// catch event & silent it
event.preventDefault();
event.stopImmediatePropagation();

// init expandable behaviour
$input.expandableInput();

// retrigger event
$input.trigger(event.type);
$input.trigger( $.Event(event) );
});


Expand Down
2 changes: 1 addition & 1 deletion bower_components/bootstrap-expandable-input/bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bootstrap-expandable-input",
"version": "0.0.6",
"version": "0.0.14",
"authors": [
"Gregor Martynus <gregor@martynus.net>"
],
Expand Down

0 comments on commit 4b06b4c

Please sign in to comment.