omf_web 1.2.5 → 1.2.6

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 (35) hide show
  1. data/lib/omf-web/content/content_proxy.rb +28 -15
  2. data/lib/omf-web/content/file_repository.rb +16 -57
  3. data/lib/omf-web/content/git_repository.rb +2 -121
  4. data/lib/omf-web/content/irods_repository.rb +2 -0
  5. data/lib/omf-web/content/repository.rb +121 -10
  6. data/lib/omf-web/content/static_repository.rb +3 -3
  7. data/lib/omf-web/rack/content_handler.rb +8 -6
  8. data/lib/omf-web/theme.rb +16 -2
  9. data/lib/omf-web/thin/logging.rb +13 -4
  10. data/lib/omf-web/version.rb +1 -1
  11. data/lib/omf-web/widget/text/maruku/output/to_html.rb +29 -23
  12. data/lib/omf-web/widget/text/maruku.rb +20 -5
  13. data/omf_web.gemspec +1 -1
  14. data/share/htdocs/graph/js/code_mirror.js +2 -1
  15. data/share/htdocs/vendor/VERSION_MAP.yaml +1 -0
  16. data/share/htdocs/vendor/smartmenus-0.9.6/LICENSE-MIT +22 -0
  17. data/share/htdocs/vendor/smartmenus-0.9.6/README.md +26 -0
  18. data/share/htdocs/vendor/smartmenus-0.9.6/addons/bootstrap/jquery.smartmenus.bootstrap.css +103 -0
  19. data/share/htdocs/vendor/smartmenus-0.9.6/addons/bootstrap/jquery.smartmenus.bootstrap.js +72 -0
  20. data/share/htdocs/vendor/smartmenus-0.9.6/addons/bootstrap/jquery.smartmenus.bootstrap.min.js +3 -0
  21. data/share/htdocs/vendor/smartmenus-0.9.6/addons/keyboard/jquery.smartmenus.keyboard.js +192 -0
  22. data/share/htdocs/vendor/smartmenus-0.9.6/addons/keyboard/jquery.smartmenus.keyboard.min.js +3 -0
  23. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-blue/css-gradients-fallback/current-item-bg.png +0 -0
  24. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-blue/css-gradients-fallback/main-item-hover-bg.png +0 -0
  25. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-blue/css-gradients-fallback/main-menu-bg.png +0 -0
  26. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-blue/css-gradients-fallback/sub-item-hover-bg.png +0 -0
  27. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-blue/css-gradients-fallback/vertical-main-item-bg.png +0 -0
  28. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-blue/sm-blue.css +409 -0
  29. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-clean/sm-clean.css +315 -0
  30. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-core-css.css +23 -0
  31. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-mint/sm-mint.css +320 -0
  32. data/share/htdocs/vendor/smartmenus-0.9.6/css/sm-simple/sm-simple.css +218 -0
  33. data/share/htdocs/vendor/smartmenus-0.9.6/jquery.smartmenus.js +1041 -0
  34. data/share/htdocs/vendor/smartmenus-0.9.6/jquery.smartmenus.min.js +3 -0
  35. metadata +218 -229
