| | |
| | | var tinymce = {
|
| | | majorVersion : '3',
|
| | |
|
| | | minorVersion : '4.1',
|
| | | minorVersion : '4.2',
|
| | |
|
| | | releaseDate : '2011-03-24',
|
| | | releaseDate : '2011-04-07',
|
| | |
|
| | | _init : function() {
|
| | | var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
|
| | |
| | | name = styleList[i];
|
| | | value = styles[name];
|
| | |
|
| | | if (value !== undef)
|
| | | if (value !== undef && value.length > 0)
|
| | | css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
|
| | | }
|
| | | }
|
| | |
| | | for (name in styles) {
|
| | | value = styles[name];
|
| | |
|
| | | if (value !== undef)
|
| | | if (value !== undef && value.length > 0)
|
| | | css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
|
| | | }
|
| | | }
|
| | |
| | | },
|
| | |
|
| | | isEmpty : function(elements) {
|
| | | var self = this, node = self.firstChild, i;
|
| | | var self = this, node = self.firstChild, i, name;
|
| | |
|
| | | if (node) {
|
| | | do {
|
| | | if (node.type === 1) {
|
| | | // Ignore bogus elements
|
| | | if (node.attributes.map['data-mce-bogus'])
|
| | | continue;
|
| | |
|
| | |
| | | if (elements[node.name])
|
| | | return false;
|
| | |
|
| | | // Keep elements with data attributes
|
| | | // Keep elements with data attributes or name attribute like <a name="1"></a>
|
| | | i = node.attributes.length;
|
| | | while (i--) {
|
| | | if (node.attributes[i].name.indexOf('data-') === 0)
|
| | | name = node.attributes[i].name;
|
| | | if (name === "name" || name.indexOf('data-') === 0)
|
| | | return false;
|
| | | }
|
| | | }
|
| | |
| | | var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
|
| | |
|
| | | settings = settings || {};
|
| | | settings.validate = "validate" in settings ? settings.validate : true;
|
| | | settings.root_name = settings.root_name || 'body';
|
| | | self.schema = schema = schema || new tinymce.html.Schema();
|
| | |
|
| | |
| | | };
|
| | |
|
| | | self.parse = function(html, args) {
|
| | | var parser, rootNode, node, nodes, i, l, fi, fl, list, name,
|
| | | var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate,
|
| | | blockElements, startWhiteSpaceRegExp, invalidChildren = [],
|
| | | endWhiteSpaceRegExp, allWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements;
|
| | |
|
| | | args = args || {};
|
| | | matchedNodes = {};
|
| | | matchedAttributes = {};
|
| | | blockElements = tinymce.extend(tinymce.makeMap('script,style,head,title,meta,param'), schema.getBlockElements());
|
| | | blockElements = tinymce.extend(tinymce.makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
|
| | | nonEmptyElements = schema.getNonEmptyElements();
|
| | | children = schema.children;
|
| | | validate = settings.validate;
|
| | |
|
| | | whiteSpaceElements = schema.getWhiteSpaceElements();
|
| | | startWhiteSpaceRegExp = /^[ \t\r\n]+/;
|
| | |
| | | return node;
|
| | | };
|
| | |
|
| | | function removeWhitespaceBefore(node) {
|
| | | var textNode, textVal, sibling;
|
| | |
|
| | | for (textNode = node.prev; textNode && textNode.type === 3; ) {
|
| | | textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
|
| | |
|
| | | if (textVal.length > 0) {
|
| | | textNode.value = textVal;
|
| | | textNode = textNode.prev;
|
| | | } else {
|
| | | sibling = textNode.prev;
|
| | | textNode.remove();
|
| | | textNode = sibling;
|
| | | }
|
| | | }
|
| | | };
|
| | |
|
| | | parser = new tinymce.html.SaxParser({
|
| | | validate : settings.validate,
|
| | | fix_self_closing : false, // Let the DOM parser handle <li> in <li> or <p> in <p> for better results
|
| | | validate : validate,
|
| | | fix_self_closing : !validate, // Let the DOM parser handle <li> in <li> or <p> in <p> for better results
|
| | |
|
| | | cdata: function(text) {
|
| | | node.append(createNode('#cdata', 4)).value = text;
|
| | |
| | |
|
| | | pi: function(name, text) {
|
| | | node.append(createNode(name, 7)).value = text;
|
| | | removeWhitespaceBefore(node);
|
| | | },
|
| | |
|
| | | doctype: function(text) {
|
| | | node.append(createNode('#doctype', 10)).value = text;
|
| | | var newNode;
|
| | | |
| | | newNode = node.append(createNode('#doctype', 10));
|
| | | newNode.value = text;
|
| | | removeWhitespaceBefore(node);
|
| | | },
|
| | |
|
| | | start: function(name, attrs, empty) {
|
| | | var newNode, attrFiltersLen, elementRule, textNode, attrName, text, sibling, parent;
|
| | |
|
| | | elementRule = schema.getElementRule(name);
|
| | | elementRule = validate ? schema.getElementRule(name) : {};
|
| | | if (elementRule) {
|
| | | newNode = createNode(elementRule.outputName || name, 1);
|
| | | newNode.attributes = attrs;
|
| | |
| | | }
|
| | |
|
| | | // Trim whitespace before block
|
| | | if (blockElements[name]) {
|
| | | for (textNode = newNode.prev; textNode && textNode.type === 3; ) {
|
| | | text = textNode.value.replace(endWhiteSpaceRegExp, '');
|
| | |
|
| | | if (text.length > 0) {
|
| | | textNode.value = text;
|
| | | textNode = textNode.prev;
|
| | | } else {
|
| | | sibling = textNode.prev;
|
| | | textNode.remove();
|
| | | textNode = sibling;
|
| | | }
|
| | | }
|
| | | }
|
| | | if (blockElements[name])
|
| | | removeWhitespaceBefore(newNode);
|
| | |
|
| | | // Change current node if the element wasn't empty i.e not <br /> or <img />
|
| | | if (!empty)
|
| | |
| | | end: function(name) {
|
| | | var textNode, elementRule, text, sibling, tempNode;
|
| | |
|
| | | elementRule = schema.getElementRule(name);
|
| | | elementRule = validate ? schema.getElementRule(name) : {};
|
| | | if (elementRule) {
|
| | | if (blockElements[name]) {
|
| | | if (!whiteSpaceElements[node.name]) {
|
| | |
| | |
|
| | | parser.parse(html);
|
| | |
|
| | | fixInvalidChildren(invalidChildren);
|
| | | if (validate)
|
| | | fixInvalidChildren(invalidChildren);
|
| | |
|
| | | // Run node filters
|
| | | for (name in matchedNodes) {
|
| | |
| | | else
|
| | | html[html.length] = ' />';
|
| | |
|
| | | /*if (indent && indentAfter[name])
|
| | | html.push('\n');*/
|
| | | if (empty && indent && indentAfter[name] && html.length > 0) {
|
| | | value = html[html.length - 1];
|
| | |
|
| | | if (value.length > 0 && value !== '\n')
|
| | | html.push('\n');
|
| | | }
|
| | | },
|
| | |
|
| | | end: function(name) {
|
| | |
| | | html.push('<?', name, ' ', text, '?>');
|
| | | else
|
| | | html.push('<?', name, '?>');
|
| | |
|
| | | if (indent)
|
| | | html.push('\n');
|
| | | },
|
| | |
|
| | | doctype: function(text) {
|
| | | html.push('<!DOCTYPE', text, '>');
|
| | | html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
|
| | | },
|
| | |
|
| | | reset: function() {
|
| | |
| | | isIE = tinymce.isIE,
|
| | | Entities = tinymce.html.Entities,
|
| | | simpleSelectorRe = /^([a-z0-9],?)+$/i,
|
| | | blockElementsMap = tinymce.html.Schema.blockElementsMap;
|
| | | blockElementsMap = tinymce.html.Schema.blockElementsMap,
|
| | | whiteSpaceRegExp = /^[ \t\r\n]*$/;
|
| | |
|
| | | tinymce.create('tinymce.dom.DOMUtils', {
|
| | | doc : null,
|
| | |
| | | keep_values : false,
|
| | | hex_colors : 1
|
| | | }, s);
|
| | |
|
| | | |
| | | t.schema = s.schema;
|
| | | t.styles = new tinymce.html.Styles({
|
| | | url_converter : s.url_converter,
|
| | | url_converter_scope : s.url_converter_scope
|
| | |
| | |
|
| | | remove : function(node, keep_children) {
|
| | | return this.run(node, function(node) {
|
| | | var parent, child;
|
| | |
|
| | | parent = node.parentNode;
|
| | | var child, parent = node.parentNode;
|
| | |
|
| | | if (!parent)
|
| | | return null;
|
| | |
| | | n = this.get(n);
|
| | |
|
| | | if (!n)
|
| | | return false;
|
| | | return;
|
| | |
|
| | | // Gecko
|
| | | if (this.doc.defaultView && c) {
|
| | |
| | | if (n.currentStyle && c)
|
| | | return n.currentStyle[na];
|
| | |
|
| | | return n.style[na];
|
| | | return n.style ? n.style[na] : undefined;
|
| | | },
|
| | |
|
| | | setStyles : function(e, o) {
|
| | |
| | |
|
| | | removeAllAttribs: function(e) {
|
| | | return this.run(e, function(e) {
|
| | | var attrs = e.attributes;
|
| | | for (var i = attrs.length - 1; i >= 0; i--) {
|
| | | var i, attrs = e.attributes;
|
| | | for (i = attrs.length - 1; i >= 0; i--) {
|
| | | e.removeAttributeNode(attrs.item(i));
|
| | | }
|
| | | });
|
| | |
| | | }
|
| | |
|
| | | break;
|
| | | |
| | |
|
| | | case "shape":
|
| | | e.setAttribute('data-mce-style', v);
|
| | | break;
|
| | |
| | | return n.attributes;
|
| | | },
|
| | |
|
| | | isEmpty : function(node, elements) {
|
| | | var self = this, i, attributes, type, walker, name;
|
| | |
|
| | | node = node.firstChild;
|
| | | if (node) {
|
| | | walker = new tinymce.dom.TreeWalker(node);
|
| | | elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;
|
| | |
|
| | | do {
|
| | | type = node.nodeType;
|
| | |
|
| | | if (type === 1) {
|
| | | // Ignore bogus elements
|
| | | if (node.getAttribute('data-mce-bogus'))
|
| | | continue;
|
| | |
|
| | | // Keep empty elements like <img />
|
| | | if (elements && elements[node.nodeName.toLowerCase()])
|
| | | return false;
|
| | |
|
| | | // Keep elements with data attributes or name attribute like <a name="1"></a>
|
| | | attributes = self.getAttribs(node);
|
| | | i = node.attributes.length;
|
| | | while (i--) {
|
| | | name = node.attributes[i].nodeName;
|
| | | if (name === "name" || name.indexOf('data-') === 0)
|
| | | return false;
|
| | | }
|
| | | }
|
| | |
|
| | | // Keep non whitespace text nodes
|
| | | if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue)))
|
| | | return false;
|
| | | } while (node = walker.next());
|
| | | }
|
| | |
|
| | | return true;
|
| | | },
|
| | |
|
| | | destroy : function(s) {
|
| | | var t = this;
|
| | |
|
| | |
| | | // this function will then trim of empty edges and produce:
|
| | | // <p>text 1</p><b>CHOP</b><p>text 2</p>
|
| | | function trim(node) {
|
| | | var i, children = node.childNodes;
|
| | | var i, children = node.childNodes, type = node.nodeType;
|
| | |
|
| | | if (node.nodeType == 1 && node.getAttribute('data-mce-type') == 'bookmark')
|
| | | if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark')
|
| | | return;
|
| | |
|
| | | for (i = children.length - 1; i >= 0; i--)
|
| | | trim(children[i]);
|
| | |
|
| | | if (node.nodeType != 9) {
|
| | | if (type != 9) {
|
| | | // Keep non whitespace text nodes
|
| | | if (node.nodeType == 3 && node.nodeValue.length > 0) {
|
| | | if (type == 3 && node.nodeValue.length > 0) {
|
| | | // If parent element isn't a block or there isn't any useful contents for example "<p> </p>"
|
| | | if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)
|
| | | return;
|
| | | }
|
| | |
|
| | | if (node.nodeType == 1) {
|
| | | } else if (type == 1) {
|
| | | // If the only child is a bookmark then move it up
|
| | | children = node.childNodes;
|
| | | if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('data-mce-type') == 'bookmark')
|
| | |
| | |
|
| | | if (s) {
|
| | | t.explicitRange = r;
|
| | | s.removeAllRanges();
|
| | |
|
| | | try {
|
| | | s.removeAllRanges();
|
| | | } catch (ex) {
|
| | | // IE9 might throw errors here don't know why
|
| | | }
|
| | |
|
| | | s.addRange(r);
|
| | | t.selectedRange = s.getRangeAt(0);
|
| | | }
|
| | |
| | | args.content = htmlSerializer.serialize(
|
| | | htmlParser.parse(args.getInner ? node.innerHTML : tinymce.trim(dom.getOuterHTML(node), args), args)
|
| | | );
|
| | |
|
| | | // Replace all BOM characters for now until we can find a better solution
|
| | | if (!args.cleanup)
|
| | | args.content = args.content.replace(/\uFEFF/g, '');
|
| | |
|
| | | // Post process
|
| | | if (!args.no_events)
|
| | |
| | | hideMenu : function(e) {
|
| | | var t = this;
|
| | |
|
| | | // Prevent double toogles by canceling the mouse click event to the button
|
| | | if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))
|
| | | return;
|
| | | if (t.isMenuVisible) {
|
| | | // Prevent double toogles by canceling the mouse click event to the button
|
| | | if (e && e.type == "mousedown" && DOM.getParent(e.target, function(e) {return e.id === t.id + '_open';}))
|
| | | return;
|
| | |
|
| | | if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {
|
| | | DOM.removeClass(t.id, 'mceSplitButtonSelected');
|
| | | Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
|
| | | Event.remove(t.id + '_menu', 'keydown', t._keyHandler);
|
| | | DOM.hide(t.id + '_menu');
|
| | | if (!e || !DOM.getParent(e.target, '.mceSplitButtonMenu')) {
|
| | | DOM.removeClass(t.id, 'mceSplitButtonSelected');
|
| | | Event.remove(DOM.doc, 'mousedown', t.hideMenu, t);
|
| | | Event.remove(t.id + '_menu', 'keydown', t._keyHandler);
|
| | | DOM.hide(t.id + '_menu');
|
| | | }
|
| | |
|
| | | t.isMenuVisible = 0;
|
| | | }
|
| | |
|
| | | t.onHideMenu.dispatch(t);
|
| | |
|
| | | t.isMenuVisible = 0;
|
| | | t.editor.focus();
|
| | | },
|
| | |
|
| | | renderMenu : function() {
|
| | |
| | |
|
| | | (function(tinymce) {
|
| | | // Shorten class names
|
| | | var dom = tinymce.DOM, each = tinymce.each
|
| | | var dom = tinymce.DOM, each = tinymce.each;
|
| | | tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
|
| | | renderHTML : function() {
|
| | | var t = this, h = '', c, co, s = t.settings, i, pr, nx, cl;
|
| | |
| | | mceInsertContent : function(command, ui, value) {
|
| | | var caretNode, rng, rootNode, parent, node, rng, nodeRect, viewPortRect, args;
|
| | |
|
| | | function findSuitableCaretNode(start_node, root_node) {
|
| | | var node, walker = new tinymce.dom.TreeWalker(start_node, root_node);
|
| | | function findSuitableCaretNode(node, root_node, next) {
|
| | | var walker = new tinymce.dom.TreeWalker(next ? node.nextSibling : node.previousSibling, root_node);
|
| | |
|
| | | while ((node = walker.current())) {
|
| | | if ((node.nodeType == 3 && tinymce.trim(node.nodeValue).length) || node.nodeName == 'BR' || node.nodeName == 'IMG')
|
| | | return node;
|
| | |
|
| | | walker.prev();
|
| | | if (next)
|
| | | walker.next();
|
| | | else
|
| | | walker.prev();
|
| | | }
|
| | | };
|
| | |
|
| | |
| | |
|
| | | // Move the caret into the last suitable location within the previous sibling if it's a block since the block might be split
|
| | | if (caretNode.previousSibling && dom.isBlock(caretNode.previousSibling) || caretNode.parentNode == rootNode) {
|
| | | node = findSuitableCaretNode(caretNode.previousSibling, rootNode);
|
| | | node = findSuitableCaretNode(caretNode, rootNode);
|
| | | if (node) {
|
| | | if (node.nodeName == 'BR')
|
| | | node.parentNode.insertBefore(caretNode, node);
|
| | |
| | | // This will remove invalid elements/attributes and fix nesting issues
|
| | | dom.setOuterHTML(parent,
|
| | | new tinymce.html.Serializer({}, editor.schema).serialize(
|
| | | new tinymce.html.DomParser({
|
| | | remove_trailing_brs : true
|
| | | }, editor.schema).parse(dom.getOuterHTML(parent))
|
| | | editor.parser.parse(dom.getOuterHTML(parent))
|
| | | )
|
| | | );
|
| | |
|
| | |
| | | // Find caret after cleanup and move selection to that location
|
| | | caretNode = dom.select('#__mce')[0];
|
| | | if (caretNode) {
|
| | | node = findSuitableCaretNode(caretNode.previousSibling, rootNode);
|
| | | node = findSuitableCaretNode(caretNode, rootNode) || findSuitableCaretNode(caretNode, rootNode, true);
|
| | | dom.remove(caretNode);
|
| | |
|
| | | if (node) {
|
| | |
| | | },
|
| | |
|
| | | mceReplaceContent : function(command, ui, value) {
|
| | | editor.execCommand('mceInsertContent', false, selection.setContent(value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'}))));
|
| | | editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'})));
|
| | | },
|
| | |
|
| | | mceInsertLink : function(command, ui, value) {
|
| | |
| | | return rng2.cloneContents().textContent.length == 0;
|
| | | };
|
| | |
|
| | | function isEmpty(n) {
|
| | | n = n.innerHTML;
|
| | |
|
| | | n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars
|
| | | n = n.replace(/<[^>]+>/g, ''); // Remove all tags
|
| | |
|
| | | return n.replace(/[ \u00a0\t\r\n]+/g, '') == '';
|
| | | };
|
| | |
|
| | | function splitList(selection, dom, li) {
|
| | | var listBlock, block;
|
| | |
|
| | | if (isEmpty(li)) {
|
| | | if (dom.isEmpty(li)) {
|
| | | listBlock = dom.getParent(li, 'ul,ol');
|
| | |
|
| | | if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {
|
| | |
| | | aft.innerHTML = aft.firstChild.innerHTML;
|
| | |
|
| | | // Padd empty blocks
|
| | | if (isEmpty(bef))
|
| | | if (dom.isEmpty(bef))
|
| | | bef.innerHTML = '<br />';
|
| | |
|
| | | function appendStyles(e, en) {
|
| | |
| | | };
|
| | |
|
| | | // Fill empty afterblook with current style
|
| | | if (isEmpty(aft))
|
| | | if (dom.isEmpty(aft))
|
| | | car = appendStyles(aft, en);
|
| | |
|
| | | // Opera needs this one backwards for older versions
|