concen 0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/LICENSE +20 -0
  2. data/lib/concen/version.rb +1 -1
  3. data/public/concen/images/button-grid.png +0 -0
  4. data/public/concen/images/button-next-disabled.png +0 -0
  5. data/public/concen/images/button-next.png +0 -0
  6. data/public/concen/images/button-prev-disabled.png +0 -0
  7. data/public/concen/images/button-prev.png +0 -0
  8. data/public/concen/images/fileuploader-loading.gif +0 -0
  9. data/public/concen/images/glyph-small-check-alt.png +0 -0
  10. data/public/concen/images/glyph-small-check.png +0 -0
  11. data/public/concen/images/glyph-small-delete.png +0 -0
  12. data/public/concen/images/glyph-small-handle.png +0 -0
  13. data/public/concen/images/grid.png +0 -0
  14. data/public/concen/images/icon-handle.png +0 -0
  15. data/public/concen/images/magnifying-glass.png +0 -0
  16. data/public/concen/images/original/button-grid.png +0 -0
  17. data/public/concen/images/original/button-next-disabled.png +0 -0
  18. data/public/concen/images/original/button-next.png +0 -0
  19. data/public/concen/images/original/button-prev-disabled.png +0 -0
  20. data/public/concen/images/original/button-prev.png +0 -0
  21. data/public/concen/images/original/glyph-small-check-alt.png +0 -0
  22. data/public/concen/images/original/glyph-small-check.png +0 -0
  23. data/public/concen/images/original/glyph-small-delete.png +0 -0
  24. data/public/concen/images/original/glyph-small-handle.png +0 -0
  25. data/public/concen/images/original/magnifying-glass.png +0 -0
  26. data/public/concen/images/original/record-visit.gif +0 -0
  27. data/public/concen/images/original/search-field.png +0 -0
  28. data/public/concen/images/record-visit.gif +0 -0
  29. data/public/concen/images/search-field.png +0 -0
  30. data/public/concen/javascripts/ace/ace-uncompressed.js +17635 -0
  31. data/public/concen/javascripts/ace/ace.js +1 -0
  32. data/public/concen/javascripts/ace/cockpit-uncompressed.js +2504 -0
  33. data/public/concen/javascripts/ace/cockpit.js +1 -0
  34. data/public/concen/javascripts/ace/keybinding-emacs.js +1 -0
  35. data/public/concen/javascripts/ace/keybinding-vim.js +1 -0
  36. data/public/concen/javascripts/ace/mode-c_cpp.js +1 -0
  37. data/public/concen/javascripts/ace/mode-clojure.js +1 -0
  38. data/public/concen/javascripts/ace/mode-coffee.js +1 -0
  39. data/public/concen/javascripts/ace/mode-csharp.js +1 -0
  40. data/public/concen/javascripts/ace/mode-css.js +1 -0
  41. data/public/concen/javascripts/ace/mode-groovy.js +1 -0
  42. data/public/concen/javascripts/ace/mode-html.js +1 -0
  43. data/public/concen/javascripts/ace/mode-java.js +1 -0
  44. data/public/concen/javascripts/ace/mode-javascript.js +1 -0
  45. data/public/concen/javascripts/ace/mode-json.js +1 -0
  46. data/public/concen/javascripts/ace/mode-ocaml.js +1 -0
  47. data/public/concen/javascripts/ace/mode-perl.js +1 -0
  48. data/public/concen/javascripts/ace/mode-php.js +1 -0
  49. data/public/concen/javascripts/ace/mode-python.js +1 -0
  50. data/public/concen/javascripts/ace/mode-ruby.js +1 -0
  51. data/public/concen/javascripts/ace/mode-scad.js +1 -0
  52. data/public/concen/javascripts/ace/mode-scala.js +1 -0
  53. data/public/concen/javascripts/ace/mode-scss.js +1 -0
  54. data/public/concen/javascripts/ace/mode-svg.js +1 -0
  55. data/public/concen/javascripts/ace/mode-textile.js +1 -0
  56. data/public/concen/javascripts/ace/mode-xml.js +1 -0
  57. data/public/concen/javascripts/ace/theme-clouds.js +1 -0
  58. data/public/concen/javascripts/ace/theme-clouds_midnight.js +1 -0
  59. data/public/concen/javascripts/ace/theme-cobalt.js +1 -0
  60. data/public/concen/javascripts/ace/theme-crimson_editor.js +1 -0
  61. data/public/concen/javascripts/ace/theme-dawn.js +1 -0
  62. data/public/concen/javascripts/ace/theme-eclipse.js +1 -0
  63. data/public/concen/javascripts/ace/theme-idle_fingers.js +1 -0
  64. data/public/concen/javascripts/ace/theme-kr_theme.js +1 -0
  65. data/public/concen/javascripts/ace/theme-merbivore.js +1 -0
  66. data/public/concen/javascripts/ace/theme-merbivore_soft.js +1 -0
  67. data/public/concen/javascripts/ace/theme-mono_industrial.js +1 -0
  68. data/public/concen/javascripts/ace/theme-monokai.js +1 -0
  69. data/public/concen/javascripts/ace/theme-pastel_on_dark.js +1 -0
  70. data/public/concen/javascripts/ace/theme-solarized_dark.js +1 -0
  71. data/public/concen/javascripts/ace/theme-solarized_light.js +1 -0
  72. data/public/concen/javascripts/ace/theme-textmate.js +1 -0
  73. data/public/concen/javascripts/ace/theme-twilight.js +1 -0
  74. data/public/concen/javascripts/ace/theme-vibrant_ink.js +1 -0
  75. data/public/concen/javascripts/ace/worker-coffee.js +1 -0
  76. data/public/concen/javascripts/ace/worker-css.js +1 -0
  77. data/public/concen/javascripts/ace/worker-javascript.js +1 -0
  78. data/public/concen/javascripts/blank.gif +0 -0
  79. data/public/concen/javascripts/controlcenter.js +173 -0
  80. data/public/concen/javascripts/excanvas.js +1427 -0
  81. data/public/concen/javascripts/fileuploader.js +1251 -0
  82. data/public/concen/javascripts/jquery-ui/jquery.ui.autocomplete.js +612 -0
  83. data/public/concen/javascripts/jquery-ui/jquery.ui.core.js +312 -0
  84. data/public/concen/javascripts/jquery-ui/jquery.ui.draggable.js +823 -0
  85. data/public/concen/javascripts/jquery-ui/jquery.ui.droppable.js +296 -0
  86. data/public/concen/javascripts/jquery-ui/jquery.ui.mouse.js +160 -0
  87. data/public/concen/javascripts/jquery-ui/jquery.ui.position.js +252 -0
  88. data/public/concen/javascripts/jquery-ui/jquery.ui.resizable.js +842 -0
  89. data/public/concen/javascripts/jquery-ui/jquery.ui.sortable.js +1077 -0
  90. data/public/concen/javascripts/jquery-ui/jquery.ui.widget.js +262 -0
  91. data/public/concen/javascripts/jquery.browser.min.js +1 -0
  92. data/public/concen/javascripts/jquery.easing.1.3.js +205 -0
  93. data/public/concen/javascripts/jquery.equal-heights.js +30 -0
  94. data/public/concen/javascripts/jquery.flot.js +2599 -0
  95. data/public/concen/javascripts/jquery.flot.resize.js +60 -0
  96. data/public/concen/javascripts/jquery.flot.stack.js +184 -0
  97. data/public/concen/javascripts/jquery.js +8981 -0
  98. data/public/concen/javascripts/jquery.min.js +18 -0
  99. data/public/concen/javascripts/jquery.placeholder.js +101 -0
  100. data/public/concen/javascripts/jquery.ui.nestedSortable.js +356 -0
  101. data/public/concen/javascripts/jquery_ujs.js +316 -0
  102. data/public/concen/javascripts/modernizr.js +1116 -0
  103. data/public/concen/javascripts/page.js +153 -0
  104. data/public/concen/javascripts/performance.js +110 -0
  105. data/public/concen/javascripts/pxToEm.js +56 -0
  106. data/public/concen/javascripts/status.js +15 -0
  107. data/public/concen/javascripts/traffic.js +113 -0
  108. data/public/concen/javascripts/user.js +14 -0
  109. data/public/concen/stylesheets/application.css +1342 -0
  110. data/public/concen/stylesheets/fileuploader.css +31 -0
  111. data/public/concen/stylesheets/ie.css +124 -0
  112. data/public/concen/stylesheets/jquery.ui.base.css +11 -0
  113. data/public/concen/stylesheets/jquery.ui.core.css +41 -0
  114. data/public/concen/stylesheets/jquery.ui.resizable.css +20 -0
  115. data/public/concen/stylesheets/main.css +1225 -0
  116. data/public/concen/stylesheets/non_ios.css +28 -0
  117. data/public/concen/stylesheets/print.css +84 -0
  118. metadata +141 -25
