|
@@ -447,8 +447,89 @@
|
|
|
getElement: function (options, param, value) {
|
|
|
return ($h.isEmpty(options) || $h.isEmpty(options[param])) ? value : $(options[param]);
|
|
|
},
|
|
|
+ createElement: function (str, tag) {
|
|
|
+ tag = tag || 'div';
|
|
|
+ return $($.parseHTML('<' + tag + '>' + str + '</' + tag + '>'));
|
|
|
+ },
|
|
|
uniqId: function () {
|
|
|
- return Math.round(new Date().getTime()) + '_' + Math.round(Math.random() * 100);
|
|
|
+ return (new Date().getTime() + Math.floor(Math.random() * Math.pow(10, 15))).toString(36);
|
|
|
+ },
|
|
|
+ parseEventCallback: function (str) {
|
|
|
+ return Function("'use strict'; return (function() { " + str + " });")();
|
|
|
+ },
|
|
|
+ cspBuffer: {
|
|
|
+ CSP_ATTRIB: 'data-csp-01928735', // a randomly named temporary attribute to store the CSP elem id
|
|
|
+ domEventsList: [
|
|
|
+ 'mousedown', 'mouseup', 'click', 'dblclick', 'mousemove', 'mouseover', 'mousewheel', 'mouseout',
|
|
|
+ 'contextmenu', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'keydown', 'keypress', 'keyup',
|
|
|
+ 'focus', 'blur', 'change', 'submit', 'scroll', 'resize', 'hashchange', 'load', 'unload',
|
|
|
+ 'cut', 'copy', 'paste'
|
|
|
+ ],
|
|
|
+ domElementEvents: {},
|
|
|
+ domElementsStyles: {},
|
|
|
+ stash: function (htmlString) {
|
|
|
+ var self = this, outerDom = $.parseHTML('<div>' + htmlString + '</div>'), $el = $(outerDom);
|
|
|
+ $el.find('[style]').each(function (key, elem) {
|
|
|
+ var $elem = $(elem), styleString = $elem.attr('style'), id = $h.uniqId(), styles = {};
|
|
|
+ if (styleString && styleString.length) {
|
|
|
+ if (styleString.indexOf(';') === -1) {
|
|
|
+ styleString += ';'
|
|
|
+ }
|
|
|
+ styleString.slice(0, styleString.length - 1).split(';').map(function (str) {
|
|
|
+ str = str.split(':');
|
|
|
+ if (str[0]) {
|
|
|
+ styles[str[0]] = str[1] ? str[1] : '';
|
|
|
+ }
|
|
|
+ });
|
|
|
+ self.domElementsStyles[id] = styles;
|
|
|
+ $elem.removeAttr('style').attr(self.CSP_ATTRIB, id);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ $el.filter('*').removeAttr('style'); // make sure all style attr are removed
|
|
|
+ $.each(self.domEventsList, function (key, eventName) { // handle onXXX events set as inline markup
|
|
|
+ var id, fn, event = 'on' + eventName, $inlineEvent = $el.find('[' + event + ']');
|
|
|
+ if ($inlineEvent.length) {
|
|
|
+ fn = $h.parseEventCallback($inlineEvent.attr(event));
|
|
|
+ if ($inlineEvent.attr(self.CSP_ATTRIB)) {
|
|
|
+ id = $inlineEvent.attr(self.CSP_ATTRIB);
|
|
|
+ } else {
|
|
|
+ id = $h.uniqId();
|
|
|
+ self.domElementEvents[id] = [];
|
|
|
+ }
|
|
|
+ self.domElementEvents[id].push({name: eventName + '.csp', handler: fn}); // special csp namespace
|
|
|
+ $inlineEvent.removeAttr(event).attr(self.CSP_ATTRIB, id);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return Object.values(outerDom).flatMap(function (elem) {
|
|
|
+ return elem.outerHTML;
|
|
|
+ }).join('');
|
|
|
+ },
|
|
|
+ apply: function (domElement) {
|
|
|
+ var self = this, $el = $(domElement);
|
|
|
+ $el.find('[' + self.CSP_ATTRIB + ']').each(function (key, elem) {
|
|
|
+ var $elem = $(elem), id = $elem.attr(self.CSP_ATTRIB), styles = self.domElementsStyles[id],
|
|
|
+ events = self.domElementEvents[id];
|
|
|
+ if (styles) {
|
|
|
+ $elem.css(styles);
|
|
|
+ }
|
|
|
+ if (events) {
|
|
|
+ $.each(events, function (key, event) {
|
|
|
+ if (event && event.name) {
|
|
|
+ $elem.off(event.name).on(event.name, event.handler);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ $elem.removeAttr(self.CSP_ATTRIB);
|
|
|
+ });
|
|
|
+ self.domElementsStyles = {};
|
|
|
+ self.domElementEvents = {};
|
|
|
+ }
|
|
|
+ },
|
|
|
+ setHtml: function ($elem, htmlString) {
|
|
|
+ var buf = $h.cspBuffer;
|
|
|
+ $elem.html(buf.stash(htmlString));
|
|
|
+ buf.apply($elem);
|
|
|
+ return $elem;
|
|
|
},
|
|
|
htmlEncode: function (str, undefVal) {
|
|
|
if (str === undefined) {
|
|
@@ -1939,7 +2020,7 @@
|
|
|
}
|
|
|
/** @namespace config.frameAttr */
|
|
|
if (!$h.isEmpty(config) && !$h.isEmpty(config.frameAttr)) {
|
|
|
- $tmp = $(document.createElement('div')).html(out);
|
|
|
+ $tmp = $h.createElement(out);
|
|
|
$tmp.find('.file-preview-initial').attr(config.frameAttr);
|
|
|
out = $tmp.html();
|
|
|
$tmp.remove();
|
|
@@ -2134,7 +2215,7 @@
|
|
|
if ($errList.length) {
|
|
|
return true;
|
|
|
}
|
|
|
- $err = $(document.createElement('div')).html(self.$errorContainer.html());
|
|
|
+ $err = $h.createElement(self.$errorContainer.html());
|
|
|
$err.find('.kv-error-close').remove();
|
|
|
$err.find('ul').remove();
|
|
|
return !!$.trim($err.text()).length;
|
|
@@ -2167,7 +2248,7 @@
|
|
|
_addError: function (msg) {
|
|
|
var self = this, $error = self.$errorContainer;
|
|
|
if (msg && $error.length) {
|
|
|
- $error.html(self.errorCloseButton + msg);
|
|
|
+ $h.setHtml($error, self.errorCloseButton + msg);
|
|
|
self._handler($error.find('.kv-error-close'), 'click', function () {
|
|
|
setTimeout(function () {
|
|
|
if (self.showPreview && !self.getFrames().length) {
|
|
@@ -2614,7 +2695,7 @@
|
|
|
},
|
|
|
_setPreviewContent: function (content) {
|
|
|
var self = this;
|
|
|
- self.$preview.html(content);
|
|
|
+ $h.setHtml(self.$preview, content);
|
|
|
self._autoFitContent();
|
|
|
},
|
|
|
_initPreviewImageOrientations: function () {
|
|
@@ -2711,12 +2792,14 @@
|
|
|
}
|
|
|
self.$modal = $(modalId);
|
|
|
if (!self.$modal || !self.$modal.length) {
|
|
|
- $dialog = $(document.createElement('div')).html(modalMain).insertAfter(self.$container);
|
|
|
+ $dialog = $h.createElement($h.cspBuffer.stash(modalMain)).insertAfter(self.$container);
|
|
|
self.$modal = $(modalId).insertBefore($dialog);
|
|
|
+ $h.cspBuffer.apply(self.$modal);
|
|
|
$dialog.remove();
|
|
|
}
|
|
|
$h.initModal(self.$modal);
|
|
|
- self.$modal.html(self._getModalContent());
|
|
|
+ self.$modal.html($h.cspBuffer.stash(self._getModalContent()));
|
|
|
+ $h.cspBuffer.apply(self.$modal);
|
|
|
$.each($h.MODAL_EVENTS, function (key, event) {
|
|
|
self._listenModalEvent(event);
|
|
|
});
|
|
@@ -2810,7 +2893,7 @@
|
|
|
$modal.removeClass('kv-single-content');
|
|
|
if (animate) {
|
|
|
$tmp = $body.addClass('file-thumb-loading').clone().insertAfter($body);
|
|
|
- $body.html(body).hide();
|
|
|
+ $h.setHtml($body, body).hide();
|
|
|
$tmp.fadeOut('fast', function () {
|
|
|
$body.fadeIn('fast', function () {
|
|
|
$body.removeClass('file-thumb-loading');
|
|
@@ -2818,7 +2901,7 @@
|
|
|
$tmp.remove();
|
|
|
});
|
|
|
} else {
|
|
|
- $body.html(body);
|
|
|
+ $h.setHtml($body, body);
|
|
|
}
|
|
|
config = self.previewZoomSettings[tmplt];
|
|
|
if (config) {
|
|
@@ -2887,7 +2970,7 @@
|
|
|
throw 'Cannot zoom to detailed preview!';
|
|
|
}
|
|
|
$h.initModal($modal);
|
|
|
- $modal.html(self._getModalContent());
|
|
|
+ $h.setHtml($modal, self._getModalContent());
|
|
|
$frame = $btn.closest($h.FRAMES);
|
|
|
self._setZoomContent($frame);
|
|
|
$modal.modal('show');
|
|
@@ -3248,7 +3331,7 @@
|
|
|
if (!allFiles) {
|
|
|
index = self.previewCache.add(content[0], config[0], tags[0], append);
|
|
|
data = self.previewCache.get(index, false);
|
|
|
- $div = $(document.createElement('div')).html(data).hide().insertAfter($thumb);
|
|
|
+ $div = $h.createElement(data).hide().insertAfter($thumb);
|
|
|
$newCache = $div.find('.kv-zoom-cache');
|
|
|
if ($newCache && $newCache.length) {
|
|
|
$newCache.insertAfter($thumb);
|
|
@@ -3990,8 +4073,11 @@
|
|
|
return prevContent + zoomContent;
|
|
|
},
|
|
|
_addToPreview: function ($preview, content) {
|
|
|
- var self = this;
|
|
|
- return self.reversePreviewOrder ? $preview.prepend(content) : $preview.append(content);
|
|
|
+ var self = this, $el;
|
|
|
+ content = $h.cspBuffer.stash(content);
|
|
|
+ $el = self.reversePreviewOrder ? $preview.prepend(content) : $preview.append(content);
|
|
|
+ $h.cspBuffer.apply($preview);
|
|
|
+ return $el;
|
|
|
},
|
|
|
_previewDefault: function (file, isDisabled) {
|
|
|
var self = this, $preview = self.$preview;
|
|
@@ -4096,7 +4182,7 @@
|
|
|
if (status === 'Success') {
|
|
|
$thumb.find('.file-drag-handle').remove();
|
|
|
}
|
|
|
- $indicator.html(config[icon]);
|
|
|
+ $h.setHtml($indicator, config[icon]);
|
|
|
$indicator.attr('title', config[msg]);
|
|
|
$thumb.addClass(css);
|
|
|
if (status === 'Error' && !self.retryErrorUploads) {
|
|
@@ -4128,9 +4214,9 @@
|
|
|
}
|
|
|
stats = stats || '';
|
|
|
out = out.setTokens({stats: stats});
|
|
|
- $el.html(out);
|
|
|
+ $h.setHtml($el, out);
|
|
|
if (error) {
|
|
|
- $el.find('[role="progressbar"]').html(error);
|
|
|
+ $h.setHtml($el.find('[role="progressbar"]'), error);
|
|
|
}
|
|
|
}
|
|
|
},
|
|
@@ -4180,7 +4266,7 @@
|
|
|
_toggleResumableProgress: function (template, message) {
|
|
|
var self = this, $progress = self.$progress;
|
|
|
if ($progress && $progress.length) {
|
|
|
- $progress.html(template.setTokens({
|
|
|
+ $h.setHtml($progress, template.setTokens({
|
|
|
percent: 101,
|
|
|
status: message,
|
|
|
stats: ''
|
|
@@ -4274,7 +4360,8 @@
|
|
|
icon = cfg.uploadRetryIcon;
|
|
|
title = cfg.uploadRetryTitle;
|
|
|
}
|
|
|
- $btn.attr('title', title).html(icon);
|
|
|
+ $btn.attr('title', title);
|
|
|
+ $h.setHtml($btn, icon);
|
|
|
},
|
|
|
_checkDimensions: function (i, chk, $img, $thumb, fname, type, params) {
|
|
|
var self = this, msg, dim, tag = chk === 'Small' ? 'min' : 'max', limit = self[tag + 'Image' + type],
|
|
@@ -4585,12 +4672,13 @@
|
|
|
}
|
|
|
self.$captionContainer.addClass('icon-visible');
|
|
|
self.$caption.attr('title', title).val(out);
|
|
|
- self.$captionIcon.html(icon);
|
|
|
+ $h.setHtml(self.$captionIcon, icon);
|
|
|
},
|
|
|
_createContainer: function () {
|
|
|
var self = this, attribs = {'class': 'file-input file-input-new' + (self.rtl ? ' kv-rtl' : '')},
|
|
|
- $container = $(document.createElement('div')).attr(attribs).html(self._renderMain());
|
|
|
- $container.insertBefore(self.$element);
|
|
|
+ $container = $h.createElement($h.cspBuffer.stash(self._renderMain()));
|
|
|
+ $h.cspBuffer.apply($container);
|
|
|
+ $container.insertBefore(self.$element).attr(attribs);
|
|
|
self._initBrowse($container);
|
|
|
if (self.theme) {
|
|
|
$container.addClass('theme-' + self.theme);
|
|
@@ -4600,7 +4688,7 @@
|
|
|
_refreshContainer: function () {
|
|
|
var self = this, $container = self.$container, $el = self.$element;
|
|
|
$el.insertAfter($container);
|
|
|
- $container.html(self._renderMain());
|
|
|
+ $h.setHtml($container, self._renderMain());
|
|
|
self._initBrowse($container);
|
|
|
self._validateDisabled();
|
|
|
},
|
|
@@ -5042,7 +5130,7 @@
|
|
|
if (self.duplicateErrors.length) {
|
|
|
errors = '<li>' + self.duplicateErrors.join('</li><li>') + '</li>';
|
|
|
if ($error.find('ul').length === 0) {
|
|
|
- $error.html(self.errorCloseButton + '<ul>' + errors + '</ul>');
|
|
|
+ $h.setHtml($error, self.errorCloseButton + '<ul>' + errors + '</ul>');
|
|
|
} else {
|
|
|
$error.find('ul').append(errors);
|
|
|
}
|
|
@@ -5587,7 +5675,7 @@
|
|
|
return;
|
|
|
}
|
|
|
$h.initModal($modal);
|
|
|
- $modal.html(self._getModalContent());
|
|
|
+ $h.setHtml($modal, self._getModalContent());
|
|
|
self._setZoomContent($frame);
|
|
|
$modal.modal('show');
|
|
|
self._initZoomButtons();
|
|
@@ -5703,7 +5791,7 @@
|
|
|
deleteExtraData: {},
|
|
|
overwriteInitial: true,
|
|
|
sanitizeZoomCache: function (content) {
|
|
|
- var $container = $(document.createElement('div')).append(content);
|
|
|
+ var $container = $h.createElement(content);
|
|
|
$container.find('input,select,.file-thumbnail-footer').remove();
|
|
|
return $container.html();
|
|
|
},
|