We use cookies to make our website more effective. By using our website you agree to our privacy policy.

Source: content.js

/**
 * content.js is part of Aloha Editor project http://www.alohaeditor.org
 *
 * Aloha Editor ● JavaScript Content Editing Library
 * Copyright (c) 2010-2015 Gentics Software GmbH, Vienna, Austria.
 * Contributors http://www.alohaeditor.org/docs/contributing.html
 *
 * @todo consider moving this into html/
 * @namespace content
 */
define(['maps', 'arrays'], function (Maps, Arrays) {
	'use strict';

	var TABLE_CHILDREN = {
		'CAPTION'  : true,
		'COLGROUP' : true,
		'THEAD'    : true,
		'TBODY'    : true,
		'TFOOT'    : true,
		'TR'       : true
	};

	var TR_CHILDREN = {
		'TH' : true,
		'TD' : true
	};

	var SELECT_CHILDREN = {
		'OPTION'   : true,
		'OPTGROUP' : true
	};

	var RUBY_CHILDREN = {
		'_PHRASING_' : true,
		'RT'         : true,
		'RP'         : true
	};

	var MENU_CHILDREN = {
		'LI'     : true,
		'_FLOW_' : true
	};

	var HGROUP_CHILDREN = {
		'H1' : true,
		'H2' : true,
		'H3' : true,
		'H4' : true,
		'H5' : true,
		'H6' : true
	};

	var FIGURE_CHILDREN = {
		'FIGCAPTION' : true,
		'_FLOW_'     : true
	};

	var FIELDSET_CHILDREN = {
		'LEGEND' : true,
		'_FLOW_' : true
	};

	var DL_CHILDREN = {
		'DT' : true,
		'DD' : true
	};

	var DETAILS = {
		'SUMMARY' : true,
		'_FLOW_'  : true
	};

	var DATALIST = {
		'_PHRASING_' : true,
		'OPTION'     : true
	};

	/**
	 * The complete set of HTML(5) elements, mapped to their respective content
	 * models, which define what types of HTML nodes are permitted as their
	 * children.
	 *
	 * http://www.w3.org/html/wg/drafts/html/master/index.html#elements-1
	 * http://www.whatwg.org/specs/web-apps/current-work/#elements-1
	 */
	var ALLOWED_CHILDREN = {
		'A'                  : '_PHRASING_', // transparent
		'ABBR'               : '_PHRASING_',
		'ADDRESS'            : '_FLOW_',
		'AREA'               : '_EMPTY_',
		'ARTICLE'            : '_FLOW_',
		'ASIDE'              : '_FLOW_',
		'AUDIO'              : 'SOURCE', // transparent
		'B'                  : '_PHRASING_',
		'BASE'               : '_EMPTY_',
		'BDO'                : '_PHRASING_',
		'BLOCKQUOTE'         : '_PHRASING_',
		'BODY'               : '_FLOW_',
		'BR'                 : '_EMPTY_',
		'BUTTON'             : '_PHRASING_',
		'CANVAS'             : '_PHRASING_', // transparent
		'CAPTION'            : '_FLOW_',
		'CITE'               : '_PHRASING_',
		'CODE'               : '_PHRASING_',
		'COL'                : '_EMPTY_',
		'COLGROUP'           : 'COL',
		'COMMAND'            : '_EMPTY_',
		'DATALIST'           : DATALIST,
		'DD'                 : '_FLOW_',
		'DEL'                : '_PHRASING_',
		'DIV'                : '_FLOW_',
		'DETAILS'            : DETAILS,
		'DFN'                : '_FLOW_',
		'DL'                 : DL_CHILDREN,
		'DT'                 : '_PHRASING_', // varies
		'EM'                 : '_PHRASING_',
		'EMBED'              : '_EMPTY_',
		'FIELDSET'           : FIELDSET_CHILDREN,
		'FIGCAPTION'         : '_FLOW_',
		'FIGURE'             : FIGURE_CHILDREN,
		 // Because processing content orginating from paste events my contain
		 // font nodes, we need to accomodate this element, even though it is
		 // non-standard.
		 // http://htmlhelp.com/reference/html40/special/font.html
		'FONT'               : '_PHRASING_',
		'FOOTER'             : '_FLOW_',
		'FORM'               : '_FLOW_',
		'H1'                 : '_PHRASING_',
		'H2'                 : '_PHRASING_',
		'H3'                 : '_PHRASING_',
		'H4'                 : '_PHRASING_',
		'H5'                 : '_PHRASING_',
		'H6'                 : '_PHRASING_',
		'HEAD'               : '_META_DATA_',
		'HEADER'             : '_FLOW_',
		'HGROUP'             : HGROUP_CHILDREN,
		'HR'                 : '_EMPTY_',
		'I'                  : '_PHRASING_',
		'IFRAME'             : '#TEXT',
		'IMG'                : '_EMPTY_',
		'INPUT'              : '_EMPTY_',
		'INS'                : '_PHRASING_', // transparent
		'KBD'                : '_PHRASING_',
		'KEYGEN'             : '_EMPTY_',
		'LABEL'              : '_PHRASING_',
		'LEGEND'             : '_PHRASING_',
		'LI'                 : '_FLOW_',
		'LINK'               : '_EMPTY_',
		'MAP'                : 'AREA', // transparent
		'MARK'               : '_PHRASING_',
		'MENU'               : MENU_CHILDREN,
		'META'               : '_EMPTY_',
		'METER'              : '_PHRASING_',
		'NAV'                : '_FLOW_',
		'NOSCRIPT'           : '_PHRASING_', // varies
		'OBJECT'             : 'PARAM', // transparent
		'OL'                 : 'LI',
		'OPTGROUP'           : 'OPTION',
		'OPTION'             : '#TEXT',
		'OUTPUT'             : '_PHRASING_',
		'P'                  : '_PHRASING_',
		'PARAM'              : '_EMPTY_',
		'PRE'                : '_PHRASING_',
		'PROGRESS'           : '_PHRASING_',
		'Q'                  : '_PHRASING_',
		'RP'                 : '_PHRASING_',
		'RT'                 : '_PHRASING_',
		'RUBY'               : RUBY_CHILDREN,
		'S'                  : '_PHRASING_',
		'SAMP'               : 'pharsing',
		'SCRIPT'             : '#script', //script
		'SECTION'            : '_FLOW_',
		'SELECT'             : SELECT_CHILDREN,
		'SMALL'              : '_PHRASING_',
		'SOURCE'             : '_EMPTY_',
		'SPAN'               : '_PHRASING_',
		'STRONG'             : '_PHRASING_',
		'STYLE'              : '_PHRASING_', // varies
		'SUB'                : '_PHRASING_',
		'SUMMARY'            : '_PHRASING_',
		'SUP'                : '_PHRASING_',
		'TABLE'              : TABLE_CHILDREN,
		'TBODY'              : 'TR',
		'TD'                 : '_FLOW_',
		'TEXTAREA'           : '#TEXT',
		'TFOOT'              : 'TR',
		'TH'                 : '_PHRASING_',
		'THEAD'              : 'TR',
		'TIME'               : '_PHRASING_',
		'TITLE'              : '#TEXT',
		'TR'                 : TR_CHILDREN,
		'TRACK'              : '_EMPTY_',
		'U'                  : '_PHRASING_',
		'UL'                 : 'LI',
		'VAR'                : '_PHRASING_',
		'VIDEO'              : 'SOURCE', // transparent
		'WBR'                : '_EMPTY_',
		'#DOCUMENT-FRAGMENT' : '_FLOW_'
	};

	var FLOW_PHRASING_CATEGORY = {
		'_FLOW_'     : true,
		'_PHRASING_' : true
	};

	var FLOW_SECTIONING_CATEGORY = {
		'_FLOW_'     : true,
		'_PHRASING_' : true
	};

	var FLOW_HEADING_CATEGORY = {
		'_FLOW_'     : true,
		'_HEADER_'   : true
	};

	var FLOW_CATEGORY = {
		'_FLOW_'     : true
	};

	var CONTENT_CATEGORIES = {
		'A'          : {
			'_FLOW_'        : true,
			'_INTERACTIVE_' : true,
			'_PHRASING_'    : true
		},
		'ABBR'       : FLOW_PHRASING_CATEGORY,
		'ADDRESS'    : FLOW_CATEGORY,
		'AREA'       : FLOW_PHRASING_CATEGORY,
		'ARTICLE'    : FLOW_SECTIONING_CATEGORY,
		'ASIDE'      : FLOW_SECTIONING_CATEGORY,
		'AUDIO'      : {
			'_EMBEDDED_'    : true,
			'_FLOW_'        : true,
			'_INTERACTIVE_' : true,
			'_PHRASING_'    : true
		},
		'B'          : FLOW_PHRASING_CATEGORY,
		'BASE'       : {
			'_META_DATA_'   : true
		},
		'BDI'        : FLOW_PHRASING_CATEGORY,
		'BDO'        : FLOW_PHRASING_CATEGORY,
		'BLOCKQUOTE' : {
			'_FLOW_'            : true,
			'_SECTIONING_ROOT_' : true
		},
		'BODY'       : {
			'_SECTIONING_ROOT_' : true
		},
		'BR'         : FLOW_PHRASING_CATEGORY,
		'BUTTON'     : {
			'_EMBEDDED_'        : true,
			'_FLOW_'            : true,
			'_INTERACTIVE_'     : true,
			'_PHRASING_'        : true,
			'_LISTED_'          : true,
			'_LABELABLE_'       : true,
			'_SUBMITTABLE_'     : true,
			'_REASSOCIATABLE_'  : true,
			'_FORM_ASSOCIATED_' : true
		},
		'CANVAS'     : {
			'_EMBEDDED_'        : true,
			'_FLOW_'            : true,
			'_PHRASING_'        : true
		},
		'CAPTION'    : {},
		'CITE'       : FLOW_PHRASING_CATEGORY,
		'CODE'       : FLOW_PHRASING_CATEGORY,
		'COL'        : {},
		'COLGROUP'   : {},
		'COMMAND'    : {}, // ?
		'DATALIST'   : FLOW_PHRASING_CATEGORY,
		'DD'         : {},
		'DEL'        : FLOW_PHRASING_CATEGORY,
		'DETAILS'    : {
			'_FLOW_'            : true,
			'_SECTIONING_ROOT_' : true,
			'_INTERACTIVE_'     : true
		},
		'DFN'        : FLOW_PHRASING_CATEGORY,
		'DIV'        : FLOW_CATEGORY,
		'DL'         : FLOW_CATEGORY,
		'DT'         : {},
		'EM'         : FLOW_PHRASING_CATEGORY,
		'EMBED'      : {
			'_EMBEDDED_'        : true,
			'_FLOW_'            : true,
			'_INTERACTIVE_'     : true,
			'_PHRASING_'        : true
		},
		'FIELDSET'   : {
			'_FLOW_'            : true,
			'_FORM_ASSOCIATED_' : true,
			'_LISTED_'          : true,
			'_REASSOCIATABLE_'  : true,
			'_SECTIONING_ROOT_' : true
		},
		'FIGCAPTION' : {},
		'FIGURE'     : {
			'_FLOW_'            : true,
			'_SECTIONING_ROOT_' : true
		},
		'FONT'       : FLOW_PHRASING_CATEGORY,
		'FOOTER'     : FLOW_CATEGORY,
		'FORM'       : FLOW_CATEGORY,
		'H1'         : FLOW_HEADING_CATEGORY,
		'H2'         : FLOW_HEADING_CATEGORY,
		'H3'         : FLOW_HEADING_CATEGORY,
		'H4'         : FLOW_HEADING_CATEGORY,
		'H5'         : FLOW_HEADING_CATEGORY,
		'H6'         : FLOW_HEADING_CATEGORY,
		'HEADER'     : FLOW_CATEGORY,
		'HGROUP'     : FLOW_HEADING_CATEGORY,
		'HR'         : FLOW_CATEGORY,
		'I'          : FLOW_PHRASING_CATEGORY,
		'IFRAME'     : {
			'_EMBEDDED_'        : true,
			'_FLOW_'            : true,
			'_INTERACTIVE_'     : true,
			'_PHRASING_'        : true
		},
		'IMG'        : {
			'_EMBEDDED_'        : true,
			'_FLOW_'            : true,
			'_FORM_ASSOCIATED_' : true,
			'_INTERACTIVE_'     : true,
			'_PHRASING_'        : true
		},
		'INPUT'      : {
			'_FLOW_'            : true,
			'_FORM_ASSOCIATED_' : true,
			'_INTERACTIVE_'     : true,
			'_LABELABLE_'       : true,
			'_LISTED_'          : true,
			'_PHRASING_'        : true,
			'_REASSOCIATABLE_'  : true,
			'_RESETTABLE_'      : true,
			'_SUBMITTABLE_'     : true
		},
		'INS'        : FLOW_PHRASING_CATEGORY,
		'KBD'        : FLOW_PHRASING_CATEGORY,
		'KEYGEN'     : {
			'_FLOW_'            : true,
			'_FORM_ASSOCIATED_' : true,
			'_INTERACTIVE_'     : true,
			'_LABELABLE_'       : true,
			'_LISTED_'          : true,
			'_PHRASING_'        : true,
			'_REASSOCIATABLE_'  : true,
			'_RESETTABLE_'      : true,
			'_SUBMITTABLE_'     : true
		},
		'LABEL'      : {
			'_FLOW_'            : true,
			'_FORM_ASSOCIATED_' : true,
			'_INTERACTIVE_'     : true,
			'_PHRASING_'        : true,
			'_REASSOCIATABLE_'  : true
		},
		'LEGEND'     : {},
		'LI'         : {},
		'LINK'       : {
			'_FLOW_'            : true,
			'_METADATA_'        : true,
			'_PHRASING_'        : true
		},
		'MAIN'       : FLOW_CATEGORY,
		'MAP'        : FLOW_PHRASING_CATEGORY,
		'MARK'       : FLOW_PHRASING_CATEGORY,
		'MENU'       : FLOW_CATEGORY,
		'MENUITEM'   : FLOW_CATEGORY,
		'META'       : {
			'_FLOW_'            : true,
			'_METADATA_'        : true,
			'_PHRASING_'        : true
		},
		'METER'      : {
			'_FLOW_'            : true,
			'_LABELABLE_'       : true,
			'_PHRASING_'        : true
		},
		'NAV'        : {
			'_FLOW_'            : true,
			'_SECTIONING_'      : true
		},
		'NOSCRIPT'   : {
			'_FLOW_'            : true,
			'_METADATA_'        : true,
			'_PHRASING_'        : true
		},
		'OBJECT'     : {
			'_FLOW_'            : true,
			'_EMBEDDABLE_'      : true,
			'_FORM_ASSOCIATED_' : true,
			'_INTERACTIVE_'     : true,
			'_LISTED_'          : true,
			'_PHRASING_'        : true,
			'_REASSOCIATABLE_'  : true,
			'_SUBMITTABLE_'     : true
		},
		'OL'         : FLOW_CATEGORY,
		'OPTGROUP'   : {},
		'OPTION'     : {},
		'OUTPUT'     : {
			'_FLOW_'            : true,
			'_PHRASING_'        : true,
			'_LISTED_'          : true,
			'_LABELABLE_'       : true,
			'_RESETTALBE_'      : true,
			'_REASSOCIATABLE_'  : true,
			'_FORM_ASSOCIATED_' : true
		},
		'P'          : FLOW_CATEGORY,
		'PARAM'      : {},
		'PRE'        : FLOW_CATEGORY,
		'PROGRESS'   : {
			'_FLOW_'            : true,
			'_PHRASING_'        : true,
			'_LABELABLE_'       : true
		},
		'Q'          : FLOW_PHRASING_CATEGORY,
		'RP'         : {},
		'RT'         : {},
		'RUBY'       : FLOW_PHRASING_CATEGORY,
		'S'          : FLOW_PHRASING_CATEGORY,
		'SAMP'       : FLOW_PHRASING_CATEGORY,
		'SCRIPT'     : {
			'_FLOW_'              : true,
			'_PHRASING_'          : true,
			'_METADATA_'          : true,
			'_SCRIPT_SUPPORTING_' : true
		},
		'SECTION'    : {
			'_FLOW_'             : true,
			'_SECTIONING_'       : true
		},
		'SELECT'     : {
			'_FLOW_'            : true,
			'_PHRASING_'        : true,
			'_INTERACTIVE_'     : true,
			'_LISTED_'          : true,
			'_LABELABLE_'       : true,
			'_SUBMITTALBE_'     : true,
			'_RESETTALBE_'      : true,
			'_REASSOCIATABLE_'  : true,
			'_FORM_ASSOCIATED_' : true
		},
		'SMALL'      : FLOW_PHRASING_CATEGORY,
		'SOURCE'     : {},
		'SPAN'       : FLOW_PHRASING_CATEGORY,
		'STRONG'     : FLOW_PHRASING_CATEGORY,
		'STYLE'      : {
			'_FLOW_'            : true,
			'_METADATA_'        : true
		},
		'SUB'        : FLOW_PHRASING_CATEGORY,
		'SUMMARY'    : {},
		'SUP'        : FLOW_PHRASING_CATEGORY,
		'TABLE'      : FLOW_CATEGORY,
		'TBODY'      : {},
		'TD'         : {
			'_SECTIONING_ROOT_' : true
		},
		'TEMPLATE'   : {
			'_FLOW_'              : true,
			'_METADATA_'          : true,
			'_PHRASING_'          : true,
			'_SCRIPT_SUPPORTING_' : true
		},
		'TEXTAREA'   : {
			'_FLOW_'            : true,
			'_PHRASING_'        : true,
			'_INTERACTIVE_'     : true,
			'_LISTED_'          : true,
			'_LABELABLE_'       : true,
			'_SUBMITTALBE_'     : true,
			'_RESETTALBE_'      : true,
			'_REASSOCIATABLE_'  : true,
			'_FORM_ASSOCIATED_' : true
		},
		'TFOOT'      : {},
		'TH'         : {},
		'THEAD'      : {},
		'TIME'       : FLOW_PHRASING_CATEGORY,
		'TITLE'      : {
			'_METADATA_'        : true
		},
		'TR'         : {},
		'TRACK'      : {},
		'U'          : FLOW_PHRASING_CATEGORY,
		'UL'         : FLOW_CATEGORY,
		'VAR'        : FLOW_PHRASING_CATEGORY,
		'VIDEO'      : {
			'_FLOW_'            : true,
			'_PHRASING_'        : true,
			'_EMBEDDED_'        : true,
			'_INTERACTIVE_'     : true
		},
		'WBR'        : FLOW_PHRASING_CATEGORY,
		'#TEXT'      : FLOW_PHRASING_CATEGORY
	};

	/**
	 * @private
	 * @type {Object.<string, Array.<string>>}
	 */
	var DEFAULT_ATTRIBUTES_WHITELIST = {
		'IMG' : ['alt', 'src'],
		'A'   : ['href', 'name', '_target'],
		'TD'  : ['colspan', 'rowspan'],
		'TH'  : ['colspan', 'rowspan'],
		'OL'  : ['start', 'type'],
		'*'   : ['xstyle']
	};

	/**
	 * @private
	 * @type {Object.<string, Array.<string>>}
	 */
	var DEFAULT_STYLES_WHITELIST = {
		'TABLE' : ['width'],
		'IMG'   : ['width', 'height'],
		'*'     : [
			// '*',
			'color',
			'font-family', 'font-size', 'font-weight', 'font-stlye', 'font-decoration',
			'background', 'background-image', 'background-color'
		]
	};

	/**
	 * @private
	 * @type {Array.<string>}
	 */
	var NODES_BLACKLIST = {
		'AUDIO'    : true,
		'COMMAND'  : true,
		'COLGROUP' : true,
		'IFRAME'   : true,
		'INPUT'    : true,
		'INS'      : true,
		'KBD'      : true,
		'KEYGEN'   : true,
		'LINK'     : true,
		'META'     : true,
		'NOSCRIPT' : true,
		'OUTPUT'   : true,
		'Q'        : true,
		'RUBY'     : true,
		'SAMP'     : true,
		'SCRIPT'   : true,
		'SELECT'   : true,
		'STYLE'    : true,
		'TEMPLATE' : true,
		'TEXTAREA' : true,
		'TITLE'    : true,
		'WBR'      : true
	};

	/**
	 * @private
	 * @type {Object.<string, string>}
	 */
	var DEFAULT_TRANSLATION = {
		'FONT': 'SPAN'
	};

	/**
	 * This function is missing documentation.
	 * @TODO Complete documentation.
	 * @memberOf content
	 */
	function allowedStyles(overrides) {
		return Maps.merge({}, DEFAULT_STYLES_WHITELIST, overrides);
	}

	/**
	 * This function is missing documentation.
	 * @TODO Complete documentation.
	 * @memberOf content
	 */
	function allowedAttributes(overrides) {
		return Maps.merge({}, DEFAULT_ATTRIBUTES_WHITELIST, overrides);
	}

	/**
	 * This function is missing documentation.
	 * @TODO Complete documentation.
	 * @memberOf content
	 */
	function disallowedNodes(overrides) {
		return Maps.merge({}, NODES_BLACKLIST, overrides);
	}

	/**
	 * This function is missing documentation.
	 * @TODO Complete documentation.
	 * @memberOf content
	 */
	function nodeTranslations(overrides) {
		return Maps.merge({}, DEFAULT_TRANSLATION, overrides);
	}

	/**
	 * Checks whether the node name `outer` is allowed to contain a node with
	 * the node name `inner` as a direct descendant based on the HTML5
	 * specification.
	 *
	 * Reference:
	 * http://www.w3.org/html/wg/drafts/html/master/index.html#elements-1
	 * http://www.whatwg.org/specs/web-apps/current-work/#elements-1
	 *
	 * @param  {string} outer
	 * @param  {string} inner
	 * @return {boolean}
	 * @memberOf content
	 */
	function allowsNesting(outer, inner) {
		var categories;
		outer = outer.toUpperCase();
		inner = inner.toUpperCase();
		var allowed = ALLOWED_CHILDREN[outer];
		if (!allowed) {
			return false;
		}
		if ('string' === typeof allowed) {
			if (allowed === inner) {
				return true;
			}
			categories = CONTENT_CATEGORIES[inner];
			if (categories && categories[allowed]) {
				return true;
			}
		} else {
			if (allowed[inner]) {
				return true;
			}
			categories = CONTENT_CATEGORIES[inner];
			var category;
			for (category in categories) {
				if (categories.hasOwnProperty(category)) {
					if (allowed[category]) {
						return true;
					}
				}
			}
		}
		return false;
	}

	return {
		allowsNesting     : allowsNesting,
		allowedStyles     : allowedStyles,
		allowedAttributes : allowedAttributes,
		disallowedNodes   : disallowedNodes,
		nodeTranslations  : nodeTranslations
	};
});
comments powered by Disqus