Linux websever 5.15.0-153-generic #163-Ubuntu SMP Thu Aug 7 16:37:18 UTC 2025 x86_64
Apache/2.4.52 (Ubuntu)
: 192.168.3.70 | : 192.168.1.99
Cant Read [ /etc/named.conf ]
8.1.2-1ubuntu2.23
urlab
www.github.com/MadExploits
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
CPANEL RESET
CREATE WP USER
README
+ Create Folder
+ Create File
/
var /
www /
html /
vicas-dev /
static /
cms /
js /
modules /
[ HOME SHELL ]
Name
Size
Permission
Action
shortcuts
[ DIR ]
drwxr-xr-x
cms.base.js
15.03
KB
-rw-r--r--
cms.changeform.js
1.34
KB
-rw-r--r--
cms.changetracker.js
4.36
KB
-rw-r--r--
cms.clipboard.js
7.44
KB
-rw-r--r--
cms.messages.js
4.1
KB
-rw-r--r--
cms.modal.js
42.13
KB
-rw-r--r--
cms.navigation.js
11.01
KB
-rw-r--r--
cms.pagetree.dropdown.js
3.49
KB
-rw-r--r--
cms.pagetree.js
38.41
KB
-rw-r--r--
cms.pagetree.stickyheader.js
4.85
KB
-rw-r--r--
cms.plugins.js
76.19
KB
-rw-r--r--
cms.sideframe.js
13.79
KB
-rw-r--r--
cms.structureboard.js
52.79
KB
-rw-r--r--
cms.toolbar.js
25.56
KB
-rw-r--r--
cms.tooltip.js
4.64
KB
-rw-r--r--
cms.wizards.js
1.61
KB
-rw-r--r--
dropdown.js
4.72
KB
-rw-r--r--
get-dist-path.js
1.16
KB
-rw-r--r--
jquery.noconflict.post.js
133
B
-rw-r--r--
jquery.noconflict.pre.js
159
B
-rw-r--r--
jquery.transition.js
2.12
KB
-rw-r--r--
jquery.trap.js
6.21
KB
-rw-r--r--
jquery.ui.custom.js
49.58
KB
-rw-r--r--
jquery.ui.nestedsortable.js
26.19
KB
-rw-r--r--
jquery.ui.touchpunch.js
1.26
KB
-rw-r--r--
keyboard.js
826
B
-rw-r--r--
loader.js
1.05
KB
-rw-r--r--
nextuntil.js
808
B
-rw-r--r--
preload-images.js
680
B
-rw-r--r--
scrollbar.js
402
B
-rw-r--r--
slug.js
1.11
KB
-rw-r--r--
tmpl.js
1.51
KB
-rw-r--r--
Delete
Unzip
Zip
${this.title}
Close
Code Editor : cms.modal.js
/* * Copyright https://github.com/divio/django-cms */ import ChangeTracker from './cms.changetracker'; import keyboard from './keyboard'; import $ from 'jquery'; import './jquery.transition'; import './jquery.trap'; import { Helpers, KEYS } from './cms.base'; import { showLoader, hideLoader } from './loader'; var previousKeyboardContext; var previouslyFocusedElement; /** * The modal is triggered via API calls from the backend either * through the toolbar navigation or from plugins. The APIs allow to * open content from a url (iframe) or inject html directly. * * @class Modal * @namespace CMS */ class Modal { constructor(options) { this.options = $.extend(true, {}, Modal.options, options); // elements this._setupUI(); // states and events this.click = 'click.cms.modal'; this.pointerDown = 'pointerdown.cms.modal contextmenu.cms.modal'; this.pointerUp = 'pointerup.cms.modal pointercancel.cms.modal'; this.pointerMove = 'pointermove.cms.modal'; this.doubleClick = 'dblclick.cms.modal'; this.touchEnd = 'touchend.cms.modal'; this.keyUp = 'keyup.cms.modal'; this.maximized = false; this.minimized = false; this.triggerMaximized = false; this.saved = false; this._beforeUnloadHandler = this._beforeUnloadHandler.bind(this); } /** * Stores all jQuery references within `this.ui`. * * @method _setupUI * @private */ _setupUI() { var modal = $('.cms-modal'); this.ui = { modal: modal, body: $('html'), window: $(window), toolbarLeftPart: $('.cms-toolbar-left'), minimizeButton: modal.find('.cms-modal-minimize'), maximizeButton: modal.find('.cms-modal-maximize'), title: modal.find('.cms-modal-title'), titlePrefix: modal.find('.cms-modal-title-prefix'), titleSuffix: modal.find('.cms-modal-title-suffix'), resize: modal.find('.cms-modal-resize'), breadcrumb: modal.find('.cms-modal-breadcrumb'), closeAndCancel: modal.find('.cms-modal-close, .cms-modal-cancel'), modalButtons: modal.find('.cms-modal-buttons'), modalBody: modal.find('.cms-modal-body'), frame: modal.find('.cms-modal-frame'), shim: modal.find('.cms-modal-shim') }; } /** * Sets up all the event handlers, such as maximize/minimize and resizing. * * @method _events * @private */ _events() { var that = this; // modal behaviours this.ui.minimizeButton .off(this.click + ' ' + this.touchEnd + ' ' + this.keyUp) .on(this.click + ' ' + this.touchEnd + ' ' + this.keyUp, function(e) { if (e.type !== 'keyup' || (e.type === 'keyup' && e.keyCode === KEYS.ENTER)) { e.preventDefault(); that.minimize(); } }); this.ui.maximizeButton .off(this.click + ' ' + this.touchEnd + ' ' + this.keyUp) .on(this.click + ' ' + this.touchEnd + ' ' + this.keyUp, function(e) { if (e.type !== 'keyup' || (e.type === 'keyup' && e.keyCode === KEYS.ENTER)) { e.preventDefault(); that.maximize(); } }); this.ui.title.off(this.pointerDown).on(this.pointerDown, function(e) { e.preventDefault(); that._startMove(e); }); this.ui.title.off(this.doubleClick).on(this.doubleClick, function() { that.maximize(); }); this.ui.resize.off(this.pointerDown).on(this.pointerDown, function(e) { e.preventDefault(); that._startResize(e); }); this.ui.closeAndCancel .off(this.click + ' ' + this.touchEnd + ' ' + this.keyUp) .on(this.click + ' ' + this.touchEnd + ' ' + this.keyUp, function(e) { if (e.type !== 'keyup' || (e.type === 'keyup' && e.keyCode === KEYS.ENTER)) { e.preventDefault(); that._cancelHandler(); } }); // elements within the window this.ui.breadcrumb.off(this.click, 'a').on(this.click, 'a', function(e) { e.preventDefault(); that._changeIframe($(this)); }); } /** * Opens the modal either in an iframe or renders markup. * * @method open * @chainable * @param {Object} opts either `opts.url` or `opts.html` are required * @param {Object[]} [opts.breadcrumbs] collection of breadcrumb items * @param {String|HTMLNode|jQuery} [opts.html] html markup to render * @param {String} [opts.title] modal window main title (bold) * @param {String} [opts.subtitle] modal window secondary title (normal) * @param {String} [opts.url] url to render iframe, takes precedence over `opts.html` * @param {Number} [opts.width] sets the width of the modal * @param {Number} [opts.height] sets the height of the modal * @returns {Class} this */ open(opts) { // setup internals if (!((opts && opts.url) || (opts && opts.html))) { throw new Error('The arguments passed to "open" were invalid.'); } // We have to rebind events every time we open a modal // because the event handlers contain references to the instance // and since we reuse the same markup we need to update // that instance reference every time. this._events(); Helpers.dispatchEvent('modal-load', { instance: this }); // // trigger the event also on the dom element, // // because if we load another modal while one is already open // // the older instance won't receive any updates // this.ui.modal.trigger('cms.modal.load'); // common elements state this.ui.resize.toggle(this.options.resizable); this.ui.minimizeButton.toggle(this.options.minimizable); this.ui.maximizeButton.toggle(this.options.maximizable); var position = this._calculateNewPosition(opts); this.ui.maximizeButton.removeClass('cms-modal-maximize-active'); this.maximized = false; // because a new instance is called, we have to ensure minimized state is removed #3620 if (this.ui.body.hasClass('cms-modal-minimized')) { this.minimized = true; this.minimize(); } // clear elements this.ui.modalButtons.empty(); this.ui.breadcrumb.empty(); // remove class from modal when no breadcrumbs is rendered this.ui.modal.removeClass('cms-modal-has-breadcrumb'); // hide tooltip CMS.API.Tooltip.hide(); // redirect to iframe rendering if url is provided if (opts.url) { this._loadIframe({ url: opts.url, title: opts.title, breadcrumbs: opts.breadcrumbs }); } else { // if url is not provided we go for html this._loadMarkup({ html: opts.html, title: opts.title, subtitle: opts.subtitle }); } Helpers.dispatchEvent('modal-loaded', { instance: this }); var currentContext = keyboard.getContext(); if (currentContext !== 'modal') { previousKeyboardContext = keyboard.getContext(); previouslyFocusedElement = $(document.activeElement); } // display modal this._show( $.extend( { duration: this.options.modalDuration }, position ) ); keyboard.setContext('modal'); this.ui.modal.trap(); return this; } /** * Calculates coordinates and dimensions for modal placement * * @method _calculateNewPosition * @private * @param {Object} [opts] * @param {Number} [opts.width] desired width of the modal * @param {Number} [opts.height] desired height of the modal * @returns {Object} */ // eslint-disable-next-line complexity _calculateNewPosition(opts) { // lets set the modal width and height to the size of the browser var widthOffset = 300; // adds margin left and right var heightOffset = 300; // adds margin top and bottom; var screenWidth = this.ui.window.width(); var screenHeight = this.ui.window.height(); var modalWidth = opts.width || this.options.minWidth; var modalHeight = opts.height || this.options.minHeight; // screen width and height calculation, WC = width var screenWidthCalc = screenWidth >= modalWidth + widthOffset; var screenHeightCalc = screenHeight >= modalHeight + heightOffset; var width = screenWidthCalc && !opts.width ? screenWidth - widthOffset : modalWidth; var height = screenHeightCalc && !opts.height ? screenHeight - heightOffset : modalHeight; var currentLeft = this.ui.modal.css('left'); var currentTop = this.ui.modal.css('top'); var newLeft; var newTop; // jquery made me do it if (currentLeft === '50%') { currentLeft = screenWidth / 2; } if (currentTop === '50%') { currentTop = screenHeight / 2; } currentTop = parseInt(currentTop, 10); currentLeft = parseInt(currentLeft, 10); // if new width/height go out of the screen - reset position to center of screen if ( width / 2 + currentLeft > screenWidth || height / 2 + currentTop > screenHeight || currentLeft - width / 2 < 0 || currentTop - height / 2 < 0 ) { newLeft = screenWidth / 2; newTop = screenHeight / 2; } // in case, the modal is larger than the window, we trigger fullscreen mode if (width >= screenWidth || height >= screenHeight) { this.triggerMaximized = true; } return { width: width, height: height, top: newTop, left: newLeft }; } /** * Animation helper for opening the sideframe. * * @method _show * @private * @param {Object} opts * @param {Number} opts.width width of the modal * @param {Number} opts.height height of the modal * @param {Number} opts.left left in px of the center of the modal * @param {Number} opts.top top in px of the center of the modal * @param {Number} opts.duration speed of opening, ms (not really used yet) */ _show(opts) { // we need to position the modal in the center var that = this; var width = opts.width; var height = opts.height; var speed = opts.duration; var top = opts.top; var left = opts.left; if (this.ui.modal.hasClass('cms-modal-open')) { this.ui.modal.addClass('cms-modal-morphing'); } this.ui.modal.css({ display: 'block', width: width, height: height, top: top, left: left, 'margin-left': -(width / 2), 'margin-top': -(height / 2) }); // setImmediate is required to go into the next frame setTimeout(function() { that.ui.modal.addClass('cms-modal-open'); }, 0); this.ui.modal .one('cmsTransitionEnd', function() { that.ui.modal.removeClass('cms-modal-morphing'); that.ui.modal.css({ 'margin-left': -(width / 2), 'margin-top': -(height / 2) }); // check if we should maximize if (that.triggerMaximized) { that.maximize(); } // changed locked status to allow other modals again CMS.API.locked = false; Helpers.dispatchEvent('modal-shown', { instance: that }); }) .emulateTransitionEnd(speed); // add esc close event this.ui.body.off('keydown.cms.close').on('keydown.cms.close', function(e) { if (e.keyCode === KEYS.ESC && that.options.closeOnEsc) { e.stopPropagation(); if (that._confirmDirtyEscCancel()) { that._cancelHandler(); } } }); // set focus to modal this.ui.modal.focus(); } /** * Closes the current instance. * * @method close * @returns {Boolean|void} */ close() { var event = Helpers.dispatchEvent('modal-close', { instance: this }); if (event.isDefaultPrevented()) { return false; } Helpers._getWindow().removeEventListener('beforeunload', this._beforeUnloadHandler); // handle refresh option if (this.options.onClose) { Helpers.reloadBrowser(this.options.onClose, false, true); } this._hide({ duration: this.options.modalDuration / 2 }); this.ui.modal.untrap(); keyboard.setContext(previousKeyboardContext); try { previouslyFocusedElement.focus(); } catch (e) {} } /** * Animation helper for closing the iframe. * * @method _hide * @private * @param {Object} opts * @param {Number} [opts.duration=this.options.modalDuration] animation duration */ _hide(opts) { var that = this; var duration = this.options.modalDuration; if (opts && opts.duration) { duration = opts.duration; } this.ui.frame.empty(); this.ui.modalBody.removeClass('cms-loader'); this.ui.modal.removeClass('cms-modal-open'); this.ui.modal .one('cmsTransitionEnd', function() { that.ui.modal.css('display', 'none'); }) .emulateTransitionEnd(duration); // reset maximize or minimize states for #3111 setTimeout(function() { if (that.minimized) { that.minimize(); } if (that.maximized) { that.maximize(); } hideLoader(); Helpers.dispatchEvent('modal-closed', { instance: that }); }, this.options.duration); this.ui.body.off('keydown.cms.close'); } /** * Minimizes the modal onto the toolbar. * * @method minimize * @returns {Boolean|void} */ minimize() { var MINIMIZED_OFFSET = 50; // cancel action if maximized if (this.maximized) { return false; } if (this.minimized === false) { // save initial state this.ui.modal.data('css', this.ui.modal.css(['left', 'top', 'margin-left', 'margin-top'])); // minimize this.ui.body.addClass('cms-modal-minimized'); this.ui.modal.css({ left: this.ui.toolbarLeftPart.outerWidth(true) + MINIMIZED_OFFSET }); this.minimized = true; } else { // maximize this.ui.body.removeClass('cms-modal-minimized'); this.ui.modal.css(this.ui.modal.data('css')); this.minimized = false; } } /** * Maximizes the window according to the browser size. * * @method maximize * @returns {Boolean|void} */ maximize() { // cancel action when minimized if (this.minimized) { return false; } if (this.maximized === false) { // save initial state this.ui.modal.data( 'css', this.ui.modal.css(['left', 'top', 'margin-left', 'margin-top', 'width', 'height']) ); this.ui.body.addClass('cms-modal-maximized'); this.maximized = true; Helpers.dispatchEvent('modal-maximized', { instance: this }); } else { // minimize this.ui.body.removeClass('cms-modal-maximized'); this.ui.modal.css(this.ui.modal.data('css')); this.maximized = false; Helpers.dispatchEvent('modal-restored', { instance: this }); } } /** * Initiates the start move event from `_events`. * * @method _startMove * @private * @param {Object} pointerEvent passes starting event * @returns {Boolean|void} */ _startMove(pointerEvent) { // cancel if maximized or minimized if (this.maximized || this.minimized) { return false; } var that = this; var position = this.ui.modal.position(); var left; var top; this.ui.shim.show(); // create event for stopping this.ui.body.on(this.pointerUp, function(e) { that._stopMove(e); }); this.ui.body .on(this.pointerMove, function(e) { left = position.left - (pointerEvent.originalEvent.pageX - e.originalEvent.pageX); top = position.top - (pointerEvent.originalEvent.pageY - e.originalEvent.pageY); that.ui.modal.css({ left: left, top: top }); }) .attr('data-touch-action', 'none'); } /** * Initiates the stop move event from `_startMove`. * * @method _stopMove * @private */ _stopMove() { this.ui.shim.hide(); this.ui.body.off(this.pointerMove + ' ' + this.pointerUp).removeAttr('data-touch-action'); } /** * Initiates the start resize event from `_events`. * * @method _startResize * @private * @param {Object} pointerEvent passes starting event * @returns {Boolean|void} */ _startResize(pointerEvent) { // cancel if in fullscreen if (this.maximized) { return false; } // continue var that = this; var width = this.ui.modal.width(); var height = this.ui.modal.height(); var modalLeft = this.ui.modal.position().left; var modalTop = this.ui.modal.position().top; // create event for stopping this.ui.body.on(this.pointerUp, function(e) { that._stopResize(e); }); this.ui.shim.show(); this.ui.body .on(this.pointerMove, function(e) { var mvX = pointerEvent.originalEvent.pageX - e.originalEvent.pageX; var mvY = pointerEvent.originalEvent.pageY - e.originalEvent.pageY; var w = width - mvX * 2; var h = height - mvY * 2; var wMin = that.options.minWidth; var hMin = that.options.minHeight; var left = mvX + modalLeft; var top = mvY + modalTop; // add some limits if (w <= wMin) { w = wMin; left = modalLeft + width / 2 - w / 2; } if (h <= hMin) { h = hMin; top = modalTop + height / 2 - h / 2; } // set centered animation that.ui.modal.css({ width: w, height: h, left: left, top: top }); }) .attr('data-touch-action', 'none'); } /** * Initiates the stop resize event from `_startResize`. * * @method _stopResize * @private */ _stopResize() { this.ui.shim.hide(); this.ui.body.off(this.pointerMove + ' ' + this.pointerUp).removeAttr('data-touch-action'); } /** * Sets the breadcrumb inside the modal. * * @method _setBreadcrumb * @private * @param {Object[]} breadcrumbs renderes breadcrumb on modal * @returns {Boolean|void} */ _setBreadcrumb(breadcrumbs) { var crumb = ''; var template = '<a href="{1}" class="{2}"><span>{3}</span></a>'; // cancel if there is no breadcrumbs) if (!breadcrumbs || breadcrumbs.length <= 1) { return false; } if (!breadcrumbs[0].title) { return false; } // add class to modal this.ui.modal.addClass('cms-modal-has-breadcrumb'); // load breadcrumbs $.each(breadcrumbs, function(index, item) { // check if the item is the last one var last = index >= breadcrumbs.length - 1 ? 'active' : ''; // render breadcrumbs crumb += template.replace('{1}', item.url).replace('{2}', last).replace('{3}', item.title); }); // attach elements this.ui.breadcrumb.html(crumb); } /** * Sets the buttons inside the modal. * * @method _setButtons * @private * @param {jQuery} iframe loaded iframe element */ _setButtons(iframe) { var djangoSuit = iframe.contents().find('.suit-columns').length > 0; var that = this; var group = $('<div class="cms-modal-item-buttons"></div>'); var render = $('<div class="cms-modal-buttons-inner"></div>'); var cancel = $('<a href="#" class="cms-btn">' + CMS.config.lang.cancel + '</a>'); var row; var tmp; // istanbul ignore if if (djangoSuit) { row = iframe.contents().find('.save-box:eq(0)'); } else { row = iframe.contents().find('.submit-row:eq(0)'); } var form = iframe.contents().find('form'); // avoids conflict between the browser's form validation and Django's validation form.on('submit', function() { // default submit button was clicked // meaning, if you have save - it should close the iframe, // if you hit save and continue editing it should be default form behaviour if (that.hideFrame) { that.ui.modal.find('.cms-modal-frame iframe').hide(); // page has been saved, run checkup that.saved = true; } }); var buttons = row.find('input, a, button'); // these are the buttons _inside_ the iframe // we need to listen to this click event to support submitting // a form by pressing enter inside of a field // click is actually triggered by submit buttons.on('click', function() { if ($(this).hasClass('default')) { that.hideFrame = true; } }); // hide all submit-rows iframe.contents().find('.submit-row').hide(); // if there are no given buttons within the submit-row area // scan deeper within the form itself // istanbul ignore next if (!buttons.length) { row = iframe.contents().find('body:not(.change-list) #content form:eq(0)'); buttons = row.find('input[type="submit"], button[type="submit"]'); buttons.addClass('deletelink').hide(); } // loop over input buttons buttons.each(function(index, btn) { var item = $(btn); item.attr('data-rel', '_' + index); // cancel if item is a hidden input if (item.attr('type') === 'hidden') { return false; } var title = item.attr('value') || item.text(); var cls = 'cms-btn'; if (item.is('button')) { title = item.text(); } // set additional special css classes if (item.hasClass('default')) { cls = 'cms-btn cms-btn-action'; } if (item.hasClass('deletelink')) { cls = 'cms-btn cms-btn-caution'; } var el = $('<a href="#" class="' + cls + ' ' + item.attr('class') + '">' + title + '</a>'); // eslint-disable-next-line complexity el.on(that.click + ' ' + that.touchEnd, function(e) { e.preventDefault(); if (item.is('a')) { that._loadIframe({ url: Helpers.updateUrlWithPath(item.prop('href')), name: title }); } // trigger only when blue action buttons are triggered if (item.hasClass('default') || item.hasClass('deletelink')) { // hide iframe when using buttons other than submit if (item.hasClass('default')) { // submit button uses the form's submit event that.hideFrame = true; } else { that.ui.modal.find('.cms-modal-frame iframe').hide(); // page has been saved or deleted, run checkup that.saved = true; if (item.hasClass('deletelink')) { that.justDeleted = true; var action = item.closest('form').prop('action'); // in case action is an input (see https://github.com/jquery/jquery/issues/3691) // it's definitely not a plugin/placeholder deletion if (typeof action === 'string' && action.match(/delete-plugin/)) { that.justDeletedPlugin = /delete-plugin\/(\d+)\//gi.exec(action)[1]; } if (typeof action === 'string' && action.match(/clear-placeholder/)) { that.justDeletedPlaceholder = /clear-placeholder\/(\d+)\//gi.exec(action)[1]; } } } } if (item.is('input') || item.is('button')) { that.ui.modalBody.addClass('cms-loader'); var frm = item.closest('form'); // In Firefox with 1Password extension installed (FF 45 1password 4.5.6 at least) // the item[0].click() doesn't work, which notably breaks // deletion of the plugin. Workaround is that if the clicked button // is the only button in the form - submit a form, otherwise // click on the button if (frm.find('button, input[type="button"], input[type="submit"]').length > 1) { // we need to use native `.click()` event specifically // as we are inside an iframe and magic is happening item[0].click(); } else { // have to dispatch native submit event so all the submit handlers // can be fired, see #5590 var evt = document.createEvent('HTMLEvents'); evt.initEvent('submit', false, true); if (frm[0].dispatchEvent(evt)) { // triggering submit event in webkit based browsers won't // actually submit the form, while in Gecko-based ones it // will and calling frm.submit() would throw NS_ERROR_UNEXPECTED try { frm[0].submit(); } catch (err) {} } } } }); el.wrap(group); // append element render.append(el.parent()); }); // manually add cancel button at the end cancel.on(that.click, function(e) { e.preventDefault(); that._cancelHandler(); }); cancel.wrap(group); render.append(cancel.parent()); // prepare groups render.find('.cms-btn-group').unwrap(); tmp = render.find('.cms-btn-group').clone(true, true); render.find('.cms-btn-group').remove(); render.append(tmp.wrapAll(group.clone().addClass('cms-modal-item-buttons-left')).parent()); // render buttons this.ui.modalButtons.html(render); } /** * Version where the modal loads an iframe. * * @method _loadIframe * @private * @param {Object} opts * @param {String} opts.url url to render iframe, takes presedence over opts.html * @param {Object[]} [opts.breadcrumbs] collection of breadcrumb items * @param {String} [opts.title] modal window main title (bold) */ _loadIframe(opts) { var that = this; const SHOW_LOADER_TIMEOUT = 500; opts.url = Helpers.makeURL(opts.url); opts.title = opts.title || ''; opts.breadcrumbs = opts.breadcrumbs || ''; showLoader(); // set classes this.ui.modal.removeClass('cms-modal-markup'); this.ui.modal.addClass('cms-modal-iframe'); // we need to render the breadcrumb this._setBreadcrumb(opts.breadcrumbs); // now refresh the content var holder = this.ui.frame; var iframe = $('<iframe tabindex="0" src="' + opts.url + '" class="" frameborder="0" />'); // set correct title var titlePrefix = this.ui.titlePrefix; var titleSuffix = this.ui.titleSuffix; iframe.css('visibility', 'hidden'); titlePrefix.text(opts.title || ''); titleSuffix.text(''); // ensure previous iframe is hidden holder.find('iframe').css('visibility', 'hidden'); const loaderTimeout = setTimeout(() => that.ui.modalBody.addClass('cms-loader'), SHOW_LOADER_TIMEOUT); // attach load event for iframe to prevent flicker effects // eslint-disable-next-line complexity iframe.on('load', function() { clearTimeout(loaderTimeout); var messages; var messageList; var contents; var body; var innerTitle; var bc; // check if iframe can be accessed try { contents = iframe.contents(); body = contents.find('body'); } catch (error) { CMS.API.Messages.open({ message: '<strong>' + CMS.config.lang.errorLoadingEditForm + '</strong>', error: true, delay: 0 }); that.close(); return; } // tabindex is required for keyboard navigation // body.attr('tabindex', '0'); iframe.on('focus', function() { if (this.contentWindow) { this.contentWindow.focus(); } }); Modal._setupCtrlEnterSave(document); // istanbul ignore else if (iframe[0].contentWindow && iframe[0].contentWindow.document) { Modal._setupCtrlEnterSave(iframe[0].contentWindow.document); } // for ckeditor we need to go deeper // istanbul ignore next if (iframe[0].contentWindow && iframe[0].contentWindow.CMS && iframe[0].contentWindow.CMS.CKEditor) { $(iframe[0].contentWindow.document).ready(function() { // setTimeout is required to battle CKEditor initialisation setTimeout(function() { var editor = iframe[0].contentWindow.CMS.CKEditor.editor; if (editor) { editor.on('instanceReady', function(e) { Modal._setupCtrlEnterSave( $(e.editor.container.$).find('iframe')[0].contentWindow.document ); }); } }, 100); // eslint-disable-line }); } var saveSuccess = Boolean(contents.find('.messagelist :not(".error")').length); // in case message didn't appear, assume that admin page is actually a success // istanbul ignore if if (!saveSuccess) { saveSuccess = Boolean(contents.find('.dashboard #content-main').length) && !contents.find('.messagelist .error').length; } // show messages in toolbar if provided messageList = contents.find('.messagelist'); messages = messageList.find('li'); if (messages.length) { CMS.API.Messages.open({ message: messages.eq(0).html() }); } messageList.remove(); // inject css class body.addClass('cms-admin cms-admin-modal'); // hide loaders that.ui.modalBody.removeClass('cms-loader'); hideLoader(); // determine if we should close the modal or reload if (messages.length && that.enforceReload) { that.ui.modalBody.addClass('cms-loader'); showLoader(); Helpers.reloadBrowser(); } if (messages.length && that.enforceClose) { that.close(); return false; } // adding django hacks contents.find('.viewsitelink').attr('target', '_top'); // set modal buttons that._setButtons($(this)); // when an error occurs, reset the saved status so the form can be checked and validated again if ( contents.find('.errornote').length || contents.find('.errorlist').length || (that.saved && !saveSuccess) ) { that.saved = false; } // when the window has been changed pressing the blue or red button, we need to run a reload check // also check that no delete-confirmation is required if (that.saved && saveSuccess && !contents.find('.delete-confirmation').length) { that.ui.modalBody.addClass('cms-loader'); if (that.options.onClose) { showLoader(); Helpers.reloadBrowser( that.options.onClose ? that.options.onClose : window.location.href, false, true ); } else { setTimeout(function() { if (that.justDeleted && (that.justDeletedPlugin || that.justDeletedPlaceholder)) { CMS.API.StructureBoard.invalidateState( that.justDeletedPlaceholder ? 'CLEAR_PLACEHOLDER' : 'DELETE', { plugin_id: that.justDeletedPlugin, placeholder_id: that.justDeletedPlaceholder, deleted: true } ); } // hello ckeditor Helpers.removeEventListener('modal-close.text-plugin'); that.close(); // must be more than 100ms }, 150); // eslint-disable-line } } else { iframe.show(); // set title of not provided innerTitle = contents.find('#content h1:eq(0)'); // case when there is no prefix // istanbul ignore next: never happens if (opts.title === undefined && that.ui.titlePrefix.text() === '') { bc = contents.find('.breadcrumbs').contents(); that.ui.titlePrefix.text(bc.eq(bc.length - 1).text().replace('›', '').trim()); } if (titlePrefix.text().trim() === '') { titlePrefix.text(innerTitle.text()); } else { titleSuffix.text(innerTitle.text()); } innerTitle.remove(); // than show iframe.css('visibility', 'visible'); // append ready state iframe.data('ready', true); // attach close event body.on('keydown.cms', function(e) { if (e.keyCode === KEYS.ESC && that.options.closeOnEsc) { e.stopPropagation(); if (that._confirmDirtyEscCancel()) { that._cancelHandler(); } } }); // figure out if .object-tools is available if (contents.find('.object-tools').length) { contents.find('#content').css('padding-top', 38); // eslint-disable-line } // this is required for IE11. we assume that when the modal is opened the user is going to interact // with it. if we don't focus the body directly the next time the user clicks on a field inside // the iframe the focus will be stolen by body thus requiring two clicks. this immediately focuses the // iframe body on load except if something is already focused there // (django tries to focus first field by default) setTimeout(() => { if (!iframe[0] || !iframe[0].contentDocument || !iframe[0].contentDocument.documentElement) { return; } if ($(iframe[0].contentDocument.documentElement).find(':focus').length) { return; } iframe.trigger('focus'); }, 0); // eslint-disable-line } that._attachContentPreservingHandlers(iframe); }); // inject holder.html(iframe); } /** * Adds handlers to prevent accidental refresh / modal close * that could lead to loss of data. * * @method _attachContentPreservingHandlers * @private * @param {jQuery} iframe */ _attachContentPreservingHandlers(iframe) { var that = this; that.tracker = new ChangeTracker(iframe); Helpers._getWindow().addEventListener('beforeunload', this._beforeUnloadHandler); } /** * @method _beforeUnloadHandler * @private * @param {Event} e * @returns {String|void} */ _beforeUnloadHandler(e) { if (this.tracker.isFormChanged()) { e.returnValue = CMS.config.lang.confirmDirty; return e.returnValue; } } /** * Similar functionality as in `_attachContentPreservingHandlers` but for canceling * the modal with the ESC button. * * @method _confirmDirtyEscCancel * @private * @returns {Boolean} */ _confirmDirtyEscCancel() { if (this.tracker && this.tracker.isFormChanged()) { return Helpers.secureConfirm(CMS.config.lang.confirmDirty + '\n\n' + CMS.config.lang.confirmDirtyESC); } return true; } /** * Version where the modal loads an url within an iframe. * * @method _changeIframe * @private * @param {jQuery} el originated element * @returns {Boolean|void} */ _changeIframe(el) { if (el.hasClass('active')) { return false; } var parents = el.parent().find('a'); parents.removeClass('active'); el.addClass('active'); this._loadIframe({ url: el.attr('href') }); this.ui.titlePrefix.text(el.text()); } /** * Version where the modal loads html markup. * * @method _loadMarkup * @private * @param {Object} opts * @param {String|HTMLNode|jQuery} opts.html html markup to render * @param {String} opts.title modal window main title (bold) * @param {String} [opts.subtitle] modal window secondary title (normal) */ _loadMarkup(opts) { this.ui.modal.removeClass('cms-modal-iframe'); this.ui.modal.addClass('cms-modal-markup'); this.ui.modalBody.removeClass('cms-loader'); // set content // empty to remove events, append to keep events this.ui.frame.empty().append(opts.html); this.ui.titlePrefix.text(opts.title || ''); this.ui.titleSuffix.text(opts.subtitle || ''); } /** * Called whenever default modal action is canceled. * * @method _cancelHandler * @private */ _cancelHandler() { this.options.onClose = null; this.close(); } /** * Sets up keyup/keydown listeners so you're able to save whatever you're * editing inside of an iframe by pressing `ctrl + enter` on windows and `cmd + enter` on mac. * * It only works with default button (e.g. action), not the `delete` button, * even though sometimes it's the only actionable button in the modal. * * @method _setupCtrlEnterSave * @private * @static * @param {HTMLElement} doc document element (iframe or parent window); */ static _setupCtrlEnterSave(doc) { var cmdPressed = false; var mac = navigator.platform.toLowerCase().indexOf('mac') + 1; $(doc) .on('keydown.cms.submit', function(e) { if (e.ctrlKey && e.keyCode === KEYS.ENTER && !mac) { $('.cms-modal-buttons .cms-btn-action:first').trigger('click'); } if (mac) { if (e.keyCode === KEYS.CMD_LEFT || e.keyCode === KEYS.CMD_RIGHT || e.keyCode === KEYS.CMD_FIREFOX) { cmdPressed = true; } if (e.keyCode === KEYS.ENTER && cmdPressed) { $('.cms-modal-buttons .cms-btn-action:first').trigger('click'); } } }) .on('keyup.cms.submit', function(e) { if (mac) { if (e.keyCode === KEYS.CMD_LEFT || e.keyCode === KEYS.CMD_RIGHT || e.keyCode === KEYS.CMD_FIREFOX) { cmdPressed = false; } } }); } } Modal.options = { onClose: false, closeOnEsc: true, minHeight: 400, minWidth: 800, modalDuration: 200, resizable: true, maximizable: true, minimizable: true }; export default Modal;
Close