/*! * syntaxhl plugin for TinyMCE 4 based on the codesample plugin * * Released under LGPL License. * Copyright (c) 1999-2015 Ephox Corp. All rights reserved * Copyright (c) 2017 Christoph M. Becker */ /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */ /*globals $code */ (function(exports, undefined) { "use strict"; var modules = {}; function require(ids, callback) { var module, defs = []; for (var i = 0; i < ids.length; ++i) { module = modules[ids[i]] || resolve(ids[i]); if (!module) { throw 'module definition dependecy not found: ' + ids[i]; } defs.push(module); } callback.apply(null, defs); } function define(id, dependencies, definition) { if (typeof id !== 'string') { throw 'invalid module definition, module id must be defined and be a string'; } if (dependencies === undefined) { throw 'invalid module definition, dependencies must be specified'; } if (definition === undefined) { throw 'invalid module definition, definition function must be specified'; } require(dependencies, function() { modules[id] = definition.apply(null, arguments); }); } function defined(id) { return !!modules[id]; } function resolve(id) { var target = exports; var fragments = id.split(/[.\/]/); for (var fi = 0; fi < fragments.length; ++fi) { if (!target[fragments[fi]]) { return; } target = target[fragments[fi]]; } return target; } function expose(ids) { var i, target, id, fragments, privateModules; for (i = 0; i < ids.length; i++) { target = exports; id = ids[i]; fragments = id.split(/[.\/]/); for (var fi = 0; fi < fragments.length - 1; ++fi) { if (target[fragments[fi]] === undefined) { target[fragments[fi]] = {}; } target = target[fragments[fi]]; } target[fragments[fragments.length - 1]] = modules[id]; } // Expose private modules for unit tests if (exports.AMDLC_TESTS) { privateModules = exports.privateModules || {}; for (id in modules) { privateModules[id] = modules[id]; } for (i = 0; i < ids.length; i++) { delete privateModules[ids[i]]; } exports.privateModules = privateModules; } } /** * Utils.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 */ /** * Various utility functions. * * @class tinymce.syntaxhl.Utils * @private */ define("tinymce/syntaxhl/Utils", [ ], function() { function isCodeSample(elm) { return elm && elm.nodeName == 'PRE' && elm.className.indexOf('brush:') !== -1; } function trimArg(predicateFn) { return function(arg1, arg2) { return predicateFn(arg2); }; } return { isCodeSample: isCodeSample, trimArg: trimArg }; }); /** * Dialog.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 */ /** * Contains all dialog logic. * * @class tinymce.syntaxhl.Dialog * @private */ define("tinymce/syntaxhl/Dialog", [ "tinymce/dom/DOMUtils", "tinymce/syntaxhl/Utils" ], function(DOMUtils, Utils) { var DOM = DOMUtils.DOM; function getLanguages(editor) { var defaultLanguages = [ {text: 'AppleScript', value: 'applescript'}, {text: 'ActionScript3', value: 'as3'}, {text: 'Bash(Shell)', value: 'bash'}, {text: 'Cold Fusion', value: 'coldfusion'}, {text: 'C#', value: 'csharp'}, {text: 'C++', value: 'cpp'}, {text: 'CSS', value: 'css'}, {text: 'Delphi', value: 'delphi'}, {text: 'Diff', value: 'diff'}, {text: 'Erlang', value: 'erlang'}, {text: 'Groovy', value: 'groovy'}, {text: 'Java', value: 'java'}, {text: 'JavaFX', value: 'javafx'}, {text: 'Jscript', value: 'jscript'}, {text: 'Perl', value: 'perl'}, {text: 'PHP', value: 'php'}, {text: 'Plain(Text)', value: 'plain'}, {text: 'PowerShell', value: 'powershell'}, {text: 'Python', value: 'python'}, {text: 'Ruby', value: 'ruby'}, {text: 'SASS', value: 'sass'}, {text: 'Scala', value: 'scala'}, {text: 'SQL', value: 'sql'}, {text: 'VB', value: 'vb'}, {text: 'XML/XHTML', value: 'xml'}, ]; var customLanguages = editor.settings.syntaxhl_languages; return customLanguages ? customLanguages : defaultLanguages; } function insertCodeSample(editor, language, code) { editor.undoManager.transact(function() { var node = getSelectedCodeSample(editor); code = DOM.encode(code); if (node) { var className = editor.dom.getAttrib(node, 'class').replace(/brush:\s*\w+;/, 'brush: ' + language + ';') editor.dom.setAttrib(node, 'class', className); node.innerHTML = code; editor.selection.select(node); } else { editor.insertContent('
' + code + '
'); editor.selection.select(editor.$('#__new').removeAttr('id')[0]); } }); } function getSelectedCodeSample(editor) { var node = editor.selection.getNode(); if (Utils.isCodeSample(node)) { return node; } return null; } function getCurrentCode(editor) { var node = getSelectedCodeSample(editor); if (node) { return node.textContent; } return ''; } function getCurrentLanguage(editor) { var matches, node = getSelectedCodeSample(editor); if (node) { matches = node.className.match(/brush: (\w+)/); return matches ? matches[1] : ''; } return ''; } return { open: function(editor) { editor.windowManager.open({ title: "Insert/Edit code sample", minWidth: Math.min(DOM.getViewPort().w, editor.getParam('syntaxhl_dialog_width', 800)), minHeight: Math.min(DOM.getViewPort().h, editor.getParam('syntaxhl_dialog_height', 650)), layout: 'flex', direction: 'column', align: 'stretch', body: [ { type: 'listbox', name: 'language', label: 'Language', maxWidth: 200, value: getCurrentLanguage(editor), values: getLanguages(editor) }, { type: 'textbox', name: 'code', multiline: true, spellcheck: false, ariaLabel: 'Code view', flex: 1, style: 'direction: ltr; text-align: left', classes: 'monospace', value: getCurrentCode(editor), autofocus: true } ], onSubmit: function(e) { insertCodeSample(editor, e.data.language, e.data.code); } }); } }; }); /** * 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 */ /** * Main plugin logic. * * @class tinymce.syntaxhl.Plugin * @private */ define("tinymce/syntaxhl/Plugin", [ "tinymce/Env", "tinymce/PluginManager", "tinymce/syntaxhl/Dialog", "tinymce/syntaxhl/Utils" ], function(Env, PluginManager, Dialog, Utils) { var addedInlineCss, trimArg = Utils.trimArg; PluginManager.add('syntaxhl', function(editor, pluginUrl) { var $ = editor.$, addedCss; if (!Env.ceFalse) { return; } editor.on('PreProcess', function(e) { $('pre[contenteditable=false]', e.node). filter(trimArg(Utils.isCodeSample)). each(function(idx, elm) { var $elm = $(elm), code = elm.textContent; $elm.attr('class', $.trim($elm.attr('class'))); $elm.removeAttr('contentEditable'); //$elm.empty().append($('').each(function() { // // Needs to be textContent since innerText produces BR:s // this.textContent = code; //})); }); }); editor.on('SetContent', function() { var unprocessedCodeSamples = $('pre').filter(trimArg(Utils.isCodeSample)).filter(function(idx, elm) { return elm.contentEditable !== "false"; }); if (unprocessedCodeSamples.length) { editor.undoManager.transact(function() { unprocessedCodeSamples.each(function(idx, elm) { $(elm).find('br').each(function(idx, elm) { elm.parentNode.replaceChild(editor.getDoc().createTextNode('\n'), elm); }); elm.contentEditable = false; elm.innerHTML = editor.dom.encode(elm.textContent); elm.className = $.trim(elm.className); }); }); } }); editor.addCommand('syntaxhl', function() { var node = editor.selection.getNode(); if (editor.selection.isCollapsed() || Utils.isCodeSample(node)) { Dialog.open(editor); } else { editor.formatter.toggle('code'); } }); editor.addButton('syntaxhl', { cmd: 'syntaxhl', image: pluginUrl + "/highlight.gif", title: 'Insert/Edit code sample' }); }); }); expose(["tinymce/syntaxhl/Utils","tinymce/syntaxhl/Dialog","tinymce/syntaxhl/Plugin"]); })(window);