parlement 0.5 → 0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/CHANGES +26 -0
  2. data/README +2 -1
  3. data/app/controllers/account_controller.rb +27 -18
  4. data/app/controllers/application.rb +1 -3
  5. data/app/controllers/elt_controller.rb +92 -42
  6. data/app/helpers/elt_helper.rb +7 -18
  7. data/app/models/elt.rb +76 -22
  8. data/app/models/mail.rb +13 -10
  9. data/app/models/mail_notify.rb +11 -27
  10. data/app/models/person.rb +1 -1
  11. data/app/views/account/_login.rhtml +14 -9
  12. data/app/views/account/_show.rhtml +23 -9
  13. data/app/views/elt/_choice.rhtml +32 -0
  14. data/app/views/elt/_elt.rhtml +90 -78
  15. data/app/views/elt/_list.rhtml +83 -50
  16. data/app/views/elt/_listByDate.rhtml +60 -0
  17. data/app/views/elt/_listByVote.rhtml +71 -0
  18. data/app/views/elt/choices.rhtml +29 -0
  19. data/app/views/elt/new.rhtml +11 -26
  20. data/app/views/elt/rss.rxml +14 -12
  21. data/app/views/elt/show.rhtml +23 -29
  22. data/app/views/elt/vote_rss.rxml +40 -0
  23. data/app/views/layouts/top.rhtml +5 -13
  24. data/config/environment.rb +3 -1
  25. data/config/routes.rb +4 -0
  26. data/db/development_structure.sql +5 -2
  27. data/db/migrate/002_nested_set.rb +22 -0
  28. data/db/migrate/003_elt_children_count.rb +12 -0
  29. data/public/images/ParlementLogo.png +0 -0
  30. data/public/images/indicator.gif +0 -0
  31. data/public/javascripts/behaviour.js +254 -0
  32. data/public/javascripts/mybehaviour.js +96 -0
  33. data/public/javascripts/slider.js +188 -163
  34. data/test/fixtures/elts.yml +19 -0
  35. data/test/fixtures/mail/mail_ruby +4 -0
  36. data/test/fixtures/mail/mail_rubyChild +1 -1
  37. data/test/fixtures/mail/mail_rubyChild2 +1 -1
  38. data/test/functional/account_controller_test.rb +24 -0
  39. data/test/functional/elt_controller_test.rb +64 -4
  40. data/test/unit/elt_test.rb +28 -4
  41. data/test/unit/mail_notify_test.rb +7 -5
  42. data/test/unit/mail_test.rb +1 -1
  43. data/vendor/plugins/google_analytics/README +19 -0
  44. data/vendor/plugins/google_analytics/Rakefile +22 -0
  45. data/vendor/plugins/google_analytics/init.rb +3 -0
  46. data/vendor/plugins/google_analytics/lib/rubaidh/google_analytics.rb +70 -0
  47. data/vendor/plugins/google_analytics/test/google_analytics_test.rb +8 -0
  48. data/vendor/plugins/output_compression/README +4 -0
  49. data/vendor/plugins/output_compression/Rakefile +22 -0
  50. data/vendor/plugins/output_compression/init.rb +2 -0
  51. data/vendor/plugins/output_compression/lib/output_compression.rb +66 -0
  52. data/vendor/plugins/output_compression/tasks/output_compression_tasks.rake +4 -0
  53. data/vendor/plugins/output_compression/test/output_compression_test.rb +8 -0
  54. metadata +32 -4
  55. data/public/images/smile.svg +0 -257
  56. data/public/javascripts/borders.js +0 -687
@@ -4,24 +4,15 @@
4
4
 
5
5
  <%= error_messages_for 'elt' %>
6
6
 
