Commit 8b9fc894 authored by Stefan Galinski's avatar Stefan Galinski 🎮

[FEATURE] Load the tinymce in the TYPO3 7 backend with requirejs

parent 7e549a3e
......@@ -53,7 +53,7 @@ class Loader {
*
* @var array
*/
protected $tinymceConfiguration = array();
protected $tinymceConfiguration = [];
/**
* Initialization flag
......@@ -67,7 +67,7 @@ class Loader {
*
* @var array
*/
protected $typo3LanguagesDifferencesToTinyMceLanguagesMap = array(
protected $typo3LanguagesDifferencesToTinyMceLanguagesMap = [
'bg' => 'bg_BG',
'fr' => 'fr_FR',
'fr_CA' => 'fr_FR',
......@@ -83,7 +83,7 @@ class Loader {
'sv' => 'sv_SE',
'th' => 'th_TH',
'zh' => 'zh_TW',
);
];
/**
* @param string $configuration file reference or configuration string (defaults to basic configuration)
......@@ -130,7 +130,7 @@ class Loader {
}
$languageFile = PATH_site . ExtensionManagementUtility::siteRelPath('tinymce') .
'Contrib/tinymce/langs/' . $languageKey . '.js';
'node_modules/tinymce/langs/' . $languageKey . '.js';
if (!is_file($languageFile)) {
$languageKey = 'en';
}
......@@ -230,7 +230,7 @@ class Loader {
self::$init = TRUE;
$pathToTinyMceExtension = ExtensionManagementUtility::extRelPath('tinymce');
$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Contrib/tinymce/tinymce.min.js';
$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'node_modules/tinymce/tinymce.min.js';
$output = '<script type="text/javascript" src="' . $script . '"></script>';
$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Contrib/WeakMap/WeakMap.js';
......@@ -264,7 +264,7 @@ class Loader {
self::$init = TRUE;
$pathToTinyMceExtension = ExtensionManagementUtility::extRelPath('tinymce');
$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Contrib/tinymce/tinymce.min.js';
$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'node_modules/tinymce/tinymce.min.js';
$pageRenderer->addJsLibrary('tinymce', $script, 'text/javascript', FALSE, TRUE, '', TRUE);
$script = $GLOBALS['BACK_PATH'] . $pathToTinyMceExtension . 'Contrib/MutationObserver/MutationObserver.js';
......@@ -287,20 +287,35 @@ class Loader {
* @see \SGalinski\Tinymce4Rte\Form\Element\RichTextElement->loadRequireModulesForRTE
*/
public function loadJsViaRequireJS() {
// if (self::$init) {
// return [];
// }
// self::$init = TRUE;
$scripts = array();
$pathToTinyMceExtension = ExtensionManagementUtility::extRelPath('tinymce');
if (self::$init) {
return [];
}
self::$init = TRUE;
$pathToTinyMceExtension = ExtensionManagementUtility::extPath('tinymce');
$tinymceSource = $pathToTinyMceExtension . 'node_modules/tinymce/tinymce.min.js';
$scripts[] = $pathToTinyMceExtension . 'Contrib/tinymce/tinymce.min.js';
$scripts[] = $pathToTinyMceExtension . 'Contrib/MutationObserver/MutationObserver.js';
$scripts[] = $pathToTinyMceExtension . 'Contrib/WeakMap/WeakMap.js';
$scripts[] = $pathToTinyMceExtension . 'Resources/Public/JavaScript/Loader.js';
$scripts[] = $this->getConfiguration();
$configuration = $this->tinymceConfiguration['preJS'];
$configuration .= '
var $ = jQuery = window.TYPO3.jQuery;
var RTEarea = RTEarea || window.RTEarea;
define([\'TYPO3/CMS/Tinymce/../../../../typo3conf/ext/tinymce/node_modules/tinymce/jquery.tinymce.min.js\'], function () {
$(\'.tinymce4_rte\').tinymce({
script_url : \'' . $this->getPath($tinymceSource, TRUE) . '\',
' . $this->replaceTypo3Paths($this->tinymceConfiguration['configurationData']) . '
});
});
';
$configuration .= $this->tinymceConfiguration['postJS'];
$filename = 'tinymceConfiguration' . sha1($configuration) . '.js';
$file = PATH_site . 'typo3temp/' . $filename;
if (!is_file($file)) {
file_put_contents($file, $configuration);
GeneralUtility::fixPermissions($file);
}
return $scripts;
return [$this->getPath($file, TRUE)];
}
/**
......@@ -310,7 +325,7 @@ class Loader {
* @return array
*/
protected function prepareTinyMCEConfiguration($configuration) {
$configurationArray = array();
$configurationArray = [];
// try to resolve a potential TYPO3 file path
$configurationFile = GeneralUtility::getFileAbsFileName($configuration);
......
<!DOCTYPE html>
<html>
<body>
<h3>Custom dialog</h3>
Input some text: <input id="content">
<button onclick="top.tinymce.activeEditor.windowManager.getWindows()[0].close();">Close window</button>
</body>
</html>
\ No newline at end of file
tinymce.PluginManager.add("example",function(a,b){a.addButton("example",{text:"My button",icon:!1,onclick:function(){a.windowManager.open({title:"Example plugin",body:[{type:"textbox",name:"title",label:"Title"}],onsubmit:function(b){a.insertContent("Title: "+b.data.title)}})}}),a.addMenuItem("example",{text:"Example plugin",context:"tools",onclick:function(){a.windowManager.open({title:"TinyMCE site",url:b+"/dialog.html",width:600,height:400,buttons:[{text:"Insert",onclick:function(){var b=a.windowManager.getWindows()[0];a.insertContent(b.getContentWindow().document.getElementById("content").value),b.close()}},{text:"Close",onclick:"close"}]})}})});
\ No newline at end of file
tinymce.PluginManager.add("example_dependency",function(){},["example"]);
\ No newline at end of file
## How to update the sources?
1. Download the latest version from "https://www.tinymce.com/download/"
1. Update tinymce with npm
2. Download the latest language pack from "http://archive.tinymce.com/i18n/index.php".
You can simplify the selection by using the following command in your developer tools console.
Deselect "ru@petr1708" and "zh_CN.GB2312" afterwards, because this language doesn't exists and leads to errors.
$('input[type=checkbox]').attr('checked', 1)
3. Unpack the downloaded tinymce source into the root folder
4. Remove the Contrib/tinymce directory inside the extension and move "tinymce/js/tinymce" from
the download source as a replacement.
5. Replace the "langs" directory inside Contrib/tinymce with the downloaded languages
(delete the folder in Contrib before you replace it)
6. Update the VERSIONS.md file
7. Push the new code to git (check the differences and test the stuff before!)
\ No newline at end of file
3. Add the "langs" directory inside node_modules/tinymce with the downloaded languages
4. Update the VERSIONS.md file
\ No newline at end of file
composer.json
bower.json
readme.md
This diff is collapsed.
!function(a){function b(){function b(a){"remove"===a&&this.each(function(a,b){var c=e(b);c&&c.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(a,b){var c=tinymce.get(b.id.replace(/_parent$/,""));c&&c.remove()})}function d(a){var c,d=this;if(null!=a)b.call(d),d.each(function(b,c){var d;(d=tinymce.get(c.id))&&d.setContent(a)});else if(d.length>0&&(c=tinymce.get(d[0].id)))return c.getContent()}function e(a){var b=null;return a&&a.id&&g.tinymce&&(b=tinymce.get(a.id)),b}function f(a){return!!(a&&a.length&&g.tinymce&&a.is(":tinymce"))}var h={};a.each(["text","html","val"],function(b,g){var i=h[g]=a.fn[g],j="text"===g;a.fn[g]=function(b){var g=this;if(!f(g))return i.apply(g,arguments);if(b!==c)return d.call(g.filter(":tinymce"),b),i.apply(g.not(":tinymce"),arguments),g;var h="",k=arguments;return(j?g:g.eq(0)).each(function(b,c){var d=e(c);h+=d?j?d.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):d.getContent({save:!0}):i.apply(a(c),k)}),h}}),a.each(["append","prepend"],function(b,d){var g=h[d]=a.fn[d],i="prepend"===d;a.fn[d]=function(a){var b=this;return f(b)?a!==c?("string"==typeof a&&b.filter(":tinymce").each(function(b,c){var d=e(c);d&&d.setContent(i?a+d.getContent():d.getContent()+a)}),g.apply(b.not(":tinymce"),arguments),b):void 0:g.apply(b,arguments)}}),a.each(["remove","replaceWith","replaceAll","empty"],function(c,d){var e=h[d]=a.fn[d];a.fn[d]=function(){return b.call(this,d),e.apply(this,arguments)}}),h.attr=a.fn.attr,a.fn.attr=function(b,g){var i=this,j=arguments;if(!b||"value"!==b||!f(i))return g!==c?h.attr.apply(i,j):h.attr.apply(i,j);if(g!==c)return d.call(i.filter(":tinymce"),g),h.attr.apply(i.not(":tinymce"),j),i;var k=i[0],l=e(k);return l?l.getContent({save:!0}):h.attr.apply(a(k),j)}}var c,d,e,f=[],g=window;a.fn.tinymce=function(c){function h(){var d=[],f=0;e||(b(),e=!0),l.each(function(a,b){var e,g=b.id,h=c.oninit;g||(b.id=g=tinymce.DOM.uniqueId()),tinymce.get(g)||(e=new tinymce.Editor(g,c,tinymce.EditorManager),d.push(e),e.on("init",function(){var a,b=h;l.css("visibility",""),h&&++f==d.length&&("string"==typeof b&&(a=-1===b.indexOf(".")?null:tinymce.resolve(b.replace(/\.\w+$/,"")),b=tinymce.resolve(b)),b.apply(a||tinymce,d))}))}),a.each(d,function(a,b){b.render()})}var i,j,k,l=this,m="";if(!l.length)return l;if(!c)return window.tinymce?tinymce.get(l[0].id):null;if(l.css("visibility","hidden"),g.tinymce||d||!(i=c.script_url))1===d?f.push(h):h();else{d=1,j=i.substring(0,i.lastIndexOf("/")),-1!=i.indexOf(".min")&&(m=".min"),g.tinymce=g.tinyMCEPreInit||{base:j,suffix:m},-1!=i.indexOf("gzip")&&(k=c.language||"en",i=i+(/\?/.test(i)?"&":"?")+"js=true&core=true&suffix="+escape(m)+"&themes="+escape(c.theme||"modern")+"&plugins="+escape(c.plugins||"")+"&languages="+(k||""),g.tinyMCE_GZ||(g.tinyMCE_GZ={start:function(){function b(a){tinymce.ScriptLoader.markDone(tinymce.baseURI.toAbsolute(a))}b("langs/"+k+".js"),b("themes/"+c.theme+"/theme"+m+".js"),b("themes/"+c.theme+"/langs/"+k+".js"),a.each(c.plugins.split(","),function(a,c){c&&(b("plugins/"+c+"/plugin"+m+".js"),b("plugins/"+c+"/langs/"+k+".js"))})},end:function(){}}));var n=document.createElement("script");n.type="text/javascript",n.onload=n.onreadystatechange=function(b){b=b||window.event,2===d||"load"!=b.type&&!/complete|loaded/.test(n.readyState)||(tinymce.dom.Event.domLoaded=1,d=2,c.script_loaded&&c.script_loaded(),h(),a.each(f,function(a,b){b()}))},n.src=i,document.body.appendChild(n)}return l},a.extend(a.expr[":"],{tinymce:function(a){var b;return a.id&&"tinymce"in window&&(b=tinymce.get(a.id),b&&b.editorManager===tinymce)?!0:!1}})}(jQuery);
\ No newline at end of file
{
"name": "tinymce",
"version": "4.3.1",
"description": "Web based JavaScript HTML WYSIWYG editor control.",
"license": "LGPL-2.1",
"keywords": [
"editor",
"wysiwyg",
"tinymce",
"richtext",
"javascript",
"html"
],
"bugs": {
"url": "http://www.tinymce.com/develop/bugtracker.php"
},
"gitHead": "321f1f97f903fd2ea2430a6b1bf995c7bbd1ab2f",
"_id": "tinymce@4.3.1",
"scripts": {},
"_shasum": "a035ae47072d122e4ebf1d0db01823e34d2b2f7d",
"_from": "tinymce@*",
"_npmVersion": "1.4.28",
"_npmUser": {
"name": "ephox",
"email": "is-accounts@ephox.com"
},
"maintainers": [
{
"name": "ephox",
"email": "is-accounts@ephox.com"
},
{
"name": "spocke",
"email": "spocke@moxiecode.com"
}
],
"dist": {
"shasum": "a035ae47072d122e4ebf1d0db01823e34d2b2f7d",
"tarball": "http://registry.npmjs.org/tinymce/-/tinymce-4.3.1.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/tinymce/-/tinymce-4.3.1.tgz"
}
/**
* plugin.js
*
* Released under LGPL License.
* Copyright (c) 1999-2015 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
/*global tinymce:true */
tinymce.PluginManager.add('advlist', function(editor) {
var olMenuItems, ulMenuItems, lastStyles = {};
function buildMenuItems(listName, styleValues) {
var items = [];
tinymce.each(styleValues.split(/[ ,]/), function(styleValue) {
items.push({
text: styleValue.replace(/\-/g, ' ').replace(/\b\w/g, function(chr) {
return chr.toUpperCase();
}),
data: styleValue == 'default' ? '' : styleValue
});
});
return items;
}
olMenuItems = buildMenuItems('OL', editor.getParam(
"advlist_number_styles",
"default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman"
));
ulMenuItems = buildMenuItems('UL', editor.getParam("advlist_bullet_styles", "default,circle,disc,square"));
function applyListFormat(listName, styleValue) {
editor.undoManager.transact(function() {
var list, dom = editor.dom, sel = editor.selection;
// Check for existing list element
list = dom.getParent(sel.getNode(), 'ol,ul');
// Switch/add list type if needed
if (!list || list.nodeName != listName || styleValue === false) {
editor.execCommand(listName == 'UL' ? 'InsertUnorderedList' : 'InsertOrderedList');
}
// Set style
styleValue = styleValue === false ? lastStyles[listName] : styleValue;
lastStyles[listName] = styleValue;
list = dom.getParent(sel.getNode(), 'ol,ul');
if (list) {
dom.setStyle(list, 'listStyleType', styleValue ? styleValue : null);
list.removeAttribute('data-mce-style');
}
editor.focus();
});
}
function updateSelection(e) {
var listStyleType = editor.dom.getStyle(editor.dom.getParent(editor.selection.getNode(), 'ol,ul'), 'listStyleType') || '';
e.control.items().each(function(ctrl) {
ctrl.active(ctrl.settings.data === listStyleType);
});
}
editor.addButton('numlist', {
type: 'splitbutton',
tooltip: 'Numbered list',
menu: olMenuItems,
onshow: updateSelection,
onselect: function(e) {
applyListFormat('OL', e.control.settings.data);
},
onclick: function() {
applyListFormat('OL', false);
}
});
editor.addButton('bullist', {
type: 'splitbutton',
tooltip: 'Bullet list',
menu: ulMenuItems,
onshow: updateSelection,
onselect: function(e) {
applyListFormat('UL', e.control.settings.data);
},
onclick: function() {
applyListFormat('UL', false);
}
});
});
\ No newline at end of file
/**
* plugin.js
*
* Released under LGPL License.
* Copyright (c) 1999-2015 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
/*global tinymce:true */
tinymce.PluginManager.add('anchor', function(editor) {
function showDialog() {
var selectedNode = editor.selection.getNode(), name = '';
var isAnchor = selectedNode.tagName == 'A' && editor.dom.getAttrib(selectedNode, 'href') === '';
if (isAnchor) {
name = selectedNode.name || selectedNode.id || '';
}
editor.windowManager.open({
title: 'Anchor',
body: {type: 'textbox', name: 'name', size: 40, label: 'Name', value: name},
onsubmit: function(e) {
var id = e.data.name;
if (isAnchor) {
selectedNode.id = id;
} else {
editor.selection.collapse(true);
editor.execCommand('mceInsertContent', false, editor.dom.createHTML('a', {
id: id
}));
}
}
});
}
editor.addCommand('mceAnchor', showDialog);
editor.addButton('anchor', {
icon: 'anchor',
tooltip: 'Anchor',
onclick: showDialog,
stateSelector: 'a:not([href])'
});
editor.addMenuItem('anchor', {
icon: 'anchor',
text: 'Anchor',
context: 'insert',
onclick: showDialog
});
});
\ No newline at end of file
/**
* plugin.js
*
* Released under LGPL License.
* Copyright (c) 1999-2015 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
/*global tinymce:true */
tinymce.PluginManager.add('autolink', function(editor) {
var AutoUrlDetectState;
editor.on("keydown", function(e) {
if (e.keyCode == 13) {
return handleEnter(editor);
}
});
// Internet Explorer has built-in automatic linking for most cases
if (tinymce.Env.ie) {
editor.on("focus", function() {
if (!AutoUrlDetectState) {
AutoUrlDetectState = true;
try {
editor.execCommand('AutoUrlDetect', false, true);
} catch (ex) {
// Ignore
}
}
});
return;
}
editor.on("keypress", function(e) {
if (e.keyCode == 41) {
return handleEclipse(editor);
}
});
editor.on("keyup", function(e) {
if (e.keyCode == 32) {
return handleSpacebar(editor);
}
});
function handleEclipse(editor) {
parseCurrentLine(editor, -1, '(', true);
}
function handleSpacebar(editor) {
parseCurrentLine(editor, 0, '', true);
}
function handleEnter(editor) {
parseCurrentLine(editor, -1, '', false);
}
function parseCurrentLine(editor, end_offset, delimiter) {
var rng, end, start, endContainer, bookmark, text, matches, prev, len, rngText;
function scopeIndex(container, index) {
if (index < 0) {
index = 0;
}
if (container.nodeType == 3) {
var len = container.data.length;
if (index > len) {
index = len;
}
}
return index;
}
function setStart(container, offset) {
</