parade 0.8.0 → 0.8.1

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.
data/README.md CHANGED
@@ -318,14 +318,40 @@ If you want to trigger some JavaScript as soon as a certain page is shown or
318
318
  when you switch to the next or previous slide, you can bind a callback to a
319
319
  custom event:
320
320
 
321
- > ### parade:show
322
- > will be triggered as soon as you enter a page
323
- > ### parade:next
324
- > will be triggered when you switch to the next page
325
- > ### parade:incr
326
- > will be triggered when you advance to the next increment on the page
327
- > ### parade:prev
328
- > will be triggered when you switch to the previous page
321
+ ### Appearance
322
+
323
+ * parade:willAppear
324
+
325
+ > triggered before the slide is presented
326
+
327
+ * parade:didAppear
328
+
329
+ > triggered after the slide is presented
330
+
331
+ * parade:show
332
+
333
+ ### Disappearance
334
+
335
+ > triggered after the slide is presented
336
+
337
+ * parade:willDisappear
338
+
339
+ > triggered before the slide disappears
340
+
341
+ * parade:didDisappear
342
+
343
+ > triggered after the slide disppeared
344
+
345
+ ### Navigation
346
+
347
+ * parade:next
348
+
349
+ > triggered when an attempt to move to the next slide or incremental bullet point
350
+
351
+ * parade:prev
352
+
353
+ > triggered when an attempt to move back a slide or incremental bullet point
354
+
329
355
 
330
356
  These events are triggered on the "div.content" child of the slide, so you must
331
357
  add a custom and unique class to your SLIDE to identify it:
@@ -335,12 +361,14 @@ add a custom and unique class to your SLIDE to identify it:
335
361
  # 1st Example h1
336
362
  <script>
337
363
  // bind to custom event