@@ -0,0 +1,612 @@
1
+ /*
2
+ * jQuery UI Autocomplete 1.8.14
3
+ *
4
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6
+ * http://jquery.org/license
7
+ *
8
+ * http://docs.jquery.com/UI/Autocomplete
9
+ *
10
+ * Depends:
11
+ * jquery.ui.core.js
12
+ * jquery.ui.widget.js
13
+ * jquery.ui.position.js
14
+ */
15
+ (function( $, undefined ) {
16
+
17
+ // used to prevent race conditions with remote data sources
18
+ var requestIndex = 0;
19
+
20
+ $.widget( "ui.autocomplete", {
21
+ options: {
22
+ appendTo: "body",
23
+ autoFocus: false,
24
+ delay: 300,
25
+ minLength: 1,
26
+ position: {
27
+ my: "left top",
28
+ at: "left bottom",
29
+ collision: "none"
30
+ },
31
+ source: null
32
+ },
33
+
34
+ pending: 0,
35
+
36
+ _create: function() {
37
+ var self = this,
38
+ doc = this.element[ 0 ].ownerDocument,
39
+ suppressKeyPress;
40
+
41
+ this.element
42
+ .addClass( "ui-autocomplete-input" )
43
+ .attr( "autocomplete", "off" )
44
+ // TODO verify these actually work as intended
45
+ .attr({
46
+ role: "textbox",
47
+ "aria-autocomplete": "list",
48
+ "aria-haspopup": "true"
49
+ })
50
+ .bind( "keydown.autocomplete", function( event ) {
51
+ if ( self.options.disabled || self.element.attr( "readonly" ) ) {
52
+ return;
53
+ }
54
+
55
+ suppressKeyPress = false;
56
+ var keyCode = $.ui.keyCode;
57
+ switch( event.keyCode ) {
58
+ case keyCode.PAGE_UP:
59
+ self._move( "previousPage", event );
60
+ break;
61
+ case keyCode.PAGE_DOWN:
62
+ self._move( "nextPage", event );
63
+ break;
64
+ case keyCode.UP:
65
+ self._move( "previous", event );
66
+ // prevent moving cursor to beginning of text field in some browsers
67
+ event.preventDefault();
68
+ break;
69
+ case keyCode.DOWN:
70
+ self._move( "next", event );
71
+ // prevent moving cursor to end of text field in some browsers
72
+ event.preventDefault();
73
+ break;
74
+ case keyCode.ENTER:
75
+ case keyCode.NUMPAD_ENTER:
76
+ // when menu is open and has focus
77
+ if ( self.menu.active ) {
78
+ // #6055 - Opera still allows the keypress to occur
79
+ // which causes forms to submit
80
+ suppressKeyPress = true;
81
+ event.preventDefault();
82
+ }
83
+ //passthrough - ENTER and TAB both select the current element
84
+ case keyCode.TAB:
85
+ if ( !self.menu.active ) {
86
+ return;
87
+ }
88
+ self.menu.select( event );
89
+ break;
90
+ case keyCode.ESCAPE:
91
+ self.element.val( self.term );
92
+ self.close( event );
93
+ break;
94
+ default:
95
+ // keypress is triggered before the input value is changed
96
+ clearTimeout( self.searching );
97
+ self.searching = setTimeout(function() {
98
+ // only search if the value has changed
99
+ if ( self.term != self.element.val() ) {
100
+ self.selectedItem = null;
101
+ self.search( null, event );
102
+ }
103
+ }, self.options.delay );
104
+ break;
105
+ }
106
+ })
107
+ .bind( "keypress.autocomplete", function( event ) {
108
+ if ( suppressKeyPress ) {
109
+ suppressKeyPress = false;
110
+ event.preventDefault();
111
+ }
112
+ })
113
+ .bind( "focus.autocomplete", function() {
114
+ if ( self.options.disabled ) {
115
+ return;
116
+ }
117
+
118
+ self.selectedItem = null;
119
+ self.previous = self.element.val();
120
+ })
121
+ .bind( "blur.autocomplete", function( event ) {
122
+ if ( self.options.disabled ) {
123
+ return;
124
+ }
125
+
126
+ clearTimeout( self.searching );
127
+ // clicks on the menu (or a button to trigger a search) will cause a blur event
128
+ self.closing = setTimeout(function() {
129
+ self.close( event );
130
+ self._change( event );
131
+ }, 150 );
132
+ });
133
+ this._initSource();
134
+ this.response = function() {
135
+ return self._response.apply( self, arguments );
136
+ };
137
+ this.menu = $( "<ul></ul>" )
138
+ .addClass( "ui-autocomplete" )
139
+ .appendTo( $( this.options.appendTo || "body", doc )[0] )
140
+ // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
141
+ .mousedown(function( event ) {
142
+ // clicking on the scrollbar causes focus to shift to the body
143
+ // but we can't detect a mouseup or a click immediately afterward
144
+ // so we have to track the next mousedown and close the menu if
145
+ // the user clicks somewhere outside of the autocomplete
146
+ var menuElement = self.menu.element[ 0 ];
147
+ if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
148
+ setTimeout(function() {
149
+ $( document ).one( 'mousedown', function( event ) {
150
+ if ( event.target !== self.element[ 0 ] &&
151
+ event.target !== menuElement &&
152
+ !$.ui.contains( menuElement, event.target ) ) {
153
+ self.close();
154
+ }
155
+ });
156
+ }, 1 );
157
+ }
158
+
159
+ // use another timeout to make sure the blur-event-handler on the input was already triggered
160
+ setTimeout(function() {
161
+ clearTimeout( self.closing );
162
+ }, 13);
163
+ })
164
+ .menu({
165
+ focus: function( event, ui ) {
166
+ var item = ui.item.data( "item.autocomplete" );
167
+ if ( false !== self._trigger( "focus", event, { item: item } ) ) {
168
+ // use value to match what will end up in the input, if it was a key event
169
+ if ( /^key/.test(event.originalEvent.type) ) {
170
+ self.element.val( item.value );
171
+ }
172
+ }
173
+ },
174
+ selected: function( event, ui ) {
175
+ var item = ui.item.data( "item.autocomplete" ),
176
+ previous = self.previous;
177
+
178
+ // only trigger when focus was lost (click on menu)
179
+ if ( self.element[0] !== doc.activeElement ) {
180
+ self.element.focus();
181
+ self.previous = previous;
182
+ // #6109 - IE triggers two focus events and the second
183
+ // is asynchronous, so we need to reset the previous
184
+ // term synchronously and asynchronously :-(
185
+ setTimeout(function() {
186
+ self.previous = previous;
187
+ self.selectedItem = item;
188
+ }, 1);
189
+ }
190
+
191
+ if ( false !== self._trigger( "select", event, { item: item } ) ) {
192
+ self.element.val( item.value );
193
+ }
194
+ // reset the term after the select event
195
+ // this allows custom select handling to work properly
196
+ self.term = self.element.val();
197
+
198
+ self.close( event );
199
+ self.selectedItem = item;
200
+ },
201
+ blur: function( event, ui ) {
202
+ // don't set the value of the text field if it's already correct
203
+ // this prevents moving the cursor unnecessarily
204
+ if ( self.menu.element.is(":visible") &&
205
+ ( self.element.val() !== self.term ) ) {
206
+ self.element.val( self.term );
207
+ }
208
+ }
209
+ })
210
+ .zIndex( this.element.zIndex() + 1 )
211
+ // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
212
+ .css({ top: 0, left: 0 })
213
+ .hide()
214
+ .data( "menu" );
215
+ if ( $.fn.bgiframe ) {
216
+ this.menu.element.bgiframe();
217
+ }
218
+ },
219
+
220
+ destroy: function() {
221
+ this.element
222
+ .removeClass( "ui-autocomplete-input" )
223
+ .removeAttr( "autocomplete" )
224
+ .removeAttr( "role" )
225
+ .removeAttr( "aria-autocomplete" )
226
+ .removeAttr( "aria-haspopup" );
227
+ this.menu.element.remove();
228
+ $.Widget.prototype.destroy.call( this );
229
+ },
230
+
231
+ _setOption: function( key, value ) {
232
+ $.Widget.prototype._setOption.apply( this, arguments );
233
+ if ( key === "source" ) {
234
+ this._initSource();
235
+ }
236
+ if ( key === "appendTo" ) {
237
+ this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )
238
+ }
239
+ if ( key === "disabled" && value && this.xhr ) {
240
+ this.xhr.abort();
241
+ }
242
+ },
243
+
244
+ _initSource: function() {
245
+ var self = this,
246
+ array,
247
+ url;
248
+ if ( $.isArray(this.options.source) ) {
249
+ array = this.options.source;
250
+ this.source = function( request, response ) {
251
+ response( $.ui.autocomplete.filter(array, request.term) );
252
+ };
253
+ } else if ( typeof this.options.source === "string" ) {
254
+ url = this.options.source;
255
+ this.source = function( request, response ) {
256
+ if ( self.xhr ) {
257
+ self.xhr.abort();
258
+ }
259
+ self.xhr = $.ajax({
260
+ url: url,
261
+ data: request,
262
+ dataType: "json",
263
+ autocompleteRequest: ++requestIndex,
264
+ success: function( data, status ) {
265
+ if ( this.autocompleteRequest === requestIndex ) {
266
+ response( data );
267
+ }
268
+ },
269
+ error: function() {
270
+ if ( this.autocompleteRequest === requestIndex ) {
271
+ response( [] );
272
+ }
273
+ }
274
+ });
275
+ };
276
+ } else {
277
+ this.source = this.options.source;
278
+ }
279
+ },
280
+
281
+ search: function( value, event ) {
282
+ value = value != null ? value : this.element.val();
283
+
284
+ // always save the actual value, not the one passed as an argument
285
+ this.term = this.element.val();
286
+
287
+ if ( value.length < this.options.minLength ) {
288
+ return this.close( event );
289
+ }
290
+
291
+ clearTimeout( this.closing );
292
+ if ( this._trigger( "search", event ) === false ) {
293
+ return;
294
+ }
295
+
296
+ return this._search( value );
297
+ },
298
+
299
+ _search: function( value ) {
300
+ this.pending++;
301
+ this.element.addClass( "ui-autocomplete-loading" );
302
+
303
+ this.source( { term: value }, this.response );
304
+ },
305
+
306
+ _response: function( content ) {
307
+ if ( !this.options.disabled && content && content.length ) {
308
+ content = this._normalize( content );
309
+ this._suggest( content );
310
+ this._trigger( "open" );
311
+ } else {
312
+ this.close();
313
+ }
314
+ this.pending--;
315
+ if ( !this.pending ) {
316
+ this.element.removeClass( "ui-autocomplete-loading" );
317
+ }
318
+ },
319
+
320
+ close: function( event ) {
321
+ clearTimeout( this.closing );
322
+ if ( this.menu.element.is(":visible") ) {
323
+ this.menu.element.hide();
324
+ this.menu.deactivate();
325
+ this._trigger( "close", event );
326
+ }
327
+ },
328
+
329
+ _change: function( event ) {
330
+ if ( this.previous !== this.element.val() ) {
331
+ this._trigger( "change", event, { item: this.selectedItem } );
332
+ }
333
+ },
334
+
335
+ _normalize: function( items ) {
336
+ // assume all items have the right format when the first item is complete
337
+ if ( items.length && items[0].label && items[0].value ) {
338
+ return items;
339
+ }
340
+ return $.map( items, function(item) {
341
+ if ( typeof item === "string" ) {
342
+ return {
343
+ label: item,
344
+ value: item
345
+ };
346
+ }
347
+ return $.extend({
348
+ label: item.label || item.value,
349
+ value: item.value || item.label
350
+ }, item );
351
+ });
352
+ },
353
+
354
+ _suggest: function( items ) {
355
+ var ul = this.menu.element
356
+ .empty()
357
+ .zIndex( this.element.zIndex() + 1 );
358
+ this._renderMenu( ul, items );
359
+ // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
360
+ this.menu.deactivate();
361
+ this.menu.refresh();
362
+
363
+ // size and position menu
364
+ ul.show();
365
+ this._resizeMenu();
366
+ ul.position( $.extend({
367
+ of: this.element
368
+ }, this.options.position ));
369
+
370
+ if ( this.options.autoFocus ) {
371
+ this.menu.next( new $.Event("mouseover") );
372
+ }
373
+ },
374
+
375
+ _resizeMenu: function() {
376
+ var ul = this.menu.element;
377
+ ul.outerWidth( Math.max(
378
+ ul.width( "" ).outerWidth(),
379
+ this.element.outerWidth()
380
+ ) );
381
+ },
382
+
383
+ _renderMenu: function( ul, items ) {
384
+ var self = this;
385
+ $.each( items, function( index, item ) {
386
+ self._renderItem( ul, item );
387
+ });
388
+ },
389
+
390
+ _renderItem: function( ul, item) {
391
+ return $( "<li></li>" )
392
+ .data( "item.autocomplete", item )
393
+ .append( $( "<a></a>" ).text( item.label ) )
394
+ .appendTo( ul );
395
+ },
396
+
397
+ _move: function( direction, event ) {
398
+ if ( !this.menu.element.is(":visible") ) {
399
+ this.search( null, event );
400
+ return;
401
+ }
402
+ if ( this.menu.first() && /^previous/.test(direction) ||
403
+ this.menu.last() && /^next/.test(direction) ) {
404
+ this.element.val( this.term );
405
+ this.menu.deactivate();
406
+ return;
407
+ }
408
+ this.menu[ direction ]( event );
409
+ },
410
+
411
+ widget: function() {
412
+ return this.menu.element;
413
+ }
414
+ });
415
+
416
+ $.extend( $.ui.autocomplete, {
417
+ escapeRegex: function( value ) {
418
+ return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
419
+ },
420
+ filter: function(array, term) {
421
+ var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
422
+ return $.grep( array, function(value) {
423
+ return matcher.test( value.label || value.value || value );
424
+ });
425
+ }
426
+ });
427
+
428
+ }( jQuery ));
429
+
430
+ /*
431
+ * jQuery UI Menu (not officially released)
432
+ *
433
+ * This widget isn't yet finished and the API is subject to change. We plan to finish
434
+ * it for the next release. You're welcome to give it a try anyway and give us feedback,
435
+ * as long as you're okay with migrating your code later on. We can help with that, too.
436
+ *
437
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
438
+ * Dual licensed under the MIT or GPL Version 2 licenses.
439
+ * http://jquery.org/license
440
+ *
441
+ * http://docs.jquery.com/UI/Menu
442
+ *
443
+ * Depends:
444
+ * jquery.ui.core.js
445
+ * jquery.ui.widget.js
446
+ */
447
+ (function($) {
448
+
449
+ $.widget("ui.menu", {
450
+ _create: function() {
451
+ var self = this;
452
+ this.element
453
+ .addClass("ui-menu ui-widget ui-widget-content ui-corner-all")
454
+ .attr({
455
+ role: "listbox",
456
+ "aria-activedescendant": "ui-active-menuitem"
457
+ })
458
+ .click(function( event ) {
459
+ if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) {
460
+ return;
461
+ }
462
+ // temporary
463
+ event.preventDefault();
464
+ self.select( event );
465
+ });
466
+ this.refresh();
467
+ },
468
+
469
+ refresh: function() {
470
+ var self = this;
471
+
472
+ // don't refresh list items that are already adapted
473
+ var items = this.element.children("li:not(.ui-menu-item):has(a)")
474
+ .addClass("ui-menu-item")
475
+ .attr("role", "menuitem");
476
+
477
+ items.children("a")
478
+ .addClass("ui-corner-all")
479
+ .attr("tabindex", -1)
480
+ // mouseenter doesn't work with event delegation
481
+ .mouseenter(function( event ) {
482
+ self.activate( event, $(this).parent() );
483
+ })
484
+ .mouseleave(function() {
485
+ self.deactivate();
486
+ });
487
+ },
488
+
489
+ activate: function( event, item ) {
490
+ this.deactivate();
491
+ if (this.hasScroll()) {
492
+ var offset = item.offset().top - this.element.offset().top,
493
+ scroll = this.element.scrollTop(),
494
+ elementHeight = this.element.height();
495
+ if (offset < 0) {
496
+ this.element.scrollTop( scroll + offset);
497
+ } else if (offset >= elementHeight) {
498
+ this.element.scrollTop( scroll + offset - elementHeight + item.height());
499
+ }
500
+ }
501
+ this.active = item.eq(0)
502
+ .children("a")
503
+ .addClass("ui-state-hover")
504
+ .attr("id", "ui-active-menuitem")
505
+ .end();
506
+ this._trigger("focus", event, { item: item });
507
+ },
508
+
509
+ deactivate: function() {
510
+ if (!this.active) { return; }
511
+
512
+ this.active.children("a")
513
+ .removeClass("ui-state-hover")
514
+ .removeAttr("id");
515
+ this._trigger("blur");
516
+ this.active = null;
517
+ },
518
+
519
+ next: function(event) {
520
+ this.move("next", ".ui-menu-item:first", event);
521
+ },
522
+
523
+ previous: function(event) {
524
+ this.move("prev", ".ui-menu-item:last", event);
525
+ },
526
+
527
+ first: function() {
528
+ return this.active && !this.active.prevAll(".ui-menu-item").length;
529
+ },
530
+
531
+ last: function() {
532
+ return this.active && !this.active.nextAll(".ui-menu-item").length;
533
+ },
534
+
535
+ move: function(direction, edge, event) {
536
+ if (!this.active) {
537
+ this.activate(event, this.element.children(edge));
538
+ return;
539
+ }
540
+ var next = this.active[direction + "All"](".ui-menu-item").eq(0);
541
+ if (next.length) {
542
+ this.activate(event, next);
543
+ } else {
544
+ this.activate(event, this.element.children(edge));
545
+ }
546
+ },
547
+
548
+ // TODO merge with previousPage
549
+ nextPage: function(event) {
550
+ if (this.hasScroll()) {
551
+ // TODO merge with no-scroll-else
552
+ if (!this.active || this.last()) {
553
+ this.activate(event, this.element.children(".ui-menu-item:first"));
554
+ return;
555
+ }
556
+ var base = this.active.offset().top,
557
+ height = this.element.height(),
558
+ result = this.element.children(".ui-menu-item").filter(function() {
559
+ var close = $(this).offset().top - base - height + $(this).height();
560
+ // TODO improve approximation
561
+ return close < 10 && close > -10;
562
+ });
563
+
564
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
565
+ if (!result.length) {
566
+ result = this.element.children(".ui-menu-item:last");
567
+ }
568
+ this.activate(event, result);
569
+ } else {
570
+ this.activate(event, this.element.children(".ui-menu-item")
571
+ .filter(!this.active || this.last() ? ":first" : ":last"));
572
+ }
573
+ },
574
+
575
+ // TODO merge with nextPage
576
+ previousPage: function(event) {
577
+ if (this.hasScroll()) {
578
+ // TODO merge with no-scroll-else
579
+ if (!this.active || this.first()) {
580
+ this.activate(event, this.element.children(".ui-menu-item:last"));
581
+ return;
582
+ }
583
+
584
+ var base = this.active.offset().top,
585
+ height = this.element.height();
586
+ result = this.element.children(".ui-menu-item").filter(function() {
587
+ var close = $(this).offset().top - base + height - $(this).height();
588
+ // TODO improve approximation
589
+ return close < 10 && close > -10;
590
+ });
591
+
592
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
593
+ if (!result.length) {
594
+ result = this.element.children(".ui-menu-item:first");
595
+ }
596
+ this.activate(event, result);
597
+ } else {
598
+ this.activate(event, this.element.children(".ui-menu-item")
599
+ .filter(!this.active || this.first() ? ":last" : ":first"));
600
+ }
601
+ },
602
+
603
+ hasScroll: function() {
604
+ return this.element.height() < this.element[ $.fn.prop ? "prop" : "attr" ]("scrollHeight");
605
+ },
606
+
607
+ select: function( event ) {
608
+ this._trigger("selected", event, { item: this.active });
609
+ }
610
+ });
611
+
612
+ }(jQuery));