-
Notifications
You must be signed in to change notification settings - Fork 116
/
sortable.coffee
156 lines (120 loc) · 4.17 KB
/
sortable.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
150
151
152
153
154
155
156
SELECTOR = 'table[data-sortable]'
numberRegExp = /^-?[£$¤]?[\d,.]+%?$/
trimRegExp = /^\s+|\s+$/g
clickEvents = ['click']
touchDevice = 'ontouchstart' of document.documentElement
clickEvents.push 'touchstart' if touchDevice
addEventListener = (el, event, handler) ->
if el.addEventListener?
el.addEventListener event, handler, false
else
el.attachEvent "on#{ event }", handler
sortable =
init: (options={}) ->
options.selector ?= SELECTOR
tables = document.querySelectorAll options.selector
sortable.initTable table for table in tables
initTable: (table) ->
return if table.tHead?.rows.length isnt 1
return if table.getAttribute('data-sortable-initialized') is 'true'
table.setAttribute 'data-sortable-initialized', 'true'
ths = table.querySelectorAll('th')
for th, i in ths
if th.getAttribute('data-sortable') isnt 'false'
sortable.setupClickableTH table, th, i
table
setupClickableTH: (table, th, i) ->
type = sortable.getColumnType table, i
eventHandled = false
onClick = (e) ->
if eventHandled
return
eventHandled = true
setTimeout ->
eventHandled = false
, 0
sorted = @getAttribute('data-sorted') is 'true'
sortedDirection = @getAttribute 'data-sorted-direction'
if sorted
newSortedDirection = if sortedDirection is 'ascending' then 'descending' else 'ascending'
else
newSortedDirection = type.defaultSortDirection
ths = @parentNode.querySelectorAll('th')
for th in ths
th.setAttribute 'data-sorted', 'false'
th.removeAttribute 'data-sorted-direction'
@setAttribute 'data-sorted', 'true'
@setAttribute 'data-sorted-direction', newSortedDirection
tBody = table.tBodies[0]
rowArray = []
if not sorted
if type.compare?
_compare = type.compare
else
_compare = (a, b) ->
b - a
compare = (a, b) ->
if a[0] is b[0]
return a[2] - b[2]
if type.reverse
_compare b[0], a[0]
else
_compare a[0], b[0]
for row, position in tBody.rows
value = sortable.getNodeValue(row.cells[i])
if type.comparator?
value = type.comparator(value)
rowArray.push [value, row, position]
rowArray.sort compare
tBody.appendChild row[1] for row in rowArray
else
rowArray.push item for item in tBody.rows
rowArray.reverse()
tBody.appendChild row for row in rowArray
if typeof window['CustomEvent'] is 'function'
table.dispatchEvent?(new CustomEvent 'Sortable.sorted', { bubbles: true })
for eventName in clickEvents
addEventListener th, eventName, onClick
getColumnType: (table, i) ->
specified = table.querySelectorAll('th')[i]?.getAttribute('data-sortable-type')
return sortable.typesObject[specified] if specified?
for row in table.tBodies[0].rows
text = sortable.getNodeValue row.cells[i]
for type in sortable.types
if type.match text
return type
return sortable.typesObject.alpha
getNodeValue: (node) ->
return '' unless node
dataValue = node.getAttribute 'data-value'
return dataValue if dataValue isnt null
return node.innerText.replace(trimRegExp, '') unless typeof node.innerText is 'undefined'
node.textContent.replace trimRegExp, ''
setupTypes: (types) ->
sortable.types = types
sortable.typesObject = {}
sortable.typesObject[type.name] = type for type in types
sortable.setupTypes [{
name: 'numeric'
defaultSortDirection: 'descending'
match: (a) -> a.match numberRegExp
comparator: (a) -> parseFloat(a.replace(/[^0-9.-]/g, ''), 10) or 0
}, {
name: 'date'
defaultSortDirection: 'ascending'
reverse: true
match: (a) -> not isNaN Date.parse a
comparator: (a) -> Date.parse(a) or 0
}, {
name: 'alpha'
defaultSortDirection: 'ascending'
match: -> true
compare: (a, b) -> a.localeCompare b
}]
setTimeout sortable.init, 0
if typeof define is 'function' and define.amd
define -> sortable
else if typeof exports isnt 'undefined'
module.exports = sortable
else
window.Sortable = sortable