cantango_editor 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. data/.rspec +2 -0
  2. data/Gemfile +26 -0
  3. data/Gemfile.lock +177 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +80 -0
  6. data/Rakefile +54 -0
  7. data/VERSION +1 -0
  8. data/app/assets/images/cantango_editor/+.png +0 -0
  9. data/app/assets/images/cantango_editor/-.png +0 -0
  10. data/app/assets/images/cantango_editor/.gitkeep +0 -0
  11. data/app/assets/images/cantango_editor/bg-1.gif +0 -0
  12. data/app/assets/images/cantango_editor/bg-2.gif +0 -0
  13. data/app/assets/images/cantango_editor/bg-model.png +0 -0
  14. data/app/assets/images/cantango_editor/bg.gif +0 -0
  15. data/app/assets/images/cantango_editor/bg_action.gif +0 -0
  16. data/app/assets/images/cantango_editor/bg_action.png +0 -0
  17. data/app/assets/images/cantango_editor/bg_can.gif +0 -0
  18. data/app/assets/images/cantango_editor/bg_can.png +0 -0
  19. data/app/assets/images/cantango_editor/bg_cannot.gif +0 -0
  20. data/app/assets/images/cantango_editor/bg_cannot.png +0 -0
  21. data/app/assets/images/cantango_editor/button-top-r.png +0 -0
  22. data/app/assets/images/cantango_editor/button-top.png +0 -0
  23. data/app/assets/images/cantango_editor/cantangologo.png +0 -0
  24. data/app/assets/images/cantango_editor/cantangologo_big.png +0 -0
  25. data/app/assets/images/cantango_editor/cantangologo_big2.png +0 -0
  26. data/app/assets/images/cantango_editor/cantangologo_big3.png +0 -0
  27. data/app/assets/images/cantango_editor/ico-uc.png +0 -0
  28. data/app/assets/images/cantango_editor/images/ui-bg_diagonal-maze_20_6e4f1c_10x10.png +0 -0
  29. data/app/assets/images/cantango_editor/images/ui-bg_diagonal-maze_40_000000_10x10.png +0 -0
  30. data/app/assets/images/cantango_editor/images/ui-bg_fine-grain_10_eceadf_60x60.png +0 -0
  31. data/app/assets/images/cantango_editor/images/ui-bg_fine-grain_10_f8f7f6_60x60.png +0 -0
  32. data/app/assets/images/cantango_editor/images/ui-bg_fine-grain_15_eceadf_60x60.png +0 -0
  33. data/app/assets/images/cantango_editor/images/ui-bg_fine-grain_15_f7f3de_60x60.png +0 -0
  34. data/app/assets/images/cantango_editor/images/ui-bg_fine-grain_15_ffffff_60x60.png +0 -0
  35. data/app/assets/images/cantango_editor/images/ui-bg_fine-grain_65_654b24_60x60.png +0 -0
  36. data/app/assets/images/cantango_editor/images/ui-bg_fine-grain_68_b83400_60x60.png +0 -0
  37. data/app/assets/images/cantango_editor/images/ui-icons_222222_256x240.png +0 -0
  38. data/app/assets/images/cantango_editor/images/ui-icons_3572ac_256x240.png +0 -0
  39. data/app/assets/images/cantango_editor/images/ui-icons_8c291d_256x240.png +0 -0
  40. data/app/assets/images/cantango_editor/images/ui-icons_b83400_256x240.png +0 -0
  41. data/app/assets/images/cantango_editor/images/ui-icons_fbdb93_256x240.png +0 -0
  42. data/app/assets/images/cantango_editor/images/ui-icons_ffffff_256x240.png +0 -0
  43. data/app/assets/images/cantango_editor/line2.png +0 -0
  44. data/app/assets/images/cantango_editor/line3.png +0 -0
  45. data/app/assets/images/cantango_editor/panel-l.gif +0 -0
  46. data/app/assets/images/cantango_editor/panel-l.png +0 -0
  47. data/app/assets/images/cantango_editor/panel-r.gif +0 -0
  48. data/app/assets/images/cantango_editor/panel-r.png +0 -0
  49. data/app/assets/images/cantango_editor/panel.gif +0 -0
  50. data/app/assets/images/cantango_editor/panel.png +0 -0
  51. data/app/assets/images/cantango_editor/remember.png +0 -0
  52. data/app/assets/images/cantango_editor/sexy.png +0 -0
  53. data/app/assets/images/cantango_editor/x.png +0 -0
  54. data/app/assets/images/cantango_editor/xx.png +0 -0
  55. data/app/assets/javascripts/cantango_editor/application.js +59 -0
  56. data/app/assets/javascripts/cantango_editor/jquery.collapse.js +149 -0
  57. data/app/assets/javascripts/cantango_editor/jquery.cookie.js +96 -0
  58. data/app/assets/javascripts/cantango_editor/jquery.ui/jquery-ui-1.8.16.custom.js +996 -0
  59. data/app/assets/javascripts/cantango_editor/jquery.ui/jquery.ui.selectmenu.js +802 -0
  60. data/app/assets/stylesheets/cantango_editor/.main.css +157 -0
  61. data/app/assets/stylesheets/cantango_editor/application.css +7 -0
  62. data/app/assets/stylesheets/cantango_editor/areset.css +53 -0
  63. data/app/assets/stylesheets/cantango_editor/clearing.css +52 -0
  64. data/app/assets/stylesheets/cantango_editor/jquery-ui-1.8.16.custom.css +290 -0
  65. data/app/assets/stylesheets/cantango_editor/jquery.ui.selectmenu.css +30 -0
  66. data/app/assets/stylesheets/cantango_editor/main.css +294 -0
  67. data/app/assets/stylesheets/cantango_editor/text.css +97 -0
  68. data/app/controllers/cantango_editor/application_controller.rb +12 -0
  69. data/app/controllers/cantango_editor/permissions_controller.rb +47 -0
  70. data/app/helpers/cantango_editor/application_helper.rb +49 -0
  71. data/app/models/cantango_editor/categories.rb +52 -0
  72. data/app/models/cantango_editor/category.rb +21 -0
  73. data/app/models/cantango_editor/permissions.rb +119 -0
  74. data/app/views/cantango_editor/permissions/_footer.html.erb +4 -0
  75. data/app/views/cantango_editor/permissions/_header.html.erb +7 -0
  76. data/app/views/cantango_editor/permissions/_menu.html.erb +7 -0
  77. data/app/views/cantango_editor/permissions/_new_categories_select.html.erb +5 -0
  78. data/app/views/cantango_editor/permissions/_new_custom_targets.html.erb +5 -0
  79. data/app/views/cantango_editor/permissions/_new_targets_select.html.erb +5 -0
  80. data/app/views/cantango_editor/permissions/_permissions_form.html.erb +63 -0
  81. data/app/views/cantango_editor/permissions/index.html.erb +5 -0
  82. data/app/views/cantango_editor/permissions/index.js.erb +2 -0
  83. data/app/views/cantango_editor/permissions/new_category.js.erb +3 -0
  84. data/app/views/cantango_editor/permissions/new_custom_target.js.erb +2 -0
  85. data/app/views/cantango_editor/permissions/new_target.js.erb +4 -0
  86. data/app/views/cantango_editor/permissions/notes.html.erb +19 -0
  87. data/app/views/cantango_editor/permissions/preview_raw.html.erb +3 -0
  88. data/app/views/cantango_editor/permissions/temp _permissions_form.html.erb +55 -0
  89. data/app/views/layouts/cantango_editor/application.html.erb +14 -0
  90. data/cantango_editor.gemspec +258 -0
  91. data/config/routes.rb +15 -0
  92. data/lib/array.rb +11 -0
  93. data/lib/cantango_editor.rb +25 -0
  94. data/lib/cantango_editor/configuration.rb +34 -0
  95. data/lib/cantango_editor/engine.rb +11 -0
  96. data/lib/cantango_editor/version.rb +3 -0
  97. data/lib/permissions_hash.rb +80 -0
  98. data/lib/tasks/cantango-editor_tasks.rake +4 -0
  99. data/lib/time_precise.rb +7 -0
  100. data/script/rails +6 -0
  101. data/spec/cantango_editor/configuration/configuration_spec.rb +33 -0
  102. data/spec/cantango_editor/models/permissions_spec.rb +9 -0
  103. data/spec/cantango_editor_spec.rb +17 -0
  104. data/spec/dummy/Rakefile +7 -0
  105. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  106. data/spec/dummy/app/assets/javascripts/main.js +2 -0
  107. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  108. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  109. data/spec/dummy/app/controllers/main_controller.rb +5 -0
  110. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  111. data/spec/dummy/app/helpers/main_helper.rb +2 -0
  112. data/spec/dummy/app/mailers/.gitkeep +0 -0
  113. data/spec/dummy/app/models/.gitkeep +0 -0
  114. data/spec/dummy/app/models/admin.rb +5 -0
  115. data/spec/dummy/app/models/article.rb +2 -0
  116. data/spec/dummy/app/models/comment.rb +2 -0
  117. data/spec/dummy/app/models/concerto.rb +2 -0
  118. data/spec/dummy/app/models/guest.rb +14 -0
  119. data/spec/dummy/app/models/improvisation.rb +2 -0
  120. data/spec/dummy/app/models/post.rb +2 -0
  121. data/spec/dummy/app/models/song.rb +2 -0
  122. data/spec/dummy/app/models/tune.rb +2 -0
  123. data/spec/dummy/app/models/user.rb +11 -0
  124. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  125. data/spec/dummy/app/views/main/index.html.erb +3 -0
  126. data/spec/dummy/config.ru +4 -0
  127. data/spec/dummy/config/application.rb +53 -0
  128. data/spec/dummy/config/boot.rb +10 -0
  129. data/spec/dummy/config/categories.yml +10 -0
  130. data/spec/dummy/config/database.yml +25 -0
  131. data/spec/dummy/config/environment.rb +5 -0
  132. data/spec/dummy/config/environments/development.rb +30 -0
  133. data/spec/dummy/config/environments/production.rb +60 -0
  134. data/spec/dummy/config/environments/test.rb +39 -0
  135. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  136. data/spec/dummy/config/initializers/cantango.rb +4 -0
  137. data/spec/dummy/config/initializers/cantango_editor.rb +4 -0
  138. data/spec/dummy/config/initializers/devise.rb +210 -0
  139. data/spec/dummy/config/initializers/inflections.rb +10 -0
  140. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  141. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  142. data/spec/dummy/config/initializers/session_store.rb +8 -0
  143. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  144. data/spec/dummy/config/locales/devise.en.yml +58 -0
  145. data/spec/dummy/config/locales/en.yml +5 -0
  146. data/spec/dummy/config/permissions.yml.save +59 -0
  147. data/spec/dummy/config/routes.rb +15 -0
  148. data/spec/dummy/db/development.sqlite3 +0 -0
  149. data/spec/dummy/db/migrate/20111018203622_create_posts.rb +9 -0
  150. data/spec/dummy/db/migrate/20111018203648_create_articles.rb +9 -0
  151. data/spec/dummy/db/migrate/20111018203800_create_comments.rb +9 -0
  152. data/spec/dummy/db/migrate/20111018203817_create_songs.rb +9 -0
  153. data/spec/dummy/db/migrate/20111018203827_create_tunes.rb +9 -0
  154. data/spec/dummy/db/migrate/20111018203859_create_concertos.rb +9 -0
  155. data/spec/dummy/db/migrate/20111018203917_create_improvisations.rb +9 -0
  156. data/spec/dummy/db/migrate/20111019122217_devise_create_users.rb +28 -0
  157. data/spec/dummy/db/schema.rb +58 -0
  158. data/spec/dummy/db/test.sqlite3 +0 -0
  159. data/spec/dummy/lib/assets/.gitkeep +0 -0
  160. data/spec/dummy/log/.gitkeep +0 -0
  161. data/spec/dummy/public/404.html +26 -0
  162. data/spec/dummy/public/422.html +26 -0
  163. data/spec/dummy/public/500.html +26 -0
  164. data/spec/dummy/public/favicon.ico +0 -0
  165. data/spec/dummy/script/rails +6 -0
  166. data/spec/integration/navigation_spec.rb +18 -0
  167. data/spec/permissions_hash_spec.rb +63 -0
  168. data/spec/requests/requests_spec.rb +9 -0
  169. data/spec/spec_helper.rb +16 -0
  170. metadata +416 -0