@@ -0,0 +1,1041 @@
1
+ /*!
2
+ * SmartMenus jQuery Plugin - v0.9.6 - March 27, 2014
3
+ * http://www.smartmenus.org/
4
+ *
5
+ * Copyright 2014 Vasil Dinkov, Vadikom Web Ltd.
6
+ * http://vadikom.com
7
+ *
8
+ * Licensed MIT
9
+ */
10
+
11
+ (function($) {
12
+
13
+ var menuTrees = [],
14
+ IE = !!window.createPopup, // we need to detect it, unfortunately
15
+ IElt9 = IE && !document.defaultView,
16
+ IElt8 = IE && !document.querySelector,
17
+ IE6 = IE && typeof document.documentElement.currentStyle.minWidth == 'undefined',
18
+ mouse = false, // optimize for touch by default - we will detect for mouse input
19
+ mouseDetectionEnabled = false;
20
+
21
+ // Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
22
+ function initMouseDetection(disable) {
23
+ if (!mouseDetectionEnabled && !disable) {
24
+ // if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
25
+ // in practice, this seems like impossible to trick unintentianally with a real mouse and a pretty safe detection on touch devices (even with older browsers that do not support touch events)
26
+ var firstTime = true,
27
+ lastMove = null;
28
+ $(document).bind({
29
+ 'mousemove.smartmenus_mouse': function(e) {
30
+ var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
31
+ if (lastMove) {
32
+ var deltaX = Math.abs(lastMove.x - thisMove.x),
33
+ deltaY = Math.abs(lastMove.y - thisMove.y);
34
+ if ((deltaX > 0 || deltaY > 0) && deltaX <= 2 && deltaY <= 2 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
35
+ mouse = true;
36
+ // if this is the first check after page load, check if we are not over some item by chance and call the mouseenter handler if yes
37
+ if (firstTime) {
38
+ var $a = $(e.target).closest('a');
39
+ if ($a.is('a')) {
40
+ $.each(menuTrees, function() {
41
+ if ($.contains(this.$root[0], $a[0])) {
42
+ this.itemEnter({ currentTarget: $a[0] });
43
+ return false;
44
+ }
45
+ });
46
+ }
47
+ firstTime = false;
48
+ }
49
+ }
50
+ }
51
+ lastMove = thisMove;
52
+ },
53
+ 'touchstart.smartmenus_mouse pointerover.smartmenus_mouse MSPointerOver.smartmenus_mouse': function(e) {
54
+ if (!/^(4|mouse)$/.test(e.originalEvent.pointerType)) {
55
+ mouse = false;
56
+ }
57
+ }
58
+ });
59
+ mouseDetectionEnabled = true;
60
+ } else if (mouseDetectionEnabled && disable) {
61
+ $(document).unbind('.smartmenus_mouse');
62
+ mouseDetectionEnabled = false;
63
+ }
64
+ };
65
+
66
+ $.SmartMenus = function(elm, options) {
67
+ this.$root = $(elm);
68
+ this.opts = options;
69
+ this.rootId = ''; // internal
70
+ this.$subArrow = null;
71
+ this.subMenus = []; // all sub menus in the tree (UL elms) in no particular order (only real - e.g. UL's in mega sub menus won't be counted)
72
+ this.activatedItems = []; // stores last activated A's for each level
73
+ this.visibleSubMenus = []; // stores visible sub menus UL's
74
+ this.showTimeout = 0;
75
+ this.hideTimeout = 0;
76
+ this.scrollTimeout = 0;
77
+ this.clickActivated = false;
78
+ this.zIndexInc = 0;
79
+ this.$firstLink = null; // we'll use these for some tests
80
+ this.$firstSub = null; // at runtime so we'll cache them
81
+ this.disabled = false;
82
+ this.$disableOverlay = null;
83
+ this.init();
84
+ };
85
+
86
+ $.extend($.SmartMenus, {
87
+ hideAll: function() {
88
+ $.each(menuTrees, function() {
89
+ this.menuHideAll();
90
+ });
91
+ },
92
+ destroy: function() {
93
+ while (menuTrees.length) {
94
+ menuTrees[0].destroy();
95
+ }
96
+ initMouseDetection(true);
97
+ },
98
+ prototype: {
99
+ init: function(refresh) {
100
+ var self = this;
101
+
102
+ if (!refresh) {
103
+ menuTrees.push(this);
104
+
105
+ this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');
106
+
107
+ if (this.$root.hasClass('sm-rtl')) {
108
+ this.opts.rightToLeftSubMenus = true;
109
+ }
110
+
111
+ // init root (main menu)
112
+ this.$root
113
+ .data('smartmenus', this)
114
+ .attr('data-smartmenus-id', this.rootId)
115
+ .dataSM('level', 1)
116
+ .bind({
117
+ 'mouseover.smartmenus focusin.smartmenus': $.proxy(this.rootOver, this),
118
+ 'mouseout.smartmenus focusout.smartmenus': $.proxy(this.rootOut, this)
119
+ })
120
+ .delegate('a', {
121
+ 'mouseenter.smartmenus': $.proxy(this.itemEnter, this),
122
+ 'mouseleave.smartmenus': $.proxy(this.itemLeave, this),
123
+ 'mousedown.smartmenus': $.proxy(this.itemDown, this),
124
+ 'focus.smartmenus': $.proxy(this.itemFocus, this),
125
+ 'blur.smartmenus': $.proxy(this.itemBlur, this),
126
+ 'click.smartmenus': $.proxy(this.itemClick, this),
127
+ 'touchend.smartmenus': $.proxy(this.itemTouchEnd, this)
128
+ });
129
+
130
+ var eNamespace = '.smartmenus' + this.rootId;
131
+ // hide menus on tap or click outside the root UL
132
+ if (this.opts.hideOnClick) {
133
+ $(document).bind('touchstart' + eNamespace, $.proxy(this.docTouchStart, this))
134
+ .bind('touchmove' + eNamespace, $.proxy(this.docTouchMove, this))
135
+ .bind('touchend' + eNamespace, $.proxy(this.docTouchEnd, this))
136
+ // for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
137
+ .bind('click' + eNamespace, $.proxy(this.docClick, this));
138
+ }
139
+ // hide sub menus on resize
140
+ $(window).bind('resize' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));
141
+
142
+ if (this.opts.subIndicators) {
143
+ this.$subArrow = $('<span/>').addClass('sub-arrow');
144
+ if (this.opts.subIndicatorsText) {
145
+ this.$subArrow.html(this.opts.subIndicatorsText);
146
+ }
147
+ }
148
+
149
+ // make sure mouse detection is enabled
150
+ initMouseDetection();
151
+ }
152
+
153
+ // init sub menus
154
+ this.$firstSub = this.$root.find('ul').each(function() { self.menuInit($(this)); }).eq(0);
155
+
156
+ this.$firstLink = this.$root.find('a').eq(0);
157
+
158
+ // find current item
159
+ if (this.opts.markCurrentItem) {
160
+ var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
161
+ reHash = /#.*/,
162
+ locHref = window.location.href.replace(reDefaultDoc, ''),
163
+ locHrefNoHash = locHref.replace(reHash, '');
164
+ this.$root.find('a').each(function() {
165
+ var href = this.href.replace(reDefaultDoc, ''),
166
+ $this = $(this);
167
+ if (href == locHref || href == locHrefNoHash) {
168
+ $this.addClass('current');
169
+ if (self.opts.markCurrentTree) {
170
+ $this.parents('li').each(function() {
171
+ var $this = $(this);
172
+ if ($this.dataSM('sub')) {
173
+ $this.children('a').addClass('current');
174
+ }
175
+ });
176
+ }
177
+ }
178
+ });
179
+ }
180
+ },
181
+ destroy: function() {
182
+ this.menuHideAll();
183
+ this.$root
184
+ .removeData('smartmenus')
185
+ .removeAttr('data-smartmenus-id')
186
+ .removeDataSM('level')
187
+ .unbind('.smartmenus')
188
+ .undelegate('.smartmenus');
189
+ var eNamespace = '.smartmenus' + this.rootId;
190
+ $(document).unbind(eNamespace);
191
+ $(window).unbind(eNamespace);
192
+ if (this.opts.subIndicators) {
193
+ this.$subArrow = null;
194
+ }
195
+ var self = this;
196
+ $.each(this.subMenus, function() {
197
+ if (this.hasClass('mega-menu')) {
198
+ this.find('ul').removeDataSM('in-mega');
199
+ }
200
+ if (this.dataSM('shown-before')) {
201
+ if (IElt8) {
202
+ this.children().css({ styleFloat: '', width: '' });
203
+ }
204
+ if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
205
+ if (!IE6) {
206
+ this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
207
+ } else {
208
+ this.css({ width: '', overflowX: '', overflowY: '' }).children().children('a').css('white-space', '');
209
+ }
210
+ }
211
+ if (this.dataSM('scroll-arrows')) {
212
+ this.dataSM('scroll-arrows').remove();
213
+ }
214
+ this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
215
+ }
216
+ if (self.opts.subIndicators) {
217
+ this.dataSM('parent-a').removeClass('has-submenu').children('span.sub-arrow').remove();
218
+ }
219
+ this.removeDataSM('shown-before')
220
+ .removeDataSM('ie-shim')
221
+ .removeDataSM('scroll-arrows')
222
+ .removeDataSM('parent-a')
223
+ .removeDataSM('level')
224
+ .removeDataSM('beforefirstshowfired')
225
+ .parent().removeDataSM('sub');
226
+ });
227
+ if (this.opts.markCurrentItem) {
228
+ this.$root.find('a.current').removeClass('current');
229
+ }
230
+ this.$root = null;
231
+ this.$firstLink = null;
232
+ this.$firstSub = null;
233
+ if (this.$disableOverlay) {
234
+ this.$disableOverlay.remove();
235
+ this.$disableOverlay = null;
236
+ }
237
+ menuTrees.splice($.inArray(this, menuTrees), 1);
238
+ },
239
+ disable: function(noOverlay) {
240
+ if (!this.disabled) {
241
+ this.menuHideAll();
242
+ // display overlay over the menu to prevent interaction
243
+ if (!noOverlay && !this.opts.isPopup && this.$root.is(':visible')) {
244
+ var pos = this.$root.offset();
245
+ this.$disableOverlay = $('<div class="sm-jquery-disable-overlay"/>').css({
246
+ position: 'absolute',
247
+ top: pos.top,
248
+ left: pos.left,
249
+ width: this.$root.outerWidth(),
250
+ height: this.$root.outerHeight(),
251
+ zIndex: this.getStartZIndex() + 1,
252
+ opacity: 0
253
+ }).appendTo(document.body);
254
+ }
255
+ this.disabled = true;
256
+ }
257
+ },
258
+ docClick: function(e) {
259
+ // hide on any click outside the menu or on a menu link
260
+ if (this.visibleSubMenus.length && !$.contains(this.$root[0], e.target) || $(e.target).is('a')) {
261
+ this.menuHideAll();
262
+ }
263
+ },
264
+ docTouchEnd: function(e) {
265
+ if (!this.lastTouch) {
266
+ return;
267
+ }
268
+ if (this.visibleSubMenus.length && (this.lastTouch.x2 === undefined || this.lastTouch.x1 == this.lastTouch.x2) && (this.lastTouch.y2 === undefined || this.lastTouch.y1 == this.lastTouch.y2) && (!this.lastTouch.target || !$.contains(this.$root[0], this.lastTouch.target))) {
269
+ if (this.hideTimeout) {
270
+ clearTimeout(this.hideTimeout);
271
+ this.hideTimeout = 0;
272
+ }
273
+ // hide with a delay to prevent triggering accidental unwanted click on some page element
274
+ var self = this;
275
+ this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, 350);
276
+ }
277
+ this.lastTouch = null;
278
+ },
279
+ docTouchMove: function(e) {
280
+ if (!this.lastTouch) {
281
+ return;
282
+ }
283
+ var touchPoint = e.originalEvent.touches[0];
284
+ this.lastTouch.x2 = touchPoint.pageX;
285
+ this.lastTouch.y2 = touchPoint.pageY;
286
+ },
287
+ docTouchStart: function(e) {
288
+ var touchPoint = e.originalEvent.touches[0];
289
+ this.lastTouch = { x1: touchPoint.pageX, y1: touchPoint.pageY, target: touchPoint.target };
290
+ },
291
+ enable: function() {
292
+ if (this.disabled) {
293
+ if (this.$disableOverlay) {
294
+ this.$disableOverlay.remove();
295
+ this.$disableOverlay = null;
296
+ }
297
+ this.disabled = false;
298
+ }
299
+ },
300
+ getHeight: function($elm) {
301
+ return this.getOffset($elm, true);
302
+ },
303
+ // returns precise width/height float values in IE9+, FF4+, recent WebKit
304
+ // http://vadikom.com/dailies/offsetwidth-offsetheight-useless-in-ie9-firefox4/
305
+ getOffset: function($elm, height) {
306
+ var old;
307
+ if ($elm.css('display') == 'none') {
308
+ old = { position: $elm[0].style.position, visibility: $elm[0].style.visibility };
309
+ $elm.css({ position: 'absolute', visibility: 'hidden' }).show();
310
+ }
311
+ var defaultView = $elm[0].ownerDocument.defaultView,
312
+ compStyle = defaultView && defaultView.getComputedStyle && defaultView.getComputedStyle($elm[0], null),
313
+ val = compStyle && parseFloat(compStyle[height ? 'height' : 'width']);
314
+ if (val) {
315
+ val += parseFloat(compStyle[height ? 'paddingTop' : 'paddingLeft'])
316
+ + parseFloat(compStyle[height ? 'paddingBottom' : 'paddingRight'])
317
+ + parseInt(compStyle[height ? 'borderTopWidth' : 'borderLeftWidth'])
318
+ + parseInt(compStyle[height ? 'borderBottomWidth' : 'borderRightWidth']);
319
+ } else {
320
+ val = height ? $elm[0].offsetHeight : $elm[0].offsetWidth;
321
+ }
322
+ if (old) {
323
+ $elm.hide().css(old);
324
+ }
325
+ return val;
326
+ },
327
+ getWidth: function($elm) {
328
+ return this.getOffset($elm);
329
+ },
330
+ getStartZIndex: function() {
331
+ var zIndex = parseInt(this.$root.css('z-index'));
332
+ return !isNaN(zIndex) ? zIndex : 1;
333
+ },
334
+ handleEvents: function() {
335
+ return !this.disabled && this.isCSSOn();
336
+ },
337
+ handleItemEvents: function($a) {
338
+ return this.handleEvents() && !this.isLinkInMegaMenu($a);
339
+ },
340
+ isCollapsible: function() {
341
+ return this.$firstSub.css('position') == 'static';
342
+ },
343
+ isCSSOn: function() {
344
+ return this.$firstLink.css('display') == 'block';
345
+ },
346
+ isFixed: function() {
347
+ return this.$root.css('position') == 'fixed';
348
+ },
349
+ isLinkInMegaMenu: function($a) {
350
+ return !$a.parent().parent().dataSM('level');
351
+ },
352
+ isTouchMode: function() {
353
+ return !mouse || this.isCollapsible();
354
+ },
355
+ itemActivate: function($a) {
356
+ var $li = $a.parent(),
357
+ $ul = $li.parent(),
358
+ level = $ul.dataSM('level');
359
+ // if for some reason the parent item is not activated (e.g. this is an API call to activate the item), activate all parent items first
360
+ if (level > 1 && (!this.activatedItems[level - 2] || this.activatedItems[level - 2][0] != $ul.dataSM('parent-a')[0])) {
361
+ var self = this;
362
+ $($ul.parentsUntil('[data-smartmenus-id]', 'ul').get().reverse()).add($ul).each(function() {
363
+ self.itemActivate($(this).dataSM('parent-a'));
364
+ });
365
+ }
366
+ // hide any visible deeper level sub menus
367
+ if (this.visibleSubMenus.length > level) {
368
+ for (var i = this.visibleSubMenus.length - 1, l = !this.activatedItems[level - 1] || this.activatedItems[level - 1][0] != $a[0] ? level - 1 : level; i > l; i--) {
369
+ this.menuHide(this.visibleSubMenus[i]);
370
+ }
371
+ }
372
+ // save new active item and sub menu for this level
373
+ this.activatedItems[level - 1] = $a;
374
+ this.visibleSubMenus[level - 1] = $ul;
375
+ if (this.$root.triggerHandler('activate.smapi', $a[0]) === false) {
376
+ return;
377
+ }
378
+ // show the sub menu if this item has one
379
+ var $sub = $li.dataSM('sub');
380
+ if ($sub && (this.isTouchMode() || (!this.opts.showOnClick || this.clickActivated))) {
381
+ this.menuShow($sub);
382
+ }
383
+ },
384
+ itemBlur: function(e) {
385
+ var $a = $(e.currentTarget);
386
+ if (!this.handleItemEvents($a)) {
387
+ return;
388
+ }
389
+ this.$root.triggerHandler('blur.smapi', $a[0]);
390
+ },
391
+ itemClick: function(e) {
392
+ var $a = $(e.currentTarget);
393
+ if (!this.handleItemEvents($a)) {
394
+ return;
395
+ }
396
+ $a.removeDataSM('mousedown');
397
+ if (this.$root.triggerHandler('click.smapi', $a[0]) === false) {
398
+ return false;
399
+ }
400
+ var $sub = $a.parent().dataSM('sub');
401
+ if (this.isTouchMode()) {
402
+ // undo fix: prevent the address bar on iPhone from sliding down when expanding a sub menu
403
+ if ($a.dataSM('href')) {
404
+ $a.attr('href', $a.dataSM('href')).removeDataSM('href');
405
+ }
406
+ // if the sub is not visible
407
+ if ($sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
408
+ // try to activate the item and show the sub
409
+ this.itemActivate($a);
410
+ // if "itemActivate" showed the sub, prevent the click so that the link is not loaded
411
+ // if it couldn't show it, then the sub menus are disabled with an !important declaration (e.g. via mobile styles) so let the link get loaded
412
+ if ($sub.is(':visible')) {
413
+ return false;
414
+ }
415
+ }
416
+ } else if (this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 && $sub) {
417
+ this.clickActivated = true;
418
+ this.menuShow($sub);
419
+ return false;
420
+ }
421
+ if ($a.hasClass('disabled')) {
422
+ return false;
423
+ }
424
+ if (this.$root.triggerHandler('select.smapi', $a[0]) === false) {
425
+ return false;
426
+ }
427
+ },
428
+ itemDown: function(e) {
429
+ var $a = $(e.currentTarget);
430
+ if (!this.handleItemEvents($a)) {
431
+ return;
432
+ }
433
+ $a.dataSM('mousedown', true);
434
+ },
435
+ itemEnter: function(e) {
436
+ var $a = $(e.currentTarget);
437
+ if (!this.handleItemEvents($a)) {
438
+ return;
439
+ }
440
+ if (!this.isTouchMode()) {
441
+ if (this.showTimeout) {
442
+ clearTimeout(this.showTimeout);
443
+ this.showTimeout = 0;
444
+ }
445
+ var self = this;
446
+ this.showTimeout = setTimeout(function() { self.itemActivate($a); }, this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 ? 1 : this.opts.showTimeout);
447
+ }
448
+ this.$root.triggerHandler('mouseenter.smapi', $a[0]);
449
+ },
450
+ itemFocus: function(e) {
451
+ var $a = $(e.currentTarget);
452
+ if (!this.handleItemEvents($a)) {
453
+ return;
454
+ }
455
+ // fix (the mousedown check): in some browsers a tap/click produces consecutive focus + click events so we don't need to activate the item on focus
456
+ if ((!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
457
+ this.itemActivate($a);
458
+ }
459
+ this.$root.triggerHandler('focus.smapi', $a[0]);
460
+ },
461
+ itemLeave: function(e) {
462
+ var $a = $(e.currentTarget);
463
+ if (!this.handleItemEvents($a)) {
464
+ return;
465
+ }
466
+ if (!this.isTouchMode()) {
467
+ if ($a[0].blur) {
468
+ $a[0].blur();
469
+ }
470
+ if (this.showTimeout) {
471
+ clearTimeout(this.showTimeout);
472
+ this.showTimeout = 0;
473
+ }
474
+ }
475
+ $a.removeDataSM('mousedown');
476
+ this.$root.triggerHandler('mouseleave.smapi', $a[0]);
477
+ },
478
+ itemTouchEnd: function(e) {
479
+ var $a = $(e.currentTarget);
480
+ if (!this.handleItemEvents($a)) {
481
+ return;
482
+ }
483
+ // prevent the address bar on iPhone from sliding down when expanding a sub menu
484
+ var $sub = $a.parent().dataSM('sub');
485
+ if ($a.attr('href').charAt(0) !== '#' && $sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
486
+ $a.dataSM('href', $a.attr('href'));
487
+ $a.attr('href', '#');
488
+ }
489
+ },
490
+ menuFixLayout: function($ul) {
491
+ // fixes a menu that is being shown for the first time
492
+ if (!$ul.dataSM('shown-before')) {
493
+ $ul.hide().dataSM('shown-before', true);
494
+ // fix the layout of the items in IE<8
495
+ if (IElt8) {
496
+ $ul.children().css({ styleFloat: 'left', width: '100%' });
497
+ }
498
+ }
499
+ },
500
+ menuHide: function($sub) {
501
+ if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
502
+ return;
503
+ }
504
+ $sub.stop(true, true);
505
+ if ($sub.is(':visible')) {
506
+ var complete = function() {
507
+ // unset z-index
508
+ if (IElt9) {
509
+ $sub.parent().css('z-index', '');
510
+ } else {
511
+ $sub.css('z-index', '');
512
+ }
513
+ };
514
+ // if sub is collapsible (mobile view)
515
+ if (this.isCollapsible()) {
516
+ if (this.opts.collapsibleHideFunction) {
517
+ this.opts.collapsibleHideFunction.call(this, $sub, complete);
518
+ } else {
519
+ $sub.hide(this.opts.collapsibleHideDuration, complete);
520
+ }
521
+ } else {
522
+ if (this.opts.hideFunction) {
523
+ this.opts.hideFunction.call(this, $sub, complete);
524
+ } else {
525
+ $sub.hide(this.opts.hideDuration, complete);
526
+ }
527
+ }
528
+ // remove IE iframe shim
529
+ if ($sub.dataSM('ie-shim')) {
530
+ $sub.dataSM('ie-shim').remove();
531
+ }
532
+ // deactivate scrolling if it is activated for this sub
533
+ if ($sub.dataSM('scroll')) {
534
+ $sub.unbind('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
535
+ }
536
+ // unhighlight parent item
537
+ $sub.dataSM('parent-a').removeClass('highlighted');
538
+ var level = $sub.dataSM('level');
539
+ this.activatedItems.splice(level - 1, 1);
540
+ this.visibleSubMenus.splice(level - 1, 1);
541
+ this.$root.triggerHandler('hide.smapi', $sub[0]);
542
+ }
543
+ },
544
+ menuHideAll: function() {
545
+ if (this.showTimeout) {
546
+ clearTimeout(this.showTimeout);
547
+ this.showTimeout = 0;
548
+ }
549
+ // hide all subs
550
+ for (var i = this.visibleSubMenus.length - 1; i > 0; i--) {
551
+ this.menuHide(this.visibleSubMenus[i]);
552
+ }
553
+ // hide root if it's popup
554
+ if (this.opts.isPopup) {
555
+ this.$root.stop(true, true);
556
+ if (this.$root.is(':visible')) {
557
+ if (this.opts.hideFunction) {
558
+ this.opts.hideFunction.call(this, this.$root);
559
+ } else {
560
+ this.$root.hide(this.opts.hideDuration);
561
+ }
562
+ // remove IE iframe shim
563
+ if (this.$root.dataSM('ie-shim')) {
564
+ this.$root.dataSM('ie-shim').remove();
565
+ }
566
+ }
567
+ }
568
+ this.activatedItems = [];
569
+ this.visibleSubMenus = [];
570
+ this.clickActivated = false;
571
+ // reset z-index increment
572
+ this.zIndexInc = 0;
573
+ },
574
+ menuIframeShim: function($ul) {
575
+ // create iframe shim for the menu
576
+ if (IE && this.opts.overlapControlsInIE && !$ul.dataSM('ie-shim')) {
577
+ $ul.dataSM('ie-shim', $('<iframe/>').attr({ src: 'javascript:0', tabindex: -9 })
578
+ .css({ position: 'absolute', top: 'auto', left: '0', opacity: 0, border: '0' })
579
+ );
580
+ }
581
+ },
582
+ menuInit: function($ul) {
583
+ if (!$ul.dataSM('in-mega')) {
584
+ this.subMenus.push($ul);
585
+ // mark UL's in mega drop downs (if any) so we can neglect them
586
+ if ($ul.hasClass('mega-menu')) {
587
+ $ul.find('ul').dataSM('in-mega', true);
588
+ }
589
+ // get level (much faster than, for example, using parentsUntil)
590
+ var level = 2,
591
+ par = $ul[0];
592
+ while ((par = par.parentNode.parentNode) != this.$root[0]) {
593
+ level++;
594
+ }
595
+ // cache stuff
596
+ $ul.dataSM('parent-a', $ul.prevAll('a').eq(-1))
597
+ .dataSM('level', level)
598
+ .parent().dataSM('sub', $ul);
599
+ // add sub indicator to parent item
600
+ if (this.opts.subIndicators) {
601
+ $ul.dataSM('parent-a').addClass('has-submenu')[this.opts.subIndicatorsPos](this.$subArrow.clone());
602
+ }
603
+ }
604
+ },
605
+ menuPosition: function($sub) {
606
+ var $a = $sub.dataSM('parent-a'),
607
+ $ul = $sub.parent().parent(),
608
+ level = $sub.dataSM('level'),
609
+ subW = this.getWidth($sub),
610
+ subH = this.getHeight($sub),
611
+ itemOffset = $a.offset(),
612
+ itemX = itemOffset.left,
613
+ itemY = itemOffset.top,
614
+ itemW = this.getWidth($a),
615
+ itemH = this.getHeight($a),
616
+ $win = $(window),
617
+ winX = $win.scrollLeft(),
618
+ winY = $win.scrollTop(),
619
+ winW = $win.width(),
620
+ winH = $win.height(),
621
+ horizontalParent = $ul.hasClass('sm') && !$ul.hasClass('sm-vertical'),
622
+ subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
623
+ subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
624
+ x, y;
625
+ if (horizontalParent) {
626
+ x = this.opts.rightToLeftSubMenus ? itemW - subW - subOffsetX : subOffsetX;
627
+ y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
628
+ } else {
629
+ x = this.opts.rightToLeftSubMenus ? subOffsetX - subW : itemW - subOffsetX;
630
+ y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
631
+ }
632
+ if (this.opts.keepInViewport && !this.isCollapsible()) {
633
+ if (this.isFixed()) {
634
+ itemX -= winX;
635
+ itemY -= winY;
636
+ winX = winY = 0;
637
+ }
638
+ var absX = itemX + x,
639
+ absY = itemY + y;
640
+ if (this.opts.rightToLeftSubMenus && absX < winX) {
641
+ x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
642
+ } else if (!this.opts.rightToLeftSubMenus && absX + subW > winX + winW) {
643
+ x = horizontalParent ? winX + winW - subW - absX + x : subOffsetX - subW;
644
+ }
645
+ if (!horizontalParent) {
646
+ if (subH < winH && absY + subH > winY + winH) {
647
+ y += winY + winH - subH - absY;
648
+ } else if (subH >= winH || absY < winY) {
649
+ y += winY - absY;
650
+ }
651
+ }
652
+ // do we need scrolling?
653
+ // 0.49 added for the sake of IE9/FF4+ where we might be dealing with float numbers for "subH"
654
+ if (mouse && (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49)) {
655
+ var self = this;
656
+ if (!$sub.dataSM('scroll-arrows')) {
657
+ $sub.dataSM('scroll-arrows', $([$('<span class="scroll-up"><span class="scroll-up-arrow"></span></span>')[0], $('<span class="scroll-down"><span class="scroll-down-arrow"></span></span>')[0]])
658
+ .bind({
659
+ mouseenter: function() { self.menuScroll($sub, $(this).hasClass('scroll-up')); },
660
+ mouseleave: function(e) {
661
+ self.menuScrollStop($sub);
662
+ self.menuScrollOut($sub, e);
663
+ },
664
+ 'mousewheel DOMMouseScroll': function(e) { e.preventDefault(); }
665
+ })
666
+ .insertAfter($sub)
667
+ );
668
+ }
669
+ // bind events to show/hide arrows on hover and save scrolling data for this sub
670
+ var vportY = winY - (itemY + itemH);
671
+ $sub.dataSM('scroll', {
672
+ vportY: vportY,
673
+ subH: subH,
674
+ winH: winH,
675
+ step: 1
676
+ })
677
+ .bind({
678
+ 'mouseover.smartmenus_scroll': function(e) { self.menuScrollOver($sub, e); },
679
+ 'mouseout.smartmenus_scroll': function(e) { self.menuScrollOut($sub, e); },
680
+ 'mousewheel.smartmenus_scroll DOMMouseScroll.smartmenus_scroll': function(e) { self.menuScrollMousewheel($sub, e); }
681
+ })
682
+ .dataSM('scroll-arrows').css({ top: 'auto', left: '0', marginLeft: x + (parseInt($sub.css('border-left-width')) || 0), width: this.getWidth($sub) - (parseInt($sub.css('border-left-width')) || 0) - (parseInt($sub.css('border-right-width')) || 0), zIndex: this.getStartZIndex() + this.zIndexInc })
683
+ .eq(0).css('margin-top', vportY).end()
684
+ .eq(1).css('margin-top', vportY + winH - this.getHeight($sub.dataSM('scroll-arrows').eq(1))).end()
685
+ .eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
686
+ }
687
+ }
688
+ $sub.css({ top: 'auto', left: '0', marginLeft: x, marginTop: y - itemH });
689
+ // IE iframe shim
690
+ this.menuIframeShim($sub);
691
+ if ($sub.dataSM('ie-shim')) {
692
+ $sub.dataSM('ie-shim').css({ zIndex: $sub.css('z-index'), width: subW, height: subH, marginLeft: x, marginTop: y - itemH });
693
+ }
694
+ },
695
+ menuScroll: function($sub, up, wheel) {
696
+ var y = parseFloat($sub.css('margin-top')),
697
+ scroll = $sub.dataSM('scroll'),
698
+ end = scroll.vportY + (up ? 0 : scroll.winH - scroll.subH),
699
+ step = wheel || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor($sub.dataSM('scroll').step);
700
+ $sub.add($sub.dataSM('ie-shim')).css('margin-top', Math.abs(end - y) > step ? y + (up ? step : -step) : end);
701
+ y = parseFloat($sub.css('margin-top'));
702
+ // show opposite arrow if appropriate
703
+ if (up && y + scroll.subH > scroll.vportY + scroll.winH || !up && y < scroll.vportY) {
704
+ $sub.dataSM('scroll-arrows').eq(up ? 1 : 0).show();
705
+ }
706
+ // accelerate when not using mousewheel to scroll
707
+ if (!wheel && this.opts.scrollAccelerate && $sub.dataSM('scroll').step < this.opts.scrollStep) {
708
+ $sub.dataSM('scroll').step += 0.5;
709
+ }
710
+ // "y" and "end" might be float numbers in IE9/FF4+ so this weird way to check is used
711
+ if (Math.abs(y - end) < 1) {
712
+ $sub.dataSM('scroll-arrows').eq(up ? 0 : 1).hide();
713
+ $sub.dataSM('scroll').step = 1;
714
+ } else if (!wheel) {
715
+ var self = this;
716
+ this.scrollTimeout = setTimeout(function() { self.menuScroll($sub, up); }, this.opts.scrollInterval);
717
+ }
718
+ },
719
+ menuScrollMousewheel: function($sub, e) {
720
+ var $closestSub = $(e.target).closest('ul');
721
+ while ($closestSub.dataSM('in-mega')) {
722
+ $closestSub = $closestSub.parent().closest('ul');
723
+ }
724
+ if ($closestSub[0] == $sub[0]) {
725
+ var up = (e.originalEvent.wheelDelta || -e.originalEvent.detail) > 0;
726
+ if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
727
+ this.menuScroll($sub, up, true);
728
+ }
729
+ }
730
+ e.preventDefault();
731
+ },
732
+ menuScrollOut: function($sub, e) {
733
+ var reClass = /^scroll-(up|down)/,
734
+ $closestSub = $(e.relatedTarget).closest('ul');
735
+ while ($closestSub.dataSM('in-mega')) {
736
+ $closestSub = $closestSub.parent().closest('ul');
737
+ }
738
+ if (!reClass.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || $closestSub[0] != $sub[0])) {
739
+ $sub.dataSM('scroll-arrows').css('visibility', 'hidden');
740
+ }
741
+ },
742
+ menuScrollOver: function($sub, e) {
743
+ var reClass = /^scroll-(up|down)/,
744
+ $closestSub = $(e.target).closest('ul');
745
+ while ($closestSub.dataSM('in-mega')) {
746
+ $closestSub = $closestSub.parent().closest('ul');
747
+ }
748
+ if (!reClass.test(e.target.className) && $closestSub[0] == $sub[0]) {
749
+ $sub.dataSM('scroll-arrows').css('visibility', 'visible');
750
+ }
751
+ },
752
+ menuScrollStop: function($sub) {
753
+ if (this.scrollTimeout) {
754
+ clearTimeout(this.scrollTimeout);
755
+ this.scrollTimeout = 0;
756
+ $sub.dataSM('scroll').step = 1;
757
+ }
758
+ },
759
+ menuShow: function($sub) {
760
+ if (!$sub.dataSM('beforefirstshowfired')) {
761
+ $sub.dataSM('beforefirstshowfired', true);
762
+ if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
763
+ return;
764
+ }
765
+ }
766
+ if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
767
+ return;
768
+ }
769
+ this.menuFixLayout($sub);
770
+ $sub.stop(true, true);
771
+ if (!$sub.is(':visible')) {
772
+ // set z-index - for IE < 9 set it to the parent LI
773
+ var zIndex = this.getStartZIndex() + (++this.zIndexInc);
774
+ if (IElt9) {
775
+ $sub.parent().css('z-index', zIndex);
776
+ } else {
777
+ $sub.css('z-index', zIndex);
778
+ }
779
+ // highlight parent item
780
+ if (this.opts.keepHighlighted || this.isCollapsible()) {
781
+ $sub.dataSM('parent-a').addClass('highlighted');
782
+ }
783
+ // min/max-width fix - no way to rely purely on CSS as all UL's are nested
784
+ if (this.opts.subMenusMinWidth || this.opts.subMenusMaxWidth) {
785
+ if (!IElt8) {
786
+ $sub.css({ width: 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
787
+ if (this.opts.subMenusMinWidth) {
788
+ $sub.css('min-width', this.opts.subMenusMinWidth);
789
+ }
790
+ if (this.opts.subMenusMaxWidth) {
791
+ var noMaxWidth = this.getWidth($sub);
792
+ $sub.css('max-width', this.opts.subMenusMaxWidth);
793
+ if (noMaxWidth > this.getWidth($sub)) {
794
+ $sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
795
+ }
796
+ }
797
+ // IE6,7
798
+ } else {
799
+ $sub.children().css('styleFloat', 'none');
800
+ if (IE6) {
801
+ $sub.width(this.opts.subMenusMinWidth ? this.opts.subMenusMinWidth : 1)
802
+ .children().children('a').css('white-space', 'nowrap');
803
+ } else { // IE7
804
+ $sub.css({ width: 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
805
+ if (this.opts.subMenusMinWidth) {
806
+ $sub.css('min-width', this.opts.subMenusMinWidth);
807
+ }
808
+ }
809
+ if (this.opts.subMenusMaxWidth) {
810
+ var noMaxWidth = $sub.width();
811
+ if (IE6) {
812
+ var maxWidth = $sub.css({ width: this.opts.subMenusMaxWidth, overflowX: 'hidden', overflowY: 'hidden' }).width();
813
+ if (noMaxWidth > maxWidth) {
814
+ $sub.css({ width: maxWidth, overflowX: 'visible', overflowY: 'visible' }).children().children('a').css('white-space', '');
815
+ } else {
816
+ $sub.css({ width: noMaxWidth, overflowX: 'visible', overflowY: 'visible' });
817
+ }
818
+ } else { // IE7
819
+ $sub.css('max-width', this.opts.subMenusMaxWidth);
820
+ if (noMaxWidth > $sub.width()) {
821
+ $sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
822
+ } else {
823
+ $sub.width(noMaxWidth);
824
+ }
825
+ }
826
+ } else {
827
+ $sub.width($sub.width());
828
+ }
829
+ $sub.children().css('styleFloat', 'left');
830
+ }
831
+ }
832
+ this.menuPosition($sub);
833
+ // insert IE iframe shim
834
+ if ($sub.dataSM('ie-shim')) {
835
+ $sub.dataSM('ie-shim').insertBefore($sub);
836
+ }
837
+ var complete = function() {
838
+ // fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
839
+ $sub.css('overflow', '');
840
+ };
841
+ // if sub is collapsible (mobile view)
842
+ if (this.isCollapsible()) {
843
+ if (this.opts.collapsibleShowFunction) {
844
+ this.opts.collapsibleShowFunction.call(this, $sub, complete);
845
+ } else {
846
+ $sub.show(this.opts.collapsibleShowDuration, complete);
847
+ }
848
+ } else {
849
+ if (this.opts.showFunction) {
850
+ this.opts.showFunction.call(this, $sub, complete);
851
+ } else {
852
+ $sub.show(this.opts.showDuration, complete);
853
+ }
854
+ }
855
+ // save new sub menu for this level
856
+ this.visibleSubMenus[$sub.dataSM('level') - 1] = $sub;
857
+ this.$root.triggerHandler('show.smapi', $sub[0]);
858
+ }
859
+ },
860
+ popupHide: function(noHideTimeout) {
861
+ if (this.hideTimeout) {
862
+ clearTimeout(this.hideTimeout);
863
+ this.hideTimeout = 0;
864
+ }
865
+ var self = this;
866
+ this.hideTimeout = setTimeout(function() {
867
+ self.menuHideAll();
868
+ }, noHideTimeout ? 1 : this.opts.hideTimeout);
869
+ },
870
+ popupShow: function(left, top) {
871
+ if (!this.opts.isPopup) {
872
+ alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
873
+ return;
874
+ }
875
+ if (this.hideTimeout) {
876
+ clearTimeout(this.hideTimeout);
877
+ this.hideTimeout = 0;
878
+ }
879
+ this.menuFixLayout(this.$root);
880
+ this.$root.stop(true, true);
881
+ if (!this.$root.is(':visible')) {
882
+ this.$root.css({ left: left, top: top });
883
+ // IE iframe shim
884
+ this.menuIframeShim(this.$root);
885
+ if (this.$root.dataSM('ie-shim')) {
886
+ this.$root.dataSM('ie-shim').css({ zIndex: this.$root.css('z-index'), width: this.getWidth(this.$root), height: this.getHeight(this.$root), left: left, top: top }).insertBefore(this.$root);
887
+ }
888
+ // show menu
889
+ if (this.opts.showFunction) {
890
+ this.opts.showFunction.call(this, this.$root);
891
+ } else {
892
+ this.$root.show(this.opts.showDuration);
893
+ }
894
+ this.visibleSubMenus[0] = this.$root;
895
+ }
896
+ },
897
+ refresh: function() {
898
+ this.menuHideAll();
899
+ this.$root.find('ul').each(function() {
900
+ var $this = $(this);
901
+ if ($this.dataSM('scroll-arrows')) {
902
+ $this.dataSM('scroll-arrows').remove();
903
+ }
904
+ })
905
+ .removeDataSM('in-mega')
906
+ .removeDataSM('shown-before')
907
+ .removeDataSM('ie-shim')
908
+ .removeDataSM('scroll-arrows')
909
+ .removeDataSM('parent-a')
910
+ .removeDataSM('level')
911
+ .removeDataSM('beforefirstshowfired');
912
+ this.$root.find('a.has-submenu').removeClass('has-submenu')
913
+ .parent().removeDataSM('sub');
914
+ if (this.opts.subIndicators) {
915
+ this.$root.find('span.sub-arrow').remove();
916
+ }
917
+ if (this.opts.markCurrentItem) {
918
+ this.$root.find('a.current').removeClass('current');
919
+ }
920
+ this.subMenus = [];
921
+ this.init(true);
922
+ },
923
+ rootOut: function(e) {
924
+ if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
925
+ return;
926
+ }
927
+ if (this.hideTimeout) {
928
+ clearTimeout(this.hideTimeout);
929
+ this.hideTimeout = 0;
930
+ }
931
+ if (!this.opts.showOnClick || !this.opts.hideOnClick) {
932
+ var self = this;
933
+ this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, this.opts.hideTimeout);
934
+ }
935
+ },
936
+ rootOver: function(e) {
937
+ if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
938
+ return;
939
+ }
940
+ if (this.hideTimeout) {
941
+ clearTimeout(this.hideTimeout);
942
+ this.hideTimeout = 0;
943
+ }
944
+ },
945
+ winResize: function(e) {
946
+ if (!this.handleEvents()) {
947
+ // we still need to resize the disable overlay if it's visible
948
+ if (this.$disableOverlay) {
949
+ var pos = this.$root.offset();
950
+ this.$disableOverlay.css({
951
+ top: pos.top,
952
+ left: pos.left,
953
+ width: this.$root.outerWidth(),
954
+ height: this.$root.outerHeight()
955
+ });
956
+ }
957
+ return;
958
+ }
959
+ // hide sub menus on resize - on mobile do it only on orientation change
960
+ if (!this.isCollapsible() && (!('onorientationchange' in window) || e.type == 'orientationchange')) {
961
+ if (this.activatedItems.length) {
962
+ this.activatedItems[this.activatedItems.length - 1][0].blur();
963
+ }
964
+ this.menuHideAll();
965
+ }
966
+ }
967
+ }
968
+ });
969
+
970
+ $.fn.dataSM = function(key, val) {
971
+ if (val) {
972
+ return this.data(key + '_smartmenus', val);
973
+ }
974
+ return this.data(key + '_smartmenus');
975
+ }
976
+
977
+ $.fn.removeDataSM = function(key) {
978
+ return this.removeData(key + '_smartmenus');
979
+ }
980
+
981
+ $.fn.smartmenus = function(options) {
982
+ if (typeof options == 'string') {
983
+ var args = arguments,
984
+ method = options;
985
+ Array.prototype.shift.call(args);
986
+ return this.each(function() {
987
+ var smartmenus = $(this).data('smartmenus');
988
+ if (smartmenus && smartmenus[method]) {
989
+ smartmenus[method].apply(smartmenus, args);
990
+ }
991
+ });
992
+ }
993
+ var opts = $.extend({}, $.fn.smartmenus.defaults, options);
994
+ return this.each(function() {
995
+ new $.SmartMenus(this, opts);
996
+ });
997
+ }
998
+
999
+ // default settings
1000
+ $.fn.smartmenus.defaults = {
1001
+ isPopup: false, // is this a popup menu (can be shown via the popupShow/popupHide methods) or a permanent menu bar
1002
+ mainMenuSubOffsetX: 0, // pixels offset from default position
1003
+ mainMenuSubOffsetY: 0, // pixels offset from default position
1004
+ subMenusSubOffsetX: 0, // pixels offset from default position
1005
+ subMenusSubOffsetY: 0, // pixels offset from default position
1006
+ subMenusMinWidth: '10em', // min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
1007
+ subMenusMaxWidth: '20em', // max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
1008
+ subIndicators: true, // create sub menu indicators - creates a SPAN and inserts it in the A
1009
+ subIndicatorsPos: 'prepend', // position of the SPAN relative to the menu item content ('prepend', 'append')
1010
+ subIndicatorsText: '+', // [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
1011
+ scrollStep: 30, // pixels step when scrolling long sub menus that do not fit in the viewport height
1012
+ scrollInterval: 30, // interval between each scrolling step
1013
+ scrollAccelerate: true, // accelerate scrolling or use a fixed step
1014
+ showTimeout: 250, // timeout before showing the sub menus
1015
+ hideTimeout: 500, // timeout before hiding the sub menus
1016
+ showDuration: 0, // duration for show animation - set to 0 for no animation - matters only if showFunction:null
1017
+ showFunction: null, // custom function to use when showing a sub menu (the default is the jQuery 'show')
1018
+ // don't forget to call complete() at the end of whatever you do
1019
+ // e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
1020
+ hideDuration: 0, // duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
1021
+ hideFunction: function($ul, complete) { $ul.fadeOut(200, complete); }, // custom function to use when hiding a sub menu (the default is the jQuery 'hide')
1022
+ // don't forget to call complete() at the end of whatever you do
1023
+ // e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
1024
+ collapsibleShowDuration:0, // duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
1025
+ collapsibleShowFunction:function($ul, complete) { $ul.slideDown(200, complete); }, // custom function to use when showing a collapsible sub menu
1026
+ // (i.e. when mobile styles are used to make the sub menus collapsible)
1027
+ collapsibleHideDuration:0, // duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
1028
+ collapsibleHideFunction:function($ul, complete) { $ul.slideUp(200, complete); }, // custom function to use when hiding a collapsible sub menu
1029
+ // (i.e. when mobile styles are used to make the sub menus collapsible)
1030
+ showOnClick: false, // show the first-level sub menus onclick instead of onmouseover (matters only for mouse input)
1031
+ hideOnClick: true, // hide the sub menus on click/tap anywhere on the page
1032
+ keepInViewport: true, // reposition the sub menus if needed to make sure they always appear inside the viewport
1033
+ keepHighlighted: true, // keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
1034
+ markCurrentItem: false, // automatically add the 'current' class to the A element of the item linking to the current URL
1035
+ markCurrentTree: true, // add the 'current' class also to the A elements of all ancestor items of the current item
1036
+ rightToLeftSubMenus: false, // right to left display of the sub menus (check the CSS for the sub indicators' position)
1037
+ bottomToTopSubMenus: false, // bottom to top display of the sub menus
1038
+ overlapControlsInIE: true // make sure sub menus appear on top of special OS controls in IE (i.e. SELECT, OBJECT, EMBED, etc.)
1039
+ };
1040
+
1041
+ })(jQuery);