Mailspring/js/index.coffee

302 lines
9 KiB
CoffeeScript
Raw Normal View History

2015-10-04 03:38:34 +08:00
---
---
2015-10-04 08:11:29 +08:00
# startSequence()
# .then(step1)
# .then(step2)
# .then(step3)
# .then(step4)
# .then(step5)
#
# # show composer
# step1 = ->
# startStep()
# .then(focusClient)
# .then(doReply)
# .then(typeReply)
# .then(addImage)
# .then(sendEmail)
#
# step2 = ->
# startStep()
# .then(addAccount)
# .then(focusPicker)
# .then(selectAccount)
# .then(swapModes)
#
# step3 = ->
# startStep()
# .then(openLabelPicker)
# .then(typeLabel)
# .then(applyLabel)
#
# step4 = ->
# startStep()
# .then(openInspectorPanel)
# .then(typeCommand)
# .then(activateExtension)
#
# step5 = ->
# startStep()
# .then(fadeClient)
# .then(showCta)
2015-10-04 03:38:34 +08:00
animationContainerSize = [0,0]
2015-10-05 04:06:53 +08:00
moveCursor = (start, end) ->
try
selection = document.getSelection?()
return unless selection
child = selection.anchorNode?.childNodes?[0]
if child
node = child
else
node = selection.anchorNode
return unless node
if selection.setBaseAndExtent
selection.setBaseAndExtent(node, start, node, end)
else if Range
range = new Range
start = Math.min(node.length ? 1000, start)
end = Math.min(node.lengty ? 1000, end)
range.setStart(node, start)
range.setEnd(node, end)
selection.removeAllRanges?()
selection.addRange?(range)
else return
catch e
console.error e
return
2015-10-04 15:45:59 +08:00
typeMe = (str, parent, {top, left}) -> new Promise (resolve, reject) ->
el = $("<div contenteditable=true id='editable'/>")
parent.append(el)
el.css {top, left}
el.focus()
sequence = Promise.resolve()
accumulator = ""
setTimeout ->
_.each str.split(''), (char, i) ->
delay = Math.random() * 120 + 10
sequence = sequence.then -> new Promise (resolve, reject) ->
accumulator += char
el.html(accumulator)
selection = document.getSelection()
2015-10-05 04:06:53 +08:00
moveCursor(accumulator.length, accumulator.length)
2015-10-04 15:45:59 +08:00
setTimeout(resolve, delay)
sequence.then ->
resolve()
, 1500
2015-10-05 03:30:42 +08:00
addFramesToAnimationContainer = (frames, {wrapId}) ->
i = 0
2015-10-05 03:30:42 +08:00
frameImgs = _.map frames, ({delay, callback}, frame) ->
i++
"<img id='#{frame}' src='images/#{frame}.png' style='z-index: #{i}'/>"
2015-10-05 03:30:42 +08:00
frameImgs = frameImgs.join('')
$("#animation-container").append("<div id='#{wrapId}'>#{frameImgs}</div>")
return
2015-10-05 03:30:42 +08:00
runFrames = (frames) ->
sequence = Promise.resolve()
2015-10-05 03:30:42 +08:00
_.each frames, ({delay, callback}, frame) ->
sequence = sequence.then -> new Promise (resolve, reject) ->
$("##{frame}").show()
2015-10-05 03:30:42 +08:00
if callback then callback(delay, resolve)
else setTimeout(resolve, delay)
return sequence
2015-10-05 05:57:20 +08:00
window.screencastSequence = ->
2015-10-05 03:30:42 +08:00
# Need to know the dimensions of the images used in step 1
animationContainerSize = [1136,823]
positionAnimationContainer()
typeInReply = (delay, resolve) ->
coords =
top: 449
left: 608
2015-10-05 04:06:53 +08:00
typeMe("Wow! Iceland looks awesome.", $("#step1"), coords)
2015-10-05 03:30:42 +08:00
.then ->
setTimeout ->
2015-10-05 04:06:53 +08:00
moveCursor(19, 26)
2015-10-05 03:30:42 +08:00
$("#1-4-hovering-toolbar").addClass("pop-in")
resolve()
, delay
markBold = (delay, resolve) ->
setTimeout ->
2015-10-05 04:06:53 +08:00
$("#editable").html("Wow! Iceland looks <strong>awesome</strong>.")
len = $("#editable").html().length
moveCursor(len, len)
2015-10-05 03:30:42 +08:00
$("#1-4-hovering-toolbar").removeClass("pop-in").addClass("pop-out")
setTimeout(resolve, 2*delay)
, delay
adjustTypedText = (delay, resolve) ->
2015-10-05 08:11:39 +08:00
try
if Audio
a = new Audio
a.src = "images/send.ogg"
a.autoplay = true
$("#step1").append(a)
a.play?()
catch
console.log "Audio not supported"
2015-10-05 03:30:42 +08:00
$("#editable").removeAttr("contenteditable")
2015-10-05 08:11:39 +08:00
$("#editable").css top: 568, left: 607
2015-10-05 03:30:42 +08:00
setTimeout(resolve, delay)
showMultiSelectToolbar = (delay, resolve) ->
$toolbarWrap = $("<div id='toolbar-wrap'><img id='toolbar' class='slide-in-from-top' src='images/2-topbar.png' style='display:block; position: relative' /></div>")
$("#step2").append($toolbarWrap)
$toolbarWrap.css
"display": "block"
"position": "absolute"
"overflow": "hidden"
"z-index": "7"
"left": "266px"
"top": "32px"
setTimeout(resolve, delay)
postArchiveUpdate = (delay, resolve) ->
$("#toolbar").removeClass("slide-in-from-top").addClass("slide-out-to-top")
$("#2-8-hover-archive").hide()
$("#2-9-depress-archive").hide()
$("#2-7-select-row-4").hide()
$("#2-4-select-row-2").hide()
setTimeout(resolve, delay)
frames =
step1:
"1-1-initial-outlook-base": {delay: 3000}
"1-2-depress-reply": {delay: 250}
"1-3-show-reply": {delay: 500, callback: typeInReply}
"1-4-hovering-toolbar": {delay: 1000, callback: markBold}
"1-5-depress-send": {delay: 300}
"1-6-sent-message": {delay: 2000, callback: adjustTypedText}
step2:
"2-1-initial-gmail-base": {delay: 2000}
"2-2-select-row-1": {delay: 400, callback: showMultiSelectToolbar}
"2-3-cursor-to-row-2": {delay: 400}
"2-4-select-row-2": {delay: 400}
"2-5-cursor-to-row-3": {delay: 250}
"2-6-cursor-to-row-4": {delay: 400}
"2-7-select-row-4": {delay: 800}
"2-8-hover-archive": {delay: 1000}
"2-9-depress-archive": {delay: 250}
"2-10-updated-threadlist": {delay: 2000, callback: postArchiveUpdate}
addFramesToAnimationContainer(frames.step1, wrapId: "step1")
addFramesToAnimationContainer(frames.step2, wrapId: "step2")
$("##{_.keys(frames.step1)[0]}").show()
2015-10-05 09:04:25 +08:00
$("#step1").append("<h4>N1 is fast, friendly, and easy to use</h4>")
2015-10-05 05:57:20 +08:00
return runFrames(frames.step1).then -> new Promise (resolve, reject) ->
2015-10-05 09:12:53 +08:00
$("#step2").append("<h4>Fresh, yet familiar</h4>")
2015-10-05 03:30:42 +08:00
$("#step1").addClass("slide-out")
$("#step2").addClass("slide-in")
$("##{_.keys(frames.step2)[0]}").show()
2015-10-05 05:57:20 +08:00
$("#step1").on "animationend", ->
$("#step1").off "animationend"
2015-10-05 03:30:42 +08:00
$("#step1").remove()
runFrames(frames.step2).then ->
2015-10-05 05:57:20 +08:00
$("#step2").removeClass("slide-in").addClass("slide-out")
$("#step2").on "animationend", ->
$("#step2").remove()
return resolve()
window.providerSequence = ->
providers = [
"outlook"
"exchange"
"gmail"
"icloud"
"yahoo"
]
imgs = providers.map (provider, i) ->
"<img id='#{provider}' class='provider-img p-#{i}' src='images/providers/#{provider}@2x.png'/>"
.join('')
2015-10-05 09:04:25 +08:00
os = "<img id='os-image' src='images/platforms.png'>"
header = "<h2>Works everywhere for everyone</h2>"
$("#animation-container").html("<div id='provider-wrap'>#{header}#{imgs}<br/>#{os}</div>")
2015-10-05 03:30:42 +08:00
positionAnimationContainer = ->
winW = $(window).width()
winH = $(window).height() - $("#nav").height()
[w,h] = animationContainerSize
2015-10-05 09:12:53 +08:00
leftoverH = Math.max(winH - h - 80, 0)
scaleW = 1 - (Math.min(winW - w, 0) / -w)
scaleH = 1 - (Math.min(winH - h, 0) / -h)
scale = Math.min(scaleW, scaleH)
$("#animation-container").css
"width": "#{w}px"
"height": "#{h}px"
"margin-left": "-#{w/2}px"
2015-10-05 09:12:53 +08:00
"margin-top": "#{leftoverH/2}px"
"-webkit-transform": "scale(#{scale})"
"-moz-transform": "scale(#{scale})"
"-ms-transform": "scale(#{scale})"
"-o-transform": "scale(#{scale})"
"transform": "scale(#{scale})"
2015-10-04 05:48:16 +08:00
2015-10-04 08:11:29 +08:00
# To allow for a fixed amount of bleed below the fold regardless of window
# size.
fixHeroHeight = ->
2015-10-04 05:48:16 +08:00
Math.max(Math.min($("#hero")?.height($(window).height() + 200), 640), 1200)
2015-10-04 08:11:29 +08:00
# To ensure that our overflowing, dynamically sized screenshot pushes the
# remaining content down the correct ammount.
fixHeroMargin = ->
2015-10-04 05:48:16 +08:00
marginBottom = Math.max(($("#main-screenshot").height() + ($("#main-screenshot").offset().top - $("#hero").offset().top)) - $("#hero").height(), 0)
$("#hero").css(marginBottom: marginBottom)
2015-10-04 08:11:29 +08:00
# To ensure there's enough white-space between the watercolor images to
# let the hero text show through.
2015-10-04 05:48:16 +08:00
fixWatercolors = ->
lCutoff = 0.55
rCutoff = 0.6
lWidth = $("#watercolor-left").width()
rWidth = $("#watercolor-right").width()
heroLeft = $("#hero-text").offset().left
leftMove = Math.max(Math.min(heroLeft - (lWidth * lCutoff), 0), -lWidth * lCutoff)
heroRight = $("#hero-text").offset().left + $("#hero-text").width()
rightMove = Math.max(Math.min(heroRight - (rWidth * rCutoff), 0), -rWidth * rCutoff)
$("#watercolor-left").css(left: leftMove)
$("#watercolor-right").css(right: rightMove)
2015-10-05 10:41:18 +08:00
fixStaticClientImages = ->
overhang = 70
padding = 40
nominalScreenshot = 1280
nominalComposer = 615
innerWidth = $("#static-client-images").innerWidth() - padding - overhang
scale = Math.min(1 - (nominalScreenshot - innerWidth) / nominalScreenshot, 1)
$("#static-screenshot, #static-screenshot-wrap").width(nominalScreenshot * scale)
$("#static-composer").width(nominalComposer * scale)
2015-10-04 05:48:16 +08:00
onResize = ->
2015-10-04 08:11:29 +08:00
fixHeroHeight()
# fixHeroMargin()
2015-10-04 05:48:16 +08:00
fixWatercolors()
2015-10-05 03:30:42 +08:00
positionAnimationContainer()
2015-10-05 10:41:18 +08:00
fixStaticClientImages()
2015-10-04 05:48:16 +08:00
window.onresize = onResize
2015-10-04 03:38:34 +08:00
window.onload = ->
2015-10-04 05:48:16 +08:00
onResize()
$("body").addClass("initial")
$("#play-intro").on "click", ->
2015-10-05 10:41:18 +08:00
$("#static-client-images").hide()
2015-10-05 05:57:20 +08:00
$("body").addClass("start-animation").removeClass("initial")
screencastSequence()
.then(providerSequence)