338
- $(".custom_and_unique_class").bind("parade:show", function (event) {
364
+ $(".custom_and_unique_class").live("parade:show", function (event) {
339
365
  // animate the h1
340
366
  var h1 = $(event.target).find("h1");
341
367
  h1.delay(500)
342
368
  .slideUp(300, function () { $(this).css({textDecoration: "line-through"}); })
343
369
  .slideDown(300);
370
+
371
+ return false;
344
372
  });
345
373
  </script>
346
374
  ```
@@ -350,17 +378,17 @@ h1-element will be animated, as soon as this event is triggered on that slide.
350
378
 
351
379
  If you bind an event handler to the custom events *parade:next* or
352
380
  *parade:prev*, you can prevent the default action (that is switching to the
353
- appropriate slide) by calling *event.preventDefault()*:
381
+ appropriate slide) by returning *false*:
354
382
 
355
383
  ```markdown
356
384
  !SLIDE prevent_default
357
385
  # 2nd Example h1
358
386
  <script>
359
- $(".prevent_default").bind("parade:next", function (event) {
387
+ $(".prevent_default").live("parade:next", function (event) {
360
388
  var h1 = $(event.target).find("h1");
361
389
  if (h1.css("text-decoration") === "none") {
362
- event.preventDefault();
363
390
  h1.css({textDecoration: "line-through"})
391
+ return false;
364
392
  }
365
393
  });
366
394
  </script>
@@ -368,7 +396,7 @@ $(".prevent_default").bind("parade:next", function (event) {
368
396
 
369
397
  This will bind an event handler for *parade:next* to your slide. When you press
370
398
  the right arrow key the first time, the h1-element will be decorated. When you
371
- press the right array key another time, you will switch to the next slide.
399
+ press the right arrow key another time, you will switch to the next slide.
372
400
 
373
401
  The same applies to the *parade:prev* event, of course.
374
402
 
@@ -382,7 +410,7 @@ be applied as soon as it is loaded.
382
410
  The content generated by the slide is wrapped with a *div* with the class .+content+ like this.
383
411
 
384
412
  ```html
385
- <div ref="intro/01_slide/1" class="content" style="margin-top: 210px;">
413
+ <div class="content">
386
414
  <h1>jQuery &amp; Sinatra</h1>
387
415
  <h2>A Classy Combination</h2>
388
416
  </div>
data/bin/parade CHANGED
@@ -106,24 +106,6 @@ command [:static] do |c|
106
106
 
107
107
  end
108
108
 
109
- pre do |global,command,options,args|
110
- # Pre logic here
111
- # Return true to proceed; false to abourt and not call the
112
- # chosen command
113
- true
114
- end
115
-
116
- post do |global,command,options,args|
117
- # Post logic here
118
- end
119
-
120
- on_error do |exception|
121
- # Error logic here
122
- # return false to skip default error handling
123
- true
124
- end
125
-
126
-
127
109
  # To allow an easier command-line to launch parade, the following format
128
110
  #
129
111
  # `parade` is converted to `parade server .`
@@ -133,6 +115,6 @@ end
133
115
  parameters = ARGV
134
116
 
135
117
  parameters = [ "server", "." ] if parameters.empty?
136
- parameters.unshift "server" if parameters.count == 1
118
+ parameters.unshift "server" if parameters.count == 1 and not parameters.include?("help")
137
119
 
138
120
  exit GLI.run(parameters)
@@ -39,5 +39,5 @@ rescue LoadError
39
39
  }
40
40
  end
41
41
 
42
- require_relative 'parade/helpers/encode_image'
43
- require_relative 'parade/server'
42
+ require 'parade/helpers/encode_image'
43
+ require 'parade/server'
@@ -1,4 +1,3 @@
1
- require_relative '../section'
2
1
  require_relative 'presentation_filepath_parser'
3
2
 
4
3
  module Parade
@@ -28,9 +28,7 @@ module Parade
28
28
  def render(content)
29
29
 
30
30
  html = Nokogiri::XML.fragment(content)
31
- parser = CommandlineParser.new
32
-
33
- html.css(".#{css_class}").each do |slide|
31
+ html.css(".content.#{css_class}").each do |slide|
34
32
 
35
33
  columns = []
36
34
  slop = []
@@ -35,7 +35,7 @@ module Parade
35
35
  #
36
36
  def self.render(html_content)
37
37
 
38
- html = Nokogiri::XML.fragment(html_content)
38
+ html = Nokogiri::HTML.fragment(html_content)
39
39
  parser = CommandlineParser.new
40
40
 
41
41
  html.css('.commandline pre').each do |code|
@@ -46,7 +46,23 @@ module Parade
46
46
  rule(:prompt => simple(:prompt), :input => simple(:input), :output => simple(:output)) do
47
47
  command = Nokogiri::XML::Node.new('pre', html)
48
48
  command.set_attribute('class', 'command')
49
- command.content = "#{prompt} #{input}"
49
+
50
+ node_prompt = Nokogiri::XML::Node.new('span', html)
51
+ # The 'nv' class specifically gives it the same code syntax highlighting
52
+ node_prompt.set_attribute('class','prompt nv')
53
+ node_prompt.content = prompt
54
+
55
+ separator = Nokogiri::XML::Text.new(' ',html)
56
+
57
+ node_input = Nokogiri::XML::Node.new('span',html)
58
+ node_input.content = input
59
+ # The 'nb' class specifically gives it the same syntax highlighting
60
+ node_input.set_attribute('class','input nb')
61
+
62
+ command << node_prompt
63
+ command << separator
64
+ command << node_input
65
+
50
66
  code << command
51
67
 
52
68
  # Add newline after the input so that users can
@@ -25,6 +25,14 @@ module Parade
25
25
  @title ? @title : (section ? section.title : "Section")
26
26
  end
27
27
 
28
+ # @return [Array<String>] the name of all the parent sections. In this
29
+ # instance we are not interested in sections without names. These are
30
+ # the lowest level sections and are usually within a parent section that
31
+ # they are acurrately named.
32
+ def hierarchy
33
+ Array(@title) + (section ? section.hierarchy : [])
34
+ end
35
+
28
36
  # @return [String] the description of the section
29
37
  attr_accessor :description
30
38
 
@@ -1,3 +1,4 @@
1
+ require_relative 'section'
1
2
  require_relative "parsers/dsl"
2
3
  require_relative 'renderers/update_image_paths'
3
4
 
@@ -5,6 +6,9 @@ require_relative 'features/live_ruby'
5
6
  require_relative 'features/pdf_presentation'
6
7
  require_relative 'features/preshow'
7
8
 
9
+ require_relative 'slide_post_renderers'
10
+ require_relative 'slide_pre_renderers'
11
+
8
12
  module Parade
9
13
 
10
14
  class Server < Sinatra::Application
@@ -73,7 +77,7 @@ module Parade
73
77
  # presentation directory.
74
78
  #
75
79
  def custom_css_files
76
- custom_resource "css" do |path|
80
+ custom_resource "css" do |path|
77
81
  css path
78
82
  end
79
83
  end
@@ -83,7 +87,7 @@ module Parade
83
87
  # presentation directory.
84
88
  #
85
89
  def custom_js_files
86
- custom_resource "js" do |path|
90
+ custom_resource "js" do |path|
87
91
  js path
88
92
  end
89
93
  end
@@ -1,8 +1,4 @@
1
1
  require_relative 'metadata'
2
- require_relative 'renderers/html_with_pygments'
3
- require_relative 'renderers/command_line_renderer'
4
- require_relative 'renderers/special_paragraph_renderer'
5
- require_relative 'renderers/columns_renderer'
6
2
 
7
3
  module Parade
8
4
 
@@ -24,8 +20,8 @@ module Parade
24
20
  section ? section.title : "Slide"
25
21
  end
26
22
 
27
- def reference
28
- "#{section ? section.title : 'slide'}/#{sequence}"
23
+ def hierarchy
24
+ section.hierarchy
29
25
  end
30
26
 
31
27
  #
@@ -72,15 +68,15 @@ module Parade
72
68
  # information for the slide
73
69
  #
74
70
  attr_accessor :metadata
75
-
71
+
76
72
  # @return [String] the CSS classes for the slide
77
73
  def slide_classes
78
- title.downcase.gsub(' ','-')
74
+ [ title.downcase.gsub(' ','-') ] + content_classes
79
75
  end
80
76
 
81
77
  # @return [String] the CSS classes for the content section of the slide
82
78
  def content_classes
83
- metadata.classes.join(" ")
79
+ metadata.classes
84
80
  end
85
81
 
86
82
  # @return [String] the transition style for the slide
@@ -94,13 +90,11 @@ module Parade
94
90
  end
95
91
 
96
92
  def pre_renderers
97
- [ Renderers::HTMLwithPygments ]
93
+ SlidePreRenderers.renderers
98
94
  end
99
95
 
100
96
  def post_renderers
101
- [ Renderers::SpecialParagraphRenderer,
102
- Renderers::CommandLineRenderer,
103
- Renderers::ColumnsRenderer.new(:css_class => 'columns',:html_element => "h2",:segments => 12) ]
97
+ SlidePostRenderers.renderers
104
98
  end
105
99
 
106
100
  # @return [String] HTML rendering of the slide's raw contents.
@@ -0,0 +1,24 @@
1
+ require_relative 'renderers/command_line_renderer'
2
+ require_relative 'renderers/special_paragraph_renderer'
3
+ require_relative 'renderers/columns_renderer'
4
+
5
+ module Parade
6
+
7
+ module SlidePostRenderers
8
+ extend self
9
+
10
+ def register(renderer)
11
+ renderers.push renderer
12
+ end
13
+
14
+ def renderers
15
+ @renderers ||= []
16
+ end
17
+ end
18
+
19
+ SlidePostRenderers.register Renderers::SpecialParagraphRenderer
20
+ SlidePostRenderers.register Renderers::CommandLineRenderer
21
+ SlidePostRenderers.register Renderers::ColumnsRenderer.new(css_class: 'columns',
22
+ html_element: "h2", segments: 12)
23
+
24
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'renderers/html_with_pygments'
2
+
3
+ module Parade
4
+
5
+ module SlidePreRenderers
6
+ extend self
7
+
8
+ def register(renderer)
9
+ renderers.push renderer
10
+ end
11
+
12
+ def renderers
13
+ @renderers ||= []
14
+ end
15
+ end
16
+
17
+ SlidePreRenderers.register Renderers::HTMLwithPygments
18
+
19
+ end
@@ -1,3 +1,3 @@
1
1
  module Parade
2
- VERSION = '0.8.0'
2
+ VERSION = '0.8.1'
3
3
  end
@@ -0,0 +1,3 @@
1
+ @media screen {
2
+
3
+ }
@@ -1,6 +1,6 @@
1
1
  @media screen {
2
2
  body {
3
- font-size: 100%;
3
+ font-size: 100%;
4
4
  font-family: "Gill Sans", Helvetica, Arial, sans-serif;
5
5
  background:#333;
6
6
  overflow:hidden;
@@ -39,7 +39,7 @@
39
39
  border-top-right-radius: 3px;
40
40
  z-index: 2147483647; /* max, see http://www.puidokas.com/max-z-index/ */
41
41
  }
42
-
42
+
43
43
  #pauseScreen {
44
44
  background: rgba(0, 0, 0, 0.85);
45
45
  width: 100%;
@@ -104,9 +104,9 @@
104
104
 
105
105
  /* plain (non-bullet) text */
106
106
  .content > p {
107
- font-size: 2em;
107
+ font-size: 2em;
108
108
  margin: 1em;
109
- text-align: center;
109
+ text-align: center;
110
110
  }
111
111
 
112
112
  .content > pre {
@@ -139,6 +139,9 @@
139
139
  display: block;
140
140
  }
141
141
 
142
+ .content.columns .grid_12 p {
143
+ font-size: 200%;
144
+ }
142
145
 
143
146
  .content.columns .grid_6 p {
144
147
  font-size: 150%;
@@ -165,9 +168,9 @@
165
168
  /* numbered lists are numbered */
166
169
  .content ol {
167
170
  margin-left: 40px;
168
- font-size: 3em;
169
- text-align: left;
170
- padding-left: 40px;
171
+ font-size: 3em;
172
+ text-align: left;
173
+ padding-left: 40px;
171
174
  }
172
175
  .content ol > li {
173
176
  list-style: decimal;
@@ -180,9 +183,9 @@
180
183
  list-style: disc;
181
184
  }
182
185
  .content > ul {
183
- font-size: 3em;
184
- text-align: left;
185
- padding-left: 40px;
186
+ font-size: 3em;
187
+ text-align: left;
188
+ padding-left: 40px;
186
189
  }
187
190
  .content > ul > li {
188
191
  padding: .5em;
@@ -192,8 +195,8 @@
192
195
  /* ironically, normal lists have bullets and 'bullets' lists don't */
193
196
  .bullets > ul {
194
197
  list-style: none;
195
- font-size: 3em;
196
- padding-left: 0px;
198
+ font-size: 3em;
199
+ padding-left: 0px;
197
200
  }
198
201
  .bullets > ul > li {
199
202
  text-align: center;
@@ -243,7 +246,7 @@
243
246
  .subsection h1 {
244
247
  background: #008;
245
248
  color: #fff;
246
- padding: .25em;
249
+ padding: .25em;
247
250
  }
248
251
 
249
252
  .small {
@@ -1,11 +1,11 @@
1
- /*--------------------------------------------------------------------
1
+ /*--------------------------------------------------------------------
2
2
  Scripts for creating and manipulating custom menus based on standard <ul> markup
3
3
  Version: 3.0, 03.31.2009
4
4
 
5
5
  By: Maggie Costello Wachs (maggie@filamentgroup.com) and Scott Jehl (scott@filamentgroup.com)
6
- http://www.filamentgroup.com
7
- * reference articles: http://www.filamentgroup.com/lab/jquery_ipod_style_drilldown_menu/
8
-
6
+ http://www.filamentgroup.com
7
+ * reference articles: http://www.filamentgroup.com/lab/jquery_ipod_style_drilldown_menu/
8
+
9
9
  Copyright (c) 2009 Filament Group
10
10
  Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
11
11
  --------------------------------------------------------------------*/
@@ -14,531 +14,567 @@ Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL
14
14
  var allUIMenus = [];
15
15
 
16
16
  $.fn.menu = function(options){
17
- var caller = this;
18
- var options = options;
19
- var m = new Menu(caller, options);
20
- allUIMenus.push(m);
21
-
22
- $(this)
23
- .mousedown(function(){
24
- if (!m.menuOpen) { m.showLoading(); };
25
- })
26
- .click(function(){
27
- if (m.menuOpen == false) { m.showMenu(); }
28
- else { m.kill(); };
29
- return false;
30
- });
17
+ var caller = this;
18
+ var options = options;
19
+ var m = new Menu(caller, options);
20
+ allUIMenus.push(m);
21
+
22
+ $(this)
23
+ .mousedown(function(){
24
+ if (!m.menuOpen) { m.showLoading(); };
25
+ })
26
+ .click(function(){
27
+ debugger
28
+ if (m.menuOpen == false) { m.showMenu(); }
29
+ else { m.kill(); };
30
+ return false;
31
+ });
31
32
  };
32
33
 
33
34
  function Menu(caller, options){
34
- var menu = this;
35
- var caller = $(caller);
36
- var container = $('<div class="fg-menu-container ui-widget ui-widget-content ui-corner-all">'+options.content+'</div>');
37
-
38
- this.menuOpen = false;
39
- this.menuExists = false;
40
-
41
- var options = jQuery.extend({
42
- content: null,
43
- width: 180, // width of menu container, must be set or passed in to calculate widths of child menus
44
- maxHeight: 180, // max height of menu (if a drilldown: height does not include breadcrumb)
45
- positionOpts: {
46
- posX: 'left',
47
- posY: 'bottom',
48
- offsetX: 0,
49
- offsetY: 0,
50
- directionH: 'right',
51
- directionV: 'down',
52
- detectH: true, // do horizontal collision detection
53
- detectV: true, // do vertical collision detection
54
- linkToFront: false
55
- },
56
- showSpeed: 200, // show/hide speed in milliseconds
57
- callerOnState: 'ui-state-active', // class to change the appearance of the link/button when the menu is showing
58
- loadingState: 'ui-state-loading', // class added to the link/button while the menu is created
59
- linkHover: 'ui-state-hover', // class for menu option hover state
60
- linkHoverSecondary: 'li-hover', // alternate class, may be used for multi-level menus
61
- // ----- multi-level menu defaults -----
62
- crossSpeed: 200, // cross-fade speed for multi-level menus
63
- crumbDefaultText: 'Choose an option:',
64
- backLink: true, // in the ipod-style menu: instead of breadcrumbs, show only a 'back' link
65
- backLinkText: 'Back',
66
- flyOut: false, // multi-level menus are ipod-style by default; this parameter overrides to make a flyout instead
67
- flyOutOnState: 'ui-state-default',
68
- nextMenuLink: 'ui-icon-triangle-1-e', // class to style the link (specifically, a span within the link) used in the multi-level menu to show the next level
69
- topLinkText: 'All',
70
- nextCrumbLink: 'ui-icon-carat-1-e'
71
- }, options);
72
-
73
- var killAllMenus = function(){
74
- $.each(allUIMenus, function(i){
75
- if (allUIMenus[i].menuOpen) { allUIMenus[i].kill(); };
76
- });
77
- };
78
-
79
- this.kill = function(){
80
- caller
81
- .removeClass(options.loadingState)
82
- .removeClass('fg-menu-open')
83
- .removeClass(options.callerOnState);
84
- container.find('li').removeClass(options.linkHoverSecondary).find('a').removeClass(options.linkHover);
85
- if (options.flyOutOnState) { container.find('li a').removeClass(options.flyOutOnState); };
86
- if (options.callerOnState) { caller.removeClass(options.callerOnState); };
87
- if (container.is('.fg-menu-ipod')) { menu.resetDrilldownMenu(); };
88
- if (container.is('.fg-menu-flyout')) { menu.resetFlyoutMenu(); };
89
- container.parent().hide();
90
- menu.menuOpen = false;
91
- $(document).unbind('click', killAllMenus);
92
- $(document).unbind('keydown');
93
- };
94
-
95
- this.showLoading = function(){
96
- caller.addClass(options.loadingState);
97
- };
98
-
99
- this.showMenu = function(){
100
- killAllMenus();
101
- if (!menu.menuExists) { menu.create() };
102
- caller
103
- .addClass('fg-menu-open')
104
- .addClass(options.callerOnState);
105
- container.parent().show().click(function(){ menu.kill(); return false; });
106
- container.hide().slideDown(options.showSpeed).find('.fg-menu:eq(0)');
107
- menu.menuOpen = true;
108
- caller.removeClass(options.loadingState);
109
- $(document).click(killAllMenus);
110
-
111
- // assign key events
112
- $(document).keydown(function(event){
113
- var e;
114
- if (event.which !="") { e = event.which; }
115
- else if (event.charCode != "") { e = event.charCode; }
116
- else if (event.keyCode != "") { e = event.keyCode; }
117
-
118
- var menuType = ($(event.target).parents('div').is('.fg-menu-flyout')) ? 'flyout' : 'ipod' ;
119
-
120
- // vi bindings
121
- switch(e) {
122
- case 72: // left arrow
123
- if (menuType == 'flyout') {
124
- $(event.target).trigger('mouseout');
125
- if ($('.'+options.flyOutOnState).size() > 0) { $('.'+options.flyOutOnState).trigger('mouseover'); };
126
- };
127
-
128
- if (menuType == 'ipod') {
129
- $(event.target).trigger('mouseout');
130
- if ($('.fg-menu-footer').find('a').size() > 0) { $('.fg-menu-footer').find('a').trigger('click'); };
131
- if ($('.fg-menu-header').find('a').size() > 0) { $('.fg-menu-current-crumb').prev().find('a').trigger('click'); };
132
- if ($('.fg-menu-current').prev().is('.fg-menu-indicator')) {
133
- $('.fg-menu-current').prev().trigger('mouseover');
134
- };
135
- };
136
- return false;
137
- break;
138
-
139
- case 75: // up arrow
140
- if ($(event.target).is('.' + options.linkHover)) {
141
- var prevLink = $(event.target).parent().prev().find('a:eq(0)');
142
- if (prevLink.size() > 0) {
143
- $(event.target).trigger('mouseout');
144
- prevLink.trigger('mouseover');
145
- };
146
- }
147
- else { container.find('a:eq(0)').trigger('mouseover'); }
148
- return false;
149
- break;
150
-
151
- case 76: // right arrow
152
- if ($(event.target).is('.fg-menu-indicator')) {
153
- if (menuType == 'flyout') {
154
- $(event.target).next().find('a:eq(0)').trigger('mouseover');
155
- }
156
- else if (menuType == 'ipod') {
157
- $(event.target).trigger('click');
158
- setTimeout(function(){
159
- $(event.target).next().find('a:eq(0)').trigger('mouseover');
160
- }, options.crossSpeed);
161
- };
162
- };
163
- return false;
164
- break;
165
-
166
- case 74: // down arrow
167
- if ($(event.target).is('.' + options.linkHover)) {
168
- var nextLink = $(event.target).parent().next().find('a:eq(0)');
169
- if (nextLink.size() > 0) {
170
- $(event.target).trigger('mouseout');
171
- nextLink.trigger('mouseover');
172
- };
173
- }
174
- else { container.find('a:eq(0)').trigger('mouseover'); }
175
- return false;
176
- break;
177
-
178
- case 27: // escape
179
- killAllMenus();
180
- break;
181
-
182
- case 13: // enter
183
- if ($(event.target).is('.fg-menu-indicator') && menuType == 'ipod') {
184
- $(event.target).trigger('click');
185
- setTimeout(function(){
186
- $(event.target).next().find('a:eq(0)').trigger('mouseover');
187
- }, options.crossSpeed);
188
- };
189
- break;
190
- };
191
- });
192
- };
193
-
194
- this.create = function(){
195
- container.css({ width: options.width }).appendTo('body').find('ul:first').not('.fg-menu-breadcrumb').addClass('fg-menu');
196
- container.find('ul, li a').addClass('ui-corner-all');
197
-
198
- // aria roles & attributes
199
- container.find('ul').attr('role', 'menu').eq(0).attr('aria-activedescendant','active-menuitem').attr('aria-labelledby', caller.attr('id'));
200
- container.find('li').attr('role', 'menuitem');
201
- container.find('li:has(ul)').attr('aria-haspopup', 'true').find('ul').attr('aria-expanded', 'false');
202
- container.find('a').attr('tabindex', '-1');
203
-
204
- // when there are multiple levels of hierarchy, create flyout or drilldown menu
205
- if (container.find('ul').size() > 1) {
206
- if (options.flyOut) { menu.flyout(container, options); }
207
- else { menu.drilldown(container, options); }
208
- }
209
- else {
210
- container.find('a').click(function(){
211
- menu.chooseItem(this);
212
- return false;
213
- });
214
- };
215
-
216
- if (options.linkHover) {
217
- var allLinks = container.find('.fg-menu li a');
218
- allLinks.hover(
219
- function(){
220
- var menuitem = $(this);
221
- $('.'+options.linkHover).removeClass(options.linkHover).blur().parent().removeAttr('id');
222
- $(this).addClass(options.linkHover).focus().parent().attr('id','active-menuitem');
223
- },
224
- function(){
225
- $(this).removeClass(options.linkHover).blur().parent().removeAttr('id');
226
- }
227
- );
228
- };
229
-
230
- if (options.linkHoverSecondary) {
231
- container.find('.fg-menu li').hover(
232
- function(){
233
- $(this).siblings('li').removeClass(options.linkHoverSecondary);
234
- if (options.flyOutOnState) { $(this).siblings('li').find('a').removeClass(options.flyOutOnState); }
235
- $(this).addClass(options.linkHoverSecondary);
236
- },
237
- function(){ $(this).removeClass(options.linkHoverSecondary); }
238
- );
239
- };
240
-
241
- menu.setPosition(container, caller, options);
242
- menu.menuExists = true;
243
- };
244
-
245
- this.chooseItem = function(item){
246
- menu.kill();
247
- gotoSlide($(item).attr('rel'));
248
- $('#navmenu').hide();
249
- };
35
+ var menu = this;
36
+ var caller = $(caller);
37
+ var container = $('<div class="fg-menu-container ui-widget ui-widget-content ui-corner-all">'+options.content+'</div>');
38
+
39
+ this.menuOpen = false;
40
+ this.menuExists = false;
41
+
42
+ $.subscribe('navigation:show',$.proxy(function() {
43
+ this.showMenu();
44
+ },this));
45
+ $.subscribe('navigation:hidden',$.proxy(function() {
46
+ this.currentSelection = this.htmlContent.find('a:first');
47
+ this.killAllMenus();
48
+ },this));
49
+ $.subscribe('navigation:left',$.proxy(function() {
50
+ this.goLeft();
51
+ },this));
52
+ $.subscribe('navigation:right',$.proxy(function() {
53
+ this.goRight();
54
+ },this));
55
+ $.subscribe('navigation:down',$.proxy(function() {
56
+ this.goDown();
57
+ },this));
58
+ $.subscribe('navigation:up',$.proxy(function() {
59
+ this.goUp();
60
+ },this));
61
+ $.subscribe('navigation:selection',$.proxy(function() {
62
+ this.makeSelection();
63
+ },this));
64
+
65
+ var options = jQuery.extend({
66
+ content: null,
67
+ width: 180, // width of menu container, must be set or passed in to calculate widths of child menus
68
+ maxHeight: 180, // max height of menu (if a drilldown: height does not include breadcrumb)
69
+ positionOpts: {
70
+ posX: 'left',
71
+ posY: 'bottom',
72
+ offsetX: 0,
73
+ offsetY: 0,
74
+ directionH: 'right',
75
+ directionV: 'down',
76
+ detectH: true, // do horizontal collision detection
77
+ detectV: true, // do vertical collision detection
78
+ linkToFront: false
79
+ },
80
+ showSpeed: 200, // show/hide speed in milliseconds
81
+ callerOnState: 'ui-state-active', // class to change the appearance of the link/button when the menu is showing
82
+ loadingState: 'ui-state-loading', // class added to the link/button while the menu is created
83
+ linkHover: 'ui-state-hover', // class for menu option hover state
84
+ linkHoverSecondary: 'li-hover', // alternate class, may be used for multi-level menus
85
+ // ----- multi-level menu defaults -----
86
+ crossSpeed: 200, // cross-fade speed for multi-level menus
87
+ crumbDefaultText: 'Choose an option:',
88
+ backLink: true, // in the ipod-style menu: instead of breadcrumbs, show only a 'back' link
89
+ backLinkText: 'Back',
90
+ flyOut: false, // multi-level menus are ipod-style by default; this parameter overrides to make a flyout instead
91
+ flyOutOnState: 'ui-state-default',
92
+ nextMenuLink: 'ui-icon-triangle-1-e', // class to style the link (specifically, a span within the link) used in the multi-level menu to show the next level
93
+ topLinkText: 'All',
94
+ nextCrumbLink: 'ui-icon-carat-1-e'
95
+ }, options);
96
+
97
+ this.options = options;
98
+
99
+ this.killAllMenus = function() {
100
+ $.each(allUIMenus, function(i){
101
+ if (allUIMenus[i].menuOpen) { allUIMenus[i].kill(); };
102
+ });
103
+ }
104
+
105
+ this.kill = function(){
106
+ caller
107
+ .removeClass(options.loadingState)
108
+ .removeClass('fg-menu-open')
109
+ .removeClass(options.callerOnState);
110
+ container.find('li').removeClass(options.linkHoverSecondary).find('a').removeClass(options.linkHover);
111
+ if (options.flyOutOnState) { container.find('li a').removeClass(options.flyOutOnState); };
112
+ if (options.callerOnState) { caller.removeClass(options.callerOnState); };
113
+ if (container.is('.fg-menu-ipod')) { menu.resetDrilldownMenu(); };
114
+ if (container.is('.fg-menu-flyout')) { menu.resetFlyoutMenu(); };
115
+ container.parent().hide();
116
+ menu.menuOpen = false;
117
+ };
118
+
119
+ this.showLoading = function(){
120
+ caller.addClass(options.loadingState);
121
+ };
122
+
123
+ this.menuIsFlyout = function() { return true; }
124
+ this.menuIsIpod = function() { return false; }
125
+
126
+ this.currentSelectionIsMenu = function() {
127
+ return this.currentSelection.hasClass('fg-menu-indicator');
128
+ }
129
+
130
+ this.currentSelectionIsMenuItem = function() {
131
+ return !this.currentSelectionIsMenu();
132
+ }
133
+
134
+ this.goLeft = function() {
135
+
136
+ if (this.menuIsFlyout()) {
137
+ this.htmlContent.trigger('mouseout');
138
+ var parentItem = $('.'+this.options.flyOutOnState);
139
+
140
+ if (parentItem.length > 1) {
141
+ parentItem.trigger('mouseover');
142
+ this.currentSelection = $(parentItem[parentItem.length - 1]);
143
+ };
144
+ }
145
+
146
+ if (this.menuIsIpod()) {
147
+ this.htmlContent.trigger('mouseout');
148
+ if (this.htmlContent.find('.fg-menu-footer').find('a').size() > 0) { this.htmlContent.find('.fg-menu-footer').find('a').trigger('click'); };
149
+ if (this.htmlContent.find('.fg-menu-header').find('a').size() > 0) { this.htmlContent.find('.fg-menu-current-crumb').prev().find('a').trigger('click'); };
150
+ if (this.htmlContent.find('.fg-menu-current').prev().is('.fg-menu-indicator')) {
151
+ this.htmlContent.find('.fg-menu-current').prev().trigger('mouseover');
152
+ }
153
+ };
154
+ }
155
+
156
+ this.goRight = function() {
157
+ if (this.currentSelectionIsMenuItem()) { return; }
158
+
159
+ if (this.menuIsFlyout()) {
160
+ var newSelection = this.currentSelection.next().find('a:eq(0)');
161
+ newSelection.trigger('mouseover');
162
+ this.currentSelection = newSelection;
163
+ }
164
+
165
+ if (this.menuIsIpod()) {
166
+ this.currentSelection.trigger('click');
167
+ setTimeout(function(){
168
+ var newSelection = $(this.currentSelection).next().find('a:eq(0)');
169
+ newSelection.trigger('mouseover');
170
+ this.currentSelection = newSelection;
171
+ }, options.crossSpeed);
172
+ }
173
+ }
174
+
175
+ this.goDown = function() {
176
+
177
+ if (this.currentSelection) {
178
+ var nextLink = $(this.currentSelection).parent().next().find('a:eq(0)');
179
+ if (nextLink.size() > 0) {
180
+ $(this.currentSelection).trigger('mouseout');
181
+ nextLink.trigger('mouseover');
182
+ this.currentSelection = nextLink
183
+ };
184
+ }
185
+ else { container.find('a:eq(0)').trigger('mouseover'); }
186
+ }
187
+
188
+ this.goUp = function() {
189
+ if (this.currentSelection) {
190
+ var prevLink = $(this.currentSelection).parent().prev().find('a:eq(0)');
191
+ if (prevLink.size() > 0) {
192
+ $(this.currentSelection).trigger('mouseout');
193
+ prevLink.trigger('mouseover');
194
+ this.currentSelection = prevLink;
195
+ };
196
+ }
197
+ else { container.find('a:eq(0)').trigger('mouseover'); }
198
+
199
+ }
200
+
201
+ this.makeSelection = function() {
202
+ if ($(event.target).is('.fg-menu-indicator') && this.menuIsIpod()) {
203
+ $(event.target).trigger('click');
204
+ setTimeout(function(){
205
+ $(event.target).next().find('a:eq(0)').trigger('mouseover');
206
+ }, options.crossSpeed);
207
+ };
208
+ }
209
+
210
+ this.showMenu = function(){
211
+ this.killAllMenus();
212
+ if (!menu.menuExists) { menu.create() };
213
+ caller.addClass('fg-menu-open').addClass(options.callerOnState);
214
+ container.parent().show().click(function(){ menu.kill(); return false; });
215
+ container.hide().slideDown(options.showSpeed).find('.fg-menu:eq(0)');
216
+ menu.menuOpen = true;
217
+ caller.removeClass(options.loadingState);
218
+
219
+ this.htmlContent = $('.fg-menu');
220
+ this.currentSelection = this.htmlContent.find('a:first');
221
+ this.currentSelection.addClass(options.linkHover);
222
+ this.currentSelection.trigger('mouseover');
223
+ };
224
+
225
+ this.create = function(){
226
+ container.css({ width: options.width }).appendTo('body').find('ul:first').not('.fg-menu-breadcrumb').addClass('fg-menu');
227
+ container.find('ul, li a').addClass('ui-corner-all');
228
+
229
+ // aria roles & attributes
230
+ container.find('ul').attr('role', 'menu').eq(0).attr('aria-activedescendant','active-menuitem').attr('aria-labelledby', caller.attr('id'));
231
+ container.find('li').attr('role', 'menuitem');
232
+ container.find('li:has(ul)').attr('aria-haspopup', 'true').find('ul').attr('aria-expanded', 'false');
233
+ container.find('a').attr('tabindex', '-1');
234
+
235
+ // when there are multiple levels of hierarchy, create flyout or drilldown menu
236
+ if (container.find('ul').size() > 1) {
237
+ if (options.flyOut) { menu.flyout(container, options); }
238
+ else { menu.drilldown(container, options); }
239
+ }
240
+ else {
241
+ container.find('a').click(function(){
242
+ menu.chooseItem(this);
243
+ return false;
244
+ });
245
+ };
246
+
247
+ if (options.linkHover) {
248
+ var allLinks = container.find('.fg-menu li a');
249
+ allLinks.hover(
250
+ function(){
251
+ var menuitem = $(this);
252
+ $('.'+options.linkHover).removeClass(options.linkHover).blur().parent().removeAttr('id');
253
+ $(this).addClass(options.linkHover).focus().parent().attr('id','active-menuitem');
254
+ },
255
+ function(){
256
+ $(this).removeClass(options.linkHover).blur().parent().removeAttr('id');
257
+ }
258
+ );
259
+ };
260
+
261
+ if (options.linkHoverSecondary) {
262
+ container.find('.fg-menu li').hover(
263
+ function(){
264
+ $(this).siblings('li').removeClass(options.linkHoverSecondary);
265
+ if (options.flyOutOnState) { $(this).siblings('li').find('a').removeClass(options.flyOutOnState); }
266
+ $(this).addClass(options.linkHoverSecondary);
267
+ },
268
+ function(){ $(this).removeClass(options.linkHoverSecondary); }
269
+ );
270
+ };
271
+
272
+ menu.setPosition(container, caller, options);
273
+ menu.menuExists = true;
274
+ };
275
+
276
+ this.chooseItem = function(item){
277
+ $.publish('navigation:hidden');
278
+ var slideNumber = $(item).attr('rel');
279
+ $.publish('presentation:slide:location:change',slideNumber);
280
+ };
250
281
  };
251
282
 
252
283
  Menu.prototype.flyout = function(container, options) {
253
- var menu = this;
254
-
255
- this.resetFlyoutMenu = function(){
256
- var allLists = container.find('ul ul');
257
- allLists.removeClass('ui-widget-content').hide();
258
- };
259
-
260
- container.addClass('fg-menu-flyout').find('li:has(ul)').each(function(){
261
- var linkWidth = container.width();
262
- var showTimer, hideTimer;
263
- var allSubLists = $(this).find('ul');
264
-
265
- allSubLists.css({ left: linkWidth, width: linkWidth }).hide();
266
-
267
- $(this).find('a:eq(0)').addClass('fg-menu-indicator').html('<span>' + $(this).find('a:eq(0)').text() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>').hover(
268
- function(){
269
- clearTimeout(hideTimer);
270
- var subList = $(this).next();
271
- if (!fitVertical(subList, $(this).offset().top)) { subList.css({ top: 'auto', bottom: 0 }); };
272
- if (!fitHorizontal(subList, $(this).offset().left + 100)) { subList.css({ left: 'auto', right: linkWidth, 'z-index': 999 }); };
273
- showTimer = setTimeout(function(){
274
- subList.addClass('ui-widget-content').show(options.showSpeed).attr('aria-expanded', 'true');
275
- }, 300);
276
- },
277
- function(){
278
- clearTimeout(showTimer);
279
- var subList = $(this).next();
280
- hideTimer = setTimeout(function(){
281
- subList.removeClass('ui-widget-content').hide(options.showSpeed).attr('aria-expanded', 'false');
282
- }, 400);
283
- }
284
- );
285
-
286
- $(this).find('ul a').hover(
287
- function(){
288
- clearTimeout(hideTimer);
289
- if ($(this).parents('ul').prev().is('a.fg-menu-indicator')) {
290
- $(this).parents('ul').prev().addClass(options.flyOutOnState);
291
- }
292
- },
293
- function(){
294
- hideTimer = setTimeout(function(){
295
- allSubLists.hide(options.showSpeed);
296
- container.find(options.flyOutOnState).removeClass(options.flyOutOnState);
297
- }, 500);
298
- }
299
- );
300
- });
301
-
302
- container.find('a').click(function(){
303
- menu.chooseItem(this);
304
- return false;
305
- });
284
+ var menu = this;
285
+
286
+ this.resetFlyoutMenu = function(){
287
+ var allLists = container.find('ul ul');
288
+ allLists.removeClass('ui-widget-content').hide();
289
+ };
290
+
291
+ container.addClass('fg-menu-flyout').find('li:has(ul)').each(function(){
292
+ var linkWidth = container.width();
293
+ var showTimer, hideTimer;
294
+ var allSubLists = $(this).find('ul');
295
+
296
+ allSubLists.css({ left: linkWidth, width: linkWidth }).hide();
297
+
298
+ $(this).find('a:eq(0)').addClass('fg-menu-indicator').html('<span>' + $(this).find('a:eq(0)').text() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>').hover(
299
+ function(){
300
+ clearTimeout(hideTimer);
301
+ var subList = $(this).next();
302
+ // Previously this would make sure that the items rocked up past the top
303
+ // when the list was too long. I would rather the list run down the length
304
+ // of the page instead of not be accessible.
305
+ // TODO: make it scrollable or provide a better interface
306
+
307
+ // if (!fitVertical(subList, $(this).offset().top)) { subList.css({ top: 'auto', bottom: 0 }); };
308
+ if (!fitHorizontal(subList, $(this).offset().left + 100)) { subList.css({ left: 'auto', right: linkWidth, 'z-index': 999 }); };
309
+ showTimer = setTimeout(function(){
310
+ subList.addClass('ui-widget-content').show(options.showSpeed).attr('aria-expanded', 'true');
311
+ }, 300);
312
+ },
313
+ function(){
314
+ clearTimeout(showTimer);
315
+ var subList = $(this).next();
316
+ hideTimer = setTimeout(function(){
317
+ subList.removeClass('ui-widget-content').hide(options.showSpeed).attr('aria-expanded', 'false');
318
+ }, 400);
319
+ }
320
+ );
321
+
322
+ $(this).find('ul a').hover(
323
+ function(){
324
+ clearTimeout(hideTimer);
325
+ if ($(this).parents('ul').prev().is('a.fg-menu-indicator')) {
326
+ $(this).parents('ul').prev().addClass(options.flyOutOnState);
327
+ }
328
+ },
329
+ function(){
330
+ hideTimer = setTimeout(function(){
331
+ allSubLists.hide(options.showSpeed);
332
+ container.find(options.flyOutOnState).removeClass(options.flyOutOnState);
333
+ }, 500);
334
+ }
335
+ );
336
+ });
337
+
338
+ container.find('a').click(function(){
339
+ menu.chooseItem(this);
340
+ return false;
341
+ });
306
342
  };
307
343
 
308
344
 
309
345
  Menu.prototype.drilldown = function(container, options) {
310
- var menu = this;
311
- var topList = container.find('.fg-menu');
312
- var breadcrumb = $('<ul class="fg-menu-breadcrumb ui-widget-header ui-corner-all ui-helper-clearfix"></ul>');
313
- var crumbDefaultHeader = $('<li class="fg-menu-breadcrumb-text">'+options.crumbDefaultText+'</li>');
314
- var firstCrumbText = (options.backLink) ? options.backLinkText : options.topLinkText;
315
- var firstCrumbClass = (options.backLink) ? 'fg-menu-prev-list' : 'fg-menu-all-lists';
316
- var firstCrumbLinkClass = (options.backLink) ? 'ui-state-default ui-corner-all' : '';
317
- var firstCrumbIcon = (options.backLink) ? '<span class="ui-icon ui-icon-triangle-1-w"></span>' : '';
318
- var firstCrumb = $('<li class="'+firstCrumbClass+'"><a href="#" class="'+firstCrumbLinkClass+'">'+firstCrumbIcon+firstCrumbText+'</a></li>');
319
-
320
- container.addClass('fg-menu-ipod');
321
-
322
- if (options.backLink) { breadcrumb.addClass('fg-menu-footer').appendTo(container).hide(); }
323
- else { breadcrumb.addClass('fg-menu-header').prependTo(container); };
324
- breadcrumb.append(crumbDefaultHeader);
325
-
326
- var checkMenuHeight = function(el){
327
- if (el.height() > options.maxHeight) { el.addClass('fg-menu-scroll') };
328
- el.css({ height: options.maxHeight });
329
- };
330
-
331
- var resetChildMenu = function(el){ el.removeClass('fg-menu-scroll').removeClass('fg-menu-current').height('auto'); };
332
-
333
- this.resetDrilldownMenu = function(){
334
- $('.fg-menu-current').removeClass('fg-menu-current');
335
- topList.animate({ left: 0 }, options.crossSpeed, function(){
336
- $(this).find('ul').each(function(){
337
- $(this).hide();
338
- resetChildMenu($(this));
339
- });
340
- topList.addClass('fg-menu-current');
341
- });
342
- $('.fg-menu-all-lists').find('span').remove();
343
- breadcrumb.empty().append(crumbDefaultHeader);
344
- $('.fg-menu-footer').empty().hide();
345
- checkMenuHeight(topList);
346
- };
347
-
348
- topList
349
- .addClass('fg-menu-content fg-menu-current ui-widget-content ui-helper-clearfix')
350
- .css({ width: container.width() })
351
- .find('ul')
352
- .css({ width: container.width(), left: container.width() })
353
- .addClass('ui-widget-content')
354
- .hide();
355
- checkMenuHeight(topList);
356
-
357
- topList.find('a').each(function(){
358
- // if the link opens a child menu:
359
- if ($(this).next().is('ul')) {
360
- $(this)
361
- .addClass('fg-menu-indicator')
362
- .each(function(){ $(this).html('<span>' + $(this).text() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>'); })
363
- .click(function(){ // ----- show the next menu
364
- var nextList = $(this).next();
365
- var parentUl = $(this).parents('ul:eq(0)');
366
- var parentLeft = (parentUl.is('.fg-menu-content')) ? 0 : parseFloat(topList.css('left'));
367
- var nextLeftVal = Math.round(parentLeft - parseFloat(container.width()));
368
- var footer = $('.fg-menu-footer');
369
-
370
- // show next menu
371
- resetChildMenu(parentUl);
372
- checkMenuHeight(nextList);
373
- topList.animate({ left: nextLeftVal }, options.crossSpeed);
374
- nextList.show().addClass('fg-menu-current').attr('aria-expanded', 'true');
375
-
376
- var setPrevMenu = function(backlink){
377
- var b = backlink;
378
- var c = $('.fg-menu-current');
379
- var prevList = c.parents('ul:eq(0)');
380
- c.hide().attr('aria-expanded', 'false');
381
- resetChildMenu(c);
382
- checkMenuHeight(prevList);
383
- prevList.addClass('fg-menu-current').attr('aria-expanded', 'true');
384
- if (prevList.hasClass('fg-menu-content')) { b.remove(); footer.hide(); };
385
- };
386
-
387
- // initialize "back" link
388
- if (options.backLink) {
389
- if (footer.find('a').size() == 0) {
390
- footer.show();
391
- $('<a href="#"><span class="ui-icon ui-icon-triangle-1-w"></span> <span>Back</span></a>')
392
- .appendTo(footer)
393
- .click(function(){ // ----- show the previous menu
394
- var b = $(this);
395
- var prevLeftVal = parseFloat(topList.css('left')) + container.width();
396
- topList.animate({ left: prevLeftVal }, options.crossSpeed, function(){
397
- setPrevMenu(b);
398
- });
399
- return false;
400
- });
401
- }
402
- }
403
- // or initialize top breadcrumb
404
- else {
405
- if (breadcrumb.find('li').size() == 1){
406
- breadcrumb.empty().append(firstCrumb);
407
- firstCrumb.find('a').click(function(){
408
- menu.resetDrilldownMenu();
409
- return false;
410
- });
411
- }
412
- $('.fg-menu-current-crumb').removeClass('fg-menu-current-crumb');
413
- var crumbText = $(this).find('span:eq(0)').text();
414
- var newCrumb = $('<li class="fg-menu-current-crumb"><a href="javascript://" class="fg-menu-crumb">'+crumbText+'</a></li>');
415
- newCrumb
416
- .appendTo(breadcrumb)
417
- .find('a').click(function(){
418
- if ($(this).parent().is('.fg-menu-current-crumb')){
419
- menu.chooseItem(this);
420
- }
421
- else {
422
- var newLeftVal = - ($('.fg-menu-current').parents('ul').size() - 1) * 180;
423
- topList.animate({ left: newLeftVal }, options.crossSpeed, function(){
424
- setPrevMenu();
425
- });
426
-
427
- // make this the current crumb, delete all breadcrumbs after this one, and navigate to the relevant menu
428
- $(this).parent().addClass('fg-menu-current-crumb').find('span').remove();
429
- $(this).parent().nextAll().remove();
430
- };
431
- return false;
432
- });
433
- newCrumb.prev().append(' <span class="ui-icon '+options.nextCrumbLink+'"></span>');
434
- };
435
- return false;
436
- });
437
- }
438
- // if the link is a leaf node (doesn't open a child menu)
439
- else {
440
- $(this).click(function(){
441
- menu.chooseItem(this);
442
- return false;
443
- });
444
- };
445
- });
346
+ var menu = this;
347
+ var topList = container.find('.fg-menu');
348
+ var breadcrumb = $('<ul class="fg-menu-breadcrumb ui-widget-header ui-corner-all ui-helper-clearfix"></ul>');
349
+ var crumbDefaultHeader = $('<li class="fg-menu-breadcrumb-text">'+options.crumbDefaultText+'</li>');
350
+ var firstCrumbText = (options.backLink) ? options.backLinkText : options.topLinkText;
351
+ var firstCrumbClass = (options.backLink) ? 'fg-menu-prev-list' : 'fg-menu-all-lists';
352
+ var firstCrumbLinkClass = (options.backLink) ? 'ui-state-default ui-corner-all' : '';
353
+ var firstCrumbIcon = (options.backLink) ? '<span class="ui-icon ui-icon-triangle-1-w"></span>' : '';
354
+ var firstCrumb = $('<li class="'+firstCrumbClass+'"><a href="#" class="'+firstCrumbLinkClass+'">'+firstCrumbIcon+firstCrumbText+'</a></li>');
355
+
356
+ container.addClass('fg-menu-ipod');
357
+
358
+ if (options.backLink) { breadcrumb.addClass('fg-menu-footer').appendTo(container).hide(); }
359
+ else { breadcrumb.addClass('fg-menu-header').prependTo(container); };
360
+ breadcrumb.append(crumbDefaultHeader);
361
+
362
+ var checkMenuHeight = function(el){
363
+ if (el.height() > options.maxHeight) { el.addClass('fg-menu-scroll') };
364
+ el.css({ height: options.maxHeight });
365
+ };
366
+
367
+ var resetChildMenu = function(el){ el.removeClass('fg-menu-scroll').removeClass('fg-menu-current').height('auto'); };
368
+
369
+ this.resetDrilldownMenu = function(){
370
+ $('.fg-menu-current').removeClass('fg-menu-current');
371
+ topList.animate({ left: 0 }, options.crossSpeed, function(){
372
+ $(this).find('ul').each(function(){
373
+ $(this).hide();
374
+ resetChildMenu($(this));
375
+ });
376
+ topList.addClass('fg-menu-current');
377
+ });
378
+ $('.fg-menu-all-lists').find('span').remove();
379
+ breadcrumb.empty().append(crumbDefaultHeader);
380
+ $('.fg-menu-footer').empty().hide();
381
+ checkMenuHeight(topList);
382
+ };
383
+
384
+ topList
385
+ .addClass('fg-menu-content fg-menu-current ui-widget-content ui-helper-clearfix')
386
+ .css({ width: container.width() })
387
+ .find('ul')
388
+ .css({ width: container.width(), left: container.width() })
389
+ .addClass('ui-widget-content')
390
+ .hide();
391
+ checkMenuHeight(topList);
392
+
393
+ topList.find('a').each(function(){
394
+ // if the link opens a child menu:
395
+ if ($(this).next().is('ul')) {
396
+ $(this)
397
+ .addClass('fg-menu-indicator')
398
+ .each(function(){ $(this).html('<span>' + $(this).text() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>'); })
399
+ .click(function(){ // ----- show the next menu
400
+ var nextList = $(this).next();
401
+ var parentUl = $(this).parents('ul:eq(0)');
402
+ var parentLeft = (parentUl.is('.fg-menu-content')) ? 0 : parseFloat(topList.css('left'));
403
+ var nextLeftVal = Math.round(parentLeft - parseFloat(container.width()));
404
+ var footer = $('.fg-menu-footer');
405
+
406
+ // show next menu
407
+ resetChildMenu(parentUl);
408
+ checkMenuHeight(nextList);
409
+ topList.animate({ left: nextLeftVal }, options.crossSpeed);
410
+ nextList.show().addClass('fg-menu-current').attr('aria-expanded', 'true');
411
+
412
+ var setPrevMenu = function(backlink){
413
+ var b = backlink;
414
+ var c = $('.fg-menu-current');
415
+ var prevList = c.parents('ul:eq(0)');
416
+ c.hide().attr('aria-expanded', 'false');
417
+ resetChildMenu(c);
418
+ checkMenuHeight(prevList);
419
+ prevList.addClass('fg-menu-current').attr('aria-expanded', 'true');
420
+ if (prevList.hasClass('fg-menu-content')) { b.remove(); footer.hide(); };
421
+ };
422
+
423
+ // initialize "back" link
424
+ if (options.backLink) {
425
+ if (footer.find('a').size() == 0) {
426
+ footer.show();
427
+ $('<a href="#"><span class="ui-icon ui-icon-triangle-1-w"></span> <span>Back</span></a>')
428
+ .appendTo(footer)
429
+ .click(function(){ // ----- show the previous menu
430
+ var b = $(this);
431
+ var prevLeftVal = parseFloat(topList.css('left')) + container.width();
432
+ topList.animate({ left: prevLeftVal }, options.crossSpeed, function(){
433
+ setPrevMenu(b);
434
+ });
435
+ return false;
436
+ });
437
+ }
438
+ }
439
+ // or initialize top breadcrumb
440
+ else {
441
+ if (breadcrumb.find('li').size() == 1){
442
+ breadcrumb.empty().append(firstCrumb);
443
+ firstCrumb.find('a').click(function(){
444
+ menu.resetDrilldownMenu();
445
+ return false;
446
+ });
447
+ }
448
+ $('.fg-menu-current-crumb').removeClass('fg-menu-current-crumb');
449
+ var crumbText = $(this).find('span:eq(0)').text();
450
+ var newCrumb = $('<li class="fg-menu-current-crumb"><a href="javascript://" class="fg-menu-crumb">'+crumbText+'</a></li>');
451
+ newCrumb
452
+ .appendTo(breadcrumb)
453
+ .find('a').click(function(){
454
+ if ($(this).parent().is('.fg-menu-current-crumb')){
455
+ menu.chooseItem(this);
456
+ }
457
+ else {
458
+ var newLeftVal = - ($('.fg-menu-current').parents('ul').size() - 1) * 180;
459
+ topList.animate({ left: newLeftVal }, options.crossSpeed, function(){
460
+ setPrevMenu();
461
+ });
462
+
463
+ // make this the current crumb, delete all breadcrumbs after this one, and navigate to the relevant menu
464
+ $(this).parent().addClass('fg-menu-current-crumb').find('span').remove();
465
+ $(this).parent().nextAll().remove();
466
+ };
467
+ return false;
468
+ });
469
+ newCrumb.prev().append(' <span class="ui-icon '+options.nextCrumbLink+'"></span>');
470
+ };
471
+ return false;
472
+ });
473
+ }
474
+ // if the link is a leaf node (doesn't open a child menu)
475
+ else {
476
+ $(this).click(function(){
477
+ menu.chooseItem(this);
478
+ return false;
479
+ });
480
+ };
481
+ });
446
482
  };
447
483
 
448
484
 
449
485
  /* Menu.prototype.setPosition parameters (defaults noted with *):
450
- referrer = the link (or other element) used to show the overlaid object
451
- settings = can override the defaults:
452
- - posX/Y: where the top left corner of the object should be positioned in relation to its referrer.
453
- X: left*, center, right
454
- Y: top, center, bottom*
455
- - offsetX/Y: the number of pixels to be offset from the x or y position. Can be a positive or negative number.
456
- - directionH/V: where the entire menu should appear in relation to its referrer.
457
- Horizontal: left*, right
458
- Vertical: up, down*
459
- - detectH/V: detect the viewport horizontally / vertically
460
- - linkToFront: copy the menu link and place it on top of the menu (visual effect to make it look like it overlaps the object) */
461
-
462
- Menu.prototype.setPosition = function(widget, caller, options) {
463
- var el = widget;
464
- var referrer = caller;
465
- var dims = {
466
- refX: referrer.offset().left,
467
- refY: referrer.offset().top,
468
- refW: referrer.getTotalWidth(),
469
- refH: referrer.getTotalHeight()
470
- };
471
- var options = options;
472
- var xVal, yVal;
473
-
474
- var helper = $('<div class="positionHelper"></div>');
475
- helper.css({ position: 'absolute', left: dims.refX, top: dims.refY, width: dims.refW, height: dims.refH });
476
- el.wrap(helper);
477
-
478
- // get X pos
479
- switch(options.positionOpts.posX) {
480
- case 'left': xVal = 0;
481
- break;
482
- case 'center': xVal = dims.refW / 2;
483
- break;
484
- case 'right': xVal = dims.refW;
485
- break;
486
- };
487
-
488
- // get Y pos
489
- switch(options.positionOpts.posY) {
490
- case 'top': yVal = 0;
491
- break;
492
- case 'center': yVal = dims.refH / 2;
493
- break;
494
- case 'bottom': yVal = dims.refH;
495
- break;
496
- };
497
-
498
- // add the offsets (zero by default)
499
- xVal += options.positionOpts.offsetX;
500
- yVal += options.positionOpts.offsetY;
501
-
502
- // position the object vertically
503
- if (options.positionOpts.directionV == 'up') {
504
- el.css({ top: 'auto', bottom: yVal });
505
- if (options.positionOpts.detectV && !fitVertical(el)) {
506
- el.css({ bottom: 'auto', top: yVal });
507
- }
508
- }
509
- else {
510
- el.css({ bottom: 'auto', top: yVal });
511
- if (options.positionOpts.detectV && !fitVertical(el)) {
512
- el.css({ top: 'auto', bottom: yVal });
513
- }
514
- };
515
-
516
- // and horizontally
517
- if (options.positionOpts.directionH == 'left') {
518
- el.css({ left: 'auto', right: xVal });
519
- if (options.positionOpts.detectH && !fitHorizontal(el)) {
520
- el.css({ right: 'auto', left: xVal });
521
- }
522
- }
523
- else {
524
- el.css({ right: 'auto', left: xVal });
525
- if (options.positionOpts.detectH && !fitHorizontal(el)) {
526
- el.css({ left: 'auto', right: xVal });
527
- }
528
- };
529
-
530
- // if specified, clone the referring element and position it so that it appears on top of the menu
531
- if (options.positionOpts.linkToFront) {
532
- referrer.clone().addClass('linkClone').css({
533
- position: 'absolute',
534
- top: 0,
535
- right: 'auto',
536
- bottom: 'auto',
537
- left: 0,
538
- width: referrer.width(),
539
- height: referrer.height()
540
- }).insertAfter(el);
541
- };
486
+ referrer = the link (or other element) used to show the overlaid object
487
+ settings = can override the defaults:
488
+ - posX/Y: where the top left corner of the object should be positioned in relation to its referrer.
489
+ X: left*, center, right
490
+ Y: top, center, bottom*
491
+ - offsetX/Y: the number of pixels to be offset from the x or y position. Can be a positive or negative number.
492
+ - directionH/V: where the entire menu should appear in relation to its referrer.
493
+ Horizontal: left*, right
494
+ Vertical: up, down*
495
+ - detectH/V: detect the viewport horizontally / vertically
496
+ - linkToFront: copy the menu link and place it on top of the menu (visual effect to make it look like it overlaps the object) */
497
+
498
+ Menu.prototype.setPosition = function(widget, caller, options) {
499
+ var el = widget;
500
+ var referrer = caller;
501
+ var dims = {
502
+ refX: referrer.offset().left,
503
+ refY: referrer.offset().top,
504
+ refW: referrer.getTotalWidth(),
505
+ refH: referrer.getTotalHeight()
506
+ };
507
+ var options = options;
508
+ var xVal, yVal;
509
+
510
+ var helper = $('<div class="positionHelper"></div>');
511
+ helper.css({ position: 'absolute', left: dims.refX, top: dims.refY, width: dims.refW, height: dims.refH });
512
+ el.wrap(helper);
513
+
514
+ // get X pos
515
+ switch(options.positionOpts.posX) {
516
+ case 'left': xVal = 0;
517
+ break;
518
+ case 'center': xVal = dims.refW / 2;
519
+ break;
520
+ case 'right': xVal = dims.refW;
521
+ break;
522
+ };
523
+
524
+ // get Y pos
525
+ switch(options.positionOpts.posY) {
526
+ case 'top': yVal = 0;
527
+ break;
528
+ case 'center': yVal = dims.refH / 2;
529
+ break;
530
+ case 'bottom': yVal = dims.refH;
531
+ break;
532
+ };
533
+
534
+ // add the offsets (zero by default)
535
+ xVal += options.positionOpts.offsetX;
536
+ yVal += options.positionOpts.offsetY;
537
+
538
+ // position the object vertically
539
+ if (options.positionOpts.directionV == 'up') {
540
+ el.css({ top: 'auto', bottom: yVal });
541
+ if (options.positionOpts.detectV && !fitVertical(el)) {
542
+ el.css({ bottom: 'auto', top: yVal });
543
+ }
544
+ }
545
+ else {
546
+ el.css({ bottom: 'auto', top: yVal });
547
+ if (options.positionOpts.detectV && !fitVertical(el)) {
548
+ el.css({ top: 'auto', bottom: yVal });
549
+ }
550
+ };
551
+
552
+ // and horizontally
553
+ if (options.positionOpts.directionH == 'left') {
554
+ el.css({ left: 'auto', right: xVal });
555
+ if (options.positionOpts.detectH && !fitHorizontal(el)) {
556
+ el.css({ right: 'auto', left: xVal });
557
+ }
558
+ }
559
+ else {
560
+ el.css({ right: 'auto', left: xVal });
561
+ if (options.positionOpts.detectH && !fitHorizontal(el)) {
562
+ el.css({ left: 'auto', right: xVal });
563
+ }
564
+ };
565
+
566
+ // if specified, clone the referring element and position it so that it appears on top of the menu
567
+ if (options.positionOpts.linkToFront) {
568
+ referrer.clone().addClass('linkClone').css({
569
+ position: 'absolute',
570
+ top: 0,
571
+ right: 'auto',
572
+ bottom: 'auto',
573
+ left: 0,
574
+ width: referrer.width(),
575
+ height: referrer.height()
576
+ }).insertAfter(el);
577
+ };
542
578
  };
543
579
 
544
580
 
@@ -547,99 +583,99 @@ Menu.prototype.setPosition = function(widget, caller, options) {
547
583
  function sortBigToSmall(a, b) { return b - a; };
548
584
 
549
585
  jQuery.fn.getTotalWidth = function(){
550
- return $(this).width() + parseInt($(this).css('paddingRight')) + parseInt($(this).css('paddingLeft')) + parseInt($(this).css('borderRightWidth')) + parseInt($(this).css('borderLeftWidth'));
586
+ return $(this).width() + parseInt($(this).css('paddingRight')) + parseInt($(this).css('paddingLeft')) + parseInt($(this).css('borderRightWidth')) + parseInt($(this).css('borderLeftWidth'));
551
587
  };
552
588
 
553
589
  jQuery.fn.getTotalHeight = function(){
554
- return $(this).height() + parseInt($(this).css('paddingTop')) + parseInt($(this).css('paddingBottom')) + parseInt($(this).css('borderTopWidth')) + parseInt($(this).css('borderBottomWidth'));
590
+ return $(this).height() + parseInt($(this).css('paddingTop')) + parseInt($(this).css('paddingBottom')) + parseInt($(this).css('borderTopWidth')) + parseInt($(this).css('borderBottomWidth'));
555
591
  };
556
592
 
557
593
  function getScrollTop(){
558
- return self.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
594
+ return self.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
559
595
  };
560
596
 
561
597
  function getScrollLeft(){
562
- return self.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
598
+ return self.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
563
599
  };
564
600
 
565
601
  function getWindowHeight(){
566
- var de = document.documentElement;
567
- return self.innerHeight || (de && de.clientHeight) || document.body.clientHeight;
602
+ var de = document.documentElement;
603
+ return self.innerHeight || (de && de.clientHeight) || document.body.clientHeight;
568
604
  };
569
605
 
570
606
  function getWindowWidth(){
571
- var de = document.documentElement;
572
- return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
607
+ var de = document.documentElement;
608
+ return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
573
609
  };
574
610
 
575
611
  /* Utilities to test whether an element will fit in the viewport
576
- Parameters:
577
- el = element to position, required
578
- leftOffset / topOffset = optional parameter if the offset cannot be calculated (i.e., if the object is in the DOM but is set to display: 'none') */
579
-
612
+ Parameters:
613
+ el = element to position, required
614
+ leftOffset / topOffset = optional parameter if the offset cannot be calculated (i.e., if the object is in the DOM but is set to display: 'none') */
615
+
580
616
  function fitHorizontal(el, leftOffset){
581
- var leftVal = parseInt(leftOffset) || $(el).offset().left;
582
- return (leftVal + $(el).width() <= getWindowWidth() + getScrollLeft() && leftVal - getScrollLeft() >= 0);
617
+ var leftVal = parseInt(leftOffset) || $(el).offset().left;
618
+ return (leftVal + $(el).width() <= getWindowWidth() + getScrollLeft() && leftVal - getScrollLeft() >= 0);
583
619
  };
584
620
 
585
621
  function fitVertical(el, topOffset){
586
- var topVal = parseInt(topOffset) || $(el).offset().top;
587
- return (topVal + $(el).height() <= getWindowHeight() + getScrollTop() && topVal - getScrollTop() >= 0);
622
+ var topVal = parseInt(topOffset) || $(el).offset().top;
623
+ return (topVal + $(el).height() <= getWindowHeight() + getScrollTop() && topVal - getScrollTop() >= 0);
588
624
  };
589
625
 
590
- /*--------------------------------------------------------------------
626
+ /*--------------------------------------------------------------------
591
627
  * javascript method: "pxToEm"
592
628
  * by:
593
- Scott Jehl (scott@filamentgroup.com)
629
+ Scott Jehl (scott@filamentgroup.com)
594
630
  Maggie Wachs (maggie@filamentgroup.com)
595
631
  http://www.filamentgroup.com
596
632
  *
597
633
  * Copyright (c) 2008 Filament Group
598
634
  * Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
599
635
  *
600
- * Description: Extends the native Number and String objects with pxToEm method. pxToEm converts a pixel value to ems depending on inherited font size.
636
+ * Description: Extends the native Number and String objects with pxToEm method. pxToEm converts a pixel value to ems depending on inherited font size.
601
637
  * Article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/
602
- * Demo: http://www.filamentgroup.com/examples/pxToEm/
603
- *
604
- * Options:
605
- scope: string or jQuery selector for font-size scoping
606
- reverse: Boolean, true reverses the conversion to em-px
607
- * Dependencies: jQuery library
638
+ * Demo: http://www.filamentgroup.com/examples/pxToEm/
639
+ *
640
+ * Options:
641
+ scope: string or jQuery selector for font-size scoping
642
+ reverse: Boolean, true reverses the conversion to em-px
643
+ * Dependencies: jQuery library
608
644
  * Usage Example: myPixelValue.pxToEm(); or myPixelValue.pxToEm({'scope':'#navigation', reverse: true});
609
645
  *
610
- * Version: 2.0, 08.01.2008
646
+ * Version: 2.0, 08.01.2008
611
647
  * Changelog:
612
- * 08.02.2007 initial Version 1.0
613
- * 08.01.2008 - fixed font-size calculation for IE
648
+ * 08.02.2007 initial Version 1.0
649
+ * 08.01.2008 - fixed font-size calculation for IE
614
650
  --------------------------------------------------------------------*/
615
651
 
616
652
  Number.prototype.pxToEm = String.prototype.pxToEm = function(settings){
617
- //set defaults
618
- settings = jQuery.extend({
619
- scope: 'body',
620
- reverse: false
621
- }, settings);
622
-
623
- var pxVal = (this == '') ? 0 : parseFloat(this);
624
- var scopeVal;
625
- var getWindowWidth = function(){
626
- var de = document.documentElement;
627
- return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
628
- };
629
-
630
- /* When a percentage-based font-size is set on the body, IE returns that percent of the window width as the font-size.
631
- For example, if the body font-size is 62.5% and the window width is 1000px, IE will return 625px as the font-size.
632
- When this happens, we calculate the correct body font-size (%) and multiply it by 16 (the standard browser font size)
633
- to get an accurate em value. */
634
-
635
- if (settings.scope == 'body' && $.browser.msie && (parseFloat($('body').css('font-size')) / getWindowWidth()).toFixed(1) > 0.0) {
636
- var calcFontSize = function(){
637
- return (parseFloat($('body').css('font-size'))/getWindowWidth()).toFixed(3) * 16;
638
- };
639
- scopeVal = calcFontSize();
640
- }
641
- else { scopeVal = parseFloat(jQuery(settings.scope).css("font-size")); };
642
-
643
- var result = (settings.reverse == true) ? (pxVal * scopeVal).toFixed(2) + 'px' : (pxVal / scopeVal).toFixed(2) + 'em';
644
- return result;
653
+ //set defaults
654
+ settings = jQuery.extend({
655
+ scope: 'body',
656
+ reverse: false
657
+ }, settings);
658
+
659
+ var pxVal = (this == '') ? 0 : parseFloat(this);
660
+ var scopeVal;
661
+ var getWindowWidth = function(){
662
+ var de = document.documentElement;
663
+ return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
664
+ };
665
+
666
+ /* When a percentage-based font-size is set on the body, IE returns that percent of the window width as the font-size.
667
+ For example, if the body font-size is 62.5% and the window width is 1000px, IE will return 625px as the font-size.
668
+ When this happens, we calculate the correct body font-size (%) and multiply it by 16 (the standard browser font size)
669
+ to get an accurate em value. */
670
+
671
+ if (settings.scope == 'body' && $.browser.msie && (parseFloat($('body').css('font-size')) / getWindowWidth()).toFixed(1) > 0.0) {
672
+ var calcFontSize = function(){
673
+ return (parseFloat($('body').css('font-size'))/getWindowWidth()).toFixed(3) * 16;
674
+ };
675
+ scopeVal = calcFontSize();
676
+ }
677
+ else { scopeVal = parseFloat(jQuery(settings.scope).css("font-size")); };
678
+
679
+ var result = (settings.reverse == true) ? (pxVal * scopeVal).toFixed(2) + 'px' : (pxVal / scopeVal).toFixed(2) + 'em';
680
+ return result;
645
681
  };