Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
637 lines
21 KiB
JavaScript
Executable file
637 lines
21 KiB
JavaScript
Executable file
/**
|
|
* Process text/html for message body purposes. Specifically:
|
|
*
|
|
* - sanitize HTML (using bleach.js): discard illegal markup entirely, render
|
|
* legal but 'regulated' markup inert (ex: links to external content).
|
|
* - TODO: perform normalization of quote markup from different clients into
|
|
* blockquotes, like how Thunderbird conversations does it.
|
|
* - snippet generation: Try and generate a usable snippet string from something
|
|
* that is not a quote.
|
|
*
|
|
* We may eventually try and perform more detailed analysis like `quotechew.js`
|
|
* does with structured markup, potentially by calling out to quotechew, but
|
|
* that's a tall order to get right, so it's mightily postponed.
|
|
**/
|
|
|
|
define(
|
|
[
|
|
'exports',
|
|
'bleach'
|
|
],
|
|
function(
|
|
exports,
|
|
$bleach
|
|
) {
|
|
|
|
/**
|
|
* Whitelisted HTML tags list. Currently from nsTreeSanitizer.cpp which credits
|
|
* Mark Pilgrim and Sam Ruby for its own initial whitelist.
|
|
*
|
|
* IMPORTANT THUNDERBIRD NOTE: Thunderbird only engages its sanitization logic
|
|
* when processing mailto URIs, when the non-default
|
|
* "view | message body as | simple html" setting is selected, or when
|
|
* displaying spam messages. Accordingly, the settings are pretty strict
|
|
* and not particularly thought-out. Non-CSS presentation is stripped, which
|
|
* is pretty much the lingua franca of e-mail. (Thunderbird itself generates
|
|
* font tags, for example.)
|
|
*
|
|
* Some things are just not in the list at all:
|
|
* - SVG: Thunderbird nukes these itself because it forces
|
|
* SanitizerCidEmbedsOnly which causes flattening of everything in the SVG
|
|
* namespace.
|
|
*
|
|
* Tags that we are opting not to include will be commented with a reason tag:
|
|
* - annoying: This thing is ruled out currently because it only allows annoying
|
|
* things to happen *given our current capabilities*.
|
|
* - scripty: This thing requires scripting to make anything happen, and we do
|
|
* not allow scripting.
|
|
* - forms: We have no UI to expose the target of a form right now, so it's
|
|
* not safe. Thunderbird displays a scam warning, which isn't realy a big
|
|
* help, but it's something. Because forms are largely unsupported or just
|
|
* broken in many places, they are rarely used, so we are turning them off
|
|
* entirely.
|
|
* - non-body: previously killed as part of the parse process because we were
|
|
* assigning to innerHTML rather than creating a document with the string in
|
|
* it. We could change this up in a future bug now.
|
|
* - dangerous: The semantics of the tag are intentionally at odds with our
|
|
* goals and/or are extensible. (ex: link tag.) Our callbacks could be
|
|
* used to only let through okay things.
|
|
* - interactive-ui: A cross between scripty and forms, things like (HTML5)
|
|
* menu and command imply some type of mutation that requires scripting.
|
|
* They also are frequently very attribute-heavy.
|
|
* - svg: it's SVG, we don't support it yet!
|
|
*/
|
|
var LEGAL_TAGS = [
|
|
'a', 'abbr', 'acronym', 'area', 'article', 'aside',
|
|
// annoying: 'audio',
|
|
'b',
|
|
'bdi', 'bdo', // (bidirectional markup stuff)
|
|
'big', 'blockquote',
|
|
// implicitly-nuked: 'body'
|
|
'br',
|
|
// forms: 'button',
|
|
// scripty: canvas
|
|
'caption',
|
|
'center',
|
|
'cite', 'code', 'col', 'colgroup',
|
|
// interactive-ui: 'command',
|
|
// forms: 'datalist',
|
|
'dd', 'del', 'details', 'dfn', 'dir', 'div', 'dl', 'dt',
|
|
'em',
|
|
// forms: 'fieldset' (but allowed by nsTreeSanitizer)
|
|
'figcaption', 'figure',
|
|
'font',
|
|
'footer',
|
|
// forms: 'form',
|
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
// non-body: 'head'
|
|
'header', 'hgroup', 'hr',
|
|
// non-body: 'html'
|
|
'i', 'img',
|
|
// forms: 'input',
|
|
'ins', // ("represents a range of text that has been inserted to a document")
|
|
'kbd', // ("The kbd element represents user input")
|
|
'label', 'legend', 'li',
|
|
// dangerous: link (for CSS styles
|
|
/* link supports many types, none of which we want, some of which are
|
|
* risky: http://dev.w3.org/html5/spec/links.html#linkTypes. Specifics:
|
|
* - "stylesheet": This would be okay for cid links, but there's no clear
|
|
* advantage over inline styles, so we forbid it, especially as supporting
|
|
* it might encourage other implementations to dangerously support link.
|
|
* - "prefetch": Its whole point is de facto information leakage.
|
|
*/
|
|
'listing', // (deprecated, like "pre")
|
|
'map', 'mark',
|
|
// interactive-ui: 'menu', 'meta', 'meter',
|
|
'nav',
|
|
'nobr', // (deprecated "white-space:nowrap" equivalent)
|
|
'noscript',
|
|
'ol',
|
|
// forms: 'optgroup',
|
|
// forms: 'option',
|
|
'output', // (HTML5 draft: "result of a calculation in a form")
|
|
'p', 'pre',
|
|
// interactive-ui: 'progress',
|
|
'q',
|
|
/* http://www.w3.org/TR/ruby/ is a pronounciation markup that is not directly
|
|
* supported by gecko at this time (although there is a Firefox extension).
|
|
* All of 'rp', 'rt', and 'ruby' are ruby tags. The spec also defines 'rb'
|
|
* and 'rbc' tags that nsTreeSanitizer does not whitelist, however.
|
|
*/
|
|
'rp', 'rt', 'ruby',
|
|
's', 'samp', 'section',
|
|
// forms: 'select',
|
|
'small',
|
|
// annoying?: 'source',
|
|
'span', 'strike', 'strong',
|
|
'style',
|
|
'sub', 'summary', 'sup',
|
|
// svg: 'svg', NB: this lives in its own namespace
|
|
'table', 'tbody', 'td',
|
|
// forms: 'textarea',
|
|
'tfoot', 'th', 'thead', 'time',
|
|
'title', // XXX does this mean anything outside head?
|
|
'tr',
|
|
// annoying?: 'track'
|
|
'tt',
|
|
'u', 'ul', 'var',
|
|
// annoying: 'video',
|
|
'wbr' // (HTML5 draft: line break opportunity)
|
|
];
|
|
|
|
/**
|
|
* Tags whose children should be removed along with the tag itself, rather than
|
|
* splicing the children into the position originally occupied by the parent.
|
|
*
|
|
* We do this for:
|
|
* - forms; see `LEGAL_TAGS` for the rationale. Note that we don't bother
|
|
* including children that should already be nuked by PRUNE_TAGS. For
|
|
* example, 'option' and 'optgroup' only make sense under 'select' or
|
|
* 'datalist', so we need not include them. This means that if the tags
|
|
* are used in nonsensical positions, they will have their contents
|
|
* merged into the document text, but that's not a major concern.
|
|
* - non-body: don't have stuff from the header show up like it's part of the
|
|
* body! For now we do want <style> tags to fall out, but we want <title>
|
|
* to not show up, etc.
|
|
* - 'script': no one wants to read the ignored JS code!
|
|
* Note that bleach.js now is aware of the special nature of 'script' and
|
|
* 'style' tags, so putting them in prune is not strictly required.
|
|
*/
|
|
var PRUNE_TAGS = [
|
|
'button', // (forms)
|
|
'datalist', // (forms)
|
|
'script', // (script)
|
|
'select', // (forms)
|
|
'svg', // (svg)
|
|
'title', // (non-body)
|
|
];
|
|
|
|
/**
|
|
* What attributes to allow globally and on specific tags.
|
|
*
|
|
* Forbidden marker names:
|
|
* - URL-like: The attribute can contain URL's and we don't care enough to
|
|
* sanitize the contents right now.
|
|
* - sanitized: We manually do something with the attribute in our processing
|
|
* logic.
|
|
* - specific: The attribute is explicitly named on the relevant element types.
|
|
* - unsupported: Gecko ignores the attribute and there is no chance of
|
|
* standardization, so just strip it.
|
|
* - microformat: we can't do anything with microformats right now, save some
|
|
* space.
|
|
* - awkward: It's not dangerous, but it's not clear how it could have useful
|
|
* semantics.
|
|
*/
|
|
var LEGAL_ATTR_MAP = {
|
|
'*': [
|
|
'abbr', // (tables: removed from HTML5)
|
|
// forms: 'accept', 'accept-charset',
|
|
// interactive-ui: 'accesskey',
|
|
// forms: 'action',
|
|
'align', // (pres)
|
|
'alt', // (fallback content)
|
|
// forms: 'autocomplete', 'autofocus',
|
|
// annoying: 'autoplay',
|
|
'axis', // (tables: removed from HTML5)
|
|
// URL-like: 'background',
|
|
'bgcolor', 'border', // (pres)
|
|
'cellpadding', 'cellspacing', // (pres)
|
|
// unsupported: 'char',
|
|
'charoff', // (tables)
|
|
// specific: 'charset'
|
|
// forms, interactive-ui: 'checked',
|
|
// URL-like: 'cite'
|
|
'class', 'clear', 'color', // (pres)
|
|
'cols', 'colspan', // (tables)
|
|
'compact', // (pres)
|
|
// dangerous: 'content', (meta content refresh is bad.)
|
|
// interactive-ui: 'contenteditable', (we already use this ourselves!)
|
|
// interactive-ui: 'contextmenu',
|
|
// annoying: 'controls', (media)
|
|
'coords', // (area image map)
|
|
'datetime', // (ins, del, time semantic markups)
|
|
// forms: 'disabled',
|
|
'dir', // (rtl)
|
|
// interactive-ui: 'draggable',
|
|
// forms: 'enctype',
|
|
'face', // (pres)
|
|
// forms: 'for',
|
|
'frame', // (tables)
|
|
'headers', // (tables)
|
|
'height', // (layout)
|
|
// interactive-ui: 'hidden', 'high',
|
|
// sanitized: 'href',
|
|
// specific: 'hreflang',
|
|
'hspace', // (pres)
|
|
// dangerous: 'http-equiv' (meta refresh, maybe other trickiness)
|
|
// interactive-ui: 'icon',
|
|
'id', // (pres; white-listed for style targets)
|
|
// specific: 'ismap', (area image map)
|
|
// microformat: 'itemid', 'itemprop', 'itemref', 'itemscope', 'itemtype',
|
|
// annoying: 'kind', (media)
|
|
// annoying, forms, interactive-ui: 'label',
|
|
'lang', // (language support)
|
|
// forms: 'list',
|
|
// dangerous: 'longdesc', (link to a long description, html5 removed)
|
|
// annoying: 'loop',
|
|
// interactive-ui: 'low',
|
|
// forms, interactive-ui: 'max',
|
|
// forms: 'maxlength',
|
|
'media', // (media-query for linky things; safe if links are safe)
|
|
// forms: 'method',
|
|
// forms, interactive-ui: 'min',
|
|
// unsupported: 'moz-do-not-send', (thunderbird internal composition)
|
|
// forms: 'multiple',
|
|
// annoying: 'muted',
|
|
// forms, interactive-ui: 'name', (although pretty safe)
|
|
'nohref', // (image maps)
|
|
// forms: 'novalidate',
|
|
'noshade', // (pres)
|
|
'nowrap', // (tables)
|
|
'open', // (for "details" element)
|
|
// interactive-ui: 'optimum',
|
|
// forms: 'pattern', 'placeholder',
|
|
// annoying: 'playbackrate',
|
|
'pointsize', // (pres)
|
|
// annoying: 'poster', 'preload',
|
|
// forms: 'prompt',
|
|
'pubdate', // ("time" element)
|
|
// forms: 'radiogroup', 'readonly',
|
|
// dangerous: 'rel', (link rel, a rel, area rel)
|
|
// forms: 'required',
|
|
// awkward: 'rev' (reverse link; you can't really link to emails)
|
|
'reversed', // (pres? "ol" reverse numbering)
|
|
// interactive-ui: 'role', We don't want a screen reader making the user
|
|
// think that part of the e-mail is part of the UI. (WAI-ARIA defines
|
|
// "accessible rich internet applications", not content markup.)
|
|
'rows', 'rowspan', 'rules', // (tables)
|
|
// sanitized: 'src',
|
|
'size', // (pres)
|
|
'scope', // (tables)
|
|
'scoped', // (pres; on "style" elem)
|
|
// forms: 'selected',
|
|
'shape', // (image maps)
|
|
'span', // (tables)
|
|
// interactive-ui: 'spellcheck',
|
|
// sanitized, dangerous: 'src'
|
|
// annoying: 'srclang',
|
|
'start', // (pres? "ol" numbering)
|
|
'summary', // (tables accessibility)
|
|
'style', // (pres)
|
|
// interactive-ui: 'tabindex',
|
|
// dangerous: 'target', (specifies a browsing context, but our semantics
|
|
// are extremely clear and don't need help.)
|
|
'title', // (advisory)
|
|
// specific, dangerous: type (various, but mime-type for links is not the
|
|
// type of thing we would ever want to propagate or potentially deceive
|
|
// the user with.)
|
|
'valign', // (pres)
|
|
'value', // (pres? "li" override for "ol"; various form uses)
|
|
'vspace', // (pres)
|
|
'width', // (layout)
|
|
// forms: 'wrap',
|
|
],
|
|
'a': ['ext-href', 'hreflang'],
|
|
'area': ['ext-href', 'hreflang'],
|
|
// these are used by our quoting and Thunderbird's quoting
|
|
'blockquote': ['cite', 'type'],
|
|
'img': ['cid-src', 'ext-src', 'ismap', 'usemap'],
|
|
// This may only end up being used as a debugging thing, but let's let charset
|
|
// through for now.
|
|
'meta': ['charset'],
|
|
'ol': ['type'], // (pres)
|
|
'style': ['type'],
|
|
};
|
|
|
|
/**
|
|
* CSS Style rules to support.
|
|
*
|
|
* nsTreeSanitizer is super lazy about style binding and does not help us out.
|
|
* What it does is nuke all rule types except NAMESPACE (@namespace), FONT_FACE
|
|
* (@font-face), and STYLE rules (actual styling). This means nuking CHARSET
|
|
* (@charset to specify the encoding of the stylesheet if the server did not
|
|
* provide it), IMPORT (@import to reference other stylesheet files), MEDIA
|
|
* (@media media queries), PAGE (@page page box info for paged media),
|
|
* MOZ_KEYFRAMES, MOZ_KEYFRAME, SUPPORTS (@supports provides support for rules
|
|
* conditioned on browser support, but is at risk.) The only style directive it
|
|
* nukes is "-moz-binding" which is the XBL magic and considered dangerous.
|
|
*
|
|
* Risks: Anything that takes a url() is dangerous insofar as we need to
|
|
* sanitize the url. XXX for now we just avoid any style that could potentially
|
|
* hold a URI.
|
|
*
|
|
* Good news: We always cram things into an iframe, so we don't need to worry
|
|
* about clever styling escaping out into our UI.
|
|
*
|
|
* New reasons not to allow:
|
|
* - animation: We don't want or need animated wackiness.
|
|
* - slow: Doing the thing is slow!
|
|
*/
|
|
var LEGAL_STYLES = [
|
|
// animation: animation*
|
|
// URI-like: background, background-image
|
|
'background-color',
|
|
// NB: border-image is not set by the 'border' aliases
|
|
'border',
|
|
'border-bottom', 'border-bottom-color', 'border-bottom-left-radius',
|
|
'border-bottom-right-radius', 'border-bottom-style', 'border-bottom-width',
|
|
'border-color',
|
|
// URI-like: border-image*
|
|
'border-left', 'border-left-color', 'border-left-style', 'border-left-width',
|
|
'border-radius',
|
|
'border-right', 'border-right-color', 'border-right-style',
|
|
'border-right-width',
|
|
'border-style',
|
|
'border-top', 'border-top-color', 'border-top-left-radius',
|
|
'border-top-right-radius', 'border-top-style', 'border-top-width',
|
|
'border-width',
|
|
// slow: box-shadow
|
|
'clear',
|
|
'color',
|
|
'display',
|
|
'float',
|
|
'font-family',
|
|
'font-size',
|
|
'font-style',
|
|
'font-weight',
|
|
'height',
|
|
'line-height',
|
|
// URI-like: list-style, list-style-image
|
|
'list-style-position',
|
|
'list-style-type',
|
|
'margin', 'margin-bottom', 'margin-left', 'margin-right', 'margin-top',
|
|
'padding', 'padding-bottom', 'padding-left', 'padding-right', 'padding-top',
|
|
'text-align', 'text-align-last',
|
|
'text-decoration', 'text-decoration-color', 'text-decoration-line',
|
|
'text-decoration-style', 'text-indent',
|
|
'vertical-align',
|
|
'white-space',
|
|
'width',
|
|
'word-break', 'word-spacing', 'word-wrap',
|
|
];
|
|
|
|
/**
|
|
* The regular expression to detect nodes that should be passed to stashLinks.
|
|
*
|
|
* ignore-case is not required; the value is checked against the lower-cased tag.
|
|
*/
|
|
var RE_NODE_NEEDS_TRANSFORM = /^(?:a|area|img)$/;
|
|
|
|
var RE_CID_URL = /^cid:/i;
|
|
var RE_HTTP_URL = /^http(?:s)?/i;
|
|
var RE_MAILTO_URL = /^mailto:/i;
|
|
|
|
var RE_IMG_TAG = /^img$/;
|
|
|
|
function getAttributeFromList(attrs, name) {
|
|
var len = attrs.length;
|
|
for (var i = 0; i < len; i++) {
|
|
var attr = attrs[i];
|
|
if (attr.name.toLowerCase() === name) {
|
|
return attr;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Transforms src tags, ensure that links are http and transform them too so
|
|
* that they don't actually navigate when clicked on but we can hook them. (The
|
|
* HTML display iframe is not intended to navigate; we just want to trigger the
|
|
* browser.
|
|
*/
|
|
function stashLinks(lowerTag, attrs) {
|
|
var classAttr;
|
|
// - img: src
|
|
if (RE_IMG_TAG.test(lowerTag)) {
|
|
// filter out things we might write to, also find the 'class attr'
|
|
attrs = attrs.filter(function(attr) {
|
|
switch (attr.name.toLowerCase()) {
|
|
case 'cid-src':
|
|
case 'ext-src':
|
|
return false;
|
|
case 'class':
|
|
classAttr = attr;
|
|
default:
|
|
return true;
|
|
}
|
|
});
|
|
|
|
var srcAttr = getAttributeFromList(attrs, 'src');
|
|
if (srcAttr) {
|
|
if (RE_CID_URL.test(srcAttr.escaped)) {
|
|
srcAttr.name = 'cid-src';
|
|
if (classAttr)
|
|
classAttr.escaped += ' moz-embedded-image';
|
|
else
|
|
attrs.push({ name: 'class', escaped: 'moz-embedded-image' });
|
|
// strip the cid: bit, it is necessarily there and therefore redundant.
|
|
srcAttr.escaped = srcAttr.escaped.substring(4);
|
|
}
|
|
else if (RE_HTTP_URL.test(srcAttr.escaped)) {
|
|
srcAttr.name = 'ext-src';
|
|
if (classAttr)
|
|
classAttr.escaped += ' moz-external-image';
|
|
else
|
|
attrs.push({ name: 'class', escaped: 'moz-external-image' });
|
|
}
|
|
}
|
|
}
|
|
// - a, area: href
|
|
else {
|
|
// filter out things we might write to, also find the 'class attr'
|
|
attrs = attrs.filter(function(attr) {
|
|
switch (attr.name.toLowerCase()) {
|
|
case 'cid-src':
|
|
case 'ext-src':
|
|
return false;
|
|
case 'class':
|
|
classAttr = attr;
|
|
default:
|
|
return true;
|
|
}
|
|
});
|
|
var linkAttr = getAttributeFromList(attrs, 'href');
|
|
if (linkAttr) {
|
|
var link = linkAttr.escaped;
|
|
if (RE_HTTP_URL.test(link) ||
|
|
RE_MAILTO_URL.test(link)) {
|
|
|
|
linkAttr.name = 'ext-href';
|
|
if (classAttr)
|
|
classAttr.escaped += ' moz-external-link';
|
|
else
|
|
attrs.push({ name: 'class', escaped: 'moz-external-link' });
|
|
}
|
|
else {
|
|
// paranoia; no known benefit if this got through
|
|
attrs.splice(attrs.indexOf(linkAttr), 1);
|
|
}
|
|
}
|
|
}
|
|
return attrs;
|
|
}
|
|
|
|
var BLEACH_SETTINGS = {
|
|
tags: LEGAL_TAGS,
|
|
strip: true,
|
|
stripComments: true,
|
|
prune: PRUNE_TAGS,
|
|
attributes: LEGAL_ATTR_MAP,
|
|
styles: LEGAL_STYLES,
|
|
asNode: true,
|
|
callbackRegexp: RE_NODE_NEEDS_TRANSFORM,
|
|
callback: stashLinks
|
|
};
|
|
|
|
var BLEACH_SNIPPET_SETTINGS = {
|
|
tags: [],
|
|
strip: true,
|
|
stripComments: true,
|
|
prune: [
|
|
'style',
|
|
'button', // (forms)
|
|
'datalist', // (forms)
|
|
'script', // (script)
|
|
'select', // (forms)
|
|
'svg', // (svg)
|
|
'title' // (non-body)
|
|
],
|
|
asNode: true,
|
|
maxLength: 100
|
|
};
|
|
|
|
/**
|
|
* @args[
|
|
* @param[htmlString String]{
|
|
* An unsanitized HTML string. The HTML content can be a fully valid HTML
|
|
* document with 'html' and 'body' tags and such, but most of that extra
|
|
* structure will currently be discarded.
|
|
*
|
|
* In the future we may try and process the body and such correctly, but for
|
|
* now we don't. This is consistent with many webmail clients who ignore
|
|
* style tags in the head, etc.
|
|
* }
|
|
* ]
|
|
* @return[HtmlString]{
|
|
* The sanitized HTML string wrapped into a div container.
|
|
* }
|
|
*/
|
|
exports.sanitizeAndNormalizeHtml = function sanitizeAndNormalize(htmlString) {
|
|
return $bleach.clean(htmlString, BLEACH_SETTINGS);
|
|
};
|
|
|
|
/**
|
|
* Derive snippet text from the an HTML string. It will also sanitize it.
|
|
* Note that it unescapes HTML enttities, so best to only use this output
|
|
* in textContent cases.
|
|
*/
|
|
exports.generateSnippet = function generateSnippet(htmlString) {
|
|
return $bleach.unescapeHTMLEntities($bleach.clean(htmlString,
|
|
BLEACH_SNIPPET_SETTINGS));
|
|
};
|
|
|
|
var BLEACH_SEARCHABLE_TEXT_WITH_QUOTES_SETTINGS = {
|
|
tags: [],
|
|
strip: true,
|
|
stripComments: true,
|
|
prune: [
|
|
'style',
|
|
'button', // (forms)
|
|
'datalist', // (forms)
|
|
'script', // (script)
|
|
'select', // (forms)
|
|
'svg', // (svg)
|
|
'title' // (non-body)
|
|
],
|
|
asNode: true,
|
|
};
|
|
|
|
var BLEACH_SEARCHABLE_TEXT_WITHOUT_QUOTES_SETTINGS = {
|
|
tags: [],
|
|
strip: true,
|
|
stripComments: true,
|
|
prune: [
|
|
'style',
|
|
'button', // (forms)
|
|
'datalist', // (forms)
|
|
'script', // (script)
|
|
'select', // (forms)
|
|
'svg', // (svg)
|
|
'title', // (non-body),
|
|
// specific to getting rid of quotes:
|
|
'blockquote'
|
|
],
|
|
asNode: true,
|
|
};
|
|
|
|
|
|
/**
|
|
* Produce a textual version of the body of the e-mail suitable for search
|
|
* purposes. This is basically the same thing as generateSnippet but without a
|
|
* length limit applied and with the ability to either include quoted text or
|
|
* not include quoted text. We do process the entire document in a go and
|
|
* return the entire results, so this could be fairly inefficient from a
|
|
* memory/time perspective.
|
|
*
|
|
* The following potential enhancements could be fairly good ideas:
|
|
* - Avoid processing the entire HTML document by passing the search string in
|
|
* or using a generator-type implementation to yield incremental string hunks.
|
|
* - Generate a semantic representation similar/identical to the one used by
|
|
* quotechew (at least for this searchable text mode.)
|
|
*/
|
|
exports.generateSearchableTextVersion = function(htmlString, includeQuotes) {
|
|
var settings;
|
|
if (includeQuotes) {
|
|
settings = BLEACH_SEARCHABLE_TEXT_WITH_QUOTES_SETTINGS;
|
|
}
|
|
else {
|
|
settings = BLEACH_SEARCHABLE_TEXT_WITHOUT_QUOTES_SETTINGS;
|
|
}
|
|
var cleaned = $bleach.clean(htmlString, settings);
|
|
return $bleach.unescapeHTMLEntities(cleaned);
|
|
};
|
|
|
|
/**
|
|
* Wrap text/plain content into a serialized HTML string safe for insertion
|
|
* via innerHTML.
|
|
*
|
|
* By default we wrap everything in a 'div' tag with 'br' indicating newlines.
|
|
* Alternately, we could use 'white-space: pre-wrap' if we were more confident
|
|
* about recipients having sufficient CSS support and our own desire to have
|
|
* things resemble text/plain.
|
|
*
|
|
*/
|
|
exports.wrapTextIntoSafeHTMLString = function(text, wrapTag,
|
|
transformNewlines, attrs) {
|
|
if (transformNewlines === undefined) {
|
|
transformNewlines = true;
|
|
}
|
|
|
|
wrapTag = wrapTag || 'div';
|
|
|
|
text = $bleach.escapePlaintextIntoElementContext(text);
|
|
text = transformNewlines ? text.replace(/\n/g, '<br/>') : text;
|
|
|
|
var attributes = '';
|
|
if (attrs) {
|
|
var len = attrs.length;
|
|
for (var i = 0; i < len; i += 2) {
|
|
attributes += ' ' + attrs[i] + '="' +
|
|
$bleach.escapePlaintextIntoAttribute(attrs[i + 1]) + '"';
|
|
}
|
|
}
|
|
|
|
return '<' + wrapTag + attributes + '>' + text + '</' + wrapTag + '>';
|
|
};
|
|
|
|
var RE_QUOTE_CHAR = /"/g;
|
|
|
|
/**
|
|
* Make an HTML attribute value safe.
|
|
*/
|
|
exports.escapeAttrValue = function(s) {
|
|
return s.replace(RE_QUOTE_CHAR, '"');
|
|
};
|
|
|
|
}); // end define
|