Implementation of "muting" of email addresses in zoom view.
This commit is contained in:
parent
d9938f59c6
commit
239cfa3e6c
7 changed files with 1003 additions and 30 deletions
|
@ -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 )
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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\"><#{HTMLEscape(item.address)}></span>"
|
||||
|
||||
result
|
||||
|
||||
# define template
|
||||
JST["app/views/#{name}"](params)
|
||||
template
|
||||
|
|
|
@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\x22/g, '"')
|
||||
|
||||
date: ( time, offset ) =>
|
||||
@convert(time, offset, @dateFormat)
|
||||
|
||||
|
|
955
app/assets/javascripts/app/lib/base/email-addresses.js
Normal file
955
app/assets/javascripts/app/lib/base/email-addresses.js
Normal 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));
|
|
@ -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>
|
||||
|
|
|
@ -77,6 +77,15 @@ function clone(object) {
|
|||
return JSON.parse(JSON.stringify(object));
|
||||
}
|
||||
|
||||
function HTMLEscape(string) {
|
||||
return ( '' + string )
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
}
|
||||
|
||||
jQuery.event.special.remove = {
|
||||
remove: function(e) {
|
||||
if (e.handler) e.handler();
|
||||
|
|
Loading…
Reference in a new issue