7
- <!--
8
- <span id="author_<%= @elt.parent_id %>">
9
- <%= render :partial => 'account/show',
10
- :locals => { :divId => 'author_'+@elt.parent_id } %>
11
- </span>
12
- -->
13
-
14
7
  <%= form_remote_tag(
15
8
  :update => 'eltSubs_'+@elt.parent_id,
16
- :url => { :action => 'create' },
9
+ :url => { :action => 'create', :id => @elt.parent },
17
10
  :position => 'bottom',
18
- :loading => visual_effect(:BlindUp, 'eltNew_'+@elt.parent.id.to_s),
19
- :complete => "Element.show('eltSubs_#{@elt.parent_id.to_s}');
20
- Element.show('eltSubsClose_#{@elt.parent_id.to_s}');",
21
- :loaded => visual_effect(:BlindDown, 'elt_'+@elt.id.to_s)) %>
11
+ :before => visual_effect(:BlindUp, 'eltNew_'+@elt.parent_id),
12
+ :loading => "Element.show('eltSubs_#{@elt.parent_id}');
13
+ Element.show('eltSubsClose_#{@elt.parent_id}')") %>
22
14
 
23
- <!--[form:elt]-->
24
- <%= hidden_field 'elt', 'parent_id' %></p>
15
+ <%= hidden_field 'elt', 'parent_id' %>
25
16
 
26
17
  <h2>
27
18
  <%= text_field 'elt', 'subject' %>
@@ -35,18 +26,14 @@
35
26
  ' } %>
36
27
  </h2>
37
28
 
38
- <div style="display:none">
39
- <label for="elt_position">Position:</label>
40
- <%= text_field 'elt', 'position', 'size' => 1, 'align' => 'right' %>
41
- </div>
42
-
43
29
  <%= text_area 'elt', 'body' %>
44
30
 
45
31
  <div class="eltNewButtons">
46
32
  <%= submit_to_remote('Preview', 'Preview',
47
- :update => 'eltNewPreview_'+@elt.parent_id,
48
- :url => { :action => 'create', :submit => :preview },
49
- :loaded => visual_effect(:BlindDown, 'eltNewPreview_'+@elt.parent_id)) %>
33
+ :update => "eltNewPreview_"+@elt.parent_id,
34
+ :url => { :action => "create", :id => @elt.parent, :submit => "preview" },
35
+ :loading => visual_effect(:BlindDown, 'eltNewPreview_'+@elt.parent_id)) %>
36
+
50
37
  <%= submit_tag 'Propose!' %>
51
38
 
52
39
  <%= render :partial => '/help',
@@ -94,9 +81,7 @@
94
81
  <a href="http://hobix.com/textile/quick.html">Textile Reference</a>
95
82
  ' } %>
96
83
  </div>
97
-
98
- <span class="eltNew" id="eltNewPreview_<%= @elt.parent_id %>"/>
99
-
100
- <!--[eoform:elt]-->
101
84
  <%= end_form_tag %>
102
85
 
86
+ <span class="elt" id="eltNewPreview_<%= @elt.parent_id %>"/>
87
+
@@ -1,16 +1,17 @@
1
1
  xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
2
2
  xml.channel do
3
- xml.title(@elt.subject)
4
- xml.link(url_for(:only_path => false, :action => nil, :id => @elt.id))
5
- xml.description @elt.subject+" (parlement)"
6
- xml.language "en"
7
- xml.language "fr"
3
+ xml.title @elt.subject
4
+ xml.link url_for(:only_path => false, :action => 'show', :id => @elt)
5
+ xml.description @elt.subject
8
6
 
9
- for item in @elt.children.reverse
7
+ Elt.find(:all,
8
+ :conditions => "lft > #{@elt.lft} AND rgt < #{@elt.rgt}",
9
+ :order => 'created_on DESC',
10
+ :limit => PAGE_ELT_LENGTH).each do |item|
10
11
  xml.item do
11
- xml.title(item.subject)
12
+ xml.title item.subject
12
13
  #xml.category(@elt.subject)
13
- xml.pubDate(item.created_on)
14
+ xml.pubDate item.created_on
14
15
 
15
16
  if url_for(:only_path => false).match(/http:\/\/echarp.dyndns.org/) and @elt.id == 'blog'
