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 +42 -14
- data/bin/parade +1 -19
- data/lib/parade.rb +2 -2
- data/lib/parade/parsers/dsl.rb +0 -1
- data/lib/parade/renderers/columns_renderer.rb +1 -3
- data/lib/parade/renderers/command_line_renderer.rb +18 -2
- data/lib/parade/section.rb +8 -0
- data/lib/parade/server.rb +6 -2
- data/lib/parade/slide.rb +7 -13
- data/lib/parade/slide_post_renderers.rb +24 -0
- data/lib/parade/slide_pre_renderers.rb +19 -0
- data/lib/parade/version.rb +1 -1
- data/lib/public/css/default.css +3 -0
- data/lib/public/css/parade.css +16 -13
- data/lib/public/js/fg.menu.js +607 -571
- data/lib/public/js/keyboard.js +39 -33
- data/lib/public/js/parade-command-input.js +14 -2
- data/lib/public/js/parade-command-visor.js +15 -18
- data/lib/public/js/parade-keyboard-input.js +69 -15
- data/lib/public/js/parade.js +137 -84
- data/lib/templates/{showoff.erb → parade.erb} +0 -0
- data/lib/views/header.erb +2 -0
- data/lib/views/slide.erb +6 -2
- metadata +5 -2
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
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
>
|
326
|
-
|
327
|
-
|
328
|
-
|
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").
|
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
|
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").
|
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
|
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
|
413
|
+
<div class="content">
|
386
414
|
<h1>jQuery & 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)
|
data/lib/parade.rb
CHANGED
data/lib/parade/parsers/dsl.rb
CHANGED
@@ -35,7 +35,7 @@ module Parade
|
|
35
35
|
#
|
36
36
|
def self.render(html_content)
|
37
37
|
|
38
|
-
html = Nokogiri::
|
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
|
-
|
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
|
data/lib/parade/section.rb
CHANGED
@@ -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
|
|
data/lib/parade/server.rb
CHANGED
@@ -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
|
data/lib/parade/slide.rb
CHANGED
@@ -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
|
28
|
-
|
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
|
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
|
-
|
93
|
+
SlidePreRenderers.renderers
|
98
94
|
end
|
99
95
|
|
100
96
|
def post_renderers
|
101
|
-
|
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
|
data/lib/parade/version.rb
CHANGED
data/lib/public/css/parade.css
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
@media screen {
|
2
2
|
body {
|
3
|
-
|
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
|
-
|
107
|
+
font-size: 2em;
|
108
108
|
margin: 1em;
|
109
|
-
|
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
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
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
|
-
|
196
|
-
|
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
|
-
|
249
|
+
padding: .25em;
|
247
250
|
}
|
248
251
|
|
249
252
|
.small {
|
data/lib/public/js/fg.menu.js
CHANGED
@@ -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
|
-
|
7
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
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
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
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
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
Menu.prototype.setPosition = function(widget, caller, options) {
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
594
|
+
return self.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
|
559
595
|
};
|
560
596
|
|
561
597
|
function getScrollLeft(){
|
562
|
-
|
598
|
+
return self.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
|
563
599
|
};
|
564
600
|
|
565
601
|
function getWindowHeight(){
|
566
|
-
|
567
|
-
|
602
|
+
var de = document.documentElement;
|
603
|
+
return self.innerHeight || (de && de.clientHeight) || document.body.clientHeight;
|
568
604
|
};
|
569
605
|
|
570
606
|
function getWindowWidth(){
|
571
|
-
|
572
|
-
|
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
|
-
|
577
|
-
|
578
|
-
|
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
|
-
|
582
|
-
|
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
|
-
|
587
|
-
|
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
|
-
|
606
|
-
|
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
|
-
*
|
613
|
-
*
|
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
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
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
|
};
|