better_select 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,185 @@
1
+ $$ = (html) ->
2
+ elm = document.createElement 'div'
3
+ elm.innerHTML = html
4
+ if elm.children.length > 1 then elm.children else elm.children[0]
5
+
6
+ getPos = (tgt, method) ->
7
+ pos = 0
8
+ while tgt
9
+ pos += tgt[method]
10
+ tgt = tgt.offsetParent
11
+ pos
12
+
13
+ getTop = (tgt) -> getPos tgt, 'offsetTop'
14
+ getLeft = (tgt) -> getPos tgt, 'offsetLeft'
15
+
16
+ getClasses = (tgt) -> tgt.getAttribute('class').split ' '
17
+
18
+ setClasses = (tgt, classes) ->
19
+ tgt.setAttribute 'class', classes.join ' '
20
+ tgt
21
+
22
+ addClass = (tgt, className) ->
23
+ classes = getClasses tgt
24
+ classes.push(className) if classes.indexOf(className) is -1
25
+ setClasses tgt, classes
26
+
27
+ removeClass = (tgt, className) ->
28
+ classes.splice(index, 1) unless index = (classes = getClasses tgt).indexOf className is -1
29
+ setClasses tgt, classes
30
+
31
+ letters = 'a b c d e f g h i j k l m n o p q r s t u v w x y z'.split ' '
32
+ numbers = '0 1 2 3 4 5 6 7 8 9 0'.split ' '
33
+
34
+ build_element = (what, orig, obj) ->
35
+ elm = $$ _.template(obj["#{what}_template"]) obj["process_#{what}"] orig
36
+ elm.orig = orig
37
+ orig.better_version = elm
38
+ elm.better_select = obj
39
+ elm
40
+
41
+ renderOption = (orig_option, bs) ->
42
+ option = build_element 'option', orig_option, bs
43
+ option.reset = ->
44
+ @orig.selected = undefined
45
+ @setAttribute 'class', 'option'
46
+ bs.reset @
47
+ option.select = ->
48
+ @orig.selected = 'selected'
49
+ @setAttribute 'class', 'option selected'
50
+ bs.toggle() if bs.select.getAttribute('class').indexOf('open') isnt -1
51
+ bs.set_selected option
52
+ option.addEventListener 'click', -> option.select()
53
+ option.addEventListener 'mouseover', -> option.better_select.set_focused option
54
+ bs.options.push option
55
+ first_char = option.innerHTML.substr(0, 1).toLowerCase()
56
+ bs.options_by_first_char[first_char] = [] unless bs.options_by_first_char[first_char]
57
+ bs.options_by_first_char[first_char].push option
58
+ bs.options_by_first_char[first_char].sort()
59
+ option
60
+
61
+ renderOptionGroup = (orig_group, bs) ->
62
+ group = build_element 'option_group', orig_group, bs
63
+ group.appendChild(renderOption child, bs) for child in orig_group.children
64
+ bs.option_groups.push group
65
+ group
66
+
67
+ class BetterSelect
68
+ defaults:
69
+ positionDropdown: true
70
+ constructor: (elm, options) ->
71
+ return unless elm && elm.tagName && elm.tagName is 'SELECT'
72
+ @settings = _.extend {}, @defaults, options
73
+ @options = []
74
+ @options_by_first_char = {}
75
+ @option_groups = []
76
+ selected = elm.selectedOptions
77
+ @select = build_element 'select', elm, @
78
+ [@selected_option, @dropdown] = @select.children
79
+ if elm.id
80
+ @select.id = "#{elm.id}-better-select"
81
+ @dropdown.id = "#{elm.id}-better-select-dropdown"
82
+ @default_selected = [elm.children[elm.selectedIndex]]
83
+ elm.parentNode.insertBefore @select, elm
84
+ elm.parentNode.insertBefore elm, @select
85
+ elm.style.display = 'none'
86
+ children = elm.children
87
+ if @settings.positionDropdown
88
+ document.body.appendChild @dropdown
89
+ @dropdown.style.left = '-9999px'
90
+ for child in children
91
+ switch child.tagName
92
+ when 'OPTION'
93
+ method = renderOption
94
+ when 'OPTGROUP'
95
+ method = renderOptionGroup
96
+ @dropdown.appendChild(method child, @)
97
+ @default_selected[0].better_version.select() if @default_selected
98
+ @selected_option.addEventListener 'click', =>
99
+ @toggle()
100
+ @set_selected @dropdown_selected_option
101
+ window.addEventListener 'click', (e) =>
102
+ unless e.target == @selected_option || e.target == @select || @options.indexOf(e.target) isnt -1
103
+ @toggle() if @open
104
+ last_char = false
105
+ @selected_option.addEventListener 'focus', =>
106
+ document.body.style.overflow = 'hidden'
107
+ addClass @select, 'focus'
108
+ @selected_option.addEventListener 'blur', =>
109
+ removeClass @select, 'focus'
110
+ document.body.style.overflow = 'auto'
111
+ @toggle() if @open is true
112
+ true
113
+ @selected_option.addEventListener 'keydown', (e) => e.preventDefault() unless [38, 40].indexOf(e.keyCode) is -1
114
+ @selected_option.addEventListener 'keyup', (e) =>
115
+ @toggle() if @open is false
116
+ keyCode = e.keyCode
117
+ switch keyCode
118
+ when 38 then @set_focused @options[if (@focus_index -= 1) < 0 then @focus_index = @options.length - 1 else @focus_index]
119
+ when 40 then @set_focused @options[if (@focus_index += 1) >= @options.length then @focus_index = 0 else @focus_index]
120
+ when 13
121
+ @focused_option.select()
122
+ else
123
+ if keyCode > 47 && keyCode < 58
124
+ char = numbers[keyCode - 47]
125
+ else if keyCode > 64 && keyCode < 91
126
+ char = letters[keyCode - 65]
127
+ else
128
+ if @focused_option
129
+ removeClass @focused_option, 'focus'
130
+ @focused_option = false
131
+ @focus_index = -1
132
+ @toggle()
133
+ if char && @options_by_first_char[char]
134
+ @options_by_first_char[char].sort() unless last_char is char
135
+ option = @options_by_first_char[char].shift()
136
+ @options_by_first_char[char].push option
137
+ @set_focused option
138
+ @focus_index = @options.indexOf option
139
+ last_character = char
140
+ @dropdown.addEventListener('click', -> console.log arguments)
141
+ e.preventDefault()
142
+ e.stopPropagation()
143
+ e.returnValue = false
144
+ false
145
+ focused_option: false
146
+ focus_index: -1
147
+ set_focused: (option) ->
148
+ class_for_selected = (option) => if @selected_option && option.innerHTML is @selected_option.innerHTML then " selected" else ""
149
+ @focused_option.setAttribute('class', "option#{class_for_selected(@focused_option)}") if @focused_option
150
+ @focused_option = option
151
+ @focused_option.setAttribute("class", "option focus#{class_for_selected(option)}")
152
+ @focus_index = @options.indexOf @focused_option
153
+ open: false
154
+ toggle: ->
155
+ (if @open = !@open then addClass else removeClass)(@select, 'open')
156
+ if @settings.positionDropdown
157
+ if @dropdown.offsetHeight > window.innerHeight
158
+ height = window.innerHeight * .50
159
+ @dropdown.style.height = height + 'px'
160
+ @dropdown.style['overflow-y'] = 'auto'
161
+ if @dropdown_selected_option.offsetTop > height || @adjust_height
162
+ @dropdown_selected_option.scrollIntoView()
163
+ @adjust_height = true
164
+ top = top || (getTop(@select) - @dropdown_selected_option.offsetTop)
165
+ top = getTop(@select) - (@dropdown_selected_option.offsetTop - @dropdown.scrollTop)
166
+ @dropdown.style.top = (if top < 0 then 0 else top) + 'px'
167
+ @dropdown.style.left = if @open then getLeft(@select) + 'px' else '-9999px'
168
+ reset: (option) -> @default_selected[0].better_version.select() if @default_selected
169
+ set_selected: (option) ->
170
+ unless @selected_option.innerHTML == option.innerHTML
171
+ removeClass(@dropdown_selected_option, 'selected') if @dropdown_selected_option
172
+ addClass @dropdown_selected_option = option, 'selected'
173
+ @selected_option.innerHTML = option.innerHTML
174
+ e = document.createEvent('Event')
175
+ e.initEvent 'change', true, true
176
+ @select.orig.dispatchEvent e
177
+ option_template: '<div class="option"><%= innerHTML %></div>'
178
+ option_group_template: '<div class="optgroup"><div class="option-group-label"><%= label %></div></div>'
179
+ select_template: '<div class="select"><a href="javascript:void(0)" class="selected-option"></a><div class="better-select-dropdown dropdown"></div></div>'
180
+ process_option: (option) -> option
181
+ process_option_group: (option_group) -> option_group
182
+ process_select: (select) -> select
183
+
184
+
185
+ window.BetterSelect = BetterSelect
@@ -0,0 +1,183 @@
1
+ $$ = (html) ->
2
+ elm = document.createElement 'div'
3
+ elm.innerHTML = html
4
+ if elm.children.length > 1 then elm.children else elm.children[0]
5
+
6
+ getPos = (tgt, method) ->
7
+ pos = 0
8
+ while tgt
9
+ pos += tgt[method]
10
+ tgt = tgt.offsetParent
11
+ pos
12
+
13
+ getTop = (tgt) -> getPos tgt, 'offsetTop'
14
+ getLeft = (tgt) -> getPos tgt, 'offsetLeft'
15
+
16
+ getClasses = (tgt) -> tgt.getAttribute('class').split ' '
17
+
18
+ setClasses = (tgt, classes) ->
19
+ tgt.setAttribute 'class', classes.join ' '
20
+ tgt
21
+
22
+ addClass = (tgt, className) ->
23
+ classes = getClasses tgt
24
+ classes.push(className) if classes.indexOf(className) is -1
25
+ setClasses tgt, classes
26
+
27
+ removeClass = (tgt, className) ->
28
+ classes.splice(index, 1) unless index = (classes = getClasses tgt).indexOf className is -1
29
+ setClasses tgt, classes
30
+
31
+ letters = 'a b c d e f g h i j k l m n o p q r s t u v w x y z'.split ' '
32
+ numbers = '0 1 2 3 4 5 6 7 8 9 0'.split ' '
33
+
34
+ build_element = (what, orig, obj) ->
35
+ ((elm = $$ _.template(obj["#{what}_template"]) obj["process_#{what}"] orig).orig = orig).better_version = elm
36
+ elm.better_select = obj
37
+ elm
38
+
39
+ renderOption = (orig_option, bs) ->
40
+ option = build_element 'option', orig_option, bs
41
+ option.reset = ->
42
+ @orig.selected = undefined
43
+ @setAttribute 'class', 'option'
44
+ bs.reset @
45
+ option.select = ->
46
+ @orig.selected = 'selected'
47
+ @setAttribute 'class', 'option selected'
48
+ bs.toggle() if bs.select.getAttribute('class').indexOf('open') isnt -1
49
+ bs.set_selected option
50
+ option.addEventListener 'click', -> option.select()
51
+ option.addEventListener 'mouseover', -> option.better_select.set_focused option
52
+ bs.options.push option
53
+ first_char = option.innerHTML.substr(0, 1).toLowerCase()
54
+ bs.options_by_first_char[first_char] = [] unless bs.options_by_first_char[first_char]
55
+ bs.options_by_first_char[first_char].push option
56
+ bs.options_by_first_char[first_char].sort()
57
+ option
58
+
59
+ renderOptionGroup = (orig_group, bs) ->
60
+ group = build_element 'option_group', orig_group, bs
61
+ group.appendChild(renderOption child, bs) for child in orig_group.children
62
+ bs.option_groups.push group
63
+ group
64
+
65
+ class BetterSelect
66
+ defaults:
67
+ positionDropdown: true
68
+ constructor: (elm, options) ->
69
+ return unless elm && elm.tagName && elm.tagName is 'SELECT'
70
+ @settings = _.extend {}, @defaults, options
71
+ @options = []
72
+ @options_by_first_char = {}
73
+ @option_groups = []
74
+ selected = elm.selectedOptions
75
+ @select = build_element 'select', elm, @
76
+ [@selected_option, @dropdown] = @select.children
77
+ if elm.id
78
+ @select.id = "#{elm.id}-better-select"
79
+ @dropdown.id = "#{elm.id}-better-select-dropdown"
80
+ @default_selected = [elm.children[elm.selectedIndex]]
81
+ elm.parentNode.insertBefore @select, elm
82
+ elm.parentNode.insertBefore elm, @select
83
+ elm.style.display = 'none'
84
+ children = elm.children
85
+ if @settings.positionDropdown
86
+ document.body.appendChild @dropdown
87
+ @dropdown.style.left = '-9999px'
88
+ for child in children
89
+ switch child.tagName
90
+ when 'OPTION'
91
+ method = renderOption
92
+ when 'OPTGROUP'
93
+ method = renderOptionGroup
94
+ @dropdown.appendChild(method child, @)
95
+ @default_selected[0].better_version.select() if @default_selected
96
+ @selected_option.addEventListener 'click', =>
97
+ @toggle()
98
+ @set_selected @dropdown_selected_option
99
+ window.addEventListener 'click', (e) =>
100
+ unless e.target == @selected_option || e.target == @select || @options.indexOf(e.target) isnt -1
101
+ @toggle() if @open
102
+ last_char = false
103
+ @selected_option.addEventListener 'focus', =>
104
+ document.body.style.overflow = 'hidden'
105
+ addClass @select, 'focus'
106
+ @selected_option.addEventListener 'blur', =>
107
+ removeClass @select, 'focus'
108
+ document.body.style.overflow = 'auto'
109
+ @toggle() if @open is true
110
+ true
111
+ @selected_option.addEventListener 'keydown', (e) => e.preventDefault() unless [38, 40].indexOf(e.keyCode) is -1
112
+ @selected_option.addEventListener 'keyup', (e) =>
113
+ @toggle() if @open is false
114
+ keyCode = e.keyCode
115
+ switch keyCode
116
+ when 38 then @set_focused @options[if (@focus_index -= 1) < 0 then @focus_index = @options.length - 1 else @focus_index]
117
+ when 40 then @set_focused @options[if (@focus_index += 1) >= @options.length then @focus_index = 0 else @focus_index]
118
+ when 13
119
+ @focused_option.select()
120
+ else
121
+ if keyCode > 47 && keyCode < 58
122
+ char = numbers[keyCode - 47]
123
+ else if keyCode > 64 && keyCode < 91
124
+ char = letters[keyCode - 65]
125
+ else
126
+ if @focused_option
127
+ removeClass @focused_option, 'focus'
128
+ @focused_option = false
129
+ @focus_index = -1
130
+ @toggle()
131
+ if char && @options_by_first_char[char]
132
+ @options_by_first_char[char].sort() unless last_char is char
133
+ option = @options_by_first_char[char].shift()
134
+ @options_by_first_char[char].push option
135
+ @set_focused option
136
+ @focus_index = @options.indexOf option
137
+ last_character = char
138
+ @dropdown.addEventListener('click', -> console.log arguments)
139
+ e.preventDefault()
140
+ e.stopPropagation()
141
+ e.returnValue = false
142
+ false
143
+ focused_option: false
144
+ focus_index: -1
145
+ set_focused: (option) ->
146
+ class_for_selected = (option) => if @selected_option && option.innerHTML is @selected_option.innerHTML then " selected" else ""
147
+ @focused_option.setAttribute('class', "option#{class_for_selected(@focused_option)}") if @focused_option
148
+ @focused_option = option
149
+ @focused_option.setAttribute("class", "option focus#{class_for_selected(option)}")
150
+ @focus_index = @options.indexOf @focused_option
151
+ open: false
152
+ toggle: ->
153
+ (if @open = !@open then addClass else removeClass)(@select, 'open')
154
+ if @settings.positionDropdown
155
+ if @dropdown.offsetHeight > window.innerHeight
156
+ height = window.innerHeight * .50
157
+ @dropdown.style.height = height + 'px'
158
+ @dropdown.style['overflow-y'] = 'auto'
159
+ if @dropdown_selected_option.offsetTop > height || @adjust_height
160
+ @dropdown_selected_option.scrollIntoView()
161
+ @adjust_height = true
162
+ top = top || (getTop(@select) - @dropdown_selected_option.offsetTop)
163
+ top = getTop(@select) - (@dropdown_selected_option.offsetTop - @dropdown.scrollTop)
164
+ @dropdown.style.top = (if top < 0 then 0 else top) + 'px'
165
+ @dropdown.style.left = if @open then getLeft(@select) + 'px' else '-9999px'
166
+ reset: (option) -> @default_selected[0].better_version.select() if @default_selected
167
+ set_selected: (option) ->
168
+ unless @selected_option.innerHTML == option.innerHTML
169
+ removeClass(@dropdown_selected_option, 'selected') if @dropdown_selected_option
170
+ addClass @dropdown_selected_option = option, 'selected'
171
+ @selected_option.innerHTML = option.innerHTML
172
+ e = document.createEvent('Event')
173
+ e.initEvent 'change', true, true
174
+ @select.orig.dispatchEvent e
175
+ option_template: '<div class="option"><%= innerHTML %></div>'
176
+ option_group_template: '<div class="optgroup"><div class="option-group-label"><%= label %></div></div>'
177
+ select_template: '<div class="select"><a href="javascript:void(0)" class="selected-option"></a><div class="better-select-dropdown dropdown"></div></div>'
178
+ process_option: (option) -> option
179
+ process_option_group: (option_group) -> option_group
180
+ process_select: (select) -> select
181
+
182
+
183
+ window.BetterSelect = BetterSelect
@@ -0,0 +1,32 @@
1
+ .better-select-dropdown {
2
+ position: fixed;
3
+
4
+ .option {
5
+ cursor: pointer;
6
+ }
7
+ }
8
+
9
+ .select {
10
+ position: static;
11
+
12
+ &.dont-position-dropdown {
13
+ position: relative;
14
+
15
+ .dropdown {
16
+ top: 100%;
17
+ left: -99999px;
18
+ }
19
+ }
20
+
21
+ &.open {
22
+ &.dont-position-dropdown {
23
+ .dropdown {
24
+ left: 0px;
25
+ }
26
+ }
27
+ }
28
+
29
+ .selected-option {
30
+ cursor: pointer;
31
+ }
32
+ }
@@ -0,0 +1,32 @@
1
+ .better-select-dropdown {
2
+ position: fixed;
3
+
4
+ .option {
5
+ cursor: pointer;
6
+ }
7
+ }
8
+
9
+ .select {
10
+ position: static;
11
+
12
+ &.dont-position-dropdown {
13
+ position: relative;
14
+
15
+ .dropdown {
16
+ top: 100%;
17
+ left: -99999px;
18
+ }
19
+ }
20
+
21
+ &.open {
22
+ &.dont-position-dropdown {
23
+ .dropdown {
24
+ left: 0px;
25
+ }
26
+ }
27
+ }
28
+
29
+ .selected-option {
30
+ cursor: pointer;
31
+ }
32
+ }
@@ -0,0 +1,16 @@
1
+ .better-select-dropdown {
2
+ position: fixed; }
3
+ .better-select-dropdown .option {
4
+ cursor: pointer; }
5
+
6
+ .select {
7
+ position: static; }
8
+ .select.dont-position-dropdown {
9
+ position: relative; }
10
+ .select.dont-position-dropdown .dropdown {
11
+ top: 100%;
12
+ left: -99999px; }
13
+ .select.open.dont-position-dropdown .dropdown {
14
+ left: 0px; }
15
+ .select .selected-option {
16
+ cursor: pointer; }