16
17
  xml.link(url_for(:only_path => false, :controller => 'perso',
@@ -18,14 +19,15 @@ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
18
19
  xml.comments(url_for(:only_path => false, :controller => 'perso',
19
20
  :action => 'blog', :id => item.id+'.html'))
20
21
  else
21
- xml.link(url_for(:only_path => false, :action => 'show', :id => item.id))
22
- xml.comments(url_for(:only_path => false, :action => 'show', :id => item.id))
22
+ xml.link(url_for(:only_path => false, :action => 'show', :id => item))
23
+ xml.comments(url_for(:only_path => false, :action => 'show', :id => item))
23
24
  end
24
25
 
25
- xml.tag!("dc:creator", item.person.name) if item.person != nil
26
+ xml.tag!("dc:creator", item.person.name) if item.person
26
27
 
27
- xml.description("<p>"+format(item.body)+"</p>")
28
+ xml.description "<p>"+format(item.body)+"</p>"
28
29
  end
29
30
  end
30
31
  end
31
32
  end
33
+
@@ -2,7 +2,7 @@
2
2
  <h3 class="boxTitle">Electoral list</h3>
3
3
  <div class="box">
4
4
  All voters:
5
- <span class="result" style="float: none">
5
+ <span class="result selected" style="float: none">
6
6
  <%= Choice.count :select => 'person_id', :distinct => true %>
7
7
  </span>
8
8
  </div>
@@ -10,36 +10,27 @@
10
10
  <h3 class="boxTitle">Identity</h3>
11
11
  <div class="box">
12
12
  <div id="author_<%= @elt.id %>">
13
- <%= render :partial => 'account/show',
14
- :locals => { :divId => 'author_'+@elt.id } %>
13
+ <%= render :partial => 'account/show', :locals => { :divId => 'author_'+@elt.id } %>
15
14
  </div>
16
15
  </div>
17
16
 
18
- <% if @elt.children.count > 0 %>
19
- <h3 class="boxTitle">Titres</h3>
20
- <% end %>
21
-
22
- <div class="box">
23
- <ul>
24
- <% for elt in @elt.children.reverse[0...PAGE_ELT_LENGTH] %>
25
- <li class="boxLine" title="<%= elt.created_on.strftime('%d/%m') %>">
26
- <span class="result"><%= sprintf("%+d", elt.result) %></span>
27
- <%= link_to elt.subject, :id => elt %>
28
- </li>
29
- <% end %>
30
- </ul>
17
+ <% if @elt.elts_count > 0 %><h3 class="boxTitle">Highest posts</h3><% end %>
18
+ <div class="box listByVote">
19
+ <%= render :partial => '/elt/listByVote' %>
20
+ </div>
31
21
 
32
- <%= link_to image_tag("/images/webfeed.gif"),
33
- :action => 'rss', :id => @elt %>
22
+ <% if @elt.elts_count > 0 %><h3 class="boxTitle">Latest posts</h3><% end %>
23
+ <div class="box listByDate">
24
+ <%= render :partial => '/elt/listByDate' %>
34
25
  </div>
35
26
 
36
27
  <h3 class="boxTitle">Subscribe</h3>
37
28
  <div class="box">
38
29
  <div id="subscription">
39
30
  <%= link_to_remote(((@session[:person] and @elt.subscribers.include?(@session[:person])) ? 'Unsubscribe!' : 'Subscribe!'),
40
- :update => 'subscription',
41
- :url => { :controller => 'subscriber', :action => 'subscribe', :id => @elt },
42
- :loaded => visual_effect(:BlindDown, 'subscription')) %>
31
+ :update => 'subscription',
32
+ :url => { :controller => 'subscriber', :action => 'subscribe', :id => @elt },
33
+ :loaded => visual_effect(:BlindDown, 'subscription')) %>
43
34
  </div>
44
35
 
45
36
  <div class="boxLine">
@@ -47,7 +38,12 @@
47
38
  <%= link_to(i.name, :controller => 'person', :action => 'show', :id => i) %>
48
39
  <% end %>
49
40
  </div>
50
- <div class="boxLineR"><%= @elt.subscribers.size %> subscriber(s)</div>
41
+
42
+ <div class="boxLineR">
43
+ <% @elt.parent.all_recipients.each do |i| %>
44
+ <%= link_to(i.name, :controller => 'person', :action => 'show', :id => i) %>
45
+ <% end %>
46
+ </div>
51
47
  </div>
52
48
  </div>
53
49
 
@@ -55,14 +51,12 @@
55
51
  <div class="<%= key %>"><%= value %></div>
56
52
  <% end %>
57
53
 
58
- <% if @elt.parent and @elt.parent.subject != '' %>
59
- <h3 class="parent">
60
- <%= link_to(textilize_without_paragraph(@elt.parent.subject),
61
- :id => @elt.parent) %>
62
- </h3>
63
- <% end %>
54
+ <h3 class="parent">
55
+ <%= link_to textilize_without_paragraph(@elt.parent.subject), :id => @elt.parent \
56
+ if @elt.parent %>
57
+ </h3>
64
58
 
65
- <ul>
59
+ <ul class="top">
66
60
  <%= render :partial => 'elt', :layout => 'top',
67
61
  :locals => { :elt => @elt, :eltTop => true } %>
68
62
  </ul>
@@ -0,0 +1,40 @@
1
+ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
2
+ xml.channel do
3
+ xml.title @elt.subject
4
+ xml.link url_for(:only_path => false, :action => 'show', :id => @elt)
5
+ xml.description @elt.subject
6
+
7
+ Elt.find(:all,
8
+ :select => 'SUM(value), elts.id, parent_id, elts.created_on, parent_id,' \
9
+ +'elts_count, position, elts.person_id, subject, body, lft, rgt',
10
+ :conditions => "lft > #{@elt.lft} AND rgt < #{@elt.rgt}",
11
+ :order => 'SUM(value) IS NULL, SUM(value) DESC, created_on DESC',
12
+ :group => 'elts.id, parent_id, elts.created_on, parent_id, elts_count,' \
13
+ +'position, elts.person_id, subject, body, lft, rgt',
14
+ :limit => PAGE_ELT_LENGTH,
15
+ :joins => "LEFT JOIN choices ON elt_id = elts.id").each do |item|
16
+
17
+ xml.item do
18
+ xml.title item.subject
19
+ #xml.category(@elt.subject)
20
+ xml.vote item.result
21
+ xml.pubDate item.created_on
22
+
23
+ if url_for(:only_path => false).match(/http:\/\/echarp.dyndns.org/) and @elt.id == 'blog'
24
+ xml.link(url_for(:only_path => false, :controller => 'perso',
25
+ :action => 'blog', :id => item.id+'.html'))
26
+ xml.comments(url_for(:only_path => false, :controller => 'perso',
27
+ :action => 'blog', :id => item.id+'.html'))
28
+ else
29
+ xml.link(url_for(:only_path => false, :action => 'show', :id => item))
30
+ xml.comments(url_for(:only_path => false, :action => 'show', :id => item))
31
+ end
32
+
33
+ xml.tag!("dc:creator", item.person.name) if item.person
34
+
35
+ xml.description "<p>"+format(item.body)+"</p>"
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -7,9 +7,9 @@
7
7
  <%= stylesheet_link_tag 'default.css' %>
8
8
  <%= javascript_include_tag "/javascripts/prototype" %>
9
9
  <%= javascript_include_tag "/javascripts/scriptaculous" %>
10
- <!--
11
- <%= javascript_include_tag "/javascripts/borders" %>
12
- -->
10
+
11
+ <%= javascript_include_tag "/javascripts/behaviour" %>
12
+ <%= javascript_include_tag "/javascripts/mybehaviour" %>
13
13
 
14
14
  <!-- compliance patch for microsoft browsers -->
15
15
  <!--[if lt IE 7]>
@@ -17,7 +17,6 @@
17
17
  <%= javascript_include_tag "/javascripts/ie7.js" %>
18
18
  <![endif]-->
19
19
 
20
-
21
20
  <link rel="shortcut icon" href="/images/world.png" type="image/png"/>
22
21
 
23
22
  <meta name="ROBOTS" content="ALL"/>
@@ -34,10 +33,10 @@
34
33
  <body>
35
34
  <div class="links">
36
35
  <a class="title" href="http://leparlement.org">Parlement</a>
37
- <div class="signets">
36
+ <span class="signets">
38
37
  <a href="http://rubyforge.org/projects/parlement">forge</a>
39
38
  <a href="http://www.gnu.org/licenses/gpl.html">GPL</a>
40
- </div>
39
+ </span>
41
40
  </div>
42
41
 
43
42
  <%= @content_for_layout %>
@@ -52,13 +51,6 @@
52
51
  <img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0 Strict"/>
53
52
  </a>
54
53
  </p>
55
-
56
- <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
57
- </script>
58
- <script type="text/javascript">
59
- _uacct = "UA-317241-1";
60
- urchinTracker();
61
- </script>
62
54
  </body>
63
55
  </html>
64
56
 
@@ -64,7 +64,7 @@ require 'rails_file_column'
64
64
  require 'jcode'
65
65
  $KCODE = 'u'
66
66
 
67
- PARLEMENT_VERSION='0.5'
67
+ PARLEMENT_VERSION='0.6'
68
68
 
69
69
  # How many elements are displayed at once
70
70
  PAGE_ELT_LENGTH = 10
@@ -101,3 +101,5 @@ module LoginEngine
101
101
  end
102
102
  Engines.start :login
103
103
 
104
+ Rubaidh::GoogleAnalytics.tracker_id = 'UA-317241-1'
105
+
data/config/routes.rb CHANGED
@@ -12,16 +12,20 @@ ActionController::Routing::Routes.draw do |map|
12
12
 
13
13
  map.connect '', :controller => 'elt', :action => 'show', :id => 'parlement'
14
14
  map.connect 'index.rss', :controller => 'elt', :action => 'rss', :id => 'parlement'
15
+ map.connect 'vote.rss', :controller => 'elt', :action => 'vote_rss', :id => 'parlement'
15
16
 
16
17
  map.connect ':id', :controller => 'elt', :action => 'rss', :requirements => { :id => /.*\.rss/ }
17
18
  map.connect ':id', :controller => 'elt', :action => 'show'
18
19
  map.connect ':id/index.rss', :controller => 'elt', :action => 'rss'
20
+ map.connect ':id/vote.rss', :controller => 'elt', :action => 'vote_rss'
19
21
  map.connect ':id//index.rss', :controller => 'elt', :action => 'rss'
22
+ map.connect ':id//vote.rss', :controller => 'elt', :action => 'vote_rss'
20
23
 
21
24
  map.connect 'perso/:id', :controller => 'elt', :action => 'show'
22
25
  map.connect 'perso/:id/index.rss', :controller => 'elt', :action => 'rss'
23
26
  map.connect 'perso/blog/:id', :controller => 'elt'
24
27
  map.connect 'perso/blog/:id/index.rss', :controller => 'elt', :action => 'rss'
28
+ map.connect 'perso/blog/:id/vote.rss', :controller => 'elt', :action => 'vote_rss'
25
29
 
26
30
  # Allow downloading Web Service WSDL as a file with an extension
27
31
  # instead of a file named 'wsdl'
@@ -70,7 +70,10 @@ CREATE TABLE elts (
70
70
  created_on timestamp with time zone DEFAULT now() NOT NULL,
71
71
  person_id text,
72
72
  subject text,
73
- body text
73
+ body text,
74
+ lft integer,
75
+ rgt integer,
76
+ elts_count integer DEFAULT 0
74
77
  );
75
78
 
76
79
 
@@ -357,4 +360,4 @@ ALTER TABLE ONLY usersold
357
360
  -- PostgreSQL database dump complete
358
361
  --
359
362
 
360
- INSERT INTO schema_info (version) VALUES (1)
363
+ INSERT INTO schema_info (version) VALUES (3)
@@ -0,0 +1,22 @@
1
+ class NestedSet < ActiveRecord::Migration
2
+
3
+ def NestedSet.nested_set_construction(e)
4
+ e.children.each { |c|
5
+ e.add_child c
6
+ nested_set_construction c
7
+ }
8
+ end
9
+
10
+ def self.up
11
+ add_column :elts, :lft, :integer
12
+ add_column :elts, :rgt, :integer
13
+
14
+ e = Elt.find 'ROOT'
15
+ NestedSet.nested_set_construction e
16
+ end
17
+
18
+ def self.down
19
+ remove_column :elts, :lft
20
+ remove_column :elts, :rgt
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ class EltChildrenCount < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :elts, :elts_count, :integer, :default => 0
4
+ execute 'UPDATE elts SET elts_count = (SELECT COUNT(*) FROM elts e2 WHERE e2.parent_id = elts.id)'
5
+ add_index "elts", ["lft"], :name => "elts_lft_key", :unique => true
6
+ add_index "elts", ["parent_id"], :name => "elts_parent_key"
7
+ end
8
+
9
+ def self.down
10
+ remove_column :elts, :elts_count
11
+ end
12
+ end
Binary file
Binary file
@@ -0,0 +1,254 @@
1
+ /*
2
+ Behaviour v1.1 by Ben Nolan, June 2005. Based largely on the work
3
+ of Simon Willison (see comments by Simon below).
4
+
5
+ Description:
6
+
7
+ Uses css selectors to apply javascript behaviours to enable
8
+ unobtrusive javascript in html documents.
9
+
10
+ Usage:
11
+
12
+ var myrules = {
13
+ 'b.someclass' : function(element){
14
+ element.onclick = function(){
15
+ alert(this.innerHTML);
16
+ }
17
+ },
18
+ '#someid u' : function(element){
19
+ element.onmouseover = function(){
20
+ this.innerHTML = "BLAH!";
21
+ }
22
+ }
23
+ };
24
+
25
+ Behaviour.register(myrules);
26
+
27
+ // Call Behaviour.apply() to re-apply the rules (if you
28
+ // update the dom, etc).
29
+
30
+ License:
31
+
32
+ This file is entirely BSD licensed.
33
+
34
+ More information:
35
+
36
+ http:/ipcord.co.nz/behaviour/
37
+
38
+ */
39
+
40
+ var Behaviour = {
41
+ list : new Array,
42
+
43
+ register : function(sheet){
44
+ Behaviour.list.push(sheet);
45
+ },
46
+
47
+ start : function(){
48
+ Behaviour.addLoadEvent(function(){
49
+ Behaviour.apply();
50
+ });
51
+ },
52
+
53
+ apply : function(){
54
+ for (h=0;sheet=Behaviour.list[h];h++){
55
+ for (selector in sheet){
56
+ list = document.getElementsBySelector(selector);
57
+
58
+ if (!list){
59
+ continue;
60
+ }
61
+
62
+ for (i=0;element=list[i];i++){
63
+ sheet[selector](element);
64
+ }
65
+ }
66
+ }
67
+ },
68
+
69
+ addLoadEvent : function(func){
70
+ var oldonload = window.onload;
71
+
72
+ if (typeof window.onload != 'function') {
73
+ window.onload = func;
74
+ } else {
75
+ window.onload = function() {
76
+ oldonload();
77
+ func();
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ Behaviour.start();
84
+
85
+ /*
86
+ The following code is Copyright (C) Simon Willison 2004.
87
+
88
+ document.getElementsBySelector(selector)
89
+ - returns an array of element objects from the current document
90
+ matching the CSS selector. Selectors can contain element names,
91
+ class names and ids and can be nested. For example:
92
+
93
+ elements = document.getElementsBySelect('div#main p a.external')
94
+
95
+ Will return an array of all 'a' elements with 'external' in their
96
+ class attribute that are contained inside 'p' elements that are
97
+ contained inside the 'div' element which has id="main"
98
+
99
+ New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
100
+ See http://www.w3.org/TR/css3-selectors/#attribute-selectors
101
+
102
+ Version 0.4 - Simon Willison, March 25th 2003
103
+ -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
104
+ -- Opera 7 fails
105
+ */
106
+
107
+ function getAllChildren(e) {
108
+ // Returns all children of element. Workaround required for IE5/Windows. Ugh.
109
+ return e.all ? e.all : e.getElementsByTagName('*');
110
+ }
111
+
112
+ document.getElementsBySelector = function(selector) {
113
+ // Attempt to fail gracefully in lesser browsers
114
+ if (!document.getElementsByTagName) {
115
+ return new Array();
116
+ }
117
+ // Split selector in to tokens
118
+ var tokens = selector.split(' ');
119
+ var currentContext = new Array(document);
120
+ for (var i = 0; i < tokens.length; i++) {
121
+ token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
122
+ if (token.indexOf('#') > -1) {
123
+ // Token is an ID selector
124
+ var bits = token.split('#');
125
+ var tagName = bits[0];
126
+ var id = bits[1];
127
+ var element = document.getElementById(id);
128
+ if (tagName && element.nodeName.toLowerCase() != tagName) {
129
+ // tag with that ID not found, return false
130
+ return new Array();
131
+ }
132
+ // Set currentContext to contain just this element
133
+ currentContext = new Array(element);
134
+ continue; // Skip to next token
135
+ }
136
+ if (token.indexOf('.') > -1) {
137
+ // Token contains a class selector
138
+ var bits = token.split('.');
139
+ var tagName = bits[0];
140
+ var className = bits[1];
141
+ if (!tagName) {
142
+ tagName = '*';
143
+ }
144
+ // Get elements matching tag, filter them for class selector
145
+ var found = new Array;
146
+ var foundCount = 0;
147
+ for (var h = 0; h < currentContext.length; h++) {
148
+ var elements;
149
+ if (tagName == '*') {
150
+ elements = getAllChildren(currentContext[h]);
151
+ } else {
152
+ elements = currentContext[h].getElementsByTagName(tagName);
153
+ }
154
+ for (var j = 0; j < elements.length; j++) {
155
+ found[foundCount++] = elements[j];
156
+ }
157
+ }
158
+ currentContext = new Array;
159
+ var currentContextIndex = 0;
160
+ for (var k = 0; k < found.length; k++) {
161
+ if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
162
+ currentContext[currentContextIndex++] = found[k];
163
+ }
164
+ }
165
+ continue; // Skip to next token
166
+ }
167
+ // Code to deal with attribute selectors
168
+ if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
169
+ var tagName = RegExp.$1;
170
+ var attrName = RegExp.$2;
171
+ var attrOperator = RegExp.$3;
172
+ var attrValue = RegExp.$4;
173
+ if (!tagName) {
174
+ tagName = '*';
175
+ }
176
+ // Grab all of the tagName elements within current context
177
+ var found = new Array;
178
+ var foundCount = 0;
179
+ for (var h = 0; h < currentContext.length; h++) {
180
+ var elements;
181
+ if (tagName == '*') {
182
+ elements = getAllChildren(currentContext[h]);
183
+ } else {
184
+ elements = currentContext[h].getElementsByTagName(tagName);
185
+ }
186
+ for (var j = 0; j < elements.length; j++) {
187
+ found[foundCount++] = elements[j];
188
+ }
189
+ }
190
+ currentContext = new Array;
191
+ var currentContextIndex = 0;
192
+ var checkFunction; // This function will be used to filter the elements
193
+ switch (attrOperator) {
194
+ case '=': // Equality
195
+ checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
196
+ break;
197
+ case '~': // Match one of space seperated words
198
+ checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
199
+ break;
200
+ case '|': // Match start with value followed by optional hyphen
201
+ checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
202
+ break;
203
+ case '^': // Match starts with value
204
+ checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
205
+ break;
206
+ case '$': // Match ends with value - fails with "Warning" in Opera 7
207
+ checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
208
+ break;
209
+ case '*': // Match ends with value
210
+ checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
211
+ break;
212
+ default :
213
+ // Just test for existence of attribute
214
+ checkFunction = function(e) { return e.getAttribute(attrName); };
215
+ }
216
+ currentContext = new Array;
217
+ var currentContextIndex = 0;
218
+ for (var k = 0; k < found.length; k++) {
219
+ if (checkFunction(found[k])) {
220
+ currentContext[currentContextIndex++] = found[k];
221
+ }
222
+ }
223
+ // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
224
+ continue; // Skip to next token
225
+ }
226
+
227
+ if (!currentContext[0]){
228
+ return;
229
+ }
230
+
231
+ // If we get here, token is JUST an element (not a class or ID selector)
232
+ tagName = token;
233
+ var found = new Array;
234
+ var foundCount = 0;
235
+ for (var h = 0; h < currentContext.length; h++) {
236
+ var elements = currentContext[h].getElementsByTagName(tagName);
237
+ for (var j = 0; j < elements.length; j++) {
238
+ found[foundCount++] = elements[j];
239
+ }
240
+ }
241
+ currentContext = found;
242
+ }
243
+ return currentContext;
244
+ }
245
+
246
+ /* That revolting regular expression explained
247
+ /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
248
+ \---/ \---/\-------------/ \-------/
249
+ | | | |
250
+ | | | The value
251
+ | | ~,|,^,$,* or =
252
+ | Attribute
253
+ Tag
254
+ */