From 9e2f9f49ec9803adfa0249161c8d2188ee0d40ca Mon Sep 17 00:00:00 2001 From: Benjamin Southall Date: Sun, 3 Sep 2017 23:15:56 +0900 Subject: Add sceditor WYSIWIG post editor support --- js/sceditor/development/plugins/xhtml.js | 1319 ++++++++++++++++++++++++++++++ 1 file changed, 1319 insertions(+) create mode 100644 js/sceditor/development/plugins/xhtml.js (limited to 'js/sceditor/development/plugins/xhtml.js') diff --git a/js/sceditor/development/plugins/xhtml.js b/js/sceditor/development/plugins/xhtml.js new file mode 100644 index 00000000..7ba03e5f --- /dev/null +++ b/js/sceditor/development/plugins/xhtml.js @@ -0,0 +1,1319 @@ +/** + * SCEditor XHTML Plugin + * http://www.sceditor.com/ + * + * Copyright (C) 2011-2013, Sam Clarke (samclarke.com) + * + * SCEditor is licensed under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + * + * @author Sam Clarke + * @requires jQuery + */ +/*global prompt: true*/ +(function ($) { + 'use strict'; + + var SCEditor = $.sceditor; + var sceditorPlugins = SCEditor.plugins; + var dom = SCEditor.dom; + + var defaultCommandsOverrides = { + bold: { + txtExec: [ + '', + '' + ] + }, + italic: { + txtExec: [ + '', + '' + ] + }, + underline: { + txtExec: [ + '', + '' + ] + }, + strike: { + txtExec: [ + '', + '' + ] + }, + subscript: { + txtExec: [ + '', + '' + ] + }, + superscript: { + txtExec: [ + '', + '' + ] + }, + left: { + txtExec: [ + '
', + '
' + ] + }, + center: { + txtExec: [ + '
', + '
' + ] + }, + right: { + txtExec: [ + '
', + '
' + ] + }, + justify: { + txtExec: [ + '
', + '
' + ] + }, + font: { + txtExec: function (caller) { + var editor = this; + + SCEditor.command.get('font')._dropDown( + editor, + caller, + function (fontName) { + editor.insertText('', ''); + } + ); + } + }, + size: { + txtExec: function (caller) { + var editor = this; + + SCEditor.command.get('size')._dropDown( + editor, + caller, + function (fontSize) { + editor.insertText('', ''); + } + ); + } + }, + color: { + txtExec: function (caller) { + var editor = this; + + SCEditor.command.get('color')._dropDown( + editor, + caller, + function (color) { + editor.insertText('', ''); + } + ); + } + }, + bulletlist: { + txtExec: [ + '' + ] + }, + orderedlist: { + txtExec: [ + '
  1. ', + '
' + ] + }, + table: { + txtExec: [ + '
', + '
' + ] + }, + horizontalrule: { + txtExec: [ + '
' + ] + }, + code: { + txtExec: [ + '', + '' + ] + }, + image: { + txtExec: function (caller, selected) { + var url = prompt(this._('Enter the image URL:'), selected); + + if (url) { + this.insertText(''); + } + } + }, + email: { + txtExec: function (caller, sel) { + var email, text, + display = sel && sel.indexOf('@') > -1 ? null : sel; + + email = prompt( + this._('Enter the e-mail address:'), + (display ? '' : sel) + ); + + text = prompt( + this._('Enter the displayed text:'), + display || email + ) || email; + + if (email) { + this.insertText( + '' + text + '' + ); + } + } + }, + link: { + txtExec: function (caller, sel) { + var display = sel && sel.indexOf('http://') > -1 ? null : sel, + url = prompt(this._('Enter URL:'), + (display ? 'http://' : sel)), + text = prompt(this._('Enter the displayed text:'), + display || url) || url; + + if (url) { + this.insertText( + '' + text + '' + ); + } + } + }, + quote: { + txtExec: [ + '
', + '
' + ] + }, + youtube: { + txtExec: function (caller) { + var editor = this; + + SCEditor.command.get('youtube')._dropDown( + editor, + caller, + function (id) { + editor.insertText( + '' + ); + } + ); + } + }, + rtl: { + txtExec: [ + '
', + '
' + ] + }, + ltr: { + txtExec: [ + '
', + '
' + ] + } + }; + + /** + * XHTMLSerializer part of the XHTML plugin. + * + * @class XHTMLSerializer + * @name jQuery.sceditor.XHTMLSerializer + * @since v1.4.1 + */ + SCEditor.XHTMLSerializer = function () { + var base = this; + + var opts = { + indentStr: '\t' + }; + + /** + * Array containing the output, used as it's faster + * than string concatenation in slow browsers. + * @type {Array} + * @private + */ + var outputStringBuilder = []; + + /** + * Current indention level + * @type {Number} + * @private + */ + var currentIndent = 0; + + /** + * @private + */ + var escapeEntites, + trim, + serializeNode, + handleDoc, + handleElement, + handleCdata, + handleComment, + handleText, + output, + canIndent; +// TODO: use escape.entities + /** + * Escapes XHTML entities + * + * @param {String} str + * @return {String} + * @private + */ + escapeEntites = function (str) { + var entites = { + '&': '&', + '<': '<', + '>': '>', + '"': '"' + }; + + return !str ? '' : str.replace(/[&<>"]/g, function (entity) { + return entites[entity] || entity; + }); + }; + + /** + * @param {string} str + * @return {string} + * @private + */ + trim = function (str) { + return str + // New lines will be shown as spaces so just convert to spaces. + .replace(/[\r\n]/, ' ') + .replace(/[^\S|\u00A0]+/g, ' '); + }; + + /** + * Serializes a node to XHTML + * + * @param {Node} node Node to serialize + * @param {Boolean} onlyChildren If to only serialize the nodes + * children and not the node + * itself + * @return {String} The serialized node + * @name serialize + * @memberOf jQuery.sceditor.XHTMLSerializer.prototype + * @since v1.4.1 + */ + base.serialize = function (node, onlyChildren) { + outputStringBuilder = []; + + if (onlyChildren) { + node = node.firstChild; + + while (node) { + serializeNode(node); + node = node.nextSibling; + } + } else { + serializeNode(node); + } + + return outputStringBuilder.join(''); + }; + + /** + * Serializes a node to the outputStringBuilder + * + * @param {Node} node + * @return {Void} + * @private + */ + serializeNode = function (node, parentIsPre) { + switch (node.nodeType) { + case 1: // element + var tagName = node.nodeName.toLowerCase(); + + // IE comment + if (tagName === '!') { + handleComment(node); + } else { + handleElement(node, parentIsPre); + } + break; + + case 3: // text + handleText(node, parentIsPre); + break; + + case 4: // cdata section + handleCdata(node); + break; + + case 8: // comment + handleComment(node); + break; + + case 9: // document + case 11: // document fragment + handleDoc(node); + break; + + // Ignored types + case 2: // attribute + case 5: // entity ref + case 6: // entity + case 7: // processing instruction + case 10: // document type + case 12: // notation + break; + } + }; + + /** + * Handles doc node + * @param {Node} node + * @return {void} + * @private + */ + handleDoc = function (node) { + var child = node.firstChild; + + while (child) { + serializeNode(child); + child = child.nextSibling; + } + }; + + /** + * Handles element nodes + * @param {Node} node + * @return {void} + * @private + */ + handleElement = function (node, parentIsPre) { + var child, attr, attrValue, + tagName = node.nodeName.toLowerCase(), + isIframe = tagName === 'iframe', + attrIdx = node.attributes.length, + firstChild = node.firstChild, + // pre || pre-wrap with any vendor prefix + isPre = parentIsPre || + /pre(?:\-wrap)?$/i.test($(node).css('whiteSpace')), + selfClosing = !node.firstChild && !dom.canHaveChildren(node) && + !isIframe; + + if ($(node).hasClass('sceditor-ignore')) { + return; + } + + output('<' + tagName, !parentIsPre && canIndent(node)); + while (attrIdx--) { + attr = node.attributes[attrIdx]; + + // IE < 8 returns all possible attributes not just specified + // ones. IE < 8 also doesn't say value on input is specified + // so just assume it is. + if (!SCEditor.ie || attr.specified || + (tagName === 'input' && attr.name === 'value')) { + // IE < 8 doesn't return the CSS for the style attribute + if (SCEditor.ie < 8 && /style/i.test(attr.name)) { + attrValue = node.style.cssText; + } else { + attrValue = attr.value; + } + + output(' ' + attr.name.toLowerCase() + '="' + + escapeEntites(attrValue) + '"', false); + } + } + output(selfClosing ? ' />' : '>', false); + + if (!isIframe) { + child = firstChild; + } + + while (child) { + currentIndent++; + + serializeNode(child, isPre); + child = child.nextSibling; + + currentIndent--; + } + + if (!selfClosing) { + output( + '', + !isPre && !isIframe && canIndent(node) && + firstChild && canIndent(firstChild) + ); + } + }; + + /** + * Handles CDATA nodes + * @param {Node} node + * @return {void} + * @private + */ + handleCdata = function (node) { + output(''); + }; + + /** + * Handles comment nodes + * @param {Node} node + * @return {void} + * @private + */ + handleComment = function (node) { + output(''); + }; + + /** + * Handles text nodes + * @param {Node} node + * @return {void} + * @private + */ + handleText = function (node, parentIsPre) { + var text = node.nodeValue; + + if (!parentIsPre) { + text = trim(text); + } + + if (text) { + output(escapeEntites(text), !parentIsPre && canIndent(node)); + } + }; + + /** + * Adds a string to the outputStringBuilder. + * + * The string will be indented unless indent is set to boolean false. + * @param {String} str + * @param {Boolean} indent + * @return {void} + * @private + */ + output = function (str, indent) { + var i = currentIndent; + + if (indent !== false) { + // Don't add a new line if it's the first element + if (outputStringBuilder.length) { + outputStringBuilder.push('\n'); + } + + while (i--) { + outputStringBuilder.push(opts.indentStr); + } + } + + outputStringBuilder.push(str); + }; + + /** + * Checks if should indent the node or not + * @param {Node} node + * @return {boolean} + * @private + */ + canIndent = function (node) { + var prev = node.previousSibling; + + if (node.nodeType !== 1 && prev) { + return !dom.isInline(prev); + } + + // first child of a block element + if (!prev && !dom.isInline(node.parentNode)) { + return true; + } + + return !dom.isInline(node); + }; + }; + + /** + * SCEditor XHTML plugin + * @class xhtml + * @name jQuery.sceditor.plugins.xhtml + * @since v1.4.1 + */ + sceditorPlugins.xhtml = function () { + var base = this; + + /** + * Tag converstions cache + * @type {Object} + * @private + */ + var tagConvertersCache = {}; + + /** + * Attributes filter cache + * @type {Object} + * @private + */ + var attrsCache = {}; + + /** + * Private methods + * @private + */ + var convertTags, + convertNode, + isEmpty, + removeTags, + mergeAttribsFilters, + removeAttribs, + wrapInlines; + + + /** + * Init + * @return {void} + */ + base.init = function () { + if (!$.isEmptyObject(sceditorPlugins.xhtml.converters || {})) { + $.each( + sceditorPlugins.xhtml.converters, + function (idx, converter) { + $.each(converter.tags, function (tagname) { + if (!tagConvertersCache[tagname]) { + tagConvertersCache[tagname] = []; + } + + tagConvertersCache[tagname].push(converter); + }); + } + ); + } + + this.commands = $.extend(true, + {}, defaultCommandsOverrides, this.commands); + }; + + /** + * Converts the WYSIWYG content to XHTML + * @param {String} html + * @param {Node} domBody + * @return {String} + * @memberOf jQuery.sceditor.plugins.xhtml.prototype + */ + base.signalToSource = function (html, domBody) { + domBody = domBody.jquery ? domBody[0] : domBody; + + convertTags(domBody); + removeTags(domBody); + removeAttribs(domBody); + wrapInlines(domBody); + + return (new SCEditor.XHTMLSerializer()).serialize(domBody, true); + }; + + /** + * Converts the XHTML to WYSIWYG content. + * + * This doesn't currently do anything as XHTML + * is valid WYSIWYG content. + * @param {String} text + * @return {String} + * @memberOf jQuery.sceditor.plugins.xhtml.prototype + */ + base.signalToWysiwyg = function (text) { + return text; + }; + + /** + * Deprecated, use dom.convertElement() instead. + * @deprecated + */ + base.convertTagTo = dom.convertElement; + + /** + * Runs all converters for the specified tagName + * against the DOM node. + * @param {String} tagName + * @param {jQuery} $node + * @return {Node} node + * @private + */ + convertNode = function (tagName, $node, node) { + if (!tagConvertersCache[tagName]) { + return; + } + + $.each(tagConvertersCache[tagName], function (idx, converter) { + if (converter.tags[tagName]) { + $.each(converter.tags[tagName], function (attr, values) { + if (!node.getAttributeNode) { + return; + } + + attr = node.getAttributeNode(attr); + + // IE < 8 always returns an attribute regardless of if + // it has been specified so must check it. + if (!attr || (SCEditor.ie < 8 && !attr.specified)) { + return; + } + + if (values && $.inArray(attr.value, values) < 0) { + return; + } + + converter.conv.call(base, node, $node); + }); + } else if (converter.conv) { + converter.conv.call(base, node, $node); + } + }); + }; + + /** + * Converts any tags/attributes to their XHTML equivalents + * @param {Node} node + * @return {Void} + * @private + */ + convertTags = function (node) { + dom.traverse(node, function (node) { + var $node = $(node), + tagName = node.nodeName.toLowerCase(); + + convertNode('*', $node, node); + convertNode(tagName, $node, node); + }, true); + }; + + /** + * Tests if a node is empty and can be removed. + * + * @param {Node} node + * @return {Boolean} + * @private + */ + isEmpty = function (node, excludeBr) { + var childNodes = node.childNodes, + tagName = node.nodeName.toLowerCase(), + nodeValue = node.nodeValue, + childrenLength = childNodes.length; + + if (excludeBr && tagName === 'br') { + return true; + } + + if ($(node).hasClass('sceditor-ignore')) { + return true; + } + + if (!dom.canHaveChildren(node)) { + return false; + } + + // \S|\u00A0 = any non space char + if (nodeValue && /\S|\u00A0/.test(nodeValue)) { + return false; + } + + while (childrenLength--) { + if (!isEmpty(childNodes[childrenLength], + excludeBr && !node.previousSibling && !node.nextSibling)) { + return false; + } + } + + return true; + }; + + /** + * Removes any tags that are not white listed or if no + * tags are white listed it will remove any tags that + * are black listed. + * + * @param {Node} rootNode + * @return {Void} + * @private + */ + removeTags = function (rootNode) { + dom.traverse(rootNode, function (node) { + var remove, + tagName = node.nodeName.toLowerCase(), + parentNode = node.parentNode, + nodeType = node.nodeType, + isBlock = !dom.isInline(node), + previousSibling = node.previousSibling, + nextSibling = node.nextSibling, + isTopLevel = parentNode === rootNode, + noSiblings = !previousSibling && !nextSibling, + empty = tagName !== 'iframe' && isEmpty(node, + isTopLevel && noSiblings && tagName !== 'br'), + document = node.ownerDocument, + allowedtags = sceditorPlugins.xhtml.allowedTags, + disallowedTags = sceditorPlugins.xhtml.disallowedTags; + + // 3 = text node + if (nodeType === 3) { + return; + } + + if (nodeType === 4) { + tagName = '!cdata'; + } else if (tagName === '!' || nodeType === 8) { + tagName = '!comment'; + } + + if (empty) { + remove = true; + // 3 is text node which do not get filtered + } else if (allowedtags && allowedtags.length) { + remove = ($.inArray(tagName, allowedtags) < 0); + } else if (disallowedTags && disallowedTags.length) { + remove = ($.inArray(tagName, disallowedTags) > -1); + } + + if (remove) { + if (!empty) { + if (isBlock && previousSibling && + dom.isInline(previousSibling)) { + parentNode.insertBefore( + document.createTextNode(' '), node); + } + + // Insert all the childen after node + while (node.firstChild) { + parentNode.insertBefore(node.firstChild, + nextSibling); + } + + if (isBlock && nextSibling && + dom.isInline(nextSibling)) { + parentNode.insertBefore( + document.createTextNode(' '), nextSibling); + } + } + + parentNode.removeChild(node); + } + }, true); + }; + + /** + * Merges two sets of attribute filters into one + * + * @param {Object} filtersA + * @param {Object} filtersB + * @return {Object} + * @private + */ + mergeAttribsFilters = function (filtersA, filtersB) { + var ret = {}; + + if (filtersA) { + $.extend(ret, filtersA); + } + + if (!filtersB) { + return ret; + } + + $.each(filtersB, function (attrName, values) { + if ($.isArray(values)) { + ret[attrName] = $.merge(ret[attrName] || [], values); + } else if (!ret[attrName]) { + ret[attrName] = null; + } + }); + + return ret; + }; + + /** + * Wraps adjacent inline child nodes of root + * in paragraphs. + * + * @param {Node} root + * @private + */ + wrapInlines = function (root) { + var adjacentInlines = []; + var wrapAdjacents = function () { + if (adjacentInlines.length) { + $('

', root.ownerDocument) + .insertBefore(adjacentInlines[0]) + .append(adjacentInlines); + + adjacentInlines = []; + } + }; + + // Strip empty text nodes so they don't get wrapped. + dom.removeWhiteSpace(root); + + var node = root.firstChild; + while (node) { + if (dom.isInline(node) && !$(node).is('.sceditor-ignore')) { + adjacentInlines.push(node); + } else { + wrapAdjacents(); + } + + node = node.nextSibling; + } + + wrapAdjacents(); + }; + + /** + * Removes any attributes that are not white listed or + * if no attributes are white listed it will remove + * any attributes that are black listed. + * @param {Node} node + * @return {Void} + * @private + */ + removeAttribs = function (node) { + var tagName, attr, attrName, attrsLength, validValues, remove, + allowedAttribs = sceditorPlugins.xhtml.allowedAttribs, + isAllowed = allowedAttribs && + !$.isEmptyObject(allowedAttribs), + disallowedAttribs = sceditorPlugins.xhtml.disallowedAttribs, + isDisallowed = disallowedAttribs && + !$.isEmptyObject(disallowedAttribs); + + attrsCache = {}; + + dom.traverse(node, function (node) { + if (!node.attributes) { + return; + } + + tagName = node.nodeName.toLowerCase(); + attrsLength = node.attributes.length; + + if (attrsLength) { + if (!attrsCache[tagName]) { + if (isAllowed) { + attrsCache[tagName] = mergeAttribsFilters( + allowedAttribs['*'], + allowedAttribs[tagName] + ); + } else { + attrsCache[tagName] = mergeAttribsFilters( + disallowedAttribs['*'], + disallowedAttribs[tagName] + ); + } + } + + while (attrsLength--) { + attr = node.attributes[attrsLength]; + attrName = attr.name; + validValues = attrsCache[tagName][attrName]; + remove = false; + + if (isAllowed) { + remove = validValues !== null && + (!$.isArray(validValues) || + $.inArray(attr.value, validValues) < 0); + } else if (isDisallowed) { + remove = validValues === null || + ($.isArray(validValues) && + $.inArray(attr.value, validValues) > -1); + } + + if (remove) { + node.removeAttribute(attrName); + } + } + } + }); + }; + }; + + /** + * Tag conveters, a converter is applied to all + * tags that match the criteria. + * @type {Array} + * @name jQuery.sceditor.plugins.xhtml.converters + * @since v1.4.1 + */ + sceditorPlugins.xhtml.converters = [ + { + tags: { + '*': { + width: null + } + }, + conv: function (node, $node) { + $node.css('width', $node.attr('width')).removeAttr('width'); + } + }, + { + tags: { + '*': { + height: null + } + }, + conv: function (node, $node) { + $node.css('height', $node.attr('height')).removeAttr('height'); + } + }, + { + tags: { + 'li': { + value: null + } + }, + conv: function (node, $node) { + if (SCEditor.ie < 8) { + node.removeAttribute('value'); + } else { + $node.removeAttr('value'); + } + } + }, + { + tags: { + '*': { + text: null + } + }, + conv: function (node, $node) { + $node.css('color', $node.attr('text')).removeAttr('text'); + } + }, + { + tags: { + '*': { + color: null + } + }, + conv: function (node, $node) { + $node.css('color', $node.attr('color')).removeAttr('color'); + } + }, + { + tags: { + '*': { + face: null + } + }, + conv: function (node, $node) { + $node.css('fontFamily', $node.attr('face')).removeAttr('face'); + } + }, + { + tags: { + '*': { + align: null + } + }, + conv: function (node, $node) { + $node.css('textAlign', $node.attr('align')).removeAttr('align'); + } + }, + { + tags: { + '*': { + border: null + } + }, + conv: function (node, $node) { + $node + .css('borderWidth',$node.attr('border')) + .removeAttr('border'); + } + }, + { + tags: { + applet: { + name: null + }, + img: { + name: null + }, + layer: { + name: null + }, + map: { + name: null + }, + object: { + name: null + }, + param: { + name: null + } + }, + conv: function (node, $node) { + if (!$node.attr('id')) { + $node.attr('id', $node.attr('name')); + } + + $node.removeAttr('name'); + } + }, + { + tags: { + '*': { + vspace: null + } + }, + conv: function (node, $node) { + $node + .css('marginTop', $node.attr('vspace') - 0) + .css('marginBottom', $node.attr('vspace') - 0) + .removeAttr('vspace'); + } + }, + { + tags: { + '*': { + hspace: null + } + }, + conv: function (node, $node) { + $node + .css('marginLeft', $node.attr('hspace') - 0) + .css('marginRight', $node.attr('hspace') - 0) + .removeAttr('hspace'); + } + }, + { + tags: { + 'hr': { + noshade: null + } + }, + conv: function (node, $node) { + $node.css('borderStyle', 'solid').removeAttr('noshade'); + } + }, + { + tags: { + '*': { + nowrap: null + } + }, + conv: function (node, $node) { + $node.css('white-space', 'nowrap').removeAttr('nowrap'); + } + }, + { + tags: { + big: null + }, + conv: function (node) { + $(this.convertTagTo(node, 'span')).css('fontSize', 'larger'); + } + }, + { + tags: { + small: null + }, + conv: function (node) { + $(this.convertTagTo(node, 'span')).css('fontSize', 'smaller'); + } + }, + { + tags: { + b: null + }, + conv: function (node) { + $(this.convertTagTo(node, 'strong')); + } + }, + { + tags: { + u: null + }, + conv: function (node) { + $(this.convertTagTo(node, 'span')) + .css('textDecoration', 'underline'); + } + }, + { + tags: { + i: null + }, + conv: function (node) { + $(this.convertTagTo(node, 'em')); + } + }, + { + tags: { + s: null, + strike: null + }, + conv: function (node) { + $(this.convertTagTo(node, 'span')) + .css('textDecoration', 'line-through'); + } + }, + { + tags: { + dir: null + }, + conv: function (node) { + this.convertTagTo(node, 'ul'); + } + }, + { + tags: { + center: null + }, + conv: function (node) { + $(this.convertTagTo(node, 'div')) + .css('textAlign', 'center'); + } + }, + { + tags: { + font: { + size: null + } + }, + conv: function (node, $node) { + var size = $node.css('fontSize'), + fontSize = size; + + // IE < 8 sets a font tag with no size to +0 so + // should just skip it. + if (fontSize !== '+0') { + // IE 8 and below incorrectly returns the value of the size + // attribute instead of the px value so must convert it + if (SCEditor.ie < 9) { + fontSize = 10; + + if (size > 1) { + fontSize = 13; + } + if (size > 2) { + fontSize = 16; + } + if (size > 3) { + fontSize = 18; + } + if (size > 4) { + fontSize = 24; + } + if (size > 5) { + fontSize = 32; + } + if (size > 6) { + fontSize = 48; + } + } + + $node.css('fontSize', fontSize); + } + + $node.removeAttr('size'); + } + }, + { + tags: { + font: null + }, + conv: function (node) { + // All it's attributes will be converted + // by the attribute converters + this.convertTagTo(node, 'span'); + } + }, + { + tags: { + '*': { + type: ['_moz'] + } + }, + conv: function (node, $node) { + $node.removeAttr('type'); + } + }, + { + tags: { + '*': { + '_moz_dirty': null + } + }, + conv: function (node, $node) { + $node.removeAttr('_moz_dirty'); + } + }, + { + tags: { + '*': { + '_moz_editor_bogus_node': null + } + }, + conv: function (node, $node) { + $node.remove(); + } + } + ]; + + /** + * Allowed attributes map. + * + * To allow an attribute for all tags use * as the tag name. + * + * Leave empty or null to allow all attributes. (the disallow + * list will be used to filter them instead) + * @type {Object} + * @name jQuery.sceditor.plugins.xhtml.allowedAttribs + * @since v1.4.1 + */ + sceditorPlugins.xhtml.allowedAttribs = {}; + + /** + * Attributes that are not allowed. + * + * Only used if allowed attributes is null or empty. + * @type {Object} + * @name jQuery.sceditor.plugins.xhtml.disallowedAttribs + * @since v1.4.1 + */ + sceditorPlugins.xhtml.disallowedAttribs = {}; + + /** + * Array containing all the allowed tags. + * + * If null or empty all tags will be allowed. + * @type {Array} + * @name jQuery.sceditor.plugins.xhtml.allowedTags + * @since v1.4.1 + */ + sceditorPlugins.xhtml.allowedTags = []; + + /** + * Array containing all the disallowed tags. + * + * Only used if allowed tags is null or empty. + * @type {Array} + * @name jQuery.sceditor.plugins.xhtml.disallowedTags + * @since v1.4.1 + */ + sceditorPlugins.xhtml.disallowedTags = []; +}(jQuery)); -- cgit v1.3