/
applayout.coffee
149 lines (123 loc) · 4.83 KB
/
applayout.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
{throttle, topof} = require '../util'
path = require 'path'
attached = false
attachListeners = ->
return if attached
window.addEventListener 'mousemove', onActivity
window.addEventListener 'click', onActivity
window.addEventListener 'keydown', onActivity
window.addEventListener 'keydown', noInputKeydown
onActivity = throttle 100, (ev) ->
# This occasionally happens to generate error when
# user clicking has generated an application event
# that is being handled while we also receive the event
# Current fix: defer the action generated during the update
setTimeout ->
action 'activity', ev.timeStamp ? Date.now()
, 1
noInputKeydown = (ev) ->
action 'noinputkeydown', ev if ev.target.tagName != 'TEXTAREA'
onScroll = throttle 20, (ev) ->
el = ev.target
child = el.children[0]
# use the saved scroll value if we have one
if el.hasAttribute('scrolltop')
el.scrollTop = el.getAttribute('scrolltop')
el.removeAttribute('scrolltop')
# calculation to see whether we are at the bottom with a tolerance value
atbottom = (el.scrollTop + el.offsetHeight) >= (child.offsetHeight - 10)
action 'atbottom', atbottom
# check whether we are at the top with a tolerance value
if el.scrollTop < 0
attop = child.offsetHeight - el.offsetHeight + el.scrollTop <= el.offsetHeight / 2
else
attop = el.scrollTop <= (el.offsetHeight / 2)
action 'attop', attop
addClass = (el, cl) ->
return unless el
return if RegExp("\\s*#{cl}").exec el.className
el.className += if el.className then " #{cl}" else cl
el
removeClass = (el, cl) ->
return unless el
el.className = el.className.replace RegExp("\\s*#{cl}"), ''
el
closest = (el, cl) ->
return unless el
cl = RegExp("\\s*#{cl}") unless cl instanceof RegExp
if el.className.match(cl) then el else closest(el.parentNode, cl)
drag = do ->
ondragover = ondragenter = (ev) ->
# this enables dragging at all
ev.preventDefault()
addClass closest(ev.target, 'dragtarget'), 'dragover'
removeClass closest(ev.target, 'dragtarget'), 'drag-timeout'
ev.dataTransfer.dropEffect = 'copy'
return false
ondrop = (ev) ->
ev.preventDefault()
removeClass closest(ev.target, 'dragtarget'), 'dragover'
removeClass closest(ev.target, 'dragtarget'), 'drag-timeout'
action 'uploadimage', ev.dataTransfer.files
ondragleave = (ev) ->
# it was firing the leave event while dragging, had to
# use a timeout to check if it was a "real" event
# by remaining out
addClass closest(ev.target, 'dragtarget'), 'drag-timeout'
setTimeout ->
if closest(ev.target, 'dragtarget').classList.contains('drag-timeout')
removeClass closest(ev.target, 'dragtarget'), 'dragover'
removeClass closest(ev.target, 'dragtarget'), 'drag-timeout'
, 200
{ondragover, ondragenter, ondrop, ondragleave}
resize = do ->
rz = null
{
onmousemove: (ev) ->
if rz and ev.buttons & 1
rz(ev)
else
rz = null
onmousedown: (ev) ->
rz = resizers[ev.target.dataset?.resize]
onmouseup: (ev) ->
rz = null
}
resizers =
leftResize: (ev) -> action 'leftresize', (Math.max 90, ev.clientX)
module.exports = exp = layout ->
platform = if process.platform is 'darwin' then 'osx' else ''
div class:'applayout ' + platform, resize, region('last'), ->
div class:'left', ->
div class:'listhead', region('listhead')
div class:'list', region('left')
div class:'lfoot', region('lfoot')
div class:'leftresize', 'data-resize':'leftResize'
div class:'right dragtarget ', drag, ->
div id: 'drop-overlay', ->
div class: 'inner-overlay', () ->
div 'Drop file here.'
div class:'convhead', region('convhead')
div class:'main', region('main'), onscroll: onScroll
div class:'maininfo', region('maininfo')
div class:'foot', region('foot')
attachListeners()
do ->
id = ofs = null
lastVisibleMessage = ->
# the viewport
screl = document.querySelector('.main')
# the pixel offset for the bottom of the viewport
bottom = screl.scrollTop + screl.offsetHeight
# all messages
last = null
last = m for m in document.querySelectorAll('.message') when topof(m) < bottom
return last
exp.recordMainPos = ->
ofs = document.querySelector('.main').scrollTop
exp.adjustMainPos = ->
return unless ofs
document.querySelector('.main').scrollTop = ofs
document.querySelector('.main').setAttribute('scrolltop', ofs)
# reset
id = ofs = null