mailcatcher/assets/javascripts/mailcatcher.js.coffee

341 lines
11 KiB
CoffeeScript
Raw Normal View History

2014-03-17 04:35:58 -04:00
#= require modernizr
#= require jquery
#= require date
2014-03-25 21:30:00 -04:00
#= require favcount
2014-03-17 04:35:58 -04:00
#= require flexie
#= require keymaster
# Add a new jQuery selector expression which does a case-insensitive :contains
2014-03-25 21:08:52 -04:00
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: ->
2014-03-25 21:08:52 -04:00
$("#messages tr").live "click", (e) =>
e.preventDefault()
2014-03-25 21:08:52 -04:00
@loadMessage $(e.currentTarget).attr("data-message-id")
2011-05-29 02:46:38 -04:00
2014-03-25 21:08:52 -04:00
$("input[name=search]").keyup (e) =>
query = $.trim $(e.currentTarget).val()
if query
@searchMessages query
else
@clearSearch()
2014-03-25 21:08:52 -04:00
$("#message .views .format.tab a").live "click", (e) =>
e.preventDefault()
2014-03-25 21:08:52 -04:00
@loadMessageBody @selectedMessage(), $($(e.currentTarget).parent("li")).data("message-format")
2011-06-10 08:47:38 -04:00
2014-03-25 21:08:52 -04:00
$("#message iframe").load =>
@decorateMessageBody()
2011-07-07 04:08:32 -04:00
2014-03-25 21:08:52 -04:00
$("#resizer").live
mousedown: (e) =>
e.preventDefault()
$(window).bind events =
mouseup: (e) =>
e.preventDefault()
$(window).unbind events
mousemove: (e) =>
e.preventDefault()
@resizeTo e.clientY
@resizeToSaved()
2014-03-25 21:08:52 -04:00
$("nav.app .clear a").live "click", (e) =>
e.preventDefault()
2011-05-31 13:15:05 -04:00
if confirm "You will lose all your received messages.\n\nAre you sure you want to clear all messages?"
2011-06-10 08:47:38 -04:00
$.ajax
2014-03-25 21:08:52 -04:00
url: "/messages"
type: "DELETE"
2013-04-08 16:20:30 -04:00
success: =>
2012-10-29 19:00:50 -04:00
@unselectMessage()
@updateMessagesCount()
2011-05-31 13:15:05 -04:00
error: ->
2014-03-25 21:08:52 -04:00
alert "Error while clearing all messages."
2011-05-29 02:46:38 -04:00
2014-03-25 21:08:52 -04:00
$("nav.app .quit a").live "click", (e) =>
e.preventDefault()
if confirm "You will lose all your received messages.\n\nAre you sure you want to quit?"
$.ajax
2014-03-25 21:08:52 -04:00
type: "DELETE"
success: ->
2014-03-25 21:08:52 -04:00
location.replace $("body > header h1 a").attr("href")
error: ->
2014-03-25 21:08:52 -04:00
alert "Error while quitting."
2011-06-10 08:47:38 -04:00
2014-03-25 21:30:00 -04:00
@favcount = new Favcount($("""link[rel="icon"]""").attr("href"))
2014-03-25 21:08:52 -04:00
key "up", =>
if @selectedMessage()
2014-03-25 21:08:52 -04:00
@loadMessage $("#messages tr.selected").prev().data("message-id")
else
2014-03-25 21:08:52 -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
2014-03-25 21:08:52 -04:00
key "down", =>
if @selectedMessage()
2014-03-25 21:08:52 -04:00
@loadMessage $("#messages tr.selected").next().data("message-id")
else
2014-03-25 21:08:52 -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
2014-03-25 21:08:52 -04:00
key "⌘+up, ctrl+up", =>
@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
2014-03-25 21:08:52 -04:00
key "⌘+down, ctrl+down", =>
@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
2014-03-25 21:08:52 -04:00
key "left", =>
2012-04-19 11:26:40 -04:00
@openTab @previousTab()
2012-10-24 17:21:08 -04:00
false
2012-04-19 11:26:40 -04:00
2014-03-25 21:08:52 -04:00
key "right", =>
2012-04-19 11:26:40 -04:00
@openTab @nextTab()
2012-10-24 17:21:08 -04:00
false
2012-04-19 11:26:40 -04:00
2014-03-25 21:08:52 -04:00
key "backspace, delete", =>
2012-10-24 18:45:26 -04:00
id = @selectedMessage()
if id?
$.ajax
2014-03-25 21:08:52 -04:00
url: "/messages/" + id
type: "DELETE"
2012-10-24 19:20:33 -04:00
success: =>
# callback will delete, this gets us into race conditions...
#@deleteMessage [id]
2012-10-24 19:20:33 -04:00
2012-10-24 18:45:26 -04:00
error: ->
2014-03-25 21:08:52 -04:00
alert "Error while removing message."
2012-10-24 18:45:26 -04:00
false
2011-05-29 02:46:38 -04:00
@refresh()
@subscribe()
2011-06-10 08:47:38 -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)
new Date match[1], match[2] - 1, match[3], match[4], match[5], match[6], 0
2011-06-10 08:47:38 -04:00
offsetTimeZone: (date) ->
offset = Date.now().getTimezoneOffset() * 60000 #convert timezone difference to milliseconds
date.setTime(date.getTime() - offset)
date
formatDate: (date) ->
date &&= @parseDate(date) if typeof(date) == "string"
date &&= @offsetTimeZone(date)
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: ->
2014-03-25 21:08:52 -04:00
$("#messages tr").length - 1
2012-04-19 11:26:40 -04:00
updateMessagesCount: ->
2014-03-25 21:30:00 -04:00
@favcount.set(@messagesCount())
2015-03-25 06:42:25 -04:00
document.title = 'MailCatcher (' + @messagesCount() + ')'
2012-04-19 11:26:40 -04:00
tabs: ->
2014-03-25 21:08:52 -04:00
$("#message ul").children(".tab")
2012-04-19 11:26:40 -04:00
getTab: (i) =>
$(@tabs()[i])
selectedTab: =>
2014-03-25 21:08:52 -04:00
@tabs().index($("#message li.tab.selected"))
2012-04-19 11:26:40 -04:00
openTab: (i) =>
2014-03-25 21:08:52 -04:00
@getTab(i).children("a").click()
2012-04-19 11:26:40 -04:00
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
2014-03-25 21:08:52 -04:00
@previousTab(i - 1)
2012-04-19 11:26:40 -04:00
nextTab: (tab) =>
i = if tab then tab else @selectedTab() + 1
i = 0 if i > @tabs().length - 1
if @getTab(i).is(":visible")
i
else
2014-03-25 21:08:52 -04:00
@nextTab(i + 1)
2012-04-19 11:26:40 -04:00
haveMessage: (id) ->
$("""#messages tbody tr[data-message-id="#{id}"]""").length > 0
2011-06-10 08:47:38 -04:00
selectedMessage: ->
2014-03-25 21:08:52 -04:00
$("#messages tr.selected").data "message-id"
searchMessages: (query) ->
2014-03-25 21:08:52 -04:00
selector = (":icontains('#{token}')" for token in query.split /\s+/).join("")
$rows = $("#messages tbody tr")
$rows.not(selector).hide()
$rows.filter(selector).show()
clearSearch: ->
2014-03-25 21:08:52 -04:00
$("#messages tbody tr").show()
2011-05-29 02:46:38 -04:00
addMessage: (message) ->
row = $("<tr />").attr("data-message-id", message.id.toString())
2014-03-25 21:08:52 -04:00
.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)))
if 'from_server' of message
row = row.append($("<td/>").text(message.from_server or "Unknown").toggleClass("blank", !message.from_server))
row.prependTo($("#messages tbody"))
@updateMessagesCount()
2011-06-10 08:47:38 -04:00
deleteMessage: (ids) ->
selectedId = @selectedMessage()
for id in ids
if @haveMessage id
messageRow = $("""#messages tbody tr[data-message-id="#{id}"]""")
if messageRow
if id == selectedId
switchTo = messageRow.next().data("message-id") || messageRow.prev().data("message-id")
messageRow.remove()
if switchTo && @haveMessage switchTo
@loadMessage switchTo
#else if selectedId
# @unselectMessage()
@updateMessagesCount()
2012-10-24 17:25:41 -04:00
scrollToRow: (row) ->
2014-03-25 21:08:52 -04:00
relativePosition = row.offset().top - $("#messages").offset().top
2012-10-24 17:25:41 -04:00
if relativePosition < 0
2014-03-25 21:08:52 -04:00
$("#messages").scrollTop($("#messages").scrollTop() + relativePosition - 20)
2012-10-24 17:25:41 -04:00
else
2014-03-25 21:08:52 -04:00
overflow = relativePosition + row.height() - $("#messages").height()
2012-10-24 17:25:41 -04:00
if overflow > 0
2014-03-25 21:08:52 -04:00
$("#messages").scrollTop($("#messages").scrollTop() + overflow + 20)
2012-10-24 17:25:41 -04:00
2012-10-29 19:00:50 -04:00
unselectMessage: ->
2014-03-25 21:08:52 -04:00
$("#messages tbody, #message .metadata dd").empty()
$("#message .metadata .attachments").hide()
$("#message iframe").attr("src", "about:blank")
true
2012-10-29 19:00:50 -04:00
2011-05-29 02:46:38 -04:00
loadMessage: (id) ->
id = id.id if id?.id?
2014-03-25 21:08:52 -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?
2014-03-25 21:08:52 -04:00
$("#messages tbody tr:not([data-message-id='#{id}'])").removeClass("selected")
2012-10-29 18:34:14 -04:00
messageRow = $("#messages tbody tr[data-message-id='#{id}']")
2014-03-25 21:08:52 -04:00
messageRow.addClass("selected")
2012-10-24 17:25:41 -04:00
@scrollToRow(messageRow)
2011-06-10 08:47:38 -04:00
2012-10-29 18:34:14 -04:00
$.getJSON "/messages/#{id}.json", (message) =>
2014-03-25 21:08:52 -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)
2014-03-25 21:08:52 -04:00
format = $el.attr("data-message-format")
2011-05-29 02:46:38 -04:00
if $.inArray(format, message.formats) >= 0
2014-03-25 21:08:52 -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
2014-03-25 21:08:52 -04:00
$("#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
2014-03-25 21:08:52 -04:00
$ul = $("<ul/>").appendTo($("#message .metadata dd.attachments").empty())
2011-05-29 02:46:38 -04:00
$.each message.attachments, (i, attachment) ->
2014-03-25 21:08:52 -04:00
$ul.append($("<li>").append($("<a>").attr("href", attachment["href"]).addClass(attachment["type"].split("/", 1)[0]).addClass(attachment["type"].replace("/", "-")).text(attachment["filename"])))
$("#message .metadata .attachments").show()
2011-05-29 02:46:38 -04:00
else
2014-03-25 21:08:52 -04:00
$("#message .metadata .attachments").hide()
2011-06-10 08:47:38 -04:00
2014-03-25 21:08:52 -04:00
$("#message .views .download a").attr("href", "/messages/#{id}.eml")
2011-06-10 08:47:38 -04:00
@loadMessageBody()
2011-07-07 04:08:32 -04:00
# 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) ->
id ||= @selectedMessage()
2014-03-25 21:08:52 -04:00
format ||= $("#message .views .tab.format.selected").attr("data-message-format")
format ||= "html"
2011-06-10 08:47:38 -04:00
2014-03-25 21:08:52 -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?
2014-03-25 21:08:52 -04:00
$("#message iframe").attr("src", "/messages/#{id}.#{format}")
decorateMessageBody: ->
2014-03-25 21:08:52 -04:00
format = $("#message .views .tab.format.selected").attr("data-message-format")
2014-03-17 01:53:13 -04:00
switch format
2014-03-25 21:08:52 -04:00
when "html"
body = $("#message iframe").contents().find("body")
$("a", body).attr("target", "_blank")
2014-03-25 21:08:52 -04:00
when "plain"
message_iframe = $("#message iframe").contents()
text = message_iframe.text()
2014-03-25 21:08:52 -04:00
text = text.replace(/((http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:\/~\+#]*[\w\-\@?^=%&amp;\/~\+#])?)/g, """<a href="$1" target="_blank">$1</a>""")
text = text.replace(/\n/g, "<br/>")
message_iframe.find("html").html("<html><body>#{text}</html></body>")
2011-05-29 02:46:38 -04:00
refresh: ->
2014-03-25 21:08:52 -04:00
$.getJSON "/messages", (messages) =>
all_msg_ids = []
2011-05-29 02:46:38 -04:00
$.each messages, (i, message) =>
id = message.id if message.id?
all_msg_ids.push(id)
unless @haveMessage id
2011-05-29 02:46:38 -04:00
@addMessage message
ids_to_remove = []
$.each $("#messages tbody tr[data-message-id]"), (i, row) =>
existing_id = $(row).data("message-id")
unless existing_id in all_msg_ids
ids_to_remove.push(existing_id)
@deleteMessage ids_to_remove
2011-05-29 02:46:38 -04:00
subscribe: ->
if WebSocket?
@subscribeWebSocket()
else
@subscribePoll()
subscribeWebSocket: ->
secure = window.location.protocol is "https:"
protocol = if secure then "wss" else "ws"
@websocket = new WebSocket("#{protocol}://#{window.location.host}/messages")
2011-05-29 02:46:38 -04:00
@websocket.onmessage = (event) =>
message = $.parseJSON event.data
if 'id' of message
@addMessage message
else
@deleteMessage message
2011-05-29 02:46:38 -04:00
subscribePoll: ->
unless @refreshInterval?
@refreshInterval = setInterval (=> @refresh()), 1000
2014-03-25 21:08:52 -04:00
resizeToSavedKey: "mailcatcherSeparatorHeight"
resizeTo: (height) ->
2014-03-25 21:08:52 -04:00
$("#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