Implementation of "muting" of email addresses in zoom view.

This commit is contained in:
Martin Edenhofer 2014-10-29 10:39:11 +01:00
parent d9938f59c6
commit 239cfa3e6c
7 changed files with 1003 additions and 30 deletions

View file

@ -229,7 +229,7 @@ class App.Controller extends Spine.Controller
title: ->
ticket_id = $(@).data('id')
ticket = App.Ticket.fullLocal( ticket_id )
App.i18n.escape( ticket.title )
HTMLEscape( ticket.title )
content: ->
ticket_id = $(@).data('id')
ticket = App.Ticket.fullLocal( ticket_id )
@ -265,7 +265,7 @@ class App.Controller extends Spine.Controller
title: ->
user_id = $(@).data('id')
user = App.User.fullLocal( user_id )
App.i18n.escape( user.displayName() )
HTMLEscape( user.displayName() )
content: ->
user_id = $(@).data('id')
user = App.User.fullLocal( user_id )
@ -314,7 +314,7 @@ class App.Controller extends Spine.Controller
title: ->
organization_id = $(@).data('id')
organization = App.Organization.fullLocal( organization_id )
App.i18n.escape( organization.name )
HTMLEscape( organization.name )
content: ->
organization_id = $(@).data('id')
organization = App.Organization.fullLocal( organization_id )

View file

@ -441,12 +441,12 @@ class App.GenericHistory extends App.ControllerModal
if item.value_from
if item.value_to
content += " #{ @T( 'from' ) }"
content += " '#{ App.i18n.escape(item.value_from) }'"
content += " '#{ HTMLEscape(item.value_from) }'"
if item.value_to
if item.value_from
content += " #{ @T( 'to' ) }"
content += " '#{ App.i18n.escape(item.value_to) }'"
content += " '#{ HTMLEscape(item.value_to) }'"
newItem.records.push content

View file

@ -103,6 +103,27 @@ class App extends Spine.Controller
params.S = ( key ) ->
App.Session.get( key )
# define address line helper
params.AddressLine = ( line ) ->
return '' if !line
items = emailAddresses.parseAddressList(line)
# line was not parsable
if !items
return line
# set markup
result = ''
for item in items
if result
result = result + ', '
if item.name
result = result + HTMLEscape(item.name) + ' '
if item.address
result = result + " <span class=\"text-muted\">&lt;#{HTMLEscape(item.address)}&gt</span>"
result
# define template
JST["app/views/#{name}"](params)
template

View file

@ -39,11 +39,6 @@ class App.i18n
_instance ?= new _i18nSingleton( args )
_instance.set( args )
@escape: ( args ) ->
if _instance == undefined
_instance ?= new _i18nSingleton( args )
_instance.escape( args )
class _i18nSingleton extends Spine.Module
@include App.LogInclude
@ -135,13 +130,13 @@ class _i18nSingleton extends Spine.Module
)
translateInline: ( string, args... ) =>
@escape( @translate( string, args... ) )
HTMLEscape( @translate( string, args... ) )
translateContent: ( string, args... ) =>
translated = @escape( @translate( string, args... ) )
# replace = '<span class="translation" contenteditable="true" data-text="' + @escape(string) + '">' + translated + '<span class="icon-edit"></span>'
translated = HTMLEscape( @translate( string, args... ) )
# replace = '<span class="translation" contenteditable="true" data-text="' + HTMLEscape(string) + '">' + translated + '<span class="icon-edit"></span>'
if App.Config.get( 'Translation' )
replace = '<span class="translation" contenteditable="true" data-text="' + @escape(string) + '">' + translated + ''
replace = '<span class="translation" contenteditable="true" data-text="' + HTMLEscape(string) + '">' + translated + ''
# if !@_translated
# replace += '<span class="missing">XX</span>'
replace += '</span>'
@ -179,13 +174,6 @@ class _i18nSingleton extends Spine.Module
# return translated string
return translated
escape: ( string ) ->
string = ( '' + string )
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\x22/g, '&quot;')
date: ( time, offset ) =>
@convert(time, offset, @dateFormat)

View file

