Skip to content

Commit

Permalink
Added proper html escaping. Fixes #735
Browse files Browse the repository at this point in the history
  • Loading branch information
t0xicCode committed Oct 1, 2014
1 parent 445e370 commit d395c9e
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 87 deletions.
158 changes: 117 additions & 41 deletions dist/js/bootstrap-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@
return text;
}


function htmlEscape(html) {
var escapeMap = {
'&': '&',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'`': '&#x60;'
};
var source = '(?:' + Object.keys(escapeMap).join('|') + ')',
testRegexp = new RegExp(source),
replaceRegexp = new RegExp(source, 'g'),
string = html == null ? '' : '' + html;
return testRegexp.test(string) ? string.replace(replaceRegexp, function (match) {
return escapeMap[match];
}) : string;
}

var Selectpicker = function (element, options, e) {
if (e) {
e.stopPropagation();
Expand Down Expand Up @@ -185,29 +204,29 @@
var header = this.options.header ? '<div class="popover-title"><button type="button" class="close" aria-hidden="true">&times;</button>' + this.options.header + '</div>' : '';
var searchbox = this.options.liveSearch ? '<div class="bs-searchbox"><input type="text" class="input-block-level form-control" autocomplete="off" /></div>' : '';
var actionsbox = this.options.actionsBox ? '<div class="bs-actionsbox">' +
'<div class="btn-group btn-block">' +
'<button class="actions-btn bs-select-all btn btn-sm btn-default">' +
this.options.selectAllText +
'</button>' +
'<button class="actions-btn bs-deselect-all btn btn-sm btn-default">' +
this.options.deselectAllText +
'</button>' +
'</div>' +
'</div>' : '';
'<div class="btn-group btn-block">' +
'<button class="actions-btn bs-select-all btn btn-sm btn-default">' +
this.options.selectAllText +
'</button>' +
'<button class="actions-btn bs-deselect-all btn btn-sm btn-default">' +
this.options.deselectAllText +
'</button>' +
'</div>' +
'</div>' : '';
var drop =
'<div class="btn-group bootstrap-select' + multiple + inputGroup + '">' +
'<button type="button" class="btn dropdown-toggle selectpicker' + btnSize + '" data-toggle="dropdown"' + autofocus + '>' +
'<span class="filter-option pull-left"></span>&nbsp;' +
'<span class="caret"></span>' +
'</button>' +
'<div class="dropdown-menu open">' +
header +
searchbox +
actionsbox +
'<ul class="dropdown-menu inner selectpicker" role="menu">' +
'</ul>' +
'</div>' +
'</div>';
'<button type="button" class="btn dropdown-toggle selectpicker' + btnSize + '" data-toggle="dropdown"' + autofocus + '>' +
'<span class="filter-option pull-left"></span>&nbsp;' +
'<span class="caret"></span>' +
'</button>' +
'<div class="dropdown-menu open">' +
header +
searchbox +
actionsbox +
'<ul class="dropdown-menu inner selectpicker" role="menu">' +
'</ul>' +
'</div>' +
'</div>';

return $(drop);
},
Expand Down Expand Up @@ -245,9 +264,9 @@
*/
var generateLI = function (content, index, classes) {
return '<li' +
(typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') +
(typeof index !== 'undefined' | null === index ? ' data-original-index="' + index + '"' : '') +
'>' + content + '</li>';
(typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') +
(typeof index !== 'undefined' | null === index ? ' data-original-index="' + index + '"' : '') +
'>' + content + '</li>';
};

/**
Expand All @@ -258,15 +277,15 @@
* @returns {string}
*/
var generateA = function (text, classes, inline, optgroup) {
var normText = normalizeToBase($.trim($("<div/>").html(text).text()).replace(/\s\s+/g, ' '));
var normText = normalizeToBase(htmlEscape(text));
return '<a tabindex="0"' +
(typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') +
(typeof inline !== 'undefined' ? ' style="' + inline + '"' : '') +
(typeof optgroup !== 'undefined' ? 'data-optgroup="' + optgroup + '"' : '') +
' data-normalized-text="' + normText + '"' +
'>' + text +
'<span class="' + that.options.iconBase + ' ' + that.options.tickIcon + ' check-mark"></span>' +
'</a>';
(typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') +
(typeof inline !== 'undefined' ? ' style="' + inline + '"' : '') +
(typeof optgroup !== 'undefined' ? 'data-optgroup="' + optgroup + '"' : '') +
' data-normalized-text="' + normText + '"' +
'>' + text +
'<span class="' + that.options.iconBase + ' ' + that.options.tickIcon + ' check-mark"></span>' +
'</a>';
};

this.$element.find('option').each(function () {
Expand Down Expand Up @@ -393,7 +412,7 @@
title = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText;
}

this.$button.attr('title', $.trim($("<div/>").html(title).text()).replace(/\s\s+/g, ' '));
this.$button.attr('title', htmlEscape(title));
this.$newElement.find('.filter-option').html(title);
},

Expand Down Expand Up @@ -488,8 +507,16 @@
minHeight = 0;
}

menu.css({'max-height': menuHeight + 'px', 'overflow': 'hidden', 'min-height': minHeight + headerHeight + searchHeight + actionsHeight + 'px'});
menuInner.css({'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - menuPadding + 'px', 'overflow-y': 'auto', 'min-height': Math.max(minHeight - menuPadding, 0) + 'px'});
menu.css({
'max-height': menuHeight + 'px',
'overflow': 'hidden',
'min-height': minHeight + headerHeight + searchHeight + actionsHeight + 'px'
});
menuInner.css({
'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - menuPadding + 'px',
'overflow-y': 'auto',
'min-height': Math.max(minHeight - menuPadding, 0) + 'px'
});
};
getSize();
this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize);
Expand Down Expand Up @@ -549,7 +576,12 @@
$drop.addClass($element.attr('class').replace(/form-control/gi, '')).toggleClass('dropup', $element.hasClass('dropup'));
pos = $element.offset();
actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight;
$drop.css({'top': pos.top + actualHeight, 'left': pos.left, 'width': $element[0].offsetWidth, 'position': 'absolute'});
$drop.css({
'top': pos.top + actualHeight,
'left': pos.left,
'width': $element[0].offsetWidth,
'position': 'absolute'
});
};
this.$newElement.on('click', function () {
if (that.isDisabled()) {
Expand Down Expand Up @@ -818,7 +850,7 @@

if (!that.$menu.find('li').filter(':visible:not(.no-results)').length) {
if (!!no_results.parent().length) no_results.remove();
no_results.html(that.options.noneResultsText + ' "' + that.$searchbox.val() + '"').show();
no_results.html(that.options.noneResultsText + ' "' + htmlEscape(that.$searchbox.val()) + '"').show();
that.$menu.find('li').last().after(no_results);
} else if (!!no_results.parent().length) {
no_results.remove();
Expand Down Expand Up @@ -870,10 +902,54 @@
prevIndex,
isActive,
keyCodeMap = {
32: ' ', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 59: ';',
65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', 76: 'l',
77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x',
89: 'y', 90: 'z', 96: '0', 97: '1', 98: '2', 99: '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9'
32: ' ',
48: '0',
49: '1',
50: '2',
51: '3',
52: '4',
53: '5',
54: '6',
55: '7',
56: '8',
57: '9',
59: ';',
65: 'a',
66: 'b',
67: 'c',
68: 'd',
69: 'e',
70: 'f',
71: 'g',
72: 'h',
73: 'i',
74: 'j',
75: 'k',
76: 'l',
77: 'm',
78: 'n',
79: 'o',
80: 'p',
81: 'q',
82: 'r',
83: 's',
84: 't',
85: 'u',
86: 'v',
87: 'w',
88: 'x',
89: 'y',
90: 'z',
96: '0',
97: '1',
98: '2',
99: '3',
100: '4',
101: '5',
102: '6',
103: '7',
104: '8',
105: '9'
};

if (that.options.liveSearch) $parent = $this.parent().parent();
Expand Down
2 changes: 1 addition & 1 deletion dist/js/bootstrap-select.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/bootstrap-select.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/js/i18n/defaults-ru_RU.min.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* Copyright 2013-2014 bootstrap-select
* Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
*/
!function(a){a.fn.selectpicker.defaults={noneSelectedText:"Ничего не выбрано",noneResultsText:"Совпадений не найдено",countSelectedText:"Выбрано {0} из {1}",maxOptionsText:["Достигнут предел ({n} {var} максимум)","Достигнут предел в группе ({n} {var} максимум)",["items","item"]],multipleSeparator:", "}}(jQuery);
!function(a){a.fn.selectpicker.defaults={noneSelectedText:"Ничего не выбрано",noneResultsText:"Совпадений не найдено",countSelectedText:"Выбрано {0} из {1}",maxOptionsText:["Достигнут предел ({n} {var} максимум)","Достигнут предел в группе ({n} {var} максимум)",["items","item"]],multipleSeparator:", "}}(jQuery);
2 changes: 1 addition & 1 deletion dist/js/i18n/defaults-ua_UA.min.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* Copyright 2013-2014 bootstrap-select
* Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
*/
!function(a){a.fn.selectpicker.defaults={noneSelectedText:"Нічого не вибрано",noneResultsText:"Збігів не знайдено",countSelectedText:"Вибрано {0} із {1}",maxOptionsText:["Досягнута межа ({n} {var} максимум)","Досягнута межа в групі ({n} {var} максимум)",["items","item"]],multipleSeparator:", "}}(jQuery);
!function(a){a.fn.selectpicker.defaults={noneSelectedText:"Нічого не вибрано",noneResultsText:"Збігів не знайдено",countSelectedText:"Вибрано {0} із {1}",maxOptionsText:["Досягнута межа ({n} {var} максимум)","Досягнута межа в групі ({n} {var} максимум)",["items","item"]],multipleSeparator:", "}}(jQuery);
8 changes: 7 additions & 1 deletion dist/js/i18n/defaults-zh_TW.min.js
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
/*! * Bootstrap-select v1.6.2 (http://silviomoreto.github.io/bootstrap-select/) * * Copyright 2013-2014 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */!function(e){e.fn.selectpicker.defaults={noneSelectedText:"沒有選取任何項目",noneResultsText:"沒有找到符合的結果",countSelectedText:"已經選取{0}個項目",maxOptionsText:["超過限制 (最多選擇{n}項)","超過限制(最多選擇{n}組)"],selectAllText:"選取全部",deselectAllText:"全部取消",multipleSeparator:", "}}(jQuery);
/*!
* Bootstrap-select v1.6.2 (http://silviomoreto.github.io/bootstrap-select/)
*
* Copyright 2013-2014 bootstrap-select
* Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
*/
!function(a){a.fn.selectpicker.defaults={noneSelectedText:"沒有選取任何項目",noneResultsText:"沒有找到符合的結果",countSelectedText:"已經選取{0}個項目",maxOptionsText:["超過限制 (最多選擇{n}項)","超過限制(最多選擇{n}組)"],selectAllText:"選取全部",deselectAllText:"全部取消",multipleSeparator:", "}}(jQuery);
Loading

3 comments on commit d395c9e

@stepanselyuk
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a significant slowdown in the program. I use a big list with about 1000 values​​. Probably due to the Regexp. Look to https://stackoverflow.com/questions/5069464/replace-multiple-strings-at-once or http://phpjs.org/functions/strtr/

@t0xicCode
Copy link
Collaborator Author

@t0xicCode t0xicCode commented on d395c9e Oct 2, 2014 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stepanselyuk
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've found my problem with freeze... See #751
Thanks.

Please sign in to comment.