2014-03-17 04:35:58 -04:00
#= require modernizr
#= require jquery
#= require date
#= require flexie
#= require keymaster
#= require xslt
2012-03-21 23:40:22 -04:00
# Add a new jQuery selector expression which does a case-insensitive :contains
jQuery . expr [ ' : ' ] . icontains = (a, i, m) ->
( a . textContent ? a . innerText ? " " ) . toUpperCase ( ) . indexOf ( m [ 3 ] . toUpperCase ( ) ) >= 0
2011-05-29 02:46:38 -04:00
class MailCatcher
constructor: ->
2011-05-31 12:40:13 -04:00
$ ( ' # messages tr ' ) . live ' click ' , (e) =>
2011-07-07 03:57:28 -04:00
e . preventDefault ( )
2011-05-29 02:46:38 -04:00
@ loadMessage $ ( e . currentTarget ) . attr ' data-message-id '
2012-03-21 23:40:22 -04:00
$ ( ' input[name=search] ' ) . keyup (e) =>
query = $ . trim $ ( e . currentTarget ) . val ( )
if query
@ searchMessages query
2012-03-12 06:29:42 -04:00
else
2012-03-21 23:40:22 -04:00
@ clearSearch ( )
2012-03-12 06:29:42 -04:00
2011-07-07 03:57:28 -04:00
$ ( ' # message .views .format.tab a ' ) . live ' click ' , (e) =>
e . preventDefault ( )
@ loadMessageBody @ selectedMessage ( ) , $ ( $ ( e . currentTarget ) . parent ( ' li ' ) ) . data ' message-format '
2011-06-10 08:47:38 -04:00
2011-07-07 04:08:32 -04:00
$ ( ' # message .views .analysis.tab a ' ) . live ' click ' , (e) =>
e . preventDefault ( )
@ loadMessageAnalysis @ selectedMessage ( )
2014-03-17 01:53:13 -04:00
2013-07-28 16:57:36 -04:00
$ ( ' # message iframe ' ) . load =>
@ decorateMessageBody ( )
2011-07-07 04:08:32 -04:00
2011-07-06 23:39:05 -04:00
$ ( ' # resizer ' ) . live
2013-08-21 18:49:34 -04:00
mousedown: (e) =>
2011-07-06 23:39:05 -04:00
e . preventDefault ( )
$ ( window ) . bind events =
2013-08-21 18:49:34 -04:00
mouseup: (e) =>
2011-07-06 23:39:05 -04:00
e . preventDefault ( )
$ ( window ) . unbind events
2013-08-21 18:49:34 -04:00
mousemove: (e) =>
2011-07-06 23:39:05 -04:00
e . preventDefault ( )
2013-08-21 18:49:34 -04:00
@ resizeTo e . clientY
@ resizeToSaved ( )
2011-07-06 23:39:05 -04:00
2011-05-31 13:15:05 -04:00
$ ( ' nav.app .clear a ' ) . live ' click ' , (e) =>
2011-10-07 11:31:02 -04:00
e . preventDefault ( )
2011-05-31 13:15:05 -04:00
if confirm " You will lose all your received messages. \n \n Are you sure you want to clear all messages? "
2011-06-10 08:47:38 -04:00
$ . ajax
2011-05-31 13:15:05 -04:00
url: ' /messages '
type: ' DELETE '
2013-04-08 16:20:30 -04:00
success: =>
2012-10-29 19:00:50 -04:00
@ unselectMessage ( )
2011-05-31 13:15:05 -04:00
error: ->
2013-04-08 16:49:04 -04:00
alert ' Error while clearing all messages. '
2011-05-29 02:46:38 -04:00
2011-05-31 13:14:56 -04:00
$ ( ' nav.app .quit a ' ) . live ' click ' , (e) =>
2011-10-07 11:31:02 -04:00
e . preventDefault ( )
2011-05-31 13:14:56 -04:00
if confirm " You will lose all your received messages. \n \n Are you sure you want to quit? "
$ . ajax
type: ' DELETE '
success: ->
location . replace $ ( ' body > header h1 a ' ) . attr ( ' href ' )
error: ->
alert ' Error while quitting. '
2011-06-10 08:47:38 -04:00
2012-04-19 11:26:40 -04:00
key ' up ' , =>
2012-10-24 17:54:07 -04:00
if @ selectedMessage ( )
2012-10-29 18:40:34 -04:00
@ loadMessage $ ( ' # messages tr.selected ' ) . prev ( ) . data ( ' message-id ' )
2012-10-24 17:54:07 -04:00
else
2012-10-29 18:40:34 -04:00
@ loadMessage $ ( ' # messages tbody tr[data-message-id]:first ' ) . data ( ' message-id ' )
2012-10-24 17:21:08 -04:00
false
2012-04-19 11:26:40 -04:00
key ' down ' , =>
2012-10-24 17:54:07 -04:00
if @ selectedMessage ( )
2012-10-29 18:40:34 -04:00
@ loadMessage $ ( ' # messages tr.selected ' ) . next ( ) . data ( ' message-id ' )
2012-10-24 17:54:07 -04:00
else
2012-10-29 18:40:34 -04:00
@ loadMessage $ ( ' # messages tbody tr[data-message-id]:first ' ) . data ( ' message-id ' )
2012-10-24 17:21:08 -04:00
false
2012-04-19 11:26:40 -04:00
key ' ⌘+up, ctrl+up ' , =>
2012-10-29 18:40:34 -04:00
@ loadMessage $ ( ' # messages tbody tr[data-message-id]:first ' ) . data ( ' message-id ' )
2012-10-24 17:21:08 -04:00
false
2012-04-19 11:26:40 -04:00
key ' ⌘+down, ctrl+down ' , =>
2012-10-29 18:40:34 -04:00
@ loadMessage $ ( ' # messages tbody tr[data-message-id]:last ' ) . data ( ' message-id ' )
2012-10-24 17:21:08 -04:00
false
2012-04-19 11:26:40 -04:00
key ' left ' , =>
@ openTab @ previousTab ( )
2012-10-24 17:21:08 -04:00
false
2012-04-19 11:26:40 -04:00
key ' right ' , =>
@ openTab @ nextTab ( )
2012-10-24 17:21:08 -04:00
false
2012-04-19 11:26:40 -04:00
2012-10-24 18:45:26 -04:00
key ' backspace, delete ' , =>
id = @ selectedMessage ( )
if id ?
$ . ajax
url: ' /messages/ ' + id
type: ' DELETE '
2012-10-24 19:20:33 -04:00
success: =>
2012-10-29 18:34:14 -04:00
messageRow = $ ( " # messages tbody tr[data-message-id= ' #{ id } ' ] " )
2012-10-29 18:40:34 -04:00
switchTo = messageRow . next ( ) . data ( ' message-id ' ) || messageRow . prev ( ) . data ( ' message-id ' )
2012-10-24 19:20:33 -04:00
messageRow . remove ( )
if switchTo
@ loadMessage switchTo
else
2012-10-29 19:00:50 -04:00
@ unselectMessage ( )
2012-10-24 19:20:33 -04:00
2012-10-24 18:45:26 -04:00
error: ->
alert ' Error while removing message. '
false
2011-05-29 02:46:38 -04:00
@ refresh ( )
@ subscribe ( )
2011-06-10 08:47:38 -04:00
2011-05-29 03:42:07 -04:00
# Only here because Safari's Date parsing *sucks*
# We throw away the timezone, but you could use it for something...
parseDateRegexp: /^(\d{4})[-\/\\](\d{2})[-\/\\](\d{2})(?:\s+|T)(\d{2})[:-](\d{2})[:-](\d{2})(?:([ +-]\d{2}:\d{2}|\s*\S+|Z?))?$/
parseDate: (date) ->
if match = @ parseDateRegexp . exec ( date )
2011-07-07 01:40:22 -04:00
new Date match [ 1 ] , match [ 2 ] - 1 , match [ 3 ] , match [ 4 ] , match [ 5 ] , match [ 6 ] , 0
2011-06-10 08:47:38 -04:00
2011-12-30 03:29:52 -05:00
offsetTimeZone: (date) ->
2012-01-01 22:01:26 -05:00
offset = Date . now ( ) . getTimezoneOffset ( ) * 60000 #convert timezone difference to milliseconds
2011-12-30 03:29:52 -05:00
date . setTime ( date . getTime ( ) - offset )
date
2011-05-29 03:42:07 -04:00
formatDate: (date) ->
2012-01-01 22:01:26 -05:00
date && = @ parseDate ( date ) if typeof ( date ) == " string "
date && = @ offsetTimeZone ( date )
2011-05-29 03:42:07 -04:00
date && = date . toString ( " dddd, d MMM yyyy h:mm:ss tt " )
2011-05-29 02:46:38 -04:00
2012-04-19 11:26:40 -04:00
messagesCount: ->
$ ( ' # messages tr ' ) . length - 1
tabs: ->
$ ( ' # message ul ' ) . children ( ' .tab ' )
getTab: (i) =>
$ ( @ tabs ( ) [ i ] )
selectedTab: =>
@ tabs ( ) . index ( $ ( ' # message li.tab.selected ' ) )
openTab: (i) =>
@ getTab ( i ) . children ( ' a ' ) . click ( )
previousTab: (tab)=>
i = if tab || tab is 0 then tab else @ selectedTab ( ) - 1
i = @ tabs ( ) . length - 1 if i < 0
if @ getTab ( i ) . is ( " :visible " )
i
else
@ previousTab ( i - 1 )
nextTab: (tab) =>
i = if tab then tab else @ selectedTab ( ) + 1
i = 0 if i > @ tabs ( ) . length - 1
if @ getTab ( i ) . is ( " :visible " )
i
else
@ nextTab ( i + 1 )
2011-05-29 02:46:38 -04:00
haveMessage: (message) ->
message = message . id if message . id ?
2011-05-31 12:40:13 -04:00
$ ( " # messages tbody tr[data-message-id= \" #{ message } \" ] " ) . length > 0
2011-06-10 08:47:38 -04:00
2011-07-07 03:57:28 -04:00
selectedMessage: ->
$ ( ' # messages tr.selected ' ) . data ' message-id '
2012-03-21 23:40:22 -04:00
searchMessages: (query) ->
selector = ( " :icontains( ' #{ token } ' ) " for token in query . split / \ s + / ) . join " "
$rows = $ ( " # messages tbody tr " )
$rows . not ( selector ) . hide ( )
$rows . filter ( selector ) . show ( )
2012-03-12 06:29:42 -04:00
clearSearch: ->
$ ( ' # messages tbody tr ' ) . show ( )
2011-05-29 02:46:38 -04:00
addMessage: (message) ->
2014-03-17 01:53:13 -04:00
$ ( ' <tr /> ' ) . attr ( ' data-message-id ' , message . id . toString ( ) )
. append ( $ ( ' <td/> ' ) . text ( message . sender or " No sender " ) . toggleClass ( " blank " , ! message . sender ) )
. append ( $ ( ' <td/> ' ) . text ( ( message . recipients || [ ] ) . join ( ' , ' ) or " No receipients " ) . toggleClass ( " blank " , ! message . recipients . length ) )
. append ( $ ( ' <td/> ' ) . text ( message . subject or " No subject " ) . toggleClass ( " blank " , ! message . subject ) )
. append ( $ ( ' <td/> ' ) . text ( @ formatDate ( message . created_at ) ) )
. prependTo ( $ ( ' # messages tbody ' ) )
2011-06-10 08:47:38 -04:00
2012-10-24 17:25:41 -04:00
scrollToRow: (row) ->
relativePosition = row . offset ( ) . top - $ ( ' # messages ' ) . offset ( ) . top
if relativePosition < 0
$ ( ' # messages ' ) . scrollTop ( $ ( ' # messages ' ) . scrollTop ( ) + relativePosition - 20 )
else
overflow = relativePosition + row . height ( ) - $ ( ' # messages ' ) . height ( )
if overflow > 0
$ ( ' # messages ' ) . scrollTop ( $ ( ' # messages ' ) . scrollTop ( ) + overflow + 20 )
2012-10-29 19:00:50 -04:00
unselectMessage: ->
$ ( ' # messages tbody, # message .metadata dd ' ) . empty ( )
$ ( ' # message .metadata .attachments ' ) . hide ( )
$ ( ' # message iframe ' ) . attr ' src ' , ' about:blank '
null
2011-05-29 02:46:38 -04:00
loadMessage: (id) ->
id = id . id if id ? . id ?
2011-05-31 12:40:13 -04:00
id || = $ ( ' # messages tr.selected ' ) . attr ' data-message-id '
2011-06-10 08:47:38 -04:00
2011-05-29 02:46:38 -04:00
if id ?
2012-10-29 18:34:14 -04:00
$ ( " # messages tbody tr:not([data-message-id= ' #{ id } ' ]) " ) . removeClass ' selected '
messageRow = $ ( " # messages tbody tr[data-message-id= ' #{ id } ' ] " )
2012-10-24 17:25:41 -04:00
messageRow . addClass ' selected '
@ scrollToRow ( messageRow )
2011-06-10 08:47:38 -04:00
2012-10-29 18:34:14 -04:00
$ . getJSON " /messages/ #{ id } .json " , (message) =>
2011-05-31 12:40:13 -04:00
$ ( ' # message .metadata dd.created_at ' ) . text @ formatDate message . created_at
$ ( ' # message .metadata dd.from ' ) . text message . sender
$ ( ' # message .metadata dd.to ' ) . text ( message . recipients || [ ] ) . join ( ' , ' )
$ ( ' # message .metadata dd.subject ' ) . text message . subject
$ ( ' # message .views .tab.format ' ) . each (i, el) ->
2011-05-29 02:46:38 -04:00
$el = $ ( el )
format = $el . attr ' data-message-format '
if $ . inArray ( format , message . formats ) >= 0
2012-10-29 18:34:14 -04:00
$el . find ( ' a ' ) . attr ( ' href ' , " /messages/ #{ id } . #{ format } " )
2011-05-29 02:46:38 -04:00
$el . show ( )
else
$el . hide ( )
2011-06-10 08:47:38 -04:00
2011-05-31 12:40:13 -04:00
if $ ( " # message .views .tab.selected:not(:visible) " ) . length
$ ( " # message .views .tab.selected " ) . removeClass " selected "
$ ( " # message .views .tab:visible:first " ) . addClass " selected "
2011-06-10 08:47:38 -04:00
2011-05-29 02:46:38 -04:00
if message . attachments . length
2011-07-08 02:47:41 -04:00
$ul = $ ( ' <ul/> ' ) . appendTo ( $ ( ' # message .metadata dd.attachments ' ) . empty ( ) )
2011-05-29 02:46:38 -04:00
$ . each message . attachments , (i, attachment) ->
2011-07-08 02:47:41 -04:00
$ul . append ( $ ( ' <li> ' ) . append ( $ ( ' <a> ' ) . attr ( ' href ' , attachment [ ' href ' ] ) . addClass ( attachment [ ' type ' ] . split ( ' / ' , 1 ) [ 0 ] ) . addClass ( attachment [ ' type ' ] . replace ( ' / ' , ' - ' ) ) . text ( attachment [ ' filename ' ] ) ) ) ;
2011-05-29 02:46:38 -04:00
$ ( ' # message .metadata .attachments ' ) . show ( )
else
$ ( ' # message .metadata .attachments ' ) . hide ( )
2011-06-10 08:47:38 -04:00
$ ( ' # message .views .download a ' ) . attr ' href ' , " /messages/ #{ id } .eml "
2011-07-07 04:08:32 -04:00
if $ ( ' # message .views .tab.analysis.selected ' ) . length
@ loadMessageAnalysis ( )
else
@ loadMessageBody ( )
# XXX: These should probably cache their iframes for the current message now we're using a remote service:
2011-05-29 02:46:38 -04:00
loadMessageBody: (id, format) ->
2011-07-07 03:57:28 -04:00
id || = @ selectedMessage ( )
2011-05-31 12:40:13 -04:00
format || = $ ( ' # message .views .tab.format.selected ' ) . attr ' data-message-format '
2011-05-29 02:46:38 -04:00
format || = ' html '
2011-06-10 08:47:38 -04:00
2011-05-31 12:40:13 -04:00
$ ( " # message .views .tab[data-message-format= \" #{ format } \" ]:not(.selected) " ) . addClass ' selected '
$ ( " # message .views .tab:not([data-message-format= \" #{ format } \" ]).selected " ) . removeClass ' selected '
2011-06-10 08:47:38 -04:00
2011-05-29 02:46:38 -04:00
if id ?
$ ( ' # message iframe ' ) . attr " src " , " /messages/ #{ id } . #{ format } "
2011-06-10 08:47:38 -04:00
2013-07-14 15:17:37 -04:00
app = this
2013-07-28 16:57:36 -04:00
decorateMessageBody: ->
format = $ ( ' # message .views .tab.format.selected ' ) . attr ' data-message-format '
2014-03-17 01:53:13 -04:00
switch format
2013-07-28 16:57:36 -04:00
when ' html '
2013-07-14 15:17:37 -04:00
body = $ ( ' # message iframe ' ) . contents ( ) . find ( ' body ' )
$ ( " a " , body ) . attr ( " target " , " _blank " )
2013-07-28 16:57:36 -04:00
when ' plain '
message_iframe = $ ( ' # message iframe ' ) . contents ( )
text = message_iframe . text ( )
text = text . replace ( /((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:\/~\+#]*[\w\-\@?^=%&\/~\+#])?)/g , ' <a href= " $1 " target= " _blank " >$1</a> ' )
text = text . replace ( /\n/g , ' <br/> ' )
message_iframe . find ( ' html ' ) . html ( ' <html><body> ' + text + ' </html></body> ' )
2013-07-14 15:17:37 -04:00
2011-07-07 04:08:32 -04:00
loadMessageAnalysis: (id) ->
id || = @ selectedMessage ( )
$ ( " # message .views .analysis.tab:not(.selected) " ) . addClass ' selected '
$ ( " # message .views :not(.analysis).tab.selected " ) . removeClass ' selected '
if id ?
2011-10-07 12:25:04 -04:00
# Makes a new iframe and populate it with a quick intro and a button
$iframe = $ ( ' # message iframe ' ) . contents ( ) . children ( )
. html ( """
2011-07-07 04:08:32 -04:00
< html >
< head >
< title > Analysis < / title >
2011-07-07 04:11:29 -04:00
#{$('link[rel="stylesheet"]')[0].outerHTML}
2011-07-07 04:08:32 -04:00
< / head >
< body class = " iframe " >
< h1 > Analyse your email with Fractal < / h1 >
< p > < a href = " http://getfractal.com/ " target = " _blank " > Fractal < /a> is a really neat service that applies common email design and development knowledge from <a href="http:/ / www . email - standards . org / " target= " _blank " >Email Standards Project</a> to your HTML email and tells you what you ' ve done wrong or what you should do instead.</p>
2012-11-15 13:05:00 -05:00
< p > Please note that this < strong > sends your email to the Fractal service < /strong> for analysis. Read their <a href="https:/ / www . getfractal . com / page / terms " target= " _blank " >terms of service</a> if you ' re paranoid.</p>
2011-10-07 12:25:04 -04:00
< form >
< input type = " submit " value = " Analyse " / > < span class = " loading " style = " color: # 999; display: none " > Analysing & hellip ; < / span >
2011-07-07 04:08:32 -04:00
< / form >
< / body >
< / html >
""" )
2011-10-07 12:25:04 -04:00
$form = $iframe . find ( ' form ' )
. submit (e) ->
e . preventDefault ( )
$ ( this )
. find ( ' input[type= " submit " ] ' ) . attr ( ' disabled ' , ' disabled ' ) . end ( )
. find ( ' .loading ' ) . show ( )
2011-12-22 14:34:40 -05:00
$ ( ' # message iframe ' ) . contents ( ) . find ( ' body ' ) . xslt ( " /messages/ #{ id } /analysis.xml " , " /stylesheets/analysis.xsl " )
2011-05-29 02:46:38 -04:00
refresh: ->
$ . getJSON ' /messages ' , (messages) =>
$ . each messages , (i, message) =>
unless @ haveMessage message
@ addMessage message
subscribe: ->
if WebSocket ?
@ subscribeWebSocket ( )
else
@ subscribePoll ( )
subscribeWebSocket: ->
secure = window . location . scheme == ' https '
2012-07-17 11:03:20 -04:00
@websocket = new WebSocket ( " #{ if secure then ' wss ' else ' ws ' } :// #{ window . location . host } /messages " )
2011-05-29 02:46:38 -04:00
@websocket.onmessage = (event) =>
@ addMessage $ . parseJSON event . data
subscribePoll: ->
unless @ refreshInterval ?
@refreshInterval = setInterval ( => @ refresh ( ) ) , 1000
2013-08-21 18:49:34 -04:00
resizeToSavedKey: ' mailcatcherSeparatorHeight '
resizeTo: (height) ->
$ ( ' # messages ' ) . css
height: height - $ ( ' # messages ' ) . offset ( ) . top
window . localStorage ? . setItem ( @ resizeToSavedKey , height )
resizeToSaved: ->
height = parseInt ( window . localStorage ? . getItem ( @ resizeToSavedKey ) )
unless isNaN height
@ resizeTo height
2011-05-29 02:46:38 -04:00
2011-06-10 08:47:38 -04:00
$ -> window . MailCatcher = new MailCatcher