aboutsummaryrefslogtreecommitdiff
path: root/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js')
-rw-r--r--src/js/bootstrap.js (renamed from src/js/bootstrap-3.3.7.js)397
-rw-r--r--src/js/entropy.js345
-rw-r--r--src/js/index.js16
3 files changed, 472 insertions, 286 deletions
diff --git a/src/js/bootstrap-3.3.7.js b/src/js/bootstrap.js
index 8a2e99a..170bd60 100644
--- a/src/js/bootstrap-3.3.7.js
+++ b/src/js/bootstrap.js
@@ -1,6 +1,6 @@
1/*! 1/*!
2 * Bootstrap v3.3.7 (http://getbootstrap.com) 2 * Bootstrap v3.4.1 (https://getbootstrap.com/)
3 * Copyright 2011-2016 Twitter, Inc. 3 * Copyright 2011-2019 Twitter, Inc.
4 * Licensed under the MIT license 4 * Licensed under the MIT license
5 */ 5 */
6 6
@@ -17,10 +17,10 @@ if (typeof jQuery === 'undefined') {
17}(jQuery); 17}(jQuery);
18 18
19/* ======================================================================== 19/* ========================================================================
20 * Bootstrap: transition.js v3.3.7 20 * Bootstrap: transition.js v3.4.1
21 * http://getbootstrap.com/javascript/#transitions 21 * https://getbootstrap.com/docs/3.4/javascript/#transitions
22 * ======================================================================== 22 * ========================================================================
23 * Copyright 2011-2016 Twitter, Inc. 23 * Copyright 2011-2019 Twitter, Inc.
24 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 24 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
25 * ======================================================================== */ 25 * ======================================================================== */
26 26
@@ -28,7 +28,7 @@ if (typeof jQuery === 'undefined') {
28+function ($) { 28+function ($) {
29 'use strict'; 29 'use strict';
30 30
31 // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) 31 // CSS TRANSITION SUPPORT (Shoutout: https://modernizr.com/)
32 // ============================================================ 32 // ============================================================
33 33
34 function transitionEnd() { 34 function transitionEnd() {
@@ -50,7 +50,7 @@ if (typeof jQuery === 'undefined') {
50 return false // explicit for ie8 ( ._.) 50 return false // explicit for ie8 ( ._.)
51 } 51 }
52 52
53 // http://blog.alexmaccaw.com/css-transitions 53 // https://blog.alexmaccaw.com/css-transitions
54 $.fn.emulateTransitionEnd = function (duration) { 54 $.fn.emulateTransitionEnd = function (duration) {
55 var called = false 55 var called = false
56 var $el = this 56 var $el = this
@@ -77,10 +77,10 @@ if (typeof jQuery === 'undefined') {
77}(jQuery); 77}(jQuery);
78 78
79/* ======================================================================== 79/* ========================================================================
80 * Bootstrap: alert.js v3.3.7 80 * Bootstrap: alert.js v3.4.1
81 * http://getbootstrap.com/javascript/#alerts 81 * https://getbootstrap.com/docs/3.4/javascript/#alerts
82 * ======================================================================== 82 * ========================================================================
83 * Copyright 2011-2016 Twitter, Inc. 83 * Copyright 2011-2019 Twitter, Inc.
84 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 84 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
85 * ======================================================================== */ 85 * ======================================================================== */
86 86
@@ -96,7 +96,7 @@ if (typeof jQuery === 'undefined') {
96 $(el).on('click', dismiss, this.close) 96 $(el).on('click', dismiss, this.close)
97 } 97 }
98 98
99 Alert.VERSION = '3.3.7' 99 Alert.VERSION = '3.4.1'
100 100
101 Alert.TRANSITION_DURATION = 150 101 Alert.TRANSITION_DURATION = 150
102 102
@@ -109,7 +109,8 @@ if (typeof jQuery === 'undefined') {
109 selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 109 selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
110 } 110 }
111 111
112 var $parent = $(selector === '#' ? [] : selector) 112 selector = selector === '#' ? [] : selector
113 var $parent = $(document).find(selector)
113 114
114 if (e) e.preventDefault() 115 if (e) e.preventDefault()
115 116
@@ -172,10 +173,10 @@ if (typeof jQuery === 'undefined') {
172}(jQuery); 173}(jQuery);
173 174
174/* ======================================================================== 175/* ========================================================================
175 * Bootstrap: button.js v3.3.7 176 * Bootstrap: button.js v3.4.1
176 * http://getbootstrap.com/javascript/#buttons 177 * https://getbootstrap.com/docs/3.4/javascript/#buttons
177 * ======================================================================== 178 * ========================================================================
178 * Copyright 2011-2016 Twitter, Inc. 179 * Copyright 2011-2019 Twitter, Inc.
179 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 180 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
180 * ======================================================================== */ 181 * ======================================================================== */
181 182
@@ -192,7 +193,7 @@ if (typeof jQuery === 'undefined') {
192 this.isLoading = false 193 this.isLoading = false
193 } 194 }
194 195
195 Button.VERSION = '3.3.7' 196 Button.VERSION = '3.4.1'
196 197
197 Button.DEFAULTS = { 198 Button.DEFAULTS = {
198 loadingText: 'loading...' 199 loadingText: 'loading...'
@@ -298,10 +299,10 @@ if (typeof jQuery === 'undefined') {
298}(jQuery); 299}(jQuery);
299 300
300/* ======================================================================== 301/* ========================================================================
301 * Bootstrap: carousel.js v3.3.7 302 * Bootstrap: carousel.js v3.4.1
302 * http://getbootstrap.com/javascript/#carousel 303 * https://getbootstrap.com/docs/3.4/javascript/#carousel
303 * ======================================================================== 304 * ========================================================================
304 * Copyright 2011-2016 Twitter, Inc. 305 * Copyright 2011-2019 Twitter, Inc.
305 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 306 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
306 * ======================================================================== */ 307 * ======================================================================== */
307 308
@@ -329,7 +330,7 @@ if (typeof jQuery === 'undefined') {
329 .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) 330 .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
330 } 331 }
331 332
332 Carousel.VERSION = '3.3.7' 333 Carousel.VERSION = '3.4.1'
333 334
334 Carousel.TRANSITION_DURATION = 600 335 Carousel.TRANSITION_DURATION = 600
335 336
@@ -443,7 +444,9 @@ if (typeof jQuery === 'undefined') {
443 var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" 444 var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
444 if ($.support.transition && this.$element.hasClass('slide')) { 445 if ($.support.transition && this.$element.hasClass('slide')) {
445 $next.addClass(type) 446 $next.addClass(type)
446 $next[0].offsetWidth // force reflow 447 if (typeof $next === 'object' && $next.length) {
448 $next[0].offsetWidth // force reflow
449 }
447 $active.addClass(direction) 450 $active.addClass(direction)
448 $next.addClass(direction) 451 $next.addClass(direction)
449 $active 452 $active
@@ -505,10 +508,17 @@ if (typeof jQuery === 'undefined') {
505 // ================= 508 // =================
506 509
507 var clickHandler = function (e) { 510 var clickHandler = function (e) {
508 var href
509 var $this = $(this) 511 var $this = $(this)
510 var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 512 var href = $this.attr('href')
513 if (href) {
514 href = href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
515 }
516
517 var target = $this.attr('data-target') || href
518 var $target = $(document).find(target)
519
511 if (!$target.hasClass('carousel')) return 520 if (!$target.hasClass('carousel')) return
521
512 var options = $.extend({}, $target.data(), $this.data()) 522 var options = $.extend({}, $target.data(), $this.data())
513 var slideIndex = $this.attr('data-slide-to') 523 var slideIndex = $this.attr('data-slide-to')
514 if (slideIndex) options.interval = false 524 if (slideIndex) options.interval = false
@@ -536,10 +546,10 @@ if (typeof jQuery === 'undefined') {
536}(jQuery); 546}(jQuery);
537 547
538/* ======================================================================== 548/* ========================================================================
539 * Bootstrap: collapse.js v3.3.7 549 * Bootstrap: collapse.js v3.4.1
540 * http://getbootstrap.com/javascript/#collapse 550 * https://getbootstrap.com/docs/3.4/javascript/#collapse
541 * ======================================================================== 551 * ========================================================================
542 * Copyright 2011-2016 Twitter, Inc. 552 * Copyright 2011-2019 Twitter, Inc.
543 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 553 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
544 * ======================================================================== */ 554 * ======================================================================== */
545 555
@@ -567,7 +577,7 @@ if (typeof jQuery === 'undefined') {
567 if (this.options.toggle) this.toggle() 577 if (this.options.toggle) this.toggle()
568 } 578 }
569 579
570 Collapse.VERSION = '3.3.7' 580 Collapse.VERSION = '3.4.1'
571 581
572 Collapse.TRANSITION_DURATION = 350 582 Collapse.TRANSITION_DURATION = 350
573 583
@@ -674,7 +684,7 @@ if (typeof jQuery === 'undefined') {
674 } 684 }
675 685
676 Collapse.prototype.getParent = function () { 686 Collapse.prototype.getParent = function () {
677 return $(this.options.parent) 687 return $(document).find(this.options.parent)
678 .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') 688 .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
679 .each($.proxy(function (i, element) { 689 .each($.proxy(function (i, element) {
680 var $element = $(element) 690 var $element = $(element)
@@ -697,7 +707,7 @@ if (typeof jQuery === 'undefined') {
697 var target = $trigger.attr('data-target') 707 var target = $trigger.attr('data-target')
698 || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 708 || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
699 709
700 return $(target) 710 return $(document).find(target)
701 } 711 }
702 712
703 713
@@ -749,10 +759,10 @@ if (typeof jQuery === 'undefined') {
749}(jQuery); 759}(jQuery);
750 760
751/* ======================================================================== 761/* ========================================================================
752 * Bootstrap: dropdown.js v3.3.7 762 * Bootstrap: dropdown.js v3.4.1
753 * http://getbootstrap.com/javascript/#dropdowns 763 * https://getbootstrap.com/docs/3.4/javascript/#dropdowns
754 * ======================================================================== 764 * ========================================================================
755 * Copyright 2011-2016 Twitter, Inc. 765 * Copyright 2011-2019 Twitter, Inc.
756 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 766 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
757 * ======================================================================== */ 767 * ======================================================================== */
758 768
@@ -769,7 +779,7 @@ if (typeof jQuery === 'undefined') {
769 $(element).on('click.bs.dropdown', this.toggle) 779 $(element).on('click.bs.dropdown', this.toggle)
770 } 780 }
771 781
772 Dropdown.VERSION = '3.3.7' 782 Dropdown.VERSION = '3.4.1'
773 783
774 function getParent($this) { 784 function getParent($this) {
775 var selector = $this.attr('data-target') 785 var selector = $this.attr('data-target')
@@ -779,7 +789,7 @@ if (typeof jQuery === 'undefined') {
779 selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 789 selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
780 } 790 }
781 791
782 var $parent = selector && $(selector) 792 var $parent = selector !== '#' ? $(document).find(selector) : null
783 793
784 return $parent && $parent.length ? $parent : $this.parent() 794 return $parent && $parent.length ? $parent : $this.parent()
785 } 795 }
@@ -915,10 +925,10 @@ if (typeof jQuery === 'undefined') {
915}(jQuery); 925}(jQuery);
916 926
917/* ======================================================================== 927/* ========================================================================
918 * Bootstrap: modal.js v3.3.7 928 * Bootstrap: modal.js v3.4.1
919 * http://getbootstrap.com/javascript/#modals 929 * https://getbootstrap.com/docs/3.4/javascript/#modals
920 * ======================================================================== 930 * ========================================================================
921 * Copyright 2011-2016 Twitter, Inc. 931 * Copyright 2011-2019 Twitter, Inc.
922 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 932 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
923 * ======================================================================== */ 933 * ======================================================================== */
924 934
@@ -930,15 +940,16 @@ if (typeof jQuery === 'undefined') {
930 // ====================== 940 // ======================
931 941
932 var Modal = function (element, options) { 942 var Modal = function (element, options) {
933 this.options = options 943 this.options = options
934 this.$body = $(document.body) 944 this.$body = $(document.body)
935 this.$element = $(element) 945 this.$element = $(element)
936 this.$dialog = this.$element.find('.modal-dialog') 946 this.$dialog = this.$element.find('.modal-dialog')
937 this.$backdrop = null 947 this.$backdrop = null
938 this.isShown = null 948 this.isShown = null
939 this.originalBodyPad = null 949 this.originalBodyPad = null
940 this.scrollbarWidth = 0 950 this.scrollbarWidth = 0
941 this.ignoreBackdropClick = false 951 this.ignoreBackdropClick = false
952 this.fixedContent = '.navbar-fixed-top, .navbar-fixed-bottom'
942 953
943 if (this.options.remote) { 954 if (this.options.remote) {
944 this.$element 955 this.$element
@@ -949,7 +960,7 @@ if (typeof jQuery === 'undefined') {
949 } 960 }
950 } 961 }
951 962
952 Modal.VERSION = '3.3.7' 963 Modal.VERSION = '3.4.1'
953 964
954 Modal.TRANSITION_DURATION = 300 965 Modal.TRANSITION_DURATION = 300
955 Modal.BACKDROP_TRANSITION_DURATION = 150 966 Modal.BACKDROP_TRANSITION_DURATION = 150
@@ -966,7 +977,7 @@ if (typeof jQuery === 'undefined') {
966 977
967 Modal.prototype.show = function (_relatedTarget) { 978 Modal.prototype.show = function (_relatedTarget) {
968 var that = this 979 var that = this
969 var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) 980 var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
970 981
971 this.$element.trigger(e) 982 this.$element.trigger(e)
972 983
@@ -1057,8 +1068,8 @@ if (typeof jQuery === 'undefined') {
1057 .off('focusin.bs.modal') // guard against infinite focus loop 1068 .off('focusin.bs.modal') // guard against infinite focus loop
1058 .on('focusin.bs.modal', $.proxy(function (e) { 1069 .on('focusin.bs.modal', $.proxy(function (e) {
1059 if (document !== e.target && 1070 if (document !== e.target &&
1060 this.$element[0] !== e.target && 1071 this.$element[0] !== e.target &&
1061 !this.$element.has(e.target).length) { 1072 !this.$element.has(e.target).length) {
1062 this.$element.trigger('focus') 1073 this.$element.trigger('focus')
1063 } 1074 }
1064 }, this)) 1075 }, this))
@@ -1160,7 +1171,7 @@ if (typeof jQuery === 'undefined') {
1160 var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight 1171 var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
1161 1172
1162 this.$element.css({ 1173 this.$element.css({
1163 paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', 1174 paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
1164 paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' 1175 paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
1165 }) 1176 })
1166 } 1177 }
@@ -1185,11 +1196,26 @@ if (typeof jQuery === 'undefined') {
1185 Modal.prototype.setScrollbar = function () { 1196 Modal.prototype.setScrollbar = function () {
1186 var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) 1197 var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
1187 this.originalBodyPad = document.body.style.paddingRight || '' 1198 this.originalBodyPad = document.body.style.paddingRight || ''
1188 if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) 1199 var scrollbarWidth = this.scrollbarWidth
1200 if (this.bodyIsOverflowing) {
1201 this.$body.css('padding-right', bodyPad + scrollbarWidth)
1202 $(this.fixedContent).each(function (index, element) {
1203 var actualPadding = element.style.paddingRight
1204 var calculatedPadding = $(element).css('padding-right')
1205 $(element)
1206 .data('padding-right', actualPadding)
1207 .css('padding-right', parseFloat(calculatedPadding) + scrollbarWidth + 'px')
1208 })
1209 }
1189 } 1210 }
1190 1211
1191 Modal.prototype.resetScrollbar = function () { 1212 Modal.prototype.resetScrollbar = function () {
1192 this.$body.css('padding-right', this.originalBodyPad) 1213 this.$body.css('padding-right', this.originalBodyPad)
1214 $(this.fixedContent).each(function (index, element) {
1215 var padding = $(element).data('padding-right')
1216 $(element).removeData('padding-right')
1217 element.style.paddingRight = padding ? padding : ''
1218 })
1193 } 1219 }
1194 1220
1195 Modal.prototype.measureScrollbar = function () { // thx walsh 1221 Modal.prototype.measureScrollbar = function () { // thx walsh
@@ -1207,8 +1233,8 @@ if (typeof jQuery === 'undefined') {
1207 1233
1208 function Plugin(option, _relatedTarget) { 1234 function Plugin(option, _relatedTarget) {
1209 return this.each(function () { 1235 return this.each(function () {
1210 var $this = $(this) 1236 var $this = $(this)
1211 var data = $this.data('bs.modal') 1237 var data = $this.data('bs.modal')
1212 var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) 1238 var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
1213 1239
1214 if (!data) $this.data('bs.modal', (data = new Modal(this, options))) 1240 if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
@@ -1219,7 +1245,7 @@ if (typeof jQuery === 'undefined') {
1219 1245
1220 var old = $.fn.modal 1246 var old = $.fn.modal
1221 1247
1222 $.fn.modal = Plugin 1248 $.fn.modal = Plugin
1223 $.fn.modal.Constructor = Modal 1249 $.fn.modal.Constructor = Modal
1224 1250
1225 1251
@@ -1236,10 +1262,13 @@ if (typeof jQuery === 'undefined') {
1236 // ============== 1262 // ==============
1237 1263
1238 $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { 1264 $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
1239 var $this = $(this) 1265 var $this = $(this)
1240 var href = $this.attr('href') 1266 var href = $this.attr('href')
1241 var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 1267 var target = $this.attr('data-target') ||
1242 var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) 1268 (href && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
1269
1270 var $target = $(document).find(target)
1271 var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
1243 1272
1244 if ($this.is('a')) e.preventDefault() 1273 if ($this.is('a')) e.preventDefault()
1245 1274
@@ -1255,18 +1284,148 @@ if (typeof jQuery === 'undefined') {
1255}(jQuery); 1284}(jQuery);
1256 1285
1257/* ======================================================================== 1286/* ========================================================================
1258 * Bootstrap: tooltip.js v3.3.7 1287 * Bootstrap: tooltip.js v3.4.1
1259 * http://getbootstrap.com/javascript/#tooltip 1288 * https://getbootstrap.com/docs/3.4/javascript/#tooltip
1260 * Inspired by the original jQuery.tipsy by Jason Frame 1289 * Inspired by the original jQuery.tipsy by Jason Frame
1261 * ======================================================================== 1290 * ========================================================================
1262 * Copyright 2011-2016 Twitter, Inc. 1291 * Copyright 2011-2019 Twitter, Inc.
1263 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 1292 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1264 * ======================================================================== */ 1293 * ======================================================================== */
1265 1294
1266
1267+function ($) { 1295+function ($) {
1268 'use strict'; 1296 'use strict';
1269 1297
1298 var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']
1299
1300 var uriAttrs = [
1301 'background',
1302 'cite',
1303 'href',
1304 'itemtype',
1305 'longdesc',
1306 'poster',
1307 'src',
1308 'xlink:href'
1309 ]
1310
1311 var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
1312
1313 var DefaultWhitelist = {
1314 // Global attributes allowed on any supplied element below.
1315 '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
1316 a: ['target', 'href', 'title', 'rel'],
1317 area: [],
1318 b: [],
1319 br: [],
1320 col: [],
1321 code: [],
1322 div: [],
1323 em: [],
1324 hr: [],
1325 h1: [],
1326 h2: [],
1327 h3: [],
1328 h4: [],
1329 h5: [],
1330 h6: [],
1331 i: [],
1332 img: ['src', 'alt', 'title', 'width', 'height'],
1333 li: [],
1334 ol: [],
1335 p: [],
1336 pre: [],
1337 s: [],
1338 small: [],
1339 span: [],
1340 sub: [],
1341 sup: [],
1342 strong: [],
1343 u: [],
1344 ul: []
1345 }
1346
1347 /**
1348 * A pattern that recognizes a commonly useful subset of URLs that are safe.
1349 *
1350 * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
1351 */
1352 var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi
1353
1354 /**
1355 * A pattern that matches safe data URLs. Only matches image, video and audio types.
1356 *
1357 * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
1358 */
1359 var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i
1360
1361 function allowedAttribute(attr, allowedAttributeList) {
1362 var attrName = attr.nodeName.toLowerCase()
1363
1364 if ($.inArray(attrName, allowedAttributeList) !== -1) {
1365 if ($.inArray(attrName, uriAttrs) !== -1) {
1366 return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))
1367 }
1368
1369 return true
1370 }
1371
1372 var regExp = $(allowedAttributeList).filter(function (index, value) {
1373 return value instanceof RegExp
1374 })
1375
1376 // Check if a regular expression validates the attribute.
1377 for (var i = 0, l = regExp.length; i < l; i++) {
1378 if (attrName.match(regExp[i])) {
1379 return true
1380 }
1381 }
1382
1383 return false
1384 }
1385
1386 function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
1387 if (unsafeHtml.length === 0) {
1388 return unsafeHtml
1389 }
1390
1391 if (sanitizeFn && typeof sanitizeFn === 'function') {
1392 return sanitizeFn(unsafeHtml)
1393 }
1394
1395 // IE 8 and below don't support createHTMLDocument
1396 if (!document.implementation || !document.implementation.createHTMLDocument) {
1397 return unsafeHtml
1398 }
1399
1400 var createdDocument = document.implementation.createHTMLDocument('sanitization')
1401 createdDocument.body.innerHTML = unsafeHtml
1402
1403 var whitelistKeys = $.map(whiteList, function (el, i) { return i })
1404 var elements = $(createdDocument.body).find('*')
1405
1406 for (var i = 0, len = elements.length; i < len; i++) {
1407 var el = elements[i]
1408 var elName = el.nodeName.toLowerCase()
1409
1410 if ($.inArray(elName, whitelistKeys) === -1) {
1411 el.parentNode.removeChild(el)
1412
1413 continue
1414 }
1415
1416 var attributeList = $.map(el.attributes, function (el) { return el })
1417 var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])
1418
1419 for (var j = 0, len2 = attributeList.length; j < len2; j++) {
1420 if (!allowedAttribute(attributeList[j], whitelistedAttributes)) {
1421 el.removeAttribute(attributeList[j].nodeName)
1422 }
1423 }
1424 }
1425
1426 return createdDocument.body.innerHTML
1427 }
1428
1270 // TOOLTIP PUBLIC CLASS DEFINITION 1429 // TOOLTIP PUBLIC CLASS DEFINITION
1271 // =============================== 1430 // ===============================
1272 1431
@@ -1282,7 +1441,7 @@ if (typeof jQuery === 'undefined') {
1282 this.init('tooltip', element, options) 1441 this.init('tooltip', element, options)
1283 } 1442 }
1284 1443
1285 Tooltip.VERSION = '3.3.7' 1444 Tooltip.VERSION = '3.4.1'
1286 1445
1287 Tooltip.TRANSITION_DURATION = 150 1446 Tooltip.TRANSITION_DURATION = 150
1288 1447
@@ -1299,7 +1458,10 @@ if (typeof jQuery === 'undefined') {
1299 viewport: { 1458 viewport: {
1300 selector: 'body', 1459 selector: 'body',
1301 padding: 0 1460 padding: 0
1302 } 1461 },
1462 sanitize : true,
1463 sanitizeFn : null,
1464 whiteList : DefaultWhitelist
1303 } 1465 }
1304 1466
1305 Tooltip.prototype.init = function (type, element, options) { 1467 Tooltip.prototype.init = function (type, element, options) {
@@ -1307,7 +1469,7 @@ if (typeof jQuery === 'undefined') {
1307 this.type = type 1469 this.type = type
1308 this.$element = $(element) 1470 this.$element = $(element)
1309 this.options = this.getOptions(options) 1471 this.options = this.getOptions(options)
1310 this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) 1472 this.$viewport = this.options.viewport && $(document).find($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
1311 this.inState = { click: false, hover: false, focus: false } 1473 this.inState = { click: false, hover: false, focus: false }
1312 1474
1313 if (this.$element[0] instanceof document.constructor && !this.options.selector) { 1475 if (this.$element[0] instanceof document.constructor && !this.options.selector) {
@@ -1340,7 +1502,15 @@ if (typeof jQuery === 'undefined') {
1340 } 1502 }
1341 1503
1342 Tooltip.prototype.getOptions = function (options) { 1504 Tooltip.prototype.getOptions = function (options) {
1343 options = $.extend({}, this.getDefaults(), this.$element.data(), options) 1505 var dataAttributes = this.$element.data()
1506
1507 for (var dataAttr in dataAttributes) {
1508 if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) {
1509 delete dataAttributes[dataAttr]
1510 }
1511 }
1512
1513 options = $.extend({}, this.getDefaults(), dataAttributes, options)
1344 1514
1345 if (options.delay && typeof options.delay == 'number') { 1515 if (options.delay && typeof options.delay == 'number') {
1346 options.delay = { 1516 options.delay = {
@@ -1349,6 +1519,10 @@ if (typeof jQuery === 'undefined') {
1349 } 1519 }
1350 } 1520 }
1351 1521
1522 if (options.sanitize) {
1523 options.template = sanitizeHtml(options.template, options.whiteList, options.sanitizeFn)
1524 }
1525
1352 return options 1526 return options
1353 } 1527 }
1354 1528
@@ -1460,7 +1634,7 @@ if (typeof jQuery === 'undefined') {
1460 .addClass(placement) 1634 .addClass(placement)
1461 .data('bs.' + this.type, this) 1635 .data('bs.' + this.type, this)
1462 1636
1463 this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) 1637 this.options.container ? $tip.appendTo($(document).find(this.options.container)) : $tip.insertAfter(this.$element)
1464 this.$element.trigger('inserted.bs.' + this.type) 1638 this.$element.trigger('inserted.bs.' + this.type)
1465 1639
1466 var pos = this.getPosition() 1640 var pos = this.getPosition()
@@ -1562,7 +1736,16 @@ if (typeof jQuery === 'undefined') {
1562 var $tip = this.tip() 1736 var $tip = this.tip()
1563 var title = this.getTitle() 1737 var title = this.getTitle()
1564 1738
1565 $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) 1739 if (this.options.html) {
1740 if (this.options.sanitize) {
1741 title = sanitizeHtml(title, this.options.whiteList, this.options.sanitizeFn)
1742 }
1743
1744 $tip.find('.tooltip-inner').html(title)
1745 } else {
1746 $tip.find('.tooltip-inner').text(title)
1747 }
1748
1566 $tip.removeClass('fade in top bottom left right') 1749 $tip.removeClass('fade in top bottom left right')
1567 } 1750 }
1568 1751
@@ -1743,6 +1926,9 @@ if (typeof jQuery === 'undefined') {
1743 }) 1926 })
1744 } 1927 }
1745 1928
1929 Tooltip.prototype.sanitizeHtml = function (unsafeHtml) {
1930 return sanitizeHtml(unsafeHtml, this.options.whiteList, this.options.sanitizeFn)
1931 }
1746 1932
1747 // TOOLTIP PLUGIN DEFINITION 1933 // TOOLTIP PLUGIN DEFINITION
1748 // ========================= 1934 // =========================
@@ -1776,10 +1962,10 @@ if (typeof jQuery === 'undefined') {
1776}(jQuery); 1962}(jQuery);
1777 1963
1778/* ======================================================================== 1964/* ========================================================================
1779 * Bootstrap: popover.js v3.3.7 1965 * Bootstrap: popover.js v3.4.1
1780 * http://getbootstrap.com/javascript/#popovers 1966 * https://getbootstrap.com/docs/3.4/javascript/#popovers
1781 * ======================================================================== 1967 * ========================================================================
1782 * Copyright 2011-2016 Twitter, Inc. 1968 * Copyright 2011-2019 Twitter, Inc.
1783 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 1969 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1784 * ======================================================================== */ 1970 * ======================================================================== */
1785 1971
@@ -1796,7 +1982,7 @@ if (typeof jQuery === 'undefined') {
1796 1982
1797 if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') 1983 if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
1798 1984
1799 Popover.VERSION = '3.3.7' 1985 Popover.VERSION = '3.4.1'
1800 1986
1801 Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { 1987 Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
1802 placement: 'right', 1988 placement: 'right',
@@ -1822,10 +2008,25 @@ if (typeof jQuery === 'undefined') {
1822 var title = this.getTitle() 2008 var title = this.getTitle()
1823 var content = this.getContent() 2009 var content = this.getContent()
1824 2010
1825 $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) 2011 if (this.options.html) {
1826 $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events 2012 var typeContent = typeof content
1827 this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' 2013
1828 ](content) 2014 if (this.options.sanitize) {
2015 title = this.sanitizeHtml(title)
2016
2017 if (typeContent === 'string') {
2018 content = this.sanitizeHtml(content)
2019 }
2020 }
2021
2022 $tip.find('.popover-title').html(title)
2023 $tip.find('.popover-content').children().detach().end()[
2024 typeContent === 'string' ? 'html' : 'append'
2025 ](content)
2026 } else {
2027 $tip.find('.popover-title').text(title)
2028 $tip.find('.popover-content').children().detach().end().text(content)
2029 }
1829 2030
1830 $tip.removeClass('fade top bottom left right in') 2031 $tip.removeClass('fade top bottom left right in')
1831 2032
@@ -1844,8 +2045,8 @@ if (typeof jQuery === 'undefined') {
1844 2045
1845 return $e.attr('data-content') 2046 return $e.attr('data-content')
1846 || (typeof o.content == 'function' ? 2047 || (typeof o.content == 'function' ?
1847 o.content.call($e[0]) : 2048 o.content.call($e[0]) :
1848 o.content) 2049 o.content)
1849 } 2050 }
1850 2051
1851 Popover.prototype.arrow = function () { 2052 Popover.prototype.arrow = function () {
@@ -1885,10 +2086,10 @@ if (typeof jQuery === 'undefined') {
1885}(jQuery); 2086}(jQuery);
1886 2087
1887/* ======================================================================== 2088/* ========================================================================
1888 * Bootstrap: scrollspy.js v3.3.7 2089 * Bootstrap: scrollspy.js v3.4.1
1889 * http://getbootstrap.com/javascript/#scrollspy 2090 * https://getbootstrap.com/docs/3.4/javascript/#scrollspy
1890 * ======================================================================== 2091 * ========================================================================
1891 * Copyright 2011-2016 Twitter, Inc. 2092 * Copyright 2011-2019 Twitter, Inc.
1892 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 2093 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1893 * ======================================================================== */ 2094 * ======================================================================== */
1894 2095
@@ -1914,7 +2115,7 @@ if (typeof jQuery === 'undefined') {
1914 this.process() 2115 this.process()
1915 } 2116 }
1916 2117
1917 ScrollSpy.VERSION = '3.3.7' 2118 ScrollSpy.VERSION = '3.4.1'
1918 2119
1919 ScrollSpy.DEFAULTS = { 2120 ScrollSpy.DEFAULTS = {
1920 offset: 10 2121 offset: 10
@@ -2058,10 +2259,10 @@ if (typeof jQuery === 'undefined') {
2058}(jQuery); 2259}(jQuery);
2059 2260
2060/* ======================================================================== 2261/* ========================================================================
2061 * Bootstrap: tab.js v3.3.7 2262 * Bootstrap: tab.js v3.4.1
2062 * http://getbootstrap.com/javascript/#tabs 2263 * https://getbootstrap.com/docs/3.4/javascript/#tabs
2063 * ======================================================================== 2264 * ========================================================================
2064 * Copyright 2011-2016 Twitter, Inc. 2265 * Copyright 2011-2019 Twitter, Inc.
2065 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 2266 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
2066 * ======================================================================== */ 2267 * ======================================================================== */
2067 2268
@@ -2078,7 +2279,7 @@ if (typeof jQuery === 'undefined') {
2078 // jscs:enable requireDollarBeforejQueryAssignment 2279 // jscs:enable requireDollarBeforejQueryAssignment
2079 } 2280 }
2080 2281
2081 Tab.VERSION = '3.3.7' 2282 Tab.VERSION = '3.4.1'
2082 2283
2083 Tab.TRANSITION_DURATION = 150 2284 Tab.TRANSITION_DURATION = 150
2084 2285
@@ -2107,7 +2308,7 @@ if (typeof jQuery === 'undefined') {
2107 2308
2108 if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return 2309 if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
2109 2310
2110 var $target = $(selector) 2311 var $target = $(document).find(selector)
2111 2312
2112 this.activate($this.closest('li'), $ul) 2313 this.activate($this.closest('li'), $ul)
2113 this.activate($target, $target.parent(), function () { 2314 this.activate($target, $target.parent(), function () {
@@ -2132,15 +2333,15 @@ if (typeof jQuery === 'undefined') {
2132 $active 2333 $active
2133 .removeClass('active') 2334 .removeClass('active')
2134 .find('> .dropdown-menu > .active') 2335 .find('> .dropdown-menu > .active')
2135 .removeClass('active') 2336 .removeClass('active')
2136 .end() 2337 .end()
2137 .find('[data-toggle="tab"]') 2338 .find('[data-toggle="tab"]')
2138 .attr('aria-expanded', false) 2339 .attr('aria-expanded', false)
2139 2340
2140 element 2341 element
2141 .addClass('active') 2342 .addClass('active')
2142 .find('[data-toggle="tab"]') 2343 .find('[data-toggle="tab"]')
2143 .attr('aria-expanded', true) 2344 .attr('aria-expanded', true)
2144 2345
2145 if (transition) { 2346 if (transition) {
2146 element[0].offsetWidth // reflow for transition 2347 element[0].offsetWidth // reflow for transition
@@ -2152,10 +2353,10 @@ if (typeof jQuery === 'undefined') {
2152 if (element.parent('.dropdown-menu').length) { 2353 if (element.parent('.dropdown-menu').length) {
2153 element 2354 element
2154 .closest('li.dropdown') 2355 .closest('li.dropdown')
2155 .addClass('active') 2356 .addClass('active')
2156 .end() 2357 .end()
2157 .find('[data-toggle="tab"]') 2358 .find('[data-toggle="tab"]')
2158 .attr('aria-expanded', true) 2359 .attr('aria-expanded', true)
2159 } 2360 }
2160 2361
2161 callback && callback() 2362 callback && callback()
@@ -2214,10 +2415,10 @@ if (typeof jQuery === 'undefined') {
2214}(jQuery); 2415}(jQuery);
2215 2416
2216/* ======================================================================== 2417/* ========================================================================
2217 * Bootstrap: affix.js v3.3.7 2418 * Bootstrap: affix.js v3.4.1
2218 * http://getbootstrap.com/javascript/#affix 2419 * https://getbootstrap.com/docs/3.4/javascript/#affix
2219 * ======================================================================== 2420 * ========================================================================
2220 * Copyright 2011-2016 Twitter, Inc. 2421 * Copyright 2011-2019 Twitter, Inc.
2221 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 2422 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
2222 * ======================================================================== */ 2423 * ======================================================================== */
2223 2424
@@ -2231,7 +2432,9 @@ if (typeof jQuery === 'undefined') {
2231 var Affix = function (element, options) { 2432 var Affix = function (element, options) {
2232 this.options = $.extend({}, Affix.DEFAULTS, options) 2433 this.options = $.extend({}, Affix.DEFAULTS, options)
2233 2434
2234 this.$target = $(this.options.target) 2435 var target = this.options.target === Affix.DEFAULTS.target ? $(this.options.target) : $(document).find(this.options.target)
2436
2437 this.$target = target
2235 .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) 2438 .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
2236 .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) 2439 .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
2237 2440
@@ -2243,7 +2446,7 @@ if (typeof jQuery === 'undefined') {
2243 this.checkPosition() 2446 this.checkPosition()
2244 } 2447 }
2245 2448
2246 Affix.VERSION = '3.3.7' 2449 Affix.VERSION = '3.4.1'
2247 2450
2248 Affix.RESET = 'affix affix-top affix-bottom' 2451 Affix.RESET = 'affix affix-top affix-bottom'
2249 2452
diff --git a/src/js/entropy.js b/src/js/entropy.js
index 62b2711..3b62e10 100644
--- a/src/js/entropy.js
+++ b/src/js/entropy.js
@@ -16,7 +16,136 @@
16 16
17window.Entropy = new (function() { 17window.Entropy = new (function() {
18 18
19 var TWO = new libs.BigInteger.BigInteger(2); 19 let eventBits = {
20
21 "binary": {
22 "0": "0",
23 "1": "1",
24 },
25
26 // log2(6) = 2.58496 bits per roll, with bias
27 // 4 rolls give 2 bits each
28 // 2 rolls give 1 bit each
29 // Average (4*2 + 2*1) / 6 = 1.66 bits per roll without bias
30 "base 6": {
31 "0": "00",
32 "1": "01",
33 "2": "10",
34 "3": "11",
35 "4": "0",
36 "5": "1",
37 },
38
39 // log2(6) = 2.58496 bits per roll, with bias
40 // 4 rolls give 2 bits each
41 // 2 rolls give 1 bit each
42 // Average (4*2 + 2*1) / 6 = 1.66 bits per roll without bias
43 "base 6 (dice)": {
44 "0": "00", // equivalent to 0 in base 6
45 "1": "01",
46 "2": "10",
47 "3": "11",
48 "4": "0",
49 "5": "1",
50 },
51
52 // log2(10) = 3.321928 bits per digit, with bias
53 // 8 digits give 3 bits each
54 // 2 digits give 1 bit each
55 // Average (8*3 + 2*1) / 10 = 2.6 bits per digit without bias
56 "base 10": {
57 "0": "000",
58 "1": "001",
59 "2": "010",
60 "3": "011",
61 "4": "100",
62 "5": "101",
63 "6": "110",
64 "7": "111",
65 "8": "0",
66 "9": "1",
67 },
68
69 "hexadecimal": {
70 "0": "0000",
71 "1": "0001",
72 "2": "0010",
73 "3": "0011",
74 "4": "0100",
75 "5": "0101",
76 "6": "0110",
77 "7": "0111",
78 "8": "1000",
79 "9": "1001",
80 "a": "1010",
81 "b": "1011",
82 "c": "1100",
83 "d": "1101",
84 "e": "1110",
85 "f": "1111",
86 },
87
88 // log2(52) = 5.7004 bits per card, with bias
89 // 32 cards give 5 bits each
90 // 16 cards give 4 bits each
91 // 4 cards give 2 bits each
92 // Average (32*5 + 16*4 + 4*2) / 52 = 4.46 bits per card without bias
93 "card": {
94 "ac": "00000",
95 "2c": "00001",
96 "3c": "00010",
97 "4c": "00011",
98 "5c": "00100",
99 "6c": "00101",
100 "7c": "00110",
101 "8c": "00111",
102 "9c": "01000",
103 "tc": "01001",
104 "jc": "01010",
105 "qc": "01011",
106 "kc": "01100",
107 "ad": "01101",
108 "2d": "01110",
109 "3d": "01111",
110 "4d": "10000",
111 "5d": "10001",
112 "6d": "10010",
113 "7d": "10011",
114 "8d": "10100",
115 "9d": "10101",
116 "td": "10110",
117 "jd": "10111",
118 "qd": "11000",
119 "kd": "11001",
120 "ah": "11010",
121 "2h": "11011",
122 "3h": "11100",
123 "4h": "11101",
124 "5h": "11110",
125 "6h": "11111",
126 "7h": "0000",
127 "8h": "0001",
128 "9h": "0010",
129 "th": "0011",
130 "jh": "0100",
131 "qh": "0101",
132 "kh": "0110",
133 "as": "0111",
134 "2s": "1000",
135 "3s": "1001",
136 "4s": "1010",
137 "5s": "1011",
138 "6s": "1100",
139 "7s": "1101",
140 "8s": "1110",
141 "9s": "1111",
142 "ts": "00",
143 "js": "01",
144 "qs": "10",
145 "ks": "11",
146 },
147
148 }
20 149
21 // matchers returns an array of the matched events for each type of entropy. 150 // matchers returns an array of the matched events for each type of entropy.
22 // eg 151 // eg
@@ -51,48 +180,28 @@ window.Entropy = new (function() {
51 } 180 }
52 } 181 }
53 182
54 // Convert array of cards from ["ac", "4d", "ks"]
55 // to numbers between 0 and 51 [0, 16, 51]
56 function convertCardsToInts(cards) {
57 var ints = [];
58 var values = "a23456789tjqk";
59 var suits = "cdhs";
60 for (var i=0; i<cards.length; i++) {
61 var card = cards[i].toLowerCase();
62 var value = card[0];
63 var suit = card[1];
64 var asInt = 13 * suits.indexOf(suit) + values.indexOf(value);
65 ints.push(asInt);
66 }
67 return ints;
68 }
69
70 this.fromString = function(rawEntropyStr, baseStr) { 183 this.fromString = function(rawEntropyStr, baseStr) {
71 // Find type of entropy being used (binary, hex, dice etc) 184 // Find type of entropy being used (binary, hex, dice etc)
72 var base = getBase(rawEntropyStr, baseStr); 185 var base = getBase(rawEntropyStr, baseStr);
73 // Convert dice to base6 entropy (ie 1-6 to 0-5) 186 // Convert dice to base6 entropy (ie 1-6 to 0-5)
74 // This is done by changing all 6s to 0s 187 // This is done by changing all 6s to 0s
75 if (base.str == "dice") { 188 if (base.str == "dice") {
76 var newParts = []; 189 var newEvents = [];
77 var newInts = []; 190 for (var i=0; i<base.events.length; i++) {
78 for (var i=0; i<base.parts.length; i++) { 191 var c = base.events[i];
79 var c = base.parts[i];
80 if ("12345".indexOf(c) > -1) { 192 if ("12345".indexOf(c) > -1) {
81 newParts[i] = base.parts[i]; 193 newEvents[i] = base.events[i];
82 newInts[i] = base.ints[i];
83 } 194 }
84 else { 195 else {
85 newParts[i] = "0"; 196 newEvents[i] = "0";
86 newInts[i] = 0;
87 } 197 }
88 } 198 }
89 base.str = "base 6 (dice)"; 199 base.str = "base 6 (dice)";
90 base.ints = newInts; 200 base.events = newEvents;
91 base.parts = newParts;
92 base.matcher = matchers.base6; 201 base.matcher = matchers.base6;
93 } 202 }
94 // Detect empty entropy 203 // Detect empty entropy
95 if (base.parts.length == 0) { 204 if (base.events.length == 0) {
96 return { 205 return {
97 binaryStr: "", 206 binaryStr: "",
98 cleanStr: "", 207 cleanStr: "",
@@ -100,44 +209,23 @@ window.Entropy = new (function() {
100 base: base, 209 base: base,
101 }; 210 };
102 } 211 }
103 // Convert base.ints to BigInteger. 212 // Convert entropy events to binary
104 // Due to using unusual bases, eg cards of base52, this is not as simple as 213 var entropyBin = base.events.map(function(e) {
105 // using BigInteger.parse() 214 return eventBits[base.str][e.toLowerCase()];
106 var entropyInt = libs.BigInteger.BigInteger.ZERO; 215 }).join("");
107 for (var i=base.ints.length-1; i>=0; i--) { 216 // Get average bits per event
108 var thisInt = libs.BigInteger.BigInteger.parse(base.ints[i]); 217 // which may be adjusted for bias if log2(base) is fractional
109 var power = (base.ints.length - 1) - i; 218 var bitsPerEvent = base.bitsPerEvent;
110 var additionalEntropy = libs.BigInteger.BigInteger.parse(base.asInt).pow(power).multiply(thisInt);
111 entropyInt = entropyInt.add(additionalEntropy);
112 }
113 // Convert entropy to binary
114 var entropyBin = entropyInt.toString(2);
115 // If the first integer is small, it must be padded with zeros.
116 // Otherwise the chance of the first bit being 1 is 100%, which is
117 // obviously incorrect.
118 // This is not perfect for non-2^n bases.
119 var expectedBits = Math.floor(base.parts.length * Math.log2(base.asInt));
120 while (entropyBin.length < expectedBits) {
121 entropyBin = "0" + entropyBin;
122 }
123 // Calculate the number of bits per event
124 var bitsPerEvent = Math.log2(base.asInt);
125 // Cards binary must be handled differently, since they're not replaced
126 if (base.asInt == 52) {
127 var cardEntropy = processCardEntropy(base.parts);
128 entropyBin = cardEntropy.binaryStr;
129 bitsPerEvent = cardEntropy.bitsPerEvent;
130 }
131 // Supply a 'filtered' entropy string for display purposes 219 // Supply a 'filtered' entropy string for display purposes
132 var entropyClean = base.parts.join(""); 220 var entropyClean = base.events.join("");
133 var entropyHtml = base.parts.join(""); 221 var entropyHtml = base.events.join("");
134 if (base.asInt == 52) { 222 if (base.asInt == 52) {
135 entropyClean = base.parts.join(" ").toUpperCase(); 223 entropyClean = base.events.join(" ").toUpperCase();
136 entropyClean = entropyClean.replace(/C/g, "\u2663"); 224 entropyClean = entropyClean.replace(/C/g, "\u2663");
137 entropyClean = entropyClean.replace(/D/g, "\u2666"); 225 entropyClean = entropyClean.replace(/D/g, "\u2666");
138 entropyClean = entropyClean.replace(/H/g, "\u2665"); 226 entropyClean = entropyClean.replace(/H/g, "\u2665");
139 entropyClean = entropyClean.replace(/S/g, "\u2660"); 227 entropyClean = entropyClean.replace(/S/g, "\u2660");
140 entropyHtml = base.parts.join(" ").toUpperCase(); 228 entropyHtml = base.events.join(" ").toUpperCase();
141 entropyHtml = entropyHtml.replace(/C/g, "<span class='card-suit club'>\u2663</span>"); 229 entropyHtml = entropyHtml.replace(/C/g, "<span class='card-suit club'>\u2663</span>");
142 entropyHtml = entropyHtml.replace(/D/g, "<span class='card-suit diamond'>\u2666</span>"); 230 entropyHtml = entropyHtml.replace(/D/g, "<span class='card-suit diamond'>\u2666</span>");
143 entropyHtml = entropyHtml.replace(/H/g, "<span class='card-suit heart'>\u2665</span>"); 231 entropyHtml = entropyHtml.replace(/H/g, "<span class='card-suit heart'>\u2665</span>");
@@ -154,18 +242,6 @@ window.Entropy = new (function() {
154 return e; 242 return e;
155 } 243 }
156 244
157 function getSortedDeck() {
158 var s = [];
159 var suits = "CDHS";
160 var values = "A23456789TJQK";
161 for (var i=0; i<suits.length; i++) {
162 for (var j=0; j<values.length; j++) {
163 s.push(values[j]+suits[i]);
164 }
165 }
166 return s;
167 }
168
169 function getBase(str, baseStr) { 245 function getBase(str, baseStr) {
170 // Need to get the lowest base for the supplied entropy. 246 // Need to get the lowest base for the supplied entropy.
171 // This prevents interpreting, say, dice rolls as hexadecimal. 247 // This prevents interpreting, say, dice rolls as hexadecimal.
@@ -177,20 +253,21 @@ window.Entropy = new (function() {
177 var ints = binaryMatches.map(function(i) { return parseInt(i, 2) }); 253 var ints = binaryMatches.map(function(i) { return parseInt(i, 2) });
178 return { 254 return {
179 ints: ints, 255 ints: ints,
180 parts: binaryMatches, 256 events: binaryMatches,
181 matcher: matchers.binary, 257 matcher: matchers.binary,
182 asInt: 2, 258 asInt: 2,
259 bitsPerEvent: 1,
183 str: "binary", 260 str: "binary",
184 } 261 }
185 } 262 }
186 var cardMatches = matchers.card(str); 263 var cardMatches = matchers.card(str);
187 if ((cardMatches.length >= hexMatches.length / 2 && autodetect) || baseStr === "card") { 264 if ((cardMatches.length >= hexMatches.length / 2 && autodetect) || baseStr === "card") {
188 var ints = convertCardsToInts(cardMatches);
189 return { 265 return {
190 ints: ints, 266 ints: ints,
191 parts: cardMatches, 267 events: cardMatches,
192 matcher: matchers.card, 268 matcher: matchers.card,
193 asInt: 52, 269 asInt: 52,
270 bitsPerEvent: (32*5 + 16*4 + 4*2) / 52, // see cardBits
194 str: "card", 271 str: "card",
195 } 272 }
196 } 273 }
@@ -199,9 +276,10 @@ window.Entropy = new (function() {
199 var ints = diceMatches.map(function(i) { return parseInt(i) }); 276 var ints = diceMatches.map(function(i) { return parseInt(i) });
200 return { 277 return {
201 ints: ints, 278 ints: ints,
202 parts: diceMatches, 279 events: diceMatches,
203 matcher: matchers.dice, 280 matcher: matchers.dice,
204 asInt: 6, 281 asInt: 6,
282 bitsPerEvent: (4*2 + 2*1) / 6, // see diceBits
205 str: "dice", 283 str: "dice",
206 } 284 }
207 } 285 }
@@ -210,9 +288,10 @@ window.Entropy = new (function() {
210 var ints = base6Matches.map(function(i) { return parseInt(i) }); 288 var ints = base6Matches.map(function(i) { return parseInt(i) });
211 return { 289 return {
212 ints: ints, 290 ints: ints,
213 parts: base6Matches, 291 events: base6Matches,
214 matcher: matchers.base6, 292 matcher: matchers.base6,
215 asInt: 6, 293 asInt: 6,
294 bitsPerEvent: (4*2 + 2*1) / 6, // see diceBits
216 str: "base 6", 295 str: "base 6",
217 } 296 }
218 } 297 }
@@ -221,126 +300,22 @@ window.Entropy = new (function() {
221 var ints = base10Matches.map(function(i) { return parseInt(i) }); 300 var ints = base10Matches.map(function(i) { return parseInt(i) });
222 return { 301 return {
223 ints: ints, 302 ints: ints,
224 parts: base10Matches, 303 events: base10Matches,
225 matcher: matchers.base10, 304 matcher: matchers.base10,
226 asInt: 10, 305 asInt: 10,
306 bitsPerEvent: (8*3 + 2*1) / 10, // see b10Bits
227 str: "base 10", 307 str: "base 10",
228 } 308 }
229 } 309 }
230 var ints = hexMatches.map(function(i) { return parseInt(i, 16) }); 310 var ints = hexMatches.map(function(i) { return parseInt(i, 16) });
231 return { 311 return {
232 ints: ints, 312 ints: ints,
233 parts: hexMatches, 313 events: hexMatches,
234 matcher: matchers.hex, 314 matcher: matchers.hex,
235 asInt: 16, 315 asInt: 16,
316 bitsPerEvent: 4,
236 str: "hexadecimal", 317 str: "hexadecimal",
237 } 318 }
238 } 319 }
239 320
240 // Assume cards are NOT replaced.
241 // Additional entropy decreases as more cards are used. This means
242 // total possible entropy is measured using n!, not base^n.
243 // eg the second last card can be only one of two, not one of fifty two
244 // so the added entropy for that card is only one bit at most
245 function processCardEntropy(cards) {
246 // Track how many instances of each card have been used, and thus
247 // how many decks are in use.
248 var cardCounts = {};
249 var numberOfDecks = 0;
250 // Work out number of decks by max(duplicates)
251 for (var i=0; i<cards.length; i++) {
252 // Get the card that was drawn
253 var cardLower = cards[i];
254 var card = cardLower.toUpperCase();
255 // Initialize the count for this card if needed
256 if (!(card in cardCounts)) {
257 cardCounts[card] = 0;
258 }
259 cardCounts[card] += 1;
260 // See if this is max(duplicates)
261 if (cardCounts[card] > numberOfDecks) {
262 numberOfDecks = cardCounts[card];
263 }
264 }
265 // Work out the total number of bits for this many decks
266 // See http://crypto.stackexchange.com/q/41886
267 var gainedBits = 0;
268 // Equivalent of Math.log2(factorial(52*numberOfDecks))
269 // which becomes infinity for numberOfDecks > 4
270 for (var i=1; i<=52*numberOfDecks; i++) {
271 gainedBits = gainedBits + Math.log2(i);
272 }
273 var lostBits = 52 * Math.log2(factorial(numberOfDecks));
274 var maxBits = gainedBits - lostBits;
275 // Convert the drawn cards to a binary representation.
276 // The exact technique for doing this is unclear.
277 // See
278 // http://crypto.stackexchange.com/a/41896
279 // "I even doubt that this is well defined (only the average entropy
280 // is, I believe)."
281 // See
282 // https://github.com/iancoleman/bip39/issues/33#issuecomment-263021856
283 // "The binary representation can be the first log(permutations,2) bits
284 // of the sha-2 hash of the normalized deck string."
285 //
286 // In this specific implementation, the first N bits of the hash of the
287 // normalized cards string is being used. Uppercase, no spaces; eg
288 // sha256("AH8DQSTC2H")
289 var totalCards = numberOfDecks * 52;
290 var percentUsed = cards.length / totalCards;
291 // Calculate the average number of bits of entropy for the number of
292 // cards drawn.
293 var numberOfBits = Math.floor(maxBits * percentUsed);
294 // Create a normalized string of the selected cards
295 var normalizedCards = cards.join("").toUpperCase();
296 // Convert to binary using the SHA256 hash of the normalized cards.
297 // If the number of bits is more than 256, multiple hashes
298 // are used until the required number of bits is reached.
299 var entropyBin = "";
300 var iterations = 0;
301 while (entropyBin.length < numberOfBits) {
302 var hashedCards = sjcl.hash.sha256.hash(normalizedCards + ":" + iterations);
303 var hashHex = sjcl.codec.hex.fromBits(hashedCards);
304 for (var i=0; i<hashHex.length; i++) {
305 var decimal = parseInt(hashHex[i], 16);
306 var binary = decimal.toString(2);
307 while (binary.length < 4) {
308 binary = "0" + binary;
309 }
310 entropyBin = entropyBin + binary;
311 }
312 iterations = iterations + 1;
313 }
314 // Truncate to the appropriate number of bits.
315 entropyBin = entropyBin.substring(0, numberOfBits);
316 // Get the number of bits per event
317 bitsPerEvent = maxBits / totalCards;
318 return {
319 binaryStr: entropyBin,
320 bitsPerEvent: bitsPerEvent,
321 }
322 }
323
324 // Polyfill for Math.log2
325 // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2#Polyfill
326 Math.log2 = Math.log2 || function(x) {
327 // The polyfill isn't good enough because of the poor accuracy of
328 // Math.LOG2E
329 // log2(8) gave 2.9999999999999996 which when floored causes issues.
330 // So instead use the BigInteger library to get it right.
331 return libs.BigInteger.BigInteger.log(x) / libs.BigInteger.BigInteger.log(2);
332 };
333
334 // Depends on BigInteger
335 function factorial(n) {
336 if (n == 0) {
337 return 1;
338 }
339 f = libs.BigInteger.BigInteger.ONE;
340 for (var i=1; i<=n; i++) {
341 f = f.multiply(new libs.BigInteger.BigInteger(i));
342 }
343 return f;
344 }
345
346})(); 321})();
diff --git a/src/js/index.js b/src/js/index.js
index e959c58..114f74a 100644
--- a/src/js/index.js
+++ b/src/js/index.js
@@ -1737,7 +1737,7 @@
1737 var numberOfBits = entropy.binaryStr.length; 1737 var numberOfBits = entropy.binaryStr.length;
1738 var timeToCrack = "unknown"; 1738 var timeToCrack = "unknown";
1739 try { 1739 try {
1740 var z = libs.zxcvbn(entropy.base.parts.join("")); 1740 var z = libs.zxcvbn(entropy.base.events.join(""));
1741 timeToCrack = z.crack_times_display.offline_fast_hashing_1e10_per_second; 1741 timeToCrack = z.crack_times_display.offline_fast_hashing_1e10_per_second;
1742 if (z.feedback.warning != "") { 1742 if (z.feedback.warning != "") {
1743 timeToCrack = timeToCrack + " - " + z.feedback.warning; 1743 timeToCrack = timeToCrack + " - " + z.feedback.warning;
@@ -1756,7 +1756,7 @@
1756 DOM.entropyFiltered.html(entropy.cleanHtml); 1756 DOM.entropyFiltered.html(entropy.cleanHtml);
1757 DOM.entropyType.text(entropyTypeStr); 1757 DOM.entropyType.text(entropyTypeStr);
1758 DOM.entropyCrackTime.text(timeToCrack); 1758 DOM.entropyCrackTime.text(timeToCrack);
1759 DOM.entropyEventCount.text(entropy.base.ints.length); 1759 DOM.entropyEventCount.text(entropy.base.events.length);
1760 DOM.entropyBits.text(numberOfBits); 1760 DOM.entropyBits.text(numberOfBits);
1761 DOM.entropyWordCount.text(wordCount); 1761 DOM.entropyWordCount.text(wordCount);
1762 DOM.entropyBinary.text(spacedBinaryStr); 1762 DOM.entropyBinary.text(spacedBinaryStr);
@@ -1781,8 +1781,8 @@
1781 // Detect duplicates 1781 // Detect duplicates
1782 var dupes = []; 1782 var dupes = [];
1783 var dupeTracker = {}; 1783 var dupeTracker = {};
1784 for (var i=0; i<entropy.base.parts.length; i++) { 1784 for (var i=0; i<entropy.base.events.length; i++) {
1785 var card = entropy.base.parts[i]; 1785 var card = entropy.base.events[i];
1786 var cardUpper = card.toUpperCase(); 1786 var cardUpper = card.toUpperCase();
1787 if (cardUpper in dupeTracker) { 1787 if (cardUpper in dupeTracker) {
1788 dupes.push(card); 1788 dupes.push(card);
@@ -1898,6 +1898,7 @@
1898 || (name == "ESN - Ethersocial Network") 1898 || (name == "ESN - Ethersocial Network")
1899 || (name == "VET - VeChain") 1899 || (name == "VET - VeChain")
1900 || (name == "ERE - EtherCore") 1900 || (name == "ERE - EtherCore")
1901 || (name == "BSC - Binance Smart Chain")
1901 } 1902 }
1902 1903
1903 function networkIsRsk() { 1904 function networkIsRsk() {
@@ -2244,6 +2245,13 @@
2244 }, 2245 },
2245 }, 2246 },
2246 { 2247 {
2248 name: "BSC - Binance Smart Chain",
2249 onSelect: function() {
2250 network = libs.bitcoin.networks.bitcoin;
2251 setHdCoin(60);
2252 },
2253 },
2254 {
2247 name: "BSV - BitcoinSV", 2255 name: "BSV - BitcoinSV",
2248 onSelect: function() { 2256 onSelect: function() {
2249 network = libs.bitcoin.networks.bitcoinsv; 2257 network = libs.bitcoin.networks.bitcoinsv;