hierarchical_menu 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog ADDED
@@ -0,0 +1,5 @@
1
+ 0.0.1
2
+
3
+ * "release earlier, release often"
4
+
5
+
data/README.rdoc ADDED
@@ -0,0 +1,67 @@
1
+ = Hierarchical Menu
2
+
3
+ Hierarchical menu, based on <tt>rubytree gem</tt>. Outputs html (other formats
4
+ planned) with optional JavaScript show/hide.
5
+
6
+ == Synopsis
7
+
8
+ require 'hmenu'
9
+
10
+ root = HMenu::Node.new('ROOT')
11
+
12
+ Simple example:
13
+
14
+ root.add_path('/aaa/bbb/ccc/eee', {
15
+ :name => 'test2',
16
+ :href => '/xxx/yyy/2.html',
17
+ })
18
+
19
+ With HTML list generator:
20
+
21
+ html_ul_simple = root.to_html_ul
22
+
23
+ You can add any extra/custom information:
24
+
25
+ root.add_path('/aaa/bbb/ccc/ddd', {
26
+ :name => 'test1',
27
+ :href => '/xxx/yyy/zzz.html',
28
+ :extra_info => Object.new
29
+ })
30
+
31
+ And then match within a code block to ``mangle'' the output:
32
+
33
+ html_ul = root.to_html_ul do |node, output|
34
+ output[:name] += ' (extra_info is just an Object)' if
35
+ node.content.respond_to? :[] and
36
+ node.content[:extra_info].class == Object
37
+ end
38
+
39
+ Some Style (HMenu::CSS), and some JavaScript code (HMenu::JS) to show/hide
40
+ submenus:
41
+
42
+ puts <<END # you may use a template system like ERB if you wish...
43
+ <head>
44
+ <script type="text/javascript">
45
+ #{HMenu::JS.out}
46
+ </script>
47
+ <style type="text/css">
48
+ #{HMenu::CSS.out}
49
+ #{File.read File.join HMenu::ROOTDIR, 'css/hmenu.more.example.css'}
50
+ </style>
51
+ </head>
52
+ <body onload="reset_menus();">
53
+ #{html_ul}
54
+ </body>
55
+ END
56
+
57
+ == Installation
58
+
59
+ gem install hierarchical_menu
60
+
61
+ == Author
62
+ Copyright 2010 Guido De Rosa <mailto:guido.derosa@vemarsas.it>
63
+
64
+ === License
65
+ Same of {RubyTree}[http://rubytree.rubyforge.org/].
66
+
67
+
data/css/hmenu.css ADDED
@@ -0,0 +1,17 @@
1
+ .hmenu li { list-style: none; /* line-height: 1.5em; padding-left: 0; */ }
2
+ .hmenu ul { /* padding-left: 1em; margin-left: 0; */ }
3
+ .hmenu-item { }
4
+ .hmenu-submenu { }
5
+
6
+ .hmenu-bullet, .hmenu-bullet-nochildren {
7
+ width: 1.3em;
8
+ float: left;
9
+ text-align: center;
10
+ display: inline;
11
+ }
12
+
13
+ .hmenu-content {
14
+ display: inline;
15
+ }
16
+
17
+ .hmenu-bullet {cursor: pointer;}
@@ -0,0 +1,59 @@
1
+ /*
2
+ * Strongly based on OnBoard by Guido De Rosa <guido.derosa@vemarsas.it>
3
+ * http://github.com/gderosa/onboard/blob/6185d2459e75b3796f678a957507d6c42266ba56/views/css/default.css.erb
4
+ */
5
+
6
+ a, .hmenu-bullet {
7
+ color: blue;
8
+ text-decoration: none;
9
+ }
10
+
11
+ a {
12
+ font-weight: bold;
13
+ }
14
+
15
+ a:hover {
16
+ color: white;
17
+ background: blue;
18
+ }
19
+
20
+ .hmenu-bullet:hover {
21
+ color: green;
22
+ }
23
+
24
+ .hmenu-content {
25
+ padding: 0 0.3em;
26
+ }
27
+
28
+ .menu-content a {
29
+ padding-left: 0;
30
+ padding-right: 0;
31
+ margin-left: 0;
32
+ padding-right: 0;
33
+ }
34
+
35
+ li span.hmenu-selected, li div.hmenu-selected {
36
+ font-weight: bold;
37
+ color: green;
38
+ }
39
+
40
+ li.hmenu-selected > .hmenu-bullet,
41
+ li.hmenu-selected > .hmenu-bullet-nochildren {
42
+ color: green;
43
+ }
44
+
45
+ /* border-aligment hack... */
46
+ ul.hmenu li.hmenu-selected, ul.hmenu-submenu li.hmenu-selected {
47
+ border-left: 1px green solid;
48
+ padding-left: 4px; /* 4px + 1px = 5px */
49
+ }
50
+ ul.hmenu li, ul.hmenu-submenu li {
51
+ padding: 0 5px; /* 4px + 1px = 5px */
52
+ }
53
+
54
+ span.hmenu-root, div.hmenu-root {
55
+ font-weight: bold;
56
+ border: none;
57
+ }
58
+
59
+
data/example.rb ADDED
@@ -0,0 +1,43 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/lib'
2
+
3
+ require 'hmenu'
4
+
5
+ root = HMenu::Node.new('ROOT')
6
+
7
+ root.add_path('/aaa/bbb/ccc/ddd', {
8
+ :name => 'test1',
9
+ :href => '/xxx/yyy/zzz.html',
10
+ :extra_info => Object.new
11
+ })
12
+
13
+ root.add_path('/aaa/bbb/ccc/eee', {
14
+ :name => 'test2',
15
+ :href => '/xxx/yyy/2.html',
16
+ })
17
+
18
+ root.add_path('/aaa/fff/ccc/eee', {
19
+ :name => 'test2',
20
+ :href => '/xxx/yyy/2.html',
21
+ })
22
+
23
+ html_ul = root.to_html_ul do |node, output| # code block is optional!
24
+ output[:name] += ' (extra_info is just an Object)' if
25
+ node.content.respond_to? :[] and
26
+ node.content[:extra_info].class == Object
27
+ end
28
+
29
+ puts <<END # you may use a template system like ERB if you wish...
30
+ <head>
31
+ <script type="text/javascript">
32
+ #{HMenu::JS.out}
33
+ </script>
34
+ <style type="text/css">
35
+ #{HMenu::CSS.out}
36
+ #{File.read File.join HMenu::ROOTDIR, 'css/hmenu.more.example.css'}
37
+ </style>
38
+ </head>
39
+ <body onload="reset_menus();">
40
+ #{html_ul}
41
+ </body>
42
+ END
43
+
data/js/hmenu.js ADDED
@@ -0,0 +1,98 @@
1
+ // Copyright 2010 Guido De Rosa
2
+ // License: same of http://RubyTree.rubyforge.org/
3
+
4
+ // stongly based on http://nadeausoftware.com/articles/2007/12/latency_friendly_hierarchical_menus_using_unicode_bullets_and_bit_javascript
5
+
6
+ /*
7
+ ITEM = '\u25E6'; // WHITE BULLET
8
+ OPEN = '\u229F'; // SQUARED MINUS
9
+ CLOSE = '\u229E'; // SQUARED PLUS
10
+
11
+ ITEM = '\u25E6'; // WHITE BULLET
12
+ OPEN = '-'; // MINUS SIGN
13
+ CLOSE = '+'; // PLUS SIGN
14
+ */
15
+
16
+ ITEM = '\u25E6'; // WHITE BULLET
17
+ // ITEM = '\u25CF'; // BLACK CIRCLE
18
+ OPEN = '\u25BC'; // BLACK DOWN-POINTING TRIANGLE
19
+ CLOSE = '\u25BA'; // BLACK RIGHT-POINTING TRIANGLE
20
+
21
+
22
+ function toggle_submenu(e) {
23
+ if (e.innerHTML==OPEN || e.innerHTML==CLOSE) {
24
+ e.innerHTML=(e.innerHTML==OPEN) ? CLOSE : OPEN;
25
+ for (var c, p=e.parentNode, i=0; c=p.childNodes[i]; i++)
26
+ if (c.tagName=='UL') c.style.display=(c.style.display=='none') ? 'block' : 'none';
27
+ }
28
+ }
29
+
30
+ function reset_menus() {
31
+ var li_tags=document.getElementsByTagName('LI');
32
+ for (var li, i=0; li=li_tags[i]; i++) {
33
+ if (li.className.match('hmenu-item')) {
34
+ for (var c, j=0; c=li.childNodes[j]; j++) {
35
+ if (c.className.match('hmenu-bullet')) {
36
+ c.innerHTML=ITEM;
37
+ }
38
+ }
39
+ }
40
+ if (li.className.match('hmenu-submenu')) {
41
+ for (var c, j=0; c=li.childNodes[j]; j++) {
42
+ if (
43
+ c.tagName == 'UL' &&
44
+ any_descendant_className_match(c, 'hmenu-selected')
45
+ ) {
46
+ expand(c);
47
+ } else if (
48
+ c.className.match('hmenu-bullet') &&
49
+ !c.parentNode.className.match('hmenu-selected') &&
50
+ //strict inequality
51
+ any_descendant_className_match(c.parentNode, 'hmenu-selected')
52
+ ) {
53
+ expand(c);
54
+ } else {
55
+ collapse(c);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ function collapse(e) {
63
+ if (e.className.match('hmenu-bullet')) {
64
+ e.innerHTML=CLOSE;
65
+ }
66
+ else if (e.tagName=='UL') {
67
+ e.style.display = 'none';
68
+ }
69
+ }
70
+
71
+ function expand(e) {
72
+ if (e.className.match('hmenu-bullet')) {
73
+ e.innerHTML=OPEN;
74
+ }
75
+ else if (e.tagName=='UL') {
76
+ e.style.display = 'block';
77
+ }
78
+ }
79
+
80
+ function any_descendant_className_match(element, klassName) {
81
+ if (element.className && element.className.match(klassName)) {
82
+ return true;
83
+ }
84
+ else if (element.hasChildNodes) {
85
+ for (var c, i=0; c=element.childNodes[i]; i++) {
86
+ if (any_descendant_className_match(c, klassName)) { // recursion
87
+ return true;
88
+ }
89
+ }
90
+ }
91
+ return false;
92
+ }
93
+
94
+ function debug(s) {
95
+ jsdbg = document.getElementById('jsdebug');
96
+ jsdbg.innerHTML = jsdbg.innerHTML + s;
97
+ }
98
+
@@ -0,0 +1,14 @@
1
+ require 'pathname'
2
+
3
+ module HMenu
4
+
5
+ def self.rootdir_pathname
6
+ Pathname.new(__FILE__).dirname.realpath + '../..'
7
+ end
8
+
9
+
10
+ VERSION = '0.1.0'
11
+ # TODO: manage symlinks...
12
+ ROOTDIR = self.rootdir_pathname.to_s
13
+
14
+ end
data/lib/hmenu/css.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'hmenu/constants'
2
+
3
+ module HMenu
4
+ module CSS
5
+ def self.out
6
+ File.read "#{::HMenu::ROOTDIR}/css/hmenu.css"
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,76 @@
1
+ # Copyright 2009, Guido De Rosa <job at guidoderosa . net>
2
+ # Distributed under the same terms of 'rubytree' --
3
+ # http://rubytre.rubyforge.org/
4
+
5
+ require 'tree'
6
+
7
+ module Tree
8
+ class TreeNode
9
+ def add_recursive(path, content)
10
+ #
11
+ # A leading slash in the path means that it is absolute (i.e. refers
12
+ # to the tree root)
13
+ #
14
+ # path may be an Array (instead of String), but this is intended for
15
+ # "private" use only (i.e. recursive calls)
16
+ #
17
+ if path.respond_to? :split
18
+ path = path.split('/')
19
+ end
20
+ if path[0] == "" # as a result of a leading slash in path string
21
+ path.shift
22
+ return self.root.add_recursive(path, content)
23
+ end
24
+ if path.length == 1
25
+ begin
26
+ node = self.class.new(path[0], content) # it may be a derived class
27
+ add node
28
+ return node
29
+ rescue RuntimeError # node already exists
30
+ self[path[0]].content = content
31
+ end
32
+ else
33
+ begin
34
+ add self.class.new(path[0], nil) # it may be a derived class
35
+ rescue RuntimeError # node already exists
36
+ # do nothing
37
+ end
38
+ first = path.shift
39
+ self[first].add_recursive(path, content)
40
+ end
41
+ end
42
+ alias add_path add_recursive
43
+ end
44
+ end
45
+
46
+ # example/test :
47
+
48
+ # You should get:
49
+ # -> like ("awesome content")
50
+ # /
51
+ # ROOT -> path -> to -> anything -> I -<
52
+ # \ \
53
+ # \ -> like2 ("awesome content2")
54
+ # \ \
55
+ # -> absolute -> path ("abs_path_content") \
56
+ # \
57
+ # sub
58
+ # |
59
+ # path
60
+ # ("sub_path_content")
61
+ if $0 == __FILE__
62
+ # TODO: use Ruby test frameworks
63
+ require 'pp'
64
+ root = Tree::TreeNode.new("ROOT", "my content")
65
+ node1 = root.add_recursive("path/to/anything/I/like", "awesome content")
66
+ node2 = root.add_recursive("/path/to/anything/I/like2", "awesome content2")
67
+ pp root
68
+ puts
69
+ pp root['path']['to']['anything']['I']['like'].content
70
+ pp root['path']['to']['anything']['I']['like2'].content
71
+
72
+ node2.add_recursive("sub/path", "sub_path_content")
73
+ node2.add_recursive("/absolute/path", "abs_path_content")
74
+ pp root['path']['to']['anything']['I']['like2']['sub']['path'].content
75
+ pp root['absolute']['path'].content
76
+ end
data/lib/hmenu/js.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'hmenu/constants'
2
+
3
+ module HMenu
4
+ module JS
5
+ def self.out
6
+ File.read "#{::HMenu::ROOTDIR}/js/hmenu.js"
7
+ end
8
+ end
9
+ end
10
+
data/lib/hmenu/node.rb ADDED
@@ -0,0 +1,98 @@
1
+ # Thanks to: http://nadeausoftware.com/articles/2007/12/latency_friendly_hierarchical_menus_using_unicode_bullets_and_bit_javascript
2
+
3
+ require 'tree'
4
+ require 'hmenu/extensions/tree'
5
+
6
+ module HMenu
7
+ class Node < Tree::TreeNode
8
+
9
+ def to_html_ul(&block)
10
+ ctag = 'div'
11
+ btag = 'div'
12
+
13
+ o = content ? content.clone : nil
14
+ if block.respond_to? :call
15
+ block.call(self, o)
16
+ end
17
+
18
+ s = ""
19
+
20
+ hmenu_content_class = "hmenu-content"
21
+
22
+ hmenu_content_class << " hmenu-root" if isRoot?
23
+
24
+ if o
25
+ hmenu_content_class << ' ' << o[:extra_class] if o[:extra_class]
26
+ s << "<#{ctag} class=\"#{hmenu_content_class}\" title=\"#{(o[:desc] || '')}\">"
27
+ if o[:href]
28
+ s <<
29
+ "<a href=\"#{o[:href]}\">#{o[:name]}</a>"
30
+ elsif o[:name]
31
+ s << (o[:name] || '')
32
+ end
33
+ s << "</#{ctag}>"
34
+ else
35
+ s << "<#{ctag} class=\"#{hmenu_content_class}\">" << name.capitalize << "</#{ctag}>"
36
+ end
37
+
38
+ if hasChildren?
39
+
40
+ if isRoot?
41
+ s += '<ul class="hmenu">'
42
+ else
43
+ s += "<ul>"
44
+ end
45
+
46
+ children.sort.each do |child|
47
+
48
+ o = child.content ? child.content.clone : nil
49
+ if block.respond_to? :call
50
+ block.call(child, o)
51
+ end
52
+
53
+ css_li_class =
54
+ child.hasChildren? ?
55
+ 'hmenu-submenu' :
56
+ 'hmenu-item'
57
+ css_li_class << ' ' << o[:extra_class] if o and o[:extra_class]
58
+ css_bullet_class =
59
+ child.hasChildren? ?
60
+ 'hmenu-bullet' :
61
+ 'hmenu-bullet-nochildren'
62
+
63
+ s += "<li class=\"#{css_li_class}\">" << "<#{btag} class=\"#{css_bullet_class}\" onclick=\"toggle_submenu(this);\"></#{btag}>" << child.to_html_ul(&block) << "</li>"
64
+
65
+ end
66
+
67
+ s += "</ul>"
68
+
69
+ end
70
+
71
+ return s
72
+ end
73
+
74
+ def <=>(other) # for sorting
75
+ n <=> other.n
76
+ end
77
+
78
+ def n
79
+ begin
80
+ content[:n] ? content[:n] : 0
81
+ rescue
82
+ 0
83
+ end
84
+ end
85
+
86
+ def mangle_output(o, &block)
87
+ unless o
88
+ if block.respond_to? :call
89
+ o = {}
90
+ block.call(self, o)
91
+ else
92
+ o = content.dup
93
+ end
94
+ end
95
+ end
96
+
97
+ end
98
+ end
data/lib/hmenu.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'hmenu/constants'
2
+
3
+ module HMenu
4
+ autoload :CSS, 'hmenu/css'
5
+ autoload :JS, 'hmenu/js'
6
+ autoload :Node, 'hmenu/node'
7
+ end
8
+
9
+
10
+
11
+
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hierarchical_menu
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Guido De Rosa
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-27 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rubytree
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: Ruby Tree based hierarchical menus with HTML output and JavaScript show/hide
34
+ email: guido.derosa@vemarsas.it
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - README.rdoc
41
+ files:
42
+ - README.rdoc
43
+ - example.rb
44
+ - Changelog
45
+ - css/hmenu.css
46
+ - css/hmenu.more.example.css
47
+ - js/hmenu.js
48
+ - lib/hmenu.rb
49
+ - lib/hmenu/extensions/tree.rb
50
+ - lib/hmenu/constants.rb
51
+ - lib/hmenu/css.rb
52
+ - lib/hmenu/js.rb
53
+ - lib/hmenu/node.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/gderosa/hierarchical_menu
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --main
61
+ - README.rdoc
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.7
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Hierarchical menus with html output
87
+ test_files: []
88
+