@@ -0,0 +1,802 @@
1
+ /*
2
+ * jQuery UI selectmenu version 1.1.0
3
+ *
4
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
6
+ * and GPL (GPL-LICENSE.txt) licenses.
7
+ *
8
+ * http://docs.jquery.com/UI
9
+ * https://github.com/fnagel/jquery-ui/wiki/Selectmenu
10
+ */
11
+
12
+ (function($) {
13
+
14
+ $.widget("ui.selectmenu", {
15
+ getter: "value",
16
+ version: "1.8",
17
+ eventPrefix: "selectmenu",
18
+ options: {
19
+ transferClasses: true,
20
+ typeAhead: "sequential",
21
+ style: 'dropdown',
22
+ positionOptions: {
23
+ my: "left top",
24
+ at: "left bottom",
25
+ offset: null
26
+ },
27
+ width: null,
28
+ menuWidth: null,
29
+ handleWidth: 26,
30
+ maxHeight: null,
31
+ icons: null,
32
+ format: null,
33
+ bgImage: function() {},
34
+ wrapperElement: ""
35
+ },
36
+
37
+ _create: function() {
38
+ var self = this, o = this.options;
39
+
40
+ // set a default id value, generate a new random one if not set by developer
41
+ var selectmenuId = this.element.attr('id') || 'ui-selectmenu-' + Math.random().toString(16).slice(2, 10);
42
+
43
+ // quick array of button and menu id's
44
+ this.ids = [ selectmenuId + '-button', selectmenuId + '-menu' ];
45
+
46
+ // define safe mouseup for future toggling
47
+ this._safemouseup = true;
48
+
49
+ // create menu button wrapper
50
+ this.newelement = $('<a class="' + this.widgetBaseClass + ' ui-widget ui-state-default ui-corner-all" id="' + this.ids[0] + '" role="button" href="#" tabindex="0" aria-haspopup="true" aria-owns="' + this.ids[1] + '"></a>')
51
+ .insertAfter(this.element);
52
+ this.newelement.wrap(o.wrapperElement);
53
+
54
+ // transfer tabindex
55
+ var tabindex = this.element.attr('tabindex');
56
+ if (tabindex) {
57
+ this.newelement.attr('tabindex', tabindex);
58
+ }
59
+
60
+ // save reference to select in data for ease in calling methods
61
+ this.newelement.data('selectelement', this.element);
62
+
63
+ // menu icon
64
+ this.selectmenuIcon = $('<span class="' + this.widgetBaseClass + '-icon ui-icon"></span>')
65
+ .prependTo(this.newelement);
66
+
67
+ // append status span to button
68
+ this.newelement.prepend('<span class="' + self.widgetBaseClass + '-status" />');
69
+
70
+ // make associated form label trigger focus
71
+ $('label[for="' + this.element.attr('id') + '"]')
72
+ .attr('for', this.ids[0])
73
+ .bind('click.selectmenu', function() {
74
+ self.newelement[0].focus();
75
+ return false;
76
+ });
77
+
78
+ // click toggle for menu visibility
79
+ this.newelement
80
+ .bind('mousedown.selectmenu', function(event) {
81
+ self._toggle(event, true);
82
+ // make sure a click won't open/close instantly
83
+ if (o.style == "popup") {
84
+ self._safemouseup = false;
85
+ setTimeout(function() { self._safemouseup = true; }, 300);
86
+ }
87
+ return false;
88
+ })
89
+ .bind('click.selectmenu', function() {
90
+ return false;
91
+ })
92
+ .bind("keydown.selectmenu", function(event) {
93
+ var ret = false;
94
+ switch (event.keyCode) {
95
+ case $.ui.keyCode.ENTER:
96
+ ret = true;
97
+ break;
98
+ case $.ui.keyCode.SPACE:
99
+ self._toggle(event);
100
+ break;
101
+ case $.ui.keyCode.UP:
102
+ if (event.altKey) {
103
+ self.open(event);
104
+ } else {
105
+ self._moveSelection(-1);
106
+ }
107
+ break;
108
+ case $.ui.keyCode.DOWN:
109
+ if (event.altKey) {
110
+ self.open(event);
111
+ } else {
112
+ self._moveSelection(1);
113
+ }
114
+ break;
115
+ case $.ui.keyCode.LEFT:
116
+ self._moveSelection(-1);
117
+ break;
118
+ case $.ui.keyCode.RIGHT:
119
+ self._moveSelection(1);
120
+ break;
121
+ case $.ui.keyCode.TAB:
122
+ ret = true;
123
+ break;
124
+ default:
125
+ ret = true;
126
+ }
127
+ return ret;
128
+ })
129
+ .bind('keypress.selectmenu', function(event) {
130
+ self._typeAhead(event.which, 'mouseup');
131
+ return true;
132
+ })
133
+ .bind('mouseover.selectmenu focus.selectmenu', function() {
134
+ if (!o.disabled) {
135
+ $(this).addClass(self.widgetBaseClass + '-focus ui-state-hover');
136
+ }
137
+ })
138
+ .bind('mouseout.selectmenu blur.selectmenu', function() {
139
+ if (!o.disabled) {
140
+ $(this).removeClass(self.widgetBaseClass + '-focus ui-state-hover');
141
+ }
142
+ });
143
+
144
+ // document click closes menu
145
+ $(document).bind("mousedown.selectmenu", function(event) {
146
+ self.close(event);
147
+ });
148
+
149
+ // change event on original selectmenu
150
+ this.element
151
+ .bind("click.selectmenu", function() {
152
+ self._refreshValue();
153
+ })
154
+ // FIXME: newelement can be null under unclear circumstances in IE8
155
+ // TODO not sure if this is still a problem (fnagel 20.03.11)
156
+ .bind("focus.selectmenu", function() {
157
+ if (self.newelement) {
158
+ self.newelement[0].focus();
159
+ }
160
+ });
161
+
162
+ // set width when not set via options
163
+ if (!o.width) {
164
+ o.width = this.element.outerWidth();
165
+ }
166
+ // set menu button width
167
+ this.newelement.width(o.width);
168
+
169
+ // hide original selectmenu element
170
+ this.element.hide();
171
+
172
+ // create menu portion, append to body
173
+ this.list = $('<ul class="' + self.widgetBaseClass + '-menu ui-widget ui-widget-content" aria-hidden="true" role="listbox" aria-labelledby="' + this.ids[0] + '" id="' + this.ids[1] + '"></ul>').appendTo('body');
174
+ this.list.wrap(o.wrapperElement);
175
+
176
+ // transfer menu click to menu button
177
+ this.list
178
+ .bind("keydown.selectmenu", function(event) {
179
+ var ret = false;
180
+ switch (event.keyCode) {
181
+ case $.ui.keyCode.UP:
182
+ if (event.altKey) {
183
+ self.close(event, true);
184
+ } else {
185
+ self._moveFocus(-1);
186
+ }
187
+ break;
188
+ case $.ui.keyCode.DOWN:
189
+ if (event.altKey) {
190
+ self.close(event, true);
191
+ } else {
192
+ self._moveFocus(1);
193
+ }
194
+ break;
195
+ case $.ui.keyCode.LEFT:
196
+ self._moveFocus(-1);
197
+ break;
198
+ case $.ui.keyCode.RIGHT:
199
+ self._moveFocus(1);
200
+ break;
201
+ case $.ui.keyCode.HOME:
202
+ self._moveFocus(':first');
203
+ break;
204
+ case $.ui.keyCode.PAGE_UP:
205
+ self._scrollPage('up');
206
+ break;
207
+ case $.ui.keyCode.PAGE_DOWN:
208
+ self._scrollPage('down');
209
+ break;
210
+ case $.ui.keyCode.END:
211
+ self._moveFocus(':last');
212
+ break;
213
+ case $.ui.keyCode.ENTER:
214
+ case $.ui.keyCode.SPACE:
215
+ self.close(event, true);
216
+ $(event.target).parents('li:eq(0)').trigger('mouseup');
217
+ break;
218
+ case $.ui.keyCode.TAB:
219
+ ret = true;
220
+ self.close(event, true);
221
+ $(event.target).parents('li:eq(0)').trigger('mouseup');
222
+ break;
223
+ case $.ui.keyCode.ESCAPE:
224
+ self.close(event, true);
225
+ break;
226
+ default:
227
+ ret = true;
228
+ }
229
+ return ret;
230
+ })
231
+ .bind('keypress.selectmenu', function(event) {
232
+ self._typeAhead(event.which, 'focus');
233
+ return true;
234
+ })
235
+ // this allows for using the scrollbar in an overflowed list
236
+ .bind( 'mousedown.selectmenu mouseup.selectmenu', function() { return false; });
237
+
238
+
239
+ // needed when window is resized
240
+ $(window).bind( "resize.selectmenu", $.proxy( self._refreshPosition, this ) );
241
+ },
242
+
243
+ _init: function() {
244
+ var self = this, o = this.options;
245
+
246
+ // serialize selectmenu element options
247
+ var selectOptionData = [];
248
+ this.element
249
+ .find('option')
250
+ .each(function() {
251
+ selectOptionData.push({
252
+ value: $(this).attr('value'),
253
+ text: self._formatText($(this).text()),
254
+ selected: $(this).attr('selected'),
255
+ disabled: $(this).attr('disabled'),
256
+ classes: $(this).attr('class'),
257
+ typeahead: $(this).attr('typeahead'),
258
+ parentOptGroup: $(this).parent('optgroup'),
259
+ bgImage: o.bgImage.call($(this))
260
+ });
261
+ });
262
+
263
+ // active state class is only used in popup style
264
+ var activeClass = (self.options.style == "popup") ? " ui-state-active" : "";
265
+
266
+ // empty list so we can refresh the selectmenu via selectmenu()
267
+ this.list.html("");
268
+
269
+ // write li's
270
+ for (var i = 0; i < selectOptionData.length; i++) {
271
+ var thisLi = $('<li role="presentation"' + (selectOptionData[i].disabled ? ' class="' + this.namespace + '-state-disabled' + '"' : '' ) + '><a href="#" tabindex="-1" role="option"' + (selectOptionData[i].disabled ? ' aria-disabled="true"' : '' ) + ' aria-selected="false"' + (selectOptionData[i].typeahead ? ' typeahead="' + selectOptionData[i].typeahead + '"' : '' ) + '>'+ selectOptionData[i].text +'</a></li>')
272
+ .data('index', i)
273
+ .addClass(selectOptionData[i].classes)
274
+ .data('optionClasses', selectOptionData[i].classes || '')
275
+ .bind("mouseup.selectmenu", function(event) {
276
+ if (self._safemouseup && !self._disabled(event.currentTarget) && !self._disabled($( event.currentTarget ).parents( "ul>li." + self.widgetBaseClass + "-group " )) ) {
277
+ var changed = $(this).data('index') != self._selectedIndex();
278
+ self.index($(this).data('index'));
279
+ self.select(event);
280
+ if (changed) {
281
+ self.change(event);
282
+ }
283
+ self.close(event, true);
284
+ }
285
+ return false;
286
+ })
287
+ .bind("click.selectmenu", function() {
288
+ return false;
289
+ })
290
+ .bind('mouseover.selectmenu focus.selectmenu', function(e) {
291
+ // no hover if diabled
292
+ if (!$(e.currentTarget).hasClass(self.namespace + '-state-disabled')) {
293
+ self._selectedOptionLi().addClass(activeClass);
294
+ self._focusedOptionLi().removeClass(self.widgetBaseClass + '-item-focus ui-state-hover');
295
+ $(this).removeClass('ui-state-active').addClass(self.widgetBaseClass + '-item-focus ui-state-hover');
296
+ }
297
+ })
298
+ .bind('mouseout.selectmenu blur.selectmenu', function() {
299
+ if ($(this).is(self._selectedOptionLi().selector)) {
300
+ $(this).addClass(activeClass);
301
+ }
302
+ $(this).removeClass(self.widgetBaseClass + '-item-focus ui-state-hover');
303
+ });
304
+
305
+ // optgroup or not...
306
+ if ( selectOptionData[i].parentOptGroup.length ) {
307
+ var optGroupName = self.widgetBaseClass + '-group-' + this.element.find( 'optgroup' ).index( selectOptionData[i].parentOptGroup );
308
+ if (this.list.find( 'li.' + optGroupName ).length ) {
309
+ this.list.find( 'li.' + optGroupName + ':last ul' ).append( thisLi );
310
+ } else {
311
+ $(' <li role="presentation" class="' + self.widgetBaseClass + '-group ' + optGroupName + (selectOptionData[i].parentOptGroup.attr("disabled") ? ' ' + this.namespace + '-state-disabled" aria-disabled="true"' : '"' ) + '><span class="' + self.widgetBaseClass + '-group-label">' + selectOptionData[i].parentOptGroup.attr('label') + '</span><ul></ul></li> ')
312
+ .appendTo( this.list )
313
+ .find( 'ul' )
314
+ .append( thisLi );
315
+ }
316
+ } else {
317
+ thisLi.appendTo(this.list);
318
+ }
319
+
320
+ // append icon if option is specified
321
+ if (o.icons) {
322
+ for (var j in o.icons) {
323
+ if (thisLi.is(o.icons[j].find)) {
324
+ thisLi
325
+ .data('optionClasses', selectOptionData[i].classes + ' ' + self.widgetBaseClass + '-hasIcon')
326
+ .addClass(self.widgetBaseClass + '-hasIcon');
327
+ var iconClass = o.icons[j].icon || "";
328
+ thisLi
329
+ .find('a:eq(0)')
330
+ .prepend('<span class="' + self.widgetBaseClass + '-item-icon ui-icon ' + iconClass + '"></span>');
331
+ if (selectOptionData[i].bgImage) {
332
+ thisLi.find('span').css('background-image', selectOptionData[i].bgImage);
333
+ }
334
+ }
335
+ }
336
+ }
337
+ }
338
+
339
+ // we need to set and unset the CSS classes for dropdown and popup style
340
+ var isDropDown = (o.style == 'dropdown');
341
+ this.newelement
342
+ .toggleClass(self.widgetBaseClass + "-dropdown", isDropDown)
343
+ .toggleClass(self.widgetBaseClass + "-popup", !isDropDown);
344
+ this.list
345
+ .toggleClass(self.widgetBaseClass + "-menu-dropdown ui-corner-bottom", isDropDown)
346
+ .toggleClass(self.widgetBaseClass + "-menu-popup ui-corner-all", !isDropDown)
347
+ // add corners to top and bottom menu items
348
+ .find('li:first')
349
+ .toggleClass("ui-corner-top", !isDropDown)
350
+ .end().find('li:last')
351
+ .addClass("ui-corner-bottom");
352
+ this.selectmenuIcon
353
+ .toggleClass('ui-icon-triangle-1-s', isDropDown)
354
+ .toggleClass('ui-icon-triangle-2-n-s', !isDropDown);
355
+
356
+ // transfer classes to selectmenu and list
357
+ if (o.transferClasses) {
358
+ var transferClasses = this.element.attr('class') || '';
359
+ this.newelement.add(this.list).addClass(transferClasses);
360
+ }
361
+
362
+ // set menu width to either menuWidth option value, width option value, or select width
363
+ if (o.style == 'dropdown') {
364
+ this.list.width(o.menuWidth ? o.menuWidth : o.width);
365
+ } else {
366
+ this.list.width(o.menuWidth ? o.menuWidth : o.width - o.handleWidth);
367
+ }
368
+
369
+ // calculate default max height
370
+ if (o.maxHeight) {
371
+ // set max height from option
372
+ if (o.maxHeight < this.list.height()) {
373
+ this.list.height(o.maxHeight);
374
+ }
375
+ } else {
376
+ if (!o.format && ($(window).height() / 3) < this.list.height()) {
377
+ o.maxHeight = $(window).height() / 3;
378
+ this.list.height(o.maxHeight);
379
+ }
380
+ }
381
+
382
+ // save reference to actionable li's (not group label li's)
383
+ this._optionLis = this.list.find('li:not(.' + self.widgetBaseClass + '-group)');
384
+
385
+ // transfer disabled state
386
+ if ( this.element.attr( 'disabled' ) === true ) {
387
+ this.disable();
388
+ } else {
389
+ this.enable()
390
+ }
391
+
392
+ // update value
393
+ this.index(this._selectedIndex());
394
+
395
+ // needed when selectmenu is placed at the very bottom / top of the page
396
+ window.setTimeout(function() {
397
+ self._refreshPosition();
398
+ }, 200);
399
+ },
400
+
401
+ destroy: function() {
402
+ this.element.removeData( this.widgetName )
403
+ .removeClass( this.widgetBaseClass + '-disabled' + ' ' + this.namespace + '-state-disabled' )
404
+ .removeAttr( 'aria-disabled' )
405
+ .unbind( ".selectmenu" );
406
+
407
+ $( window ).unbind( ".selectmenu" );
408
+ $( document ).unbind( ".selectmenu" );
409
+
410
+ // unbind click on label, reset its for attr
411
+ $( 'label[for=' + this.newelement.attr('id') + ']' )
412
+ .attr( 'for', this.element.attr( 'id' ) )
413
+ .unbind( '.selectmenu' );
414
+
415
+ if ( this.options.wrapperElement ) {
416
+ this.newelement.find( this.options.wrapperElement ).remove();
417
+ this.list.find( this.options.wrapperElement ).remove();
418
+ } else {
419
+ this.newelement.remove();
420
+ this.list.remove();
421
+ }
422
+ this.element.show();
423
+
424
+ // call widget destroy function
425
+ $.Widget.prototype.destroy.apply(this, arguments);
426
+ },
427
+
428
+ _typeAhead: function(code, eventType){
429
+ var self = this, focusFound = false, C = String.fromCharCode(code).toUpperCase();
430
+ c = C.toLowerCase();
431
+
432
+ if (self.options.typeAhead == 'sequential') {
433
+ // clear the timeout so we can use _prevChar
434
+ window.clearTimeout('ui.selectmenu-' + self.selectmenuId);
435
+
436
+ // define our find var
437
+ var find = typeof(self._prevChar) == 'undefined' ? '' : self._prevChar.join('');
438
+
439
+ function focusOptSeq(elem, ind, c){
440
+ focusFound = true;
441
+ $(elem).trigger(eventType);
442
+ typeof(self._prevChar) == 'undefined' ? self._prevChar = [c] : self._prevChar[self._prevChar.length] = c;
443
+ }
444
+ this.list.find('li a').each(function(i) {
445
+ if (!focusFound) {
446
+ // allow the typeahead attribute on the option tag for a more specific lookup
447
+ var thisText = $(this).attr('typeahead') || $(this).text();
448
+ if (thisText.indexOf(find+C) == 0) {
449
+ focusOptSeq(this,i,C)
450
+ } else if (thisText.indexOf(find+c) == 0) {
451
+ focusOptSeq(this,i,c)
452
+ }
453
+ }
454
+ });
455
+ // set a 1 second timeout for sequenctial typeahead
456
+ // keep this set even if we have no matches so it doesnt typeahead somewhere else
457
+ window.setTimeout(function(el) {
458
+ self._prevChar = undefined;
459
+ }, 1000, self);
460
+
461
+ } else {
462
+ //define self._prevChar if needed
463
+ if (!self._prevChar){ self._prevChar = ['',0]; }
464
+
465
+ var focusFound = false;
466
+ function focusOpt(elem, ind){
467
+ focusFound = true;
468
+ $(elem).trigger(eventType);
469
+ self._prevChar[1] = ind;
470
+ }
471
+ this.list.find('li a').each(function(i){
472
+ if(!focusFound){
473
+ var thisText = $(this).text();
474
+ if( thisText.indexOf(C) == 0 || thisText.indexOf(c) == 0){
475
+ if(self._prevChar[0] == C){
476
+ if(self._prevChar[1] < i){ focusOpt(this,i); }
477
+ }
478
+ else{ focusOpt(this,i); }
479
+ }
480
+ }
481
+ });
482
+ this._prevChar[0] = C;
483
+ }
484
+ },
485
+
486
+ // returns some usefull information, called by callbacks only
487
+ _uiHash: function() {
488
+ var index = this.index();
489
+ return {
490
+ index: index,
491
+ option: $("option", this.element).get(index),
492
+ value: this.element[0].value
493
+ };
494
+ },
495
+
496
+ open: function(event) {
497
+ var self = this;
498
+ if ( this.newelement.attr("aria-disabled") != 'true' ) {
499
+ this._closeOthers(event);
500
+ this.newelement
501
+ .addClass('ui-state-active');
502
+ if (self.options.wrapperElement) {
503
+ this.list.parent().appendTo('body');
504
+ } else {
505
+ this.list.appendTo('body');
506
+ }
507
+
508
+ this.list.addClass(self.widgetBaseClass + '-open')
509
+ .attr('aria-hidden', false)
510
+ .find('li:not(.' + self.widgetBaseClass + '-group):eq(' + this._selectedIndex() + ') a')[0].focus();
511
+ if ( this.options.style == "dropdown" ) {
512
+ this.newelement.removeClass('ui-corner-all').addClass('ui-corner-top');
513
+ }
514
+ this._refreshPosition();
515
+ this._trigger("open", event, this._uiHash());
516
+ }
517
+ },
518
+
519
+ close: function(event, retainFocus) {
520
+ if ( this.newelement.is('.ui-state-active') ) {
521
+ this.newelement
522
+ .removeClass('ui-state-active');
523
+ this.list
524
+ .attr('aria-hidden', true)
525
+ .removeClass(this.widgetBaseClass + '-open');
526
+ if ( this.options.style == "dropdown" ) {
527
+ this.newelement.removeClass('ui-corner-top').addClass('ui-corner-all');
528
+ }
529
+ if ( retainFocus ) {
530
+ this.newelement.focus();
531
+ }
532
+ this._trigger("close", event, this._uiHash());
533
+ }
534
+ },
535
+
536
+ change: function(event) {
537
+ this.element.trigger("change");
538
+ this._trigger("change", event, this._uiHash());
539
+ },
540
+
541
+ select: function(event) {
542
+ if (this._disabled(event.currentTarget)) { return false; }
543
+ this._trigger("select", event, this._uiHash());
544
+ },
545
+
546
+ _closeOthers: function(event) {
547
+ $('.' + this.widgetBaseClass + '.ui-state-active').not(this.newelement).each(function() {
548
+ $(this).data('selectelement').selectmenu('close', event);
549
+ });
550
+ $('.' + this.widgetBaseClass + '.ui-state-hover').trigger('mouseout');
551
+ },
552
+
553
+ _toggle: function(event, retainFocus) {
554
+ if ( this.list.is('.' + this.widgetBaseClass + '-open') ) {
555
+ this.close(event, retainFocus);
556
+ } else {
557
+ this.open(event);
558
+ }
559
+ },
560
+
561
+ _formatText: function(text) {
562
+ return (this.options.format ? this.options.format(text) : text);
563
+ },
564
+
565
+ _selectedIndex: function() {
566
+ return this.element[0].selectedIndex;
567
+ },
568
+
569
+ _selectedOptionLi: function() {
570
+ return this._optionLis.eq(this._selectedIndex());
571
+ },
572
+
573
+ _focusedOptionLi: function() {
574
+ return this.list.find('.' + this.widgetBaseClass + '-item-focus');
575
+ },
576
+
577
+ _moveSelection: function(amt, recIndex) {
578
+ var currIndex = parseInt(this._selectedOptionLi().data('index') || 0, 10);
579
+ var newIndex = currIndex + amt;
580
+ // do not loop when using up key
581
+
582
+ if (newIndex < 0) {
583
+ newIndex = 0;
584
+ }
585
+ if (newIndex > this._optionLis.size() - 1) {
586
+ newIndex = this._optionLis.size() - 1;
587
+ }
588
+ //Occurs when a full loop has been made
589
+ if (newIndex === recIndex) { return false; }
590
+
591
+ if (this._optionLis.eq(newIndex).hasClass( this.namespace + '-state-disabled' )) {
592
+ // if option at newIndex is disabled, call _moveFocus, incrementing amt by one
593
+ (amt > 0) ? ++amt : --amt;
594
+ this._moveSelection(amt, newIndex);
595
+ } else {
596
+ return this._optionLis.eq(newIndex).trigger('mouseup');
597
+ }
598
+ },
599
+
600
+ _moveFocus: function(amt, recIndex) {
601
+ if (!isNaN(amt)) {
602
+ var currIndex = parseInt(this._focusedOptionLi().data('index') || 0, 10);
603
+ var newIndex = currIndex + amt;
604
+ }
605
+ else {
606
+ var newIndex = parseInt(this._optionLis.filter(amt).data('index'), 10);
607
+ }
608
+
609
+ if (newIndex < 0) {
610
+ newIndex = 0;
611
+ }
612
+ if (newIndex > this._optionLis.size() - 1) {
613
+ newIndex = this._optionLis.size() - 1;
614
+ }
615
+
616
+ //Occurs when a full loop has been made
617
+ if (newIndex === recIndex) { return false; }
618
+
619
+ var activeID = this.widgetBaseClass + '-item-' + Math.round(Math.random() * 1000);
620
+
621
+ this._focusedOptionLi().find('a:eq(0)').attr('id', '');
622
+
623
+ if (this._optionLis.eq(newIndex).hasClass( this.namespace + '-state-disabled' )) {
624
+ // if option at newIndex is disabled, call _moveFocus, incrementing amt by one
625
+ (amt > 0) ? ++amt : --amt;
626
+ this._moveFocus(amt, newIndex);
627
+ } else {
628
+ this._optionLis.eq(newIndex).find('a:eq(0)').attr('id',activeID).focus();
629
+ }
630
+
631
+ this.list.attr('aria-activedescendant', activeID);
632
+ },
633
+
634
+ _scrollPage: function(direction) {
635
+ var numPerPage = Math.floor(this.list.outerHeight() / this.list.find('li:first').outerHeight());
636
+ numPerPage = (direction == 'up' ? -numPerPage : numPerPage);
637
+ this._moveFocus(numPerPage);
638
+ },
639
+
640
+ _setOption: function(key, value) {
641
+ this.options[key] = value;
642
+ // set
643
+ if (key == 'disabled') {
644
+ this.close();
645
+ this.element
646
+ .add(this.newelement)
647
+ .add(this.list)[value ? 'addClass' : 'removeClass'](
648
+ this.widgetBaseClass + '-disabled' + ' ' +
649
+ this.namespace + '-state-disabled')
650
+ .attr("aria-disabled", value);
651
+ }
652
+ },
653
+
654
+ disable: function(index, type){
655
+ // if options is not provided, call the parents disable function
656
+ if ( typeof( index ) == 'undefined' ) {
657
+ this._setOption( 'disabled', true );
658
+ } else {
659
+ if ( type == "optgroup" ) {
660
+ this._disableOptgroup(index);
661
+ } else {
662
+ this._disableOption(index);
663
+ }
664
+ }
665
+ },
666
+
667
+ enable: function(index, type) {
668
+ // if options is not provided, call the parents enable function
669
+ if ( typeof( index ) == 'undefined' ) {
670
+ this._setOption('disabled', false);
671
+ } else {
672
+ if ( type == "optgroup" ) {
673
+ this._enableOptgroup(index);
674
+ } else {
675
+ this._enableOption(index);
676
+ }
677
+ }
678
+ },
679
+
680
+ _disabled: function(elem) {
681
+ return $(elem).hasClass( this.namespace + '-state-disabled' );
682
+ },
683
+
684
+
685
+ _disableOption: function(index) {
686
+ var optionElem = this._optionLis.eq(index);
687
+ if (optionElem) {
688
+ optionElem.addClass(this.namespace + '-state-disabled')
689
+ .find("a").attr("aria-disabled", true);
690
+ this.element.find("option").eq(index).attr("disabled", "disabled");
691
+ }
692
+ },
693
+
694
+ _enableOption: function(index) {
695
+ var optionElem = this._optionLis.eq(index);
696
+ if (optionElem) {
697
+ optionElem.removeClass( this.namespace + '-state-disabled' )
698
+ .find("a").attr("aria-disabled", false);
699
+ this.element.find("option").eq(index).removeAttr("disabled");
700
+ }
701
+ },
702
+
703
+ _disableOptgroup: function(index) {
704
+ var optGroupElem = this.list.find( 'li.' + this.widgetBaseClass + '-group-' + index );
705
+ if (optGroupElem) {
706
+ optGroupElem.addClass(this.namespace + '-state-disabled')
707
+ .attr("aria-disabled", true);
708
+ this.element.find("optgroup").eq(index).attr("disabled", "disabled");
709
+ }
710
+ },
711
+
712
+ _enableOptgroup: function(index) {
713
+ var optGroupElem = this.list.find( 'li.' + this.widgetBaseClass + '-group-' + index );
714
+ if (optGroupElem) {
715
+ optGroupElem.removeClass(this.namespace + '-state-disabled')
716
+ .attr("aria-disabled", false);
717
+ this.element.find("optgroup").eq(index).removeAttr("disabled");
718
+ }
719
+ },
720
+
721
+ index: function(newValue) {
722
+ if (arguments.length) {
723
+ if (!this._disabled($(this._optionLis[newValue]))) {
724
+ this.element[0].selectedIndex = newValue;
725
+ this._refreshValue();
726
+ } else {
727
+ return false;
728
+ }
729
+ } else {
730
+ return this._selectedIndex();
731
+ }
732
+ },
733
+
734
+ value: function(newValue) {
735
+ if (arguments.length) {
736
+ this.element[0].value = newValue;
737
+ this._refreshValue();
738
+ } else {
739
+ return this.element[0].value;
740
+ }
741
+ },
742
+
743
+ _refreshValue: function() {
744
+ var activeClass = (this.options.style == "popup") ? " ui-state-active" : "";
745
+ var activeID = this.widgetBaseClass + '-item-' + Math.round(Math.random() * 1000);
746
+ // deselect previous
747
+ this.list
748
+ .find('.' + this.widgetBaseClass + '-item-selected')
749
+ .removeClass(this.widgetBaseClass + "-item-selected" + activeClass)
750
+ .find('a')
751
+ .attr('aria-selected', 'false')
752
+ .attr('id', '');
753
+ // select new
754
+ this._selectedOptionLi()
755
+ .addClass(this.widgetBaseClass + "-item-selected" + activeClass)
756
+ .find('a')
757
+ .attr('aria-selected', 'true')
758
+ .attr('id', activeID);
759
+
760
+ // toggle any class brought in from option
761
+ var currentOptionClasses = (this.newelement.data('optionClasses') ? this.newelement.data('optionClasses') : "");
762
+ var newOptionClasses = (this._selectedOptionLi().data('optionClasses') ? this._selectedOptionLi().data('optionClasses') : "");
763
+ this.newelement
764
+ .removeClass(currentOptionClasses)
765
+ .data('optionClasses', newOptionClasses)
766
+ .addClass( newOptionClasses )
767
+ .find('.' + this.widgetBaseClass + '-status')
768
+ .html(
769
+ this._selectedOptionLi()
770
+ .find('a:eq(0)')
771
+ .html()
772
+ );
773
+
774
+ this.list.attr('aria-activedescendant', activeID);
775
+ },
776
+
777
+ _refreshPosition: function() {
778
+ var o = this.options;
779
+ // if its a native pop-up we need to calculate the position of the selected li
780
+ if (o.style == "popup" && !o.positionOptions.offset) {
781
+ var selected = this._selectedOptionLi();
782
+ var _offset = "0 -" + (selected.outerHeight() + selected.offset().top - this.list.offset().top);
783
+ }
784
+ // update zIndex if jQuery UI is able to process
785
+ var zIndexElement = this.element.zIndex();
786
+ if (zIndexElement) {
787
+ this.list.css({
788
+ zIndex: zIndexElement
789
+ });
790
+ }
791
+ this.list.position({
792
+ // set options for position plugin
793
+ of: o.positionOptions.of || this.newelement,
794
+ my: o.positionOptions.my,
795
+ at: o.positionOptions.at,
796
+ offset: o.positionOptions.offset || _offset,
797
+ collision: o.positionOptions.collision || 'flip'
798
+ });
799
+ }
800
+ });
801
+
802
+ })(jQuery);