@ -0,0 +1,955 @@
// email-addresses.js - RFC 5322 email address parser
// v 2.0.0
//
// http://tools.ietf.org/html/rfc5322
//
// This library does not validate email addresses.
// emailAddresses attempts to parse addresses using the (fairly liberal)
// grammar specified in RFC 5322.
//
// email-addresses returns {
// ast: <an abstract syntax tree based on rfc5322>,
// addresses: [{
// node: <node in ast for this address>,
// name: <display-name>,
// address: <addr-spec>,
// local: <local-part>,
// domain: <domain>
// }, ...]
// }
//
// emailAddresses.parseOneAddress and emailAddresses.parseAddressList
// work as you might expect. Try it out.
//
// Many thanks to Dominic Sayers and his documentation on the is_email function,
// http://code.google.com/p/isemail/ , which helped greatly in writing this parser.
(function (global) {
"use strict";
function parse5322(opts) {
// tokenizing functions
function inStr() { return pos < len; }
function curTok() { return parseString[pos]; }
function getPos() { return pos; }
function setPos(i) { pos = i; }
function nextTok() { pos += 1; }
function initialize() {
pos = 0;
len = parseString.length;
}
// parser helper functions
function o(name, value) {
return {
name: name,
tokens: value || "",
semantic: value || "",
children: []
};
}
function wrap(name, ast) {
var n;
if (ast === null) { return null; }
n = o(name);
n.tokens = ast.tokens;
n.semantic = ast.semantic;
n.children.push(ast);
return n;
}
function add(parent, child) {
if (child !== null) {
parent.tokens += child.tokens;
parent.semantic += child.semantic;
}
parent.children.push(child);
return parent;
}
function compareToken(fxnCompare) {
var tok;
if (!inStr()) { return null; }
tok = curTok();
if (fxnCompare(tok)) {
nextTok();
return o('token', tok);
}
return null;
}
function literal(lit) {
return function literalFunc() {
return wrap('literal', compareToken(function (tok) {
return tok === lit;
}));
};
}
function and() {
var args = arguments;
return function andFunc() {
var i, s, result, start;
start = getPos();
s = o('and');
for (i = 0; i < args.length; i += 1) {
result = args[i]();
if (result === null) {
setPos(start);
return null;
}
add(s, result);
}
return s;
};
}
function or() {
var args = arguments;
return function orFunc() {
var i, result, start;
start = getPos();
for (i = 0; i < args.length; i += 1) {
result = args[i]();
if (result !== null) {
return result;
}
setPos(start);
}
return null;
};
}
function opt(prod) {
return function optFunc() {
var result, start;
start = getPos();
result = prod();
if (result !== null) {
return result;
}
else {
setPos(start);
return o('opt');
}
};
}
function invis(prod) {
return function invisFunc() {
var result = prod();
if (result !== null) {
result.semantic = "";
}
return result;
};
}
function colwsp(prod) {
return function collapseSemanticWhitespace() {
var result = prod();
if (result !== null && result.semantic.length > 0) {
result.semantic = " ";
}
return result;
};
}
function star(prod, minimum) {
return function starFunc() {
var s, result, count, start, min;
start = getPos();
s = o('star');
count = 0;
min = minimum === undefined ? 0 : minimum;
while ((result = prod()) !== null) {
count = count + 1;
add(s, result);
}
if (count >= min) {
return s;
}
else {
setPos(start);
return null;
}
};
}
// One expects names to get normalized like this:
// " First Last " -> "First Last"
// "First Last" -> "First Last"
// "First Last" -> "First Last"
function collapseWhitespace(s) {
function isWhitespace(c) {
return c === ' ' ||
c === '\t' ||
c === '\r' ||
c === '\n';
}
var i, str;
str = "";
for (i = 0; i < s.length; i += 1) {
if (!isWhitespace(s[i]) || !isWhitespace(s[i + 1])) {
str += s[i];
}
}
if (isWhitespace(str[0])) {
str = str.substring(1);
}
if (isWhitespace(str[str.length - 1])) {
str = str.substring(0, str.length - 1);
}
return str;
}
// UTF-8 pseudo-production (RFC 6532)
// RFC 6532 extends RFC 5322 productions to include UTF-8
// using the following productions:
// UTF8-non-ascii = UTF8-2 / UTF8-3 / UTF8-4
// UTF8-2 = <Defined in Section 4 of RFC3629>
// UTF8-3 = <Defined in Section 4 of RFC3629>
// UTF8-4 = <Defined in Section 4 of RFC3629>
//
// For reference, the extended RFC 5322 productions are:
// VCHAR =/ UTF8-non-ascii
// ctext =/ UTF8-non-ascii
// atext =/ UTF8-non-ascii
// qtext =/ UTF8-non-ascii
// dtext =/ UTF8-non-ascii
function isUTF8NonAscii(tok) {
// In JavaScript, we just deal directly with Unicode code points,
// so we aren't checking individual bytes for UTF-8 encoding.
// Just check that the character is non-ascii.
return tok.charCodeAt(0) >= 128;
}
// common productions (RFC 5234)
// http://tools.ietf.org/html/rfc5234
// B.1. Core Rules
// CR = %x0D
// ; carriage return
function cr() { return wrap('cr', literal('\r')()); }
// CRLF = CR LF
// ; Internet standard newline
function crlf() { return wrap('crlf', and(cr, lf)()); }
// DQUOTE = %x22
// ; " (Double Quote)
function dquote() { return wrap('dquote', literal('"')()); }
// HTAB = %x09
// ; horizontal tab
function htab() { return wrap('htab', literal('\t')()); }
// LF = %x0A
// ; linefeed
function lf() { return wrap('lf', literal('\n')()); }
// SP = %x20
function sp() { return wrap('sp', literal(' ')()); }
// VCHAR = %x21-7E
// ; visible (printing) characters
function vchar() {
return wrap('vchar', compareToken(function vcharFunc(tok) {
var code = tok.charCodeAt(0);
var accept = (0x21 <= code && code <= 0x7E);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
}));
}
// WSP = SP / HTAB
// ; white space
function wsp() { return wrap('wsp', or(sp, htab)()); }
// email productions (RFC 5322)
// http://tools.ietf.org/html/rfc5322
// 3.2.1. Quoted characters
// quoted-pair = ("\" (VCHAR / WSP)) / obs-qp
function quotedPair() {
var qp = wrap('quoted-pair',
or(
and(literal('\\'), or(vchar, wsp)),
obsQP
)());
if (qp === null) { return null; }
// a quoted pair will be two characters, and the "\" character
// should be semantically "invisible" (RFC 5322 3.2.1)
qp.semantic = qp.semantic[1];
return qp;
}
// 3.2.2. Folding White Space and Comments
// FWS = ([*WSP CRLF] 1*WSP) / obs-FWS
// obs-FWS = 1*WSP *(CRLF 1*WSP)
function fws() {
return wrap('fws', or(
obsFws,
and(
opt(and(
star(wsp),
invis(crlf)
)),
star(wsp, 1)
)
)());
}
// ctext = %d33-39 / ; Printable US-ASCII
// %d42-91 / ; characters not including
// %d93-126 / ; "(", ")", or "\"
// obs-ctext
function ctext() {
return wrap('ctext', or(
function ctextFunc1() {
return compareToken(function ctextFunc2(tok) {
var code = tok.charCodeAt(0);
var accept =
(33 <= code && code <= 39) ||
(42 <= code && code <= 91) ||
(93 <= code && code <= 126);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
});
},
obsCtext
)());
}
// ccontent = ctext / quoted-pair / comment
function ccontent() {
return wrap('ccontent', or(ctext, quotedPair, comment)());
}
// comment = "(" *([FWS] ccontent) [FWS] ")"
function comment() {
return wrap('comment', and(
literal('('),
star(and(opt(fws), ccontent)),
opt(fws),
literal(')')
)());
}
// CFWS = (1*([FWS] comment) [FWS]) / FWS
function cfws() {
return wrap('cfws', or(
and(
star(
and(opt(fws), comment),
1
),
opt(fws)
),
fws
)());
}
// 3.2.3. Atom
//atext = ALPHA / DIGIT / ; Printable US-ASCII
// "!" / "#" / ; characters not including
// "$" / "%" / ; specials. Used for atoms.
// "&" / "'" /
// "*" / "+" /
// "-" / "/" /
// "=" / "?" /
// "^" / "_" /
// "`" / "{" /
// "|" / "}" /
// "~"
function atext() {
return wrap('atext', compareToken(function atextFunc(tok) {
var accept =
('a' <= tok && tok <= 'z') ||
('A' <= tok && tok <= 'Z') ||
('0' <= tok && tok <= '9') ||
(['!', '#', '$', '%', '&', '\'', '*', '+', '-', '/',
'=', '?', '^', '_', '`', '{', '|', '}', '~'].indexOf(tok) >= 0);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
}));
}
// atom = [CFWS] 1*atext [CFWS]
function atom() {
return wrap('atom', and(colwsp(opt(cfws)), star(atext, 1), colwsp(opt(cfws)))());
}
// dot-atom-text = 1*atext *("." 1*atext)
function dotAtomText() {
var s, maybeText;
s = wrap('dot-atom-text', star(atext, 1)());
if (s === null) { return s; }
maybeText = star(and(literal('.'), star(atext, 1)))();
if (maybeText !== null) {
add(s, maybeText);
}
return s;
}
// dot-atom = [CFWS] dot-atom-text [CFWS]
function dotAtom() {
return wrap('dot-atom', and(invis(opt(cfws)), dotAtomText, invis(opt(cfws)))());
}
// 3.2.4. Quoted Strings
// qtext = %d33 / ; Printable US-ASCII
// %d35-91 / ; characters not including
// %d93-126 / ; "\" or the quote character
// obs-qtext
function qtext() {
return wrap('qtext', or(
function qtextFunc1() {
return compareToken(function qtextFunc2(tok) {
var code = tok.charCodeAt(0);
var accept =
(33 === code) ||
(35 <= code && code <= 91) ||
(93 <= code && code <= 126);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
});
},
obsQtext
)());
}
// qcontent = qtext / quoted-pair
function qcontent() {
return wrap('qcontent', or(qtext, quotedPair)());
}
// quoted-string = [CFWS]
// DQUOTE *([FWS] qcontent) [FWS] DQUOTE
// [CFWS]
function quotedString() {
return wrap('quoted-string', and(
invis(opt(cfws)),
invis(dquote), star(and(opt(colwsp(fws)), qcontent)), opt(invis(fws)), invis(dquote),
invis(opt(cfws))
)());
}
// 3.2.5 Miscellaneous Tokens
// word = atom / quoted-string
function word() {
return wrap('word', or(atom, quotedString)());
}
// phrase = 1*word / obs-phrase
function phrase() {
return wrap('phrase', or(star(word, 1), obsPhrase)());
}
// 3.4. Address Specification
// address = mailbox / group
function address() {
return wrap('address', or(mailbox, group)());
}
// mailbox = name-addr / addr-spec
function mailbox() {
return wrap('mailbox', or(nameAddr, addrSpec)());
}
// name-addr = [display-name] angle-addr
function nameAddr() {
return wrap('name-addr', and(opt(displayName), angleAddr)());
}
// angle-addr = [CFWS] "<" addr-spec ">" [CFWS] /
// obs-angle-addr
function angleAddr() {
return wrap('angle-addr', or(
and(
invis(opt(cfws)),
literal('<'),
addrSpec,
literal('>'),
invis(opt(cfws))
),
obsAngleAddr
)());
}
// group = display-name ":" [group-list] ";" [CFWS]
function group() {
return wrap('group', and(
displayName,
literal(':'),
opt(groupList),
literal(';'),
invis(opt(cfws))
)());
}
// display-name = phrase
function displayName() {
return wrap('display-name', function phraseFixedSemantic() {
var result = phrase();
if (result !== null) {
result.semantic = collapseWhitespace(result.semantic);
}
return result;
}());
}
// mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list
function mailboxList() {
return wrap('mailbox-list', or(
and(
mailbox,
star(and(literal(','), mailbox))
),
obsMboxList
)());
}
// address-list = (address *("," address)) / obs-addr-list
function addressList() {
return wrap('address-list', or(
and(
address,
star(and(literal(','), address))
),
obsAddrList
)());
}
// group-list = mailbox-list / CFWS / obs-group-list
function groupList() {
return wrap('group-list', or(
mailboxList,
invis(cfws),
obsGroupList
)());
}
// 3.4.1 Addr-Spec Specification
// local-part = dot-atom / quoted-string / obs-local-part
function localPart() {
// note: quoted-string, dotAtom are proper subsets of obs-local-part
// so we really just have to look for obsLocalPart, if we don't care about the exact parse tree
return wrap('local-part', or(obsLocalPart, dotAtom, quotedString)());
}
// dtext = %d33-90 / ; Printable US-ASCII
// %d94-126 / ; characters not including
// obs-dtext ; "[", "]", or "\"
function dtext() {
return wrap('dtext', or(
function dtextFunc1() {
return compareToken(function dtextFunc2(tok) {
var code = tok.charCodeAt(0);
var accept =
(33 <= code && code <= 90) ||
(94 <= code && code <= 126);
if (opts.rfc6532) {
accept = accept || isUTF8NonAscii(tok);
}
return accept;
});
},
obsDtext
)()
);
}
// domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
function domainLiteral() {
return wrap('domain-literal', and(
invis(opt(cfws)),
literal('['),
star(and(opt(fws), dtext)),
opt(fws),
literal(']'),
invis(opt(cfws))
)());
}
// domain = dot-atom / domain-literal / obs-domain
function domain() {
return wrap('domain', function domainCheckTLD() {
var result = or(obsDomain, dotAtom, domainLiteral)();
if (opts.rejectTLD) {
if (result.semantic.indexOf('.') < 0) {
return null;
}
}
return result;
}());
}
// addr-spec = local-part "@" domain
function addrSpec() {
return wrap('addr-spec', and(
localPart, literal('@'), domain
)());
}
// 4.1. Miscellaneous Obsolete Tokens
// obs-NO-WS-CTL = %d1-8 / ; US-ASCII control
// %d11 / ; characters that do not
// %d12 / ; include the carriage
// %d14-31 / ; return, line feed, and
// %d127 ; white space characters
function obsNoWsCtl() {
return opts.strict ? null : wrap('obs-NO-WS-CTL', compareToken(function (tok) {
var code = tok.charCodeAt(0);
return ((1 <= code && code <= 8) ||
(11 === code || 12 === code) ||
(14 <= code && code <= 31) ||
(127 === code));
}));
}
// obs-ctext = obs-NO-WS-CTL
function obsCtext() { return opts.strict ? null : wrap('obs-ctext', obsNoWsCtl()); }
// obs-qtext = obs-NO-WS-CTL
function obsQtext() { return opts.strict ? null : wrap('obs-qtext', obsNoWsCtl()); }
// obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR)
function obsQP() {
return opts.strict ? null : wrap('obs-qp', and(
literal('\\'),
or(literal('\0'), obsNoWsCtl, lf, cr)
)());
}
// obs-phrase = word *(word / "." / CFWS)
function obsPhrase() {
return opts.strict ? null : wrap('obs-phrase', and(
word,
star(or(word, literal('.'), colwsp(cfws)))
)());
}
// 4.2. Obsolete Folding White Space
// NOTE: read the errata http://www.rfc-editor.org/errata_search.php?rfc=5322&eid=1908
// obs-FWS = 1*([CRLF] WSP)
function obsFws() {
return opts.strict ? null : wrap('obs-FWS', star(
and(invis(opt(crlf)), wsp),
1
)());
}
// 4.4. Obsolete Addressing
// obs-angle-addr = [CFWS] "<" obs-route addr-spec ">" [CFWS]
function obsAngleAddr() {
return opts.strict ? null : wrap('obs-angle-addr', and(
invis(opt(cfws)),
literal('<'),
obsRoute,
addrSpec,
literal('>'),
invis(opt(cfws))
)());
}
// obs-route = obs-domain-list ":"
function obsRoute() {
return opts.strict ? null : wrap('obs-route', and(
obsDomainList,
literal(':')
)());
}
// obs-domain-list = *(CFWS / ",") "@" domain
// *("," [CFWS] ["@" domain])
function obsDomainList() {
return opts.strict ? null : wrap('obs-domain-list', and(
star(or(invis(cfws), literal(','))),
literal('@'),
domain,
star(and(
literal(','),
invis(opt(cfws)),
opt(and(literal('@'), domain))
))
)());
}
// obs-mbox-list = *([CFWS] ",") mailbox *("," [mailbox / CFWS])
function obsMboxList() {
return opts.strict ? null : wrap('obs-mbox-list', and(
star(and(
invis(opt(cfws)),
literal(',')
)),
mailbox,
star(and(
literal(','),
opt(and(
mailbox,
invis(cfws)
))
))
)());
}
// obs-addr-list = *([CFWS] ",") address *("," [address / CFWS])
function obsAddrList() {
return opts.strict ? null : wrap('obs-addr-list', and(
star(and(
invis(opt(cfws)),
literal(',')
)),
address,
star(and(
literal(','),
opt(and(
address,
invis(cfws)
))
))
)());
}
// obs-group-list = 1*([CFWS] ",") [CFWS]
function obsGroupList() {
return opts.strict ? null : wrap('obs-group-list', and(
star(and(
invis(opt(cfws)),
literal(',')
), 1),
invis(opt(cfws))
)());
}
// obs-local-part = word *("." word)
function obsLocalPart() {
return opts.strict ? null : wrap('obs-local-part', and(word, star(and(literal('.'), word)))());
}
// obs-domain = atom *("." atom)
function obsDomain() {
return opts.strict ? null : wrap('obs-domain', and(atom, star(and(literal('.'), atom)))());
}
// obs-dtext = obs-NO-WS-CTL / quoted-pair
function obsDtext() {
return opts.strict ? null : wrap('obs-dtext', or(obsNoWsCtl, quotedPair)());
}
/////////////////////////////////////////////////////
// ast analysis
function findNode(name, root) {
var i, queue, node;
if (root === null || root === undefined) { return null; }
queue = [root];
while (queue.length > 0) {
node = queue.shift();
if (node.name === name) {
return node;
}
for (i = 0; i < node.children.length; i += 1) {
queue.push(node.children[i]);
}
}
return null;
}
function findAllNodes(name, root) {
var i, queue, node, result;
if (root === null || root === undefined) { return null; }
queue = [root];
result = [];
while (queue.length > 0) {
node = queue.shift();
if (node.name === name) {
result.push(node);
}
for (i = 0; i < node.children.length; i += 1) {
queue.push(node.children[i]);
}
}
return result;
}
function giveResult(ast) {
function grabSemantic(n) {
return n !== null ? n.semantic : null;
}
var i, ret, addresses, addr, name, aspec, local, domain;
if (ast === null) {
return null;
}
ret = { ast: ast };
addresses = findAllNodes('address', ast);
ret.addresses = [];
for (i = 0; i < addresses.length; i += 1) {
addr = addresses[i];
name = findNode('display-name', addr);
aspec = findNode('addr-spec', addr);
local = findNode('local-part', aspec);
domain = findNode('domain', aspec);
ret.addresses.push({
node: addr,
parts: {
name: name,
address: aspec,
local: local,
domain: domain
},
name: grabSemantic(name),
address: grabSemantic(aspec),
local: grabSemantic(local),
domain: grabSemantic(domain)
});
}
if (opts.simple) {
ret = ret.addresses;
for (i = 0; i < ret.length; i += 1) {
delete ret[i].node;
}
}
return ret;
}
/////////////////////////////////////////////////////
var parseString, pos, len, parsed;
opts = handleOpts(opts, {});
if (opts === null) { return null; }
parseString = opts.input;
if (!opts.strict) {
initialize();
opts.strict = true;
parsed = addressList(parseString);
if (opts.partial || !inStr()) {
return giveResult(parsed);
}
opts.strict = false;
}
initialize();
parsed = addressList(parseString);
if (!opts.partial && inStr()) { return null; }
return giveResult(parsed);
}
function parseOneAddressSimple(opts) {
var result;
opts = handleOpts(opts, {
rfc6532: true,
simple: true
});
if (opts === null) { return null; }
result = parse5322(opts);
if ((!result) ||
(!opts.partial &&
(opts.simple && result.length > 1) ||
(!opts.simple && result.addresses.length > 1))) {
return null;
}
return opts.simple ?
result && result[0] :
result && result.addresses && result.addresses[0];
}
function parseAddressListSimple(opts) {
var result;
opts = handleOpts(opts, {
rfc6532: true,
simple: true
});
if (opts === null) { return null; }
result = parse5322(opts);
return opts.simple ? result : result.addresses;
}
function handleOpts(opts, defs) {
function isString(str) {
return Object.prototype.toString.call(str) === '[object String]';
}
function isObject(o) {
return o === Object(o);
}
function isNullUndef(o) {
return o === null || o === undefined;
}
var defaults, o;
if (isString(opts)) {
opts = { input: opts };
} else if (!isObject(opts)) {
return null;
}
if (!isString(opts.input)) { return null; }
if (!defs) { return null; }
defaults = {
rfc6532: false,
partial: false,
simple: false,
strict: false,
rejectTLD: false
};
for (o in defaults) {
if (isNullUndef(opts[o])) {
opts[o] = !isNullUndef(defs[o]) ? defs[o] : defaults[o];
}
opts[o] = !!opts[o];
}
return opts;
}
parse5322.parseOneAddress = parseOneAddressSimple;
parse5322.parseAddressList = parseAddressListSimple;
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = parse5322;
} else {
global.emailAddresses = parse5322;
}
}(this));

View file

@ -6,27 +6,27 @@
<div class="article-content-meta top hide">
<div class="article-meta top">
<% if article.from: %>
<div class="horizontal article-meta-row" title="<%- @Ti( 'From' ) %>: <%= article.from %>">
<div class="horizontal article-meta-row">
<div class="article-meta-key u-textTruncate"><%- @T( 'From' ) %></div>
<div class="article-meta-value flex u-textTruncate"><%= article.from %></div>
<div class="article-meta-value flex contain-text u-textTruncate"><%- @AddressLine(article.from) %></div>
</div>
<% end %>
<% if article.to: %>
<div class="horizontal article-meta-row" title="<%- @Ti( 'To' ) %>: <%= article.to %>">
<div class="horizontal article-meta-row">
<div class="article-meta-key u-textTruncate"><%- @T( 'To' ) %></div>
<div class="article-meta-value flex u-textTruncate"><%= article.to %></div>
<div class="article-meta-value flex contain-text u-textTruncate"><%- @AddressLine(article.to) %></div>
</div>
<% end %>
<% if article.cc: %>
<div class="horizontal article-meta-row" title="<%- @Ti( 'Kopie' ) %>: <%= article.cc %>">
<div class="article-meta-key u-textTruncate"><%- @T( 'Kopie' ) %></div>
<div class="article-meta-value flex u-textTruncate"><%= article.cc %></div>
<div class="horizontal article-meta-row">
<div class="article-meta-key u-textTruncate"><%- @T( 'Cc' ) %></div>
<div class="article-meta-value flex contain-text u-textTruncate"><%- @AddressLine(article.cc) %></div>
</div>
<% end %>
<% if article.subject: %>
<div class="horizontal article-meta-row" title="<%- @Ti( 'Subject' ) %>: <%= article.subject %>">
<div class="horizontal article-meta-row">
<div class="article-meta-key u-textTruncate"><%- @T( 'Subject' ) %></div>
<div class="article-meta-value flex u-textTruncate"><%= article.subject %></div>
<div class="article-meta-value flex contain-text u-textTruncate"><%= article.subject %></div>
</div>
<% end %>
</div>

View file

@ -77,6 +77,15 @@ function clone(object) {
return JSON.parse(JSON.stringify(object));
}
function HTMLEscape(string) {
return ( '' + string )
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
}
jQuery.event.special.remove = {
remove: function(e) {
if (e.handler) e.handler();