mirror of https://github.com/xwiki-labs/cryptpad
360 lines
14 KiB
JavaScript
360 lines
14 KiB
JavaScript
// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
define(['jquery'], function ($) {
|
|
var CKEDITOR = window.CKEDITOR;
|
|
|
|
var $iframe = $('iframe').contents().find('html');
|
|
|
|
$('body').on('click', '.cke_dialog_container a.cke_specialchar', function (e) {
|
|
e.preventDefault();
|
|
var attr = $(e.currentTarget).attr('oonclick');
|
|
if (!attr) { return; }
|
|
var reg = /CKEDITOR.tools.callFunction\(([0-9]+), this\);/;
|
|
var m = attr.match(reg);
|
|
if (!m) { return; }
|
|
var s = $iframe.scrollTop();
|
|
CKEDITOR.tools.callFunction(Number(m[1]), e.currentTarget);
|
|
$iframe.scrollTop(s);
|
|
});
|
|
|
|
// Buttons
|
|
var $a = $('.cke_toolbox_main').find('.cke_button, .cke_combo_button');
|
|
$a.each(function (i, el) {
|
|
var $el = $(el);
|
|
$el.attr('tabindex', '0');
|
|
var $icon = $el.find('span.cke_button_icon');
|
|
if ($icon.length) {
|
|
try {
|
|
var url = $icon[0].style['background-image'];
|
|
var bgImg = url.replace(/ !important$/, '');
|
|
if (bgImg) {
|
|
$icon[0].style.setProperty('background-image', bgImg, 'important');
|
|
}
|
|
} catch (e) { console.error(e); }
|
|
}
|
|
$el.on('keydown blur focus click dragstart', function (e) {
|
|
if (e.key !== 'Tab') { e.preventDefault(); }
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault();
|
|
var focusedButton = document.activeElement;
|
|
|
|
// Simulate a click event on the focused button
|
|
if (focusedButton) {
|
|
focusedButton.click();
|
|
}
|
|
}
|
|
var attr = $(el).attr('oon'+e.type);
|
|
if (!attr) { return; }
|
|
if (['blur', 'dragstart'].indexOf(e.type) !== -1) { return false; }
|
|
var reg = /CKEDITOR.tools.callFunction\(([0-9]+),(this|event)\);return false;/;
|
|
var m = attr.match(reg);
|
|
if (!m) { return; }
|
|
var f = m[1];
|
|
var arg = m[2] === "this" ? el : e.originalEvent;
|
|
var s = $iframe.scrollTop();
|
|
CKEDITOR.tools.callFunction(Number(f), arg);
|
|
$iframe.scrollTop(s);
|
|
});
|
|
});
|
|
|
|
|
|
// Dropdown menus
|
|
|
|
var frameTpl = CKEDITOR.getTemplate('panel-frame');
|
|
var panelTpl = CKEDITOR.getTemplate('panel');
|
|
var frameDocTpl = CKEDITOR.addTemplate( 'panel-frame-inner-csp', '<!DOCTYPE html>' +
|
|
'<html class="cke_panel_container {env}" dir="{dir}" lang="{langCode}">' +
|
|
'<head>{css}</head>' +
|
|
'<body class="cke_{dir}"' +
|
|
' style="margin:0;padding:0"></body>' +
|
|
'<\/html>' );
|
|
CKEDITOR.ui.panel.prototype.render = function( editor, output ) {
|
|
var data = {
|
|
editorId: editor.id,
|
|
id: this.id,
|
|
langCode: editor.langCode,
|
|
dir: editor.lang.dir,
|
|
cls: this.className,
|
|
frame: '',
|
|
env: CKEDITOR.env.cssClass,
|
|
'z-index': editor.config.baseFloatZIndex + 1
|
|
};
|
|
|
|
this.getHolderElement = function() {
|
|
var holder = this._.holder;
|
|
|
|
if ( !holder ) {
|
|
if ( this.isFramed ) {
|
|
var iframe = this.document.getById( this.id + '_frame' ),
|
|
parentDiv = iframe.getParent(),
|
|
doc = iframe.getFrameDocument();
|
|
|
|
// Make it scrollable on iOS. (https://dev.ckeditor.com/ticket/8308)
|
|
if (CKEDITOR.env.iOS) {
|
|
parentDiv.setStyles( {
|
|
'overflow': 'scroll',
|
|
'-webkit-overflow-scrolling': 'touch'
|
|
} );
|
|
}
|
|
|
|
var onLoad = CKEDITOR.tools.addFunction( CKEDITOR.tools.bind( function() {
|
|
this.isLoaded = true;
|
|
if ( this.onLoad ) {
|
|
this.onLoad();
|
|
}
|
|
}, this ) );
|
|
|
|
doc.write( frameDocTpl.output( CKEDITOR.tools.extend( {
|
|
css: CKEDITOR.tools.buildStyleHtml( this.css )
|
|
}, data ) ) );
|
|
|
|
var that = this;
|
|
$(iframe.$).on('load', function () {
|
|
that.isLoaded = true;
|
|
if (that.onLoad) {
|
|
that.onLoad();
|
|
}
|
|
}).contents().find('body').on('click dragstart mouseover mouseout', '.cke_button, a.cke_colormore, a.cke_colorbox, .cke_colorauto, .cke_combo_button, .cke_panel_listItem a, a.cke_menubutton', function (e) {
|
|
e.preventDefault();
|
|
if (e.type === 'dragstart') { return false; }
|
|
var attr = $(e.currentTarget).attr('oon'+e.type);
|
|
if (!attr) { return; }
|
|
var reg = /CKEDITOR.tools.callFunction\(([0-9]+),'?([^'"]+)'?(,'([^']+)')?\);/;
|
|
var reg2 = /CKEDITOR.tools.callFunction\(([0-9]+),'?([^'",]+)'?(,'([^']+)')?, this\);/;
|
|
var match = attr.match(reg);
|
|
var lastArg;
|
|
if (!match) {
|
|
match = attr.match(reg2);
|
|
if (!match) { return; }
|
|
lastArg = this;
|
|
}
|
|
var f = match[1];
|
|
var el = match[2] !== "null" ? match[2] : null;
|
|
var s = $iframe.scrollTop();
|
|
CKEDITOR.tools.callFunction(Number(f), el, match[4], lastArg);
|
|
$iframe.scrollTop(s);
|
|
});
|
|
|
|
// Register the CKEDITOR global.
|
|
var win = doc.getWindow();
|
|
win.$.CKEDITOR = CKEDITOR;
|
|
|
|
// Arrow keys for scrolling is only preventable with 'keypress' event in Opera (https://dev.ckeditor.com/ticket/4534).
|
|
doc.on( 'keydown', function( evt ) {
|
|
var keystroke = evt.data.getKeystroke(),
|
|
dir = this.document.getById( this.id ).getAttribute( 'dir' );
|
|
|
|
// Arrow left and right should use native behaviour inside input element
|
|
if ( evt.data.getTarget().getName() === 'input' && ( keystroke === 37 || keystroke === 39 ) ) {
|
|
return;
|
|
}
|
|
// Delegate key processing to block.
|
|
if ( this._.onKeyDown && this._.onKeyDown( keystroke ) === false ) {
|
|
if ( !( evt.data.getTarget().getName() === 'input' && keystroke === 32 ) ) {
|
|
// Don't prevent space when is pressed on a input filed.
|
|
evt.data.preventDefault();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// ESC/ARROW-LEFT(ltr) OR ARROW-RIGHT(rtl)
|
|
if ( keystroke === 27 || keystroke === ( dir === 'rtl' ? 39 : 37 ) ) {
|
|
if ( this.onEscape && this.onEscape( keystroke ) === false ) {
|
|
evt.data.preventDefault();
|
|
}
|
|
}
|
|
}, this );
|
|
|
|
holder = doc.getBody();
|
|
holder.unselectable();
|
|
if (CKEDITOR.env.air) { CKEDITOR.tools.callFunction( onLoad ); }
|
|
} else {
|
|
holder = this.document.getById( this.id );
|
|
}
|
|
|
|
this._.holder = holder;
|
|
}
|
|
|
|
return holder;
|
|
};
|
|
|
|
if ( this.isFramed ) {
|
|
// With IE, the custom domain has to be taken care at first,
|
|
// for other browers, the 'src' attribute should be left empty to
|
|
// trigger iframe's 'load' event.
|
|
var src =
|
|
CKEDITOR.env.air ? 'javascript:void(0)' :
|
|
( CKEDITOR.env.ie && !CKEDITOR.env.edge ) ? 'javascript:void(function(){' + encodeURIComponent(
|
|
'document.open();' +
|
|
// In IE, the document domain must be set any time we call document.open().
|
|
'(' + CKEDITOR.tools.fixDomain + ')();' +
|
|
'document.close();'
|
|
) + '}())' :
|
|
'';
|
|
|
|
data.frame = frameTpl.output( {
|
|
id: this.id + '_frame',
|
|
src: src
|
|
} );
|
|
}
|
|
|
|
var html = panelTpl.output( data );
|
|
|
|
if ( output ) { output.push( html ); }
|
|
|
|
return html;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Mathjax
|
|
|
|
if ( !( CKEDITOR.env.ie && CKEDITOR.env.version === 8 ) ) {
|
|
CKEDITOR.plugins.mathjax.frameWrapper = function( iFrame, editor ) {
|
|
|
|
var update = function () {}; // Placeholder
|
|
var buffer, preview, value, newValue,
|
|
doc = iFrame.getFrameDocument(),
|
|
|
|
// Is MathJax loaded and ready to work.
|
|
isInit = false,
|
|
|
|
// Is MathJax parsing Tex.
|
|
isRunning = false,
|
|
|
|
// Function called when MathJax is loaded.
|
|
loadedHandler = CKEDITOR.tools.addFunction( function() {
|
|
preview = doc.getById( 'preview' );
|
|
buffer = doc.getById( 'buffer' );
|
|
isInit = true;
|
|
|
|
if ( newValue ) { update(); }
|
|
|
|
// Private! For test usage only.
|
|
CKEDITOR.fire( 'mathJaxLoaded', iFrame );
|
|
} ),
|
|
|
|
// Function called when MathJax finish his job.
|
|
updateDoneHandler = CKEDITOR.tools.addFunction( function() {
|
|
CKEDITOR.plugins.mathjax.copyStyles( iFrame, preview );
|
|
|
|
preview.setHtml( buffer.getHtml() );
|
|
|
|
editor.fire( 'lockSnapshot' );
|
|
|
|
iFrame.setStyles( {
|
|
height: 0,
|
|
width: 0
|
|
} );
|
|
|
|
// Set iFrame dimensions.
|
|
var height = Math.max( doc.$.body.offsetHeight, doc.$.documentElement.offsetHeight ),
|
|
width = Math.max( preview.$.offsetWidth, doc.$.body.scrollWidth );
|
|
|
|
iFrame.setStyles( {
|
|
height: height + 'px',
|
|
width: width + 'px'
|
|
} );
|
|
|
|
editor.fire( 'unlockSnapshot' );
|
|
|
|
// Private! For test usage only.
|
|
CKEDITOR.fire( 'mathJaxUpdateDone', iFrame );
|
|
|
|
// If value changed in the meantime update it again.
|
|
if ( value !== newValue ) {
|
|
update();
|
|
} else {
|
|
isRunning = false;
|
|
}
|
|
} );
|
|
|
|
// Run MathJax parsing Tex.
|
|
update = function () {
|
|
isRunning = true;
|
|
|
|
value = newValue;
|
|
|
|
editor.fire( 'lockSnapshot' );
|
|
|
|
buffer.setHtml( value );
|
|
|
|
// Set loading indicator.
|
|
preview.setHtml( '<img src=' + CKEDITOR.plugins.mathjax.loadingIcon + ' alt=' + editor.lang.mathjax.loading + '>' );
|
|
|
|
iFrame.setStyles( {
|
|
height: '16px',
|
|
width: '16px',
|
|
display: 'inline',
|
|
'vertical-align': 'middle'
|
|
} );
|
|
|
|
editor.fire( 'unlockSnapshot' );
|
|
|
|
// Run MathJax.
|
|
doc.getWindow().$.update( value );
|
|
};
|
|
|
|
|
|
var load = function () {
|
|
doc = iFrame.getFrameDocument();
|
|
|
|
if ( doc.getById( 'preview' ) ) { return; }
|
|
|
|
// Because of IE9 bug in a src attribute can not be javascript
|
|
// when you undo (https://dev.ckeditor.com/ticket/10930). If you have iFrame with javascript in src
|
|
// and call insertBefore on such element then IE9 will see crash.
|
|
if ( CKEDITOR.env.ie ) { iFrame.removeAttribute( 'src' ); }
|
|
|
|
doc.write( '<!DOCTYPE html>' +
|
|
'<html>' +
|
|
'<head>' +
|
|
'<meta charset="utf-8">' +
|
|
// Load MathJax lib.
|
|
'<script src="' + ( editor.config.mathJaxLib ) + '"></script>' +
|
|
'</head>' +
|
|
'<body style="padding:0;margin:0;background:transparent;overflow:hidden">' +
|
|
'<span id="preview"></span>' +
|
|
|
|
// Render everything here and after that copy it to the preview.
|
|
'<span id="buffer" style="display:none"></span>' +
|
|
'</body>' +
|
|
'</html>' );
|
|
iFrame.$.contentWindow.mathjax_loaded = loadedHandler;
|
|
iFrame.$.contentWindow.mathjax_done = updateDoneHandler;
|
|
};
|
|
|
|
iFrame.on( 'load', load );
|
|
load();
|
|
|
|
|
|
return {
|
|
/**
|
|
* Sets the TeX value to be displayed in the `iframe` element inside
|
|
* the editor. This function will activate the MathJax
|
|
* library which interprets TeX expressions and converts them into
|
|
* their representation that is displayed in the editor.
|
|
*
|
|
* @param {String} value TeX string.
|
|
*/
|
|
setValue: function( value ) {
|
|
newValue = CKEDITOR.tools.htmlEncode( value );
|
|
|
|
if ( isInit && !isRunning ) {
|
|
update();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|