OXIESEC PANEL
- Current Dir:
/
/
var
/
www
/
reader
/
_backup
/
tinymce
/
tinymce
/
src
/
core
/
main
/
ts
/
api
/
html
Server IP: 139.59.38.164
Upload:
Create Dir:
Name
Size
Modified
Perms
📁
..
-
02/20/2020 06:12:10 AM
rwxr-xr-x
📄
DomParser.ts
23.55 KB
02/20/2020 06:12:04 AM
rw-r--r--
📄
Entities.ts
10.39 KB
02/20/2020 06:12:05 AM
rw-r--r--
📄
Node.ts
12.72 KB
02/20/2020 06:12:05 AM
rw-r--r--
📄
SaxParser.ts
17.09 KB
02/20/2020 06:12:06 AM
rw-r--r--
📄
Schema.ts
36.45 KB
02/20/2020 06:12:06 AM
rw-r--r--
📄
Serializer.ts
3.9 KB
02/20/2020 06:12:07 AM
rw-r--r--
📄
Styles.ts
11.63 KB
02/20/2020 06:12:07 AM
rw-r--r--
📄
Writer.ts
5.36 KB
02/20/2020 06:12:08 AM
rw-r--r--
Editing: Schema.ts
Close
/** * Copyright (c) Tiny Technologies, Inc. All rights reserved. * Licensed under the LGPL or a commercial license. * For LGPL see License.txt in the project root for license information. * For commercial licenses see https://www.tiny.cloud/ */ import Tools from '../util/Tools'; export type SchemaType = 'html4' | 'html5' | 'html5-strict'; export interface SchemaSettings { block_elements?: string; boolean_attributes?: string; custom_elements?: string; extended_valid_elements?: string; invalid_elements?: string; invalid_styles?: string | Record<string, string>; move_caret_before_on_enter_elements?: string; non_empty_elements?: string; schema?: SchemaType; self_closing_elements?: string; short_ended_elements?: string; special?: string; text_block_elements?: string; text_inline_elements?: string; valid_children?: string; valid_classes?: string | Record<string, string>; valid_elements?: string; valid_styles?: string | Record<string, string>; verify_html?: boolean; whitespace_elements?: string; } export type Attribute = { required?: boolean; defaultValue?: string; forcedValue?: string; validValues?: any; }; export type ElementRule = { attributes: Record<string, Attribute>; attributesOrder: string[]; attributePatterns?: RegExp[]; paddEmpty?: boolean; removeEmpty?: boolean; removeEmptyAttrs?: boolean; }; export interface SchemaElement extends ElementRule { outputName?: string; parentsRequired?: string[]; pattern?: RegExp; } export type SchemaMap = { [name: string]: {}; }; export type SchemaRegExpMap = { [name: string]: RegExp; }; interface Schema { children: Record<string, {}>; elements: Record<string, SchemaElement>; getValidStyles (): SchemaMap; getValidClasses (): SchemaMap; getBlockElements (): SchemaMap; getInvalidStyles (): SchemaMap; getShortEndedElements (): SchemaMap; getTextBlockElements (): SchemaMap; getTextInlineElements (): SchemaMap; getBoolAttrs (): SchemaMap; getElementRule (name: string): SchemaElement; getSelfClosingElements (): SchemaMap; getNonEmptyElements (): SchemaMap; getMoveCaretBeforeOnEnterElements (): SchemaMap; getWhiteSpaceElements (): SchemaMap; getSpecialElements (): SchemaRegExpMap; isValidChild (name: string, child: string): boolean; isValid (name: string, attr?: string): boolean; getCustomElements (): SchemaMap; addValidElements (validElements: string): void; setValidElements (validElements: string): void; addCustomElements (customElements: string): void; addValidChildren (validChildren: any): void; } /** * Schema validator class. * * @class tinymce.html.Schema * @example * if (tinymce.activeEditor.schema.isValidChild('p', 'span')) * alert('span is valid child of p.'); * * if (tinymce.activeEditor.schema.getElementRule('p')) * alert('P is a valid element.'); * * @class tinymce.html.Schema * @version 3.4 */ const mapCache: any = {}, dummyObj = {}; const makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray; const split = function (items, delim?) { items = Tools.trim(items); return items ? items.split(delim || ' ') : []; }; /** * Builds a schema lookup table * * @private * @param {String} type html4, html5 or html5-strict schema type. * @return {Object} Schema lookup table. */ const compileSchema = function (type: SchemaType) { const schema: any = {}; let globalAttributes, blockContent; let phrasingContent, flowContent, html4BlockContent, html4PhrasingContent; const add = function (name: string, attributes?: string, children?: string | string[]) { let ni, attributesOrder, element; const arrayToMap = function (array, obj?) { const map = {}; let i, l; for (i = 0, l = array.length; i < l; i++) { map[array[i]] = obj || {}; } return map; }; children = children || []; attributes = attributes || ''; if (typeof children === 'string') { children = split(children); } name = split(name); ni = name.length; while (ni--) { attributesOrder = split([globalAttributes, attributes].join(' ')); element = { attributes: arrayToMap(attributesOrder), attributesOrder, children: arrayToMap(children, dummyObj) }; schema[name[ni]] = element; } }; const addAttrs = function (name: string, attributes?: string) { let ni, schemaItem, i, l; name = split(name); ni = name.length; attributes = split(attributes); while (ni--) { schemaItem = schema[name[ni]]; for (i = 0, l = attributes.length; i < l; i++) { schemaItem.attributes[attributes[i]] = {}; schemaItem.attributesOrder.push(attributes[i]); } } }; // Use cached schema if (mapCache[type]) { return mapCache[type]; } // Attributes present on all elements globalAttributes = 'id accesskey class dir lang style tabindex title role'; // Event attributes can be opt-in/opt-out /*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " + "ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " + "onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " + "onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " + "onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " + "onwaiting" );*/ // Block content elements blockContent = 'address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul'; // Phrasing content elements from the HTML5 spec (inline) phrasingContent = 'a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd ' + 'label map noscript object q s samp script select small span strong sub sup ' + 'textarea u var #text #comment' ; // Add HTML5 items to globalAttributes, blockContent, phrasingContent if (type !== 'html4') { globalAttributes += ' contenteditable contextmenu draggable dropzone ' + 'hidden spellcheck translate'; blockContent += ' article aside details dialog figure main header footer hgroup section nav'; phrasingContent += ' audio canvas command datalist mark meter output picture ' + 'progress time wbr video ruby bdi keygen'; } // Add HTML4 elements unless it's html5-strict if (type !== 'html5-strict') { globalAttributes += ' xml:lang'; html4PhrasingContent = 'acronym applet basefont big font strike tt'; phrasingContent = [phrasingContent, html4PhrasingContent].join(' '); each(split(html4PhrasingContent), function (name) { add(name, '', phrasingContent); }); html4BlockContent = 'center dir isindex noframes'; blockContent = [blockContent, html4BlockContent].join(' '); // Flow content elements from the HTML5 spec (block+inline) flowContent = [blockContent, phrasingContent].join(' '); each(split(html4BlockContent), function (name) { add(name, '', flowContent); }); } // Flow content elements from the HTML5 spec (block+inline) flowContent = flowContent || [blockContent, phrasingContent].join(' '); // HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement // Schema items <element name>, <specific attributes>, <children ..> add('html', 'manifest', 'head body'); add('head', '', 'base command link meta noscript script style title'); add('title hr noscript br'); add('base', 'href target'); add('link', 'href rel media hreflang type sizes hreflang'); add('meta', 'name http-equiv content charset'); add('style', 'media type scoped'); add('script', 'src async defer type charset'); add('body', 'onafterprint onbeforeprint onbeforeunload onblur onerror onfocus ' + 'onhashchange onload onmessage onoffline ononline onpagehide onpageshow ' + 'onpopstate onresize onscroll onstorage onunload', flowContent); add('address dt dd div caption', '', flowContent); add('h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn', '', phrasingContent); add('blockquote', 'cite', flowContent); add('ol', 'reversed start type', 'li'); add('ul', '', 'li'); add('li', 'value', flowContent); add('dl', '', 'dt dd'); add('a', 'href target rel media hreflang type', phrasingContent); add('q', 'cite', phrasingContent); add('ins del', 'cite datetime', flowContent); add('img', 'src sizes srcset alt usemap ismap width height'); add('iframe', 'src name width height', flowContent); add('embed', 'src type width height'); add('object', 'data type typemustmatch name usemap form width height', [flowContent, 'param'].join(' ')); add('param', 'name value'); add('map', 'name', [flowContent, 'area'].join(' ')); add('area', 'alt coords shape href target rel media hreflang type'); add('table', 'border', 'caption colgroup thead tfoot tbody tr' + (type === 'html4' ? ' col' : '')); add('colgroup', 'span', 'col'); add('col', 'span'); add('tbody thead tfoot', '', 'tr'); add('tr', '', 'td th'); add('td', 'colspan rowspan headers', flowContent); add('th', 'colspan rowspan headers scope abbr', flowContent); add('form', 'accept-charset action autocomplete enctype method name novalidate target', flowContent); add('fieldset', 'disabled form name', [flowContent, 'legend'].join(' ')); add('label', 'form for', phrasingContent); add('input', 'accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate ' + 'formtarget height list max maxlength min multiple name pattern readonly required size src step type value width' ); add('button', 'disabled form formaction formenctype formmethod formnovalidate formtarget name type value', type === 'html4' ? flowContent : phrasingContent); add('select', 'disabled form multiple name required size', 'option optgroup'); add('optgroup', 'disabled label', 'option'); add('option', 'disabled label selected value'); add('textarea', 'cols dirname disabled form maxlength name readonly required rows wrap'); add('menu', 'type label', [flowContent, 'li'].join(' ')); add('noscript', '', flowContent); // Extend with HTML5 elements if (type !== 'html4') { add('wbr'); add('ruby', '', [phrasingContent, 'rt rp'].join(' ')); add('figcaption', '', flowContent); add('mark rt rp summary bdi', '', phrasingContent); add('canvas', 'width height', flowContent); add('video', 'src crossorigin poster preload autoplay mediagroup loop ' + 'muted controls width height buffered', [flowContent, 'track source'].join(' ')); add('audio', 'src crossorigin preload autoplay mediagroup loop muted controls ' + 'buffered volume', [flowContent, 'track source'].join(' ')); add('picture', '', 'img source'); add('source', 'src srcset type media sizes'); add('track', 'kind src srclang label default'); add('datalist', '', [phrasingContent, 'option'].join(' ')); add('article section nav aside main header footer', '', flowContent); add('hgroup', '', 'h1 h2 h3 h4 h5 h6'); add('figure', '', [flowContent, 'figcaption'].join(' ')); add('time', 'datetime', phrasingContent); add('dialog', 'open', flowContent); add('command', 'type label icon disabled checked radiogroup command'); add('output', 'for form name', phrasingContent); add('progress', 'value max', phrasingContent); add('meter', 'value min max low high optimum', phrasingContent); add('details', 'open', [flowContent, 'summary'].join(' ')); add('keygen', 'autofocus challenge disabled form keytype name'); } // Extend with HTML4 attributes unless it's html5-strict if (type !== 'html5-strict') { addAttrs('script', 'language xml:space'); addAttrs('style', 'xml:space'); addAttrs('object', 'declare classid code codebase codetype archive standby align border hspace vspace'); addAttrs('embed', 'align name hspace vspace'); addAttrs('param', 'valuetype type'); addAttrs('a', 'charset name rev shape coords'); addAttrs('br', 'clear'); addAttrs('applet', 'codebase archive code object alt name width height align hspace vspace'); addAttrs('img', 'name longdesc align border hspace vspace'); addAttrs('iframe', 'longdesc frameborder marginwidth marginheight scrolling align'); addAttrs('font basefont', 'size color face'); addAttrs('input', 'usemap align'); addAttrs('select', 'onchange'); addAttrs('textarea'); addAttrs('h1 h2 h3 h4 h5 h6 div p legend caption', 'align'); addAttrs('ul', 'type compact'); addAttrs('li', 'type'); addAttrs('ol dl menu dir', 'compact'); addAttrs('pre', 'width xml:space'); addAttrs('hr', 'align noshade size width'); addAttrs('isindex', 'prompt'); addAttrs('table', 'summary width frame rules cellspacing cellpadding align bgcolor'); addAttrs('col', 'width align char charoff valign'); addAttrs('colgroup', 'width align char charoff valign'); addAttrs('thead', 'align char charoff valign'); addAttrs('tr', 'align char charoff valign bgcolor'); addAttrs('th', 'axis align char charoff valign nowrap bgcolor width height'); addAttrs('form', 'accept'); addAttrs('td', 'abbr axis scope align char charoff valign nowrap bgcolor width height'); addAttrs('tfoot', 'align char charoff valign'); addAttrs('tbody', 'align char charoff valign'); addAttrs('area', 'nohref'); addAttrs('body', 'background bgcolor text link vlink alink'); } // Extend with HTML5 attributes unless it's html4 if (type !== 'html4') { addAttrs('input button select textarea', 'autofocus'); addAttrs('input textarea', 'placeholder'); addAttrs('a', 'download'); addAttrs('link script img', 'crossorigin'); addAttrs('iframe', 'sandbox seamless allowfullscreen'); // Excluded: srcdoc } // Special: iframe, ruby, video, audio, label // Delete children of the same name from it's parent // For example: form can't have a child of the name form each(split('a form meter progress dfn'), function (name) { if (schema[name]) { delete schema[name].children[name]; } }); // Delete header, footer, sectioning and heading content descendants /*each('dt th address', function(name) { delete schema[name].children[name]; });*/ // Caption can't have tables delete schema.caption.children.table; // Delete scripts by default due to possible XSS delete schema.script; // TODO: LI:s can only have value if parent is OL // TODO: Handle transparent elements // a ins del canvas map mapCache[type] = schema; return schema; }; const compileElementMap = function (value: string | Record<string, string>, mode?: string ) { let styles; if (value) { styles = {}; if (typeof value === 'string') { value = { '*': value }; } // Convert styles into a rule list each(value, function (value, key) { styles[key] = styles[key.toUpperCase()] = mode === 'map' ? makeMap(value, /[, ]/) : explode(value, /[, ]/); }); } return styles; }; function Schema(settings?: SchemaSettings): Schema { let elements: Record<string, SchemaElement> = {}; const children: Record<string, {}> = {}; let patternElements = []; let validStyles; let invalidStyles; let schemaItems; let whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, validClasses; let blockElementsMap, nonEmptyElementsMap, moveCaretBeforeOnEnterElementsMap, textBlockElementsMap, textInlineElementsMap; const customElementsMap = {}, specialElements = {} as SchemaRegExpMap; // Creates an lookup table map object for the specified option or the default value const createLookupTable = function (option: string, defaultValue?: string, extendWith?: string) { let value = settings[option]; if (!value) { // Get cached default map or make it if needed value = mapCache[option]; if (!value) { value = makeMap(defaultValue, ' ', makeMap(defaultValue.toUpperCase(), ' ')); value = extend(value, extendWith); mapCache[option] = value; } } else { // Create custom map value = makeMap(value, /[, ]/, makeMap(value.toUpperCase(), /[, ]/)); } return value; }; settings = settings || {}; schemaItems = compileSchema(settings.schema); // Allow all elements and attributes if verify_html is set to false if (settings.verify_html === false) { settings.valid_elements = '*[*]'; } validStyles = compileElementMap(settings.valid_styles); invalidStyles = compileElementMap(settings.invalid_styles, 'map'); validClasses = compileElementMap(settings.valid_classes, 'map'); // Setup map objects whiteSpaceElementsMap = createLookupTable( 'whitespace_elements', 'pre script noscript style textarea video audio iframe object code' ); selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr'); shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' + 'meta param embed source wbr track'); boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' + 'noshade nowrap readonly selected autoplay loop controls'); nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object ' + 'script pre code', shortEndedElementsMap); moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', 'table', nonEmptyElementsMap); textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' + 'blockquote center dir fieldset header footer article section hgroup aside main nav figure'); blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' + 'th tr td li ol ul caption dl dt dd noscript menu isindex option ' + 'datalist select optgroup figcaption details summary', textBlockElementsMap); textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' + 'dfn code mark q sup sub samp'); each((settings.special || 'script noscript noframes noembed title style textarea xmp').split(' '), function (name) { specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi'); }); // Converts a wildcard expression string to a regexp for example *a will become /.*a/. const patternToRegExp = (str) => new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$'); // Parses the specified valid_elements string and adds to the current rules // This function is a bit hard to read since it's heavily optimized for speed const addValidElements = (validElements: string) => { let ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder, prefix, outputName, globalAttributes, globalAttributesOrder, key, value; const elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/, attrRuleRegExp = /^([!\-])?(\w+[\\:]:\w+|[^=:<]+)?(?:([=:<])(.*))?$/, hasPatternsRegExp = /[*?+]/; if (validElements) { // Split valid elements into an array with rules validElements = split(validElements, ','); if (elements['@']) { globalAttributes = elements['@'].attributes; globalAttributesOrder = elements['@'].attributesOrder; } // Loop all rules for (ei = 0, el = validElements.length; ei < el; ei++) { // Parse element rule matches = elementRuleRegExp.exec(validElements[ei]); if (matches) { // Setup local names for matches prefix = matches[1]; elementName = matches[2]; outputName = matches[3]; attrData = matches[5]; // Create new attributes and attributesOrder attributes = {}; attributesOrder = []; // Create the new element element = { attributes, attributesOrder }; // Padd empty elements prefix if (prefix === '#') { element.paddEmpty = true; } // Remove empty elements prefix if (prefix === '-') { element.removeEmpty = true; } if (matches[4] === '!') { element.removeEmptyAttrs = true; } // Copy attributes from global rule into current rule if (globalAttributes) { for (key in globalAttributes) { attributes[key] = globalAttributes[key]; } attributesOrder.push.apply(attributesOrder, globalAttributesOrder); } // Attributes defined if (attrData) { attrData = split(attrData, '|'); for (ai = 0, al = attrData.length; ai < al; ai++) { matches = attrRuleRegExp.exec(attrData[ai]); if (matches) { attr = {}; attrType = matches[1]; attrName = matches[2].replace(/[\\:]:/g, ':'); prefix = matches[3]; value = matches[4]; // Required if (attrType === '!') { element.attributesRequired = element.attributesRequired || []; element.attributesRequired.push(attrName); attr.required = true; } // Denied from global if (attrType === '-') { delete attributes[attrName]; attributesOrder.splice(inArray(attributesOrder, attrName), 1); continue; } // Default value if (prefix) { // Default value if (prefix === '=') { element.attributesDefault = element.attributesDefault || []; element.attributesDefault.push({ name: attrName, value }); attr.defaultValue = value; } // Forced value if (prefix === ':') { element.attributesForced = element.attributesForced || []; element.attributesForced.push({ name: attrName, value }); attr.forcedValue = value; } // Required values if (prefix === '<') { attr.validValues = makeMap(value, '?'); } } // Check for attribute patterns if (hasPatternsRegExp.test(attrName)) { element.attributePatterns = element.attributePatterns || []; attr.pattern = patternToRegExp(attrName); element.attributePatterns.push(attr); } else { // Add attribute to order list if it doesn't already exist if (!attributes[attrName]) { attributesOrder.push(attrName); } attributes[attrName] = attr; } } } } // Global rule, store away these for later usage if (!globalAttributes && elementName === '@') { globalAttributes = attributes; globalAttributesOrder = attributesOrder; } // Handle substitute elements such as b/strong if (outputName) { element.outputName = elementName; elements[outputName] = element; } // Add pattern or exact element if (hasPatternsRegExp.test(elementName)) { element.pattern = patternToRegExp(elementName); patternElements.push(element); } else { elements[elementName] = element; } } } } }; const setValidElements = function (validElements: string) { elements = {}; patternElements = []; addValidElements(validElements); each(schemaItems, function (element, name) { children[name] = element.children; }); }; // Adds custom non HTML elements to the schema const addCustomElements = function (customElements: string) { const customElementRegExp = /^(~)?(.+)$/; if (customElements) { // Flush cached items since we are altering the default maps mapCache.text_block_elements = mapCache.block_elements = null; each(split(customElements, ','), function (rule) { const matches = customElementRegExp.exec(rule), inline = matches[1] === '~', cloneName = inline ? 'span' : 'div', name = matches[2]; children[name] = children[cloneName]; customElementsMap[name] = cloneName; // If it's not marked as inline then add it to valid block elements if (!inline) { blockElementsMap[name.toUpperCase()] = {}; blockElementsMap[name] = {}; } // Add elements clone if needed if (!elements[name]) { let customRule = elements[cloneName]; customRule = extend({}, customRule); delete customRule.removeEmptyAttrs; delete customRule.removeEmpty; elements[name] = customRule; } // Add custom elements at span/div positions each(children, function (element, elmName) { if (element[cloneName]) { children[elmName] = element = extend({}, children[elmName]); element[name] = element[cloneName]; } }); }); } }; // Adds valid children to the schema object const addValidChildren = function (validChildren) { const childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/; // Invalidate the schema cache if the schema is mutated mapCache[settings.schema] = null; if (validChildren) { each(split(validChildren, ','), function (rule) { const matches = childRuleRegExp.exec(rule); let parent, prefix; if (matches) { prefix = matches[1]; // Add/remove items from default if (prefix) { parent = children[matches[2]]; } else { parent = children[matches[2]] = { '#comment': {} }; } parent = children[matches[2]]; each(split(matches[3], '|'), function (child) { if (prefix === '-') { delete parent[child]; } else { parent[child] = {}; } }); } }); } }; const getElementRule = (name: string): ElementRule => { let element = elements[name], i; // Exact match found if (element) { return element; } // No exact match then try the patterns i = patternElements.length; while (i--) { element = patternElements[i]; if (element.pattern.test(name)) { return element; } } }; if (!settings.valid_elements) { // No valid elements defined then clone the elements from the schema spec each(schemaItems, function (element, name) { elements[name] = { attributes: element.attributes, attributesOrder: element.attributesOrder }; children[name] = element.children; }); // Switch these on HTML4 if (settings.schema !== 'html5') { each(split('strong/b em/i'), function (item) { item = split(item, '/'); elements[item[1]].outputName = item[0]; }); } // Add default alt attribute for images, removed since alt="" is treated as presentational. // elements.img.attributesDefault = [{name: 'alt', value: ''}]; // Remove these if they are empty by default each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function (name) { if (elements[name]) { elements[name].removeEmpty = true; } }); // Padd these by default each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption li'), function (name) { elements[name].paddEmpty = true; }); // Remove these if they have no attributes each(split('span'), function (name) { elements[name].removeEmptyAttrs = true; }); // Remove these by default // TODO: Reenable in 4.1 /*each(split('script style'), function(name) { delete elements[name]; });*/ } else { setValidElements(settings.valid_elements); } addCustomElements(settings.custom_elements); addValidChildren(settings.valid_children); addValidElements(settings.extended_valid_elements); // Todo: Remove this when we fix list handling to be valid addValidChildren('+ol[ul|ol],+ul[ul|ol]'); // Some elements are not valid by themselves - require parents each({ dd: 'dl', dt: 'dl', li: 'ul ol', td: 'tr', th: 'tr', tr: 'tbody thead tfoot', tbody: 'table', thead: 'table', tfoot: 'table', legend: 'fieldset', area: 'map', param: 'video audio object' }, function (parents, item) { if (elements[item]) { elements[item].parentsRequired = split(parents); } }); // Delete invalid elements if (settings.invalid_elements) { each(explode(settings.invalid_elements), function (item) { if (elements[item]) { delete elements[item]; } }); } // If the user didn't allow span only allow internal spans if (!getElementRule('span')) { addValidElements('span[!data-mce-type|*]'); } /** * Name/value map object with valid parents and children to those parents. * * @example * children = { * div:{p:{}, h1:{}} * }; * @field children * @type Object */ /** * Name/value map object with valid styles for each element. * * @method getValidStyles * @type Object */ const getValidStyles = (): SchemaMap => validStyles; /** * Name/value map object with valid styles for each element. * * @method getInvalidStyles * @type Object */ const getInvalidStyles = (): SchemaMap => invalidStyles; /** * Name/value map object with valid classes for each element. * * @method getValidClasses * @type Object */ const getValidClasses = (): SchemaMap => validClasses; /** * Returns a map with boolean attributes. * * @method getBoolAttrs * @return {Object} Name/value lookup map for boolean attributes. */ const getBoolAttrs = (): SchemaMap => boolAttrMap; /** * Returns a map with block elements. * * @method getBlockElements * @return {Object} Name/value lookup map for block elements. */ const getBlockElements = (): SchemaMap => blockElementsMap; /** * Returns a map with text block elements. Such as: p,h1-h6,div,address * * @method getTextBlockElements * @return {Object} Name/value lookup map for block elements. */ const getTextBlockElements = (): SchemaMap => textBlockElementsMap; /** * Returns a map of inline text format nodes for example strong/span or ins. * * @method getTextInlineElements * @return {Object} Name/value lookup map for text format elements. */ const getTextInlineElements = (): SchemaMap => textInlineElementsMap; /** * Returns a map with short ended elements such as BR or IMG. * * @method getShortEndedElements * @return {Object} Name/value lookup map for short ended elements. */ const getShortEndedElements = (): SchemaMap => shortEndedElementsMap; /** * Returns a map with self closing tags such as <li>. * * @method getSelfClosingElements * @return {Object} Name/value lookup map for self closing tags elements. */ const getSelfClosingElements = (): SchemaMap => selfClosingElementsMap; /** * Returns a map with elements that should be treated as contents regardless if it has text * content in them or not such as TD, VIDEO or IMG. * * @method getNonEmptyElements * @return {Object} Name/value lookup map for non empty elements. */ const getNonEmptyElements = (): SchemaMap => nonEmptyElementsMap; /** * Returns a map with elements that the caret should be moved in front of after enter is * pressed * * @method getMoveCaretBeforeOnEnterElements * @return {Object} Name/value lookup map for elements to place the caret in front of. */ const getMoveCaretBeforeOnEnterElements = (): SchemaMap => moveCaretBeforeOnEnterElementsMap; /** * Returns a map with elements where white space is to be preserved like PRE or SCRIPT. * * @method getWhiteSpaceElements * @return {Object} Name/value lookup map for white space elements. */ const getWhiteSpaceElements = (): SchemaMap => whiteSpaceElementsMap; /** * Returns a map with special elements. These are elements that needs to be parsed * in a special way such as script, style, textarea etc. The map object values * are regexps used to find the end of the element. * * @method getSpecialElements * @return {Object} Name/value lookup map for special elements. */ const getSpecialElements = (): SchemaRegExpMap => specialElements; /** * Returns true/false if the specified element and it's child is valid or not * according to the schema. * * @method isValidChild * @param {String} name Element name to check for. * @param {String} child Element child to verify. * @return {Boolean} True/false if the element is a valid child of the specified parent. */ const isValidChild = (name: string, child: string): boolean => { const parent = children[name.toLowerCase()]; return !!(parent && parent[child.toLowerCase()]); }; /** * Returns true/false if the specified element name and optional attribute is * valid according to the schema. * * @method isValid * @param {String} name Name of element to check. * @param {String} attr Optional attribute name to check for. * @return {Boolean} True/false if the element and attribute is valid. */ const isValid = (name: string, attr?: string): boolean => { let attrPatterns, i; const rule = getElementRule(name); // Check if it's a valid element if (rule) { if (attr) { // Check if attribute name exists if (rule.attributes[attr]) { return true; } // Check if attribute matches a regexp pattern attrPatterns = rule.attributePatterns; if (attrPatterns) { i = attrPatterns.length; while (i--) { if (attrPatterns[i].pattern.test(name)) { return true; } } } } else { return true; } } // No match return false; }; /** * Returns true/false if the specified element is valid or not * according to the schema. * * @method getElementRule * @param {String} name Element name to check for. * @return {Object} Element object or undefined if the element isn't valid. */ /** * Returns an map object of all custom elements. * * @method getCustomElements * @return {Object} Name/value map object of all custom elements. */ const getCustomElements = (): SchemaMap => customElementsMap; /** * Parses a valid elements string and adds it to the schema. The valid elements * format is for example "element[attr=default|otherattr]". * Existing rules will be replaced with the ones specified, so this extends the schema. * * @method addValidElements * @param {String} valid_elements String in the valid elements format to be parsed. */ /** * Parses a valid elements string and sets it to the schema. The valid elements * format is for example "element[attr=default|otherattr]". * Existing rules will be replaced with the ones specified, so this extends the schema. * * @method setValidElements * @param {String} valid_elements String in the valid elements format to be parsed. */ /** * Adds custom non HTML elements to the schema. * * @method addCustomElements * @param {String} custom_elements Comma separated list of custom elements to add. */ /** * Parses a valid children string and adds them to the schema structure. The valid children * format is for example: "element[child1|child2]". * * @method addValidChildren * @param {String} valid_children Valid children elements string to parse */ return { children, elements, getValidStyles, getValidClasses, getBlockElements, getInvalidStyles, getShortEndedElements, getTextBlockElements, getTextInlineElements, getBoolAttrs, getElementRule, getSelfClosingElements, getNonEmptyElements, getMoveCaretBeforeOnEnterElements, getWhiteSpaceElements, getSpecialElements, isValidChild, isValid, getCustomElements, addValidElements, setValidElements, addCustomElements, addValidChildren }; } export default Schema;