Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

close enough

  • Loading branch information...
commit 931b29519bf25556f6f0965b34c8064262fd2e9d 0 parents
@evan authored
Showing with 3,520 additions and 0 deletions.
  1. +21 −0 README
  2. +132 −0 Rakefile
  3. +12 −0 TODO
  4. +54 −0 httpd.conf
  5. +2 −0  public/about/body.element
  6. +1 −0  public/about/content.element
  7. +1 −0  public/about/index.html
  8. +1 −0  public/about/title.element
  9. +16 −0 public/all/content.element
  10. +1 −0  public/all/index.html
  11. +1 −0  public/all/title.element
  12. +2 −0  public/closed.element
  13. +8 −0 public/comment.element
  14. +3 −0  public/css/bax.css
  15. +1 −0  public/ellipsis.element
  16. BIN  public/images/rss.gif
  17. +80 −0 public/index.html
  18. +25 −0 public/js/bax.js
  19. +11 −0 public/js/jquery.js
  20. 0  public/nothing.element
  21. +34 −0 public/open.element
  22. +31 −0 public/post_full.element
  23. +7 −0 public/post_index.element
  24. +23 −0 public/post_short.element
  25. +1 −0  public/read_more.element
  26. +17 −0 public/static.element
  27. +118 −0 public/template.html
  28. +1 −0  public/title.element
  29. +1 −0  public/url.element
  30. +15 −0 public/xml/atom.xml
  31. +1 −0  public/xml/content.element
  32. +13 −0 public/xml/post.element
  33. +5 −0 public/yui/build/base/README
  34. +7 −0 public/yui/build/base/base-min.css
  35. +76 −0 public/yui/build/base/base.css
  36. +25 −0 public/yui/build/fonts/README
  37. +7 −0 public/yui/build/fonts/fonts-min.css
  38. +16 −0 public/yui/build/fonts/fonts.css
  39. +55 −0 public/yui/build/grids/README
  40. +7 −0 public/yui/build/grids/grids-min.css
  41. +216 −0 public/yui/build/grids/grids.css
  42. +34 −0 public/yui/build/reset/README
  43. +7 −0 public/yui/build/reset/reset-min.css
  44. +19 −0 public/yui/build/reset/reset.css
  45. +65 −0 script/bax.rb
  46. +65 −0 script/html/document.rb
  47. +530 −0 script/html/node.rb
  48. +823 −0 script/html/selector.rb
  49. +105 −0 script/html/tokenizer.rb
  50. +11 −0 script/html/version.rb
  51. +80 −0 script/import.rb
  52. +123 −0 script/support.rb
  53. +8 −0 script/twitter/lib/twitter.rb
  54. +179 −0 script/twitter/lib/twitter/base.rb
  55. +185 −0 script/twitter/lib/twitter/command.rb
  56. +22 −0 script/twitter/lib/twitter/direct_message.rb
  57. +43 −0 script/twitter/lib/twitter/easy_class_maker.rb
  58. +19 −0 script/twitter/lib/twitter/status.rb
  59. +38 −0 script/twitter/lib/twitter/user.rb
  60. +9 −0 script/twitter/lib/twitter/version.rb
  61. +96 −0 script/white_list_helper.rb
  62. +6 −0 settings.rb
  63. +5 −0 slogans.txt
21 README
@@ -0,0 +1,21 @@
+
+Bax
+
+Configured your paths in httpd.conf and start apache:
+ rake start
+
+Rotate the slogan:
+ rake slogan
+
+Build a new article:
+ rake new
+
+Index your shit:
+ rake index
+
+It's been real.
+
+===
+
+Copyright 2007 Cloudburst, LLC. Unauthorized modification, use, and/or
+distribution of this software is strictly prohibited.
132 Rakefile
@@ -0,0 +1,132 @@
+
+require 'open-uri'
+require 'rubygems'
+require 'script/support'
+require 'highline/import'
+require 'cgi'
+
+namespace :apache do
+ task :start => [:stop] do
+ Dir.chdir File.dirname(__FILE__) do
+ system("ulimit -n 1000; /opt/local/apache2/bin/httpd -f #{ROOT}/../httpd.conf")
+ end
+ end
+
+ task :stop do
+ system("ps awx | grep httpd | grep -v grep | awk '{print $1}' | xargs kill -9")
+ end
+end
+
+task :start => [:'apache:start']
+task :stop => [:'apache:stop']
+
+namespace :article do
+
+ task :link do
+ link_article find_article
+ end
+
+ task :index do
+ with_dir do
+ articles = Dir['articles/**/intro.element'].sort_by do |el|
+ File.mtime el
+ end.reverse
+ puts "found #{articles.size} articles"
+
+ articles = articles.map do |el|
+ File.dirname(el)
+ end
+
+ File.open("all/body.element", 'w') do |f|
+ articles.each do |article|
+ # build the all articles index
+ f.puts "<!--#include virtual='/#{article}/post_index.element' -->"
+ end
+ end
+
+ File.open("recent.element", "w") do |recent|
+ File.open("content.element", "w") do |content|
+ File.open("xml/content.element", "w") do |xml|
+ # build the most recent lists
+ articles[0..5].each do |article|
+ title = open("#{article}/title.element").read
+ recent.puts "<li class=\"item\"><a href=\"/#{article}\">#{title}</a></li>"
+ content.puts "<!--#include virtual='/#{article}/post_short.element' -->"
+ xml.puts "<!--#include virtual='/#{article}/xml.post.element' -->"
+ end
+ end
+ end
+ end
+
+ end
+ end
+
+ task :new do
+ base = "/articles/#{Time.now.strftime('%Y/%m/%d')}/"
+ title = ask("new url: #{base} ")
+ url = base + title.gsub(' ', '-').gsub(/[^\w\-]/, '')
+ with_dir(url) do
+ File.open('title.element', 'w') {|f| f.puts title }
+ File.open('date.element', 'w') {|f| f.write Time.now }
+ File.open('url.element', 'w') {|f| f.write url }
+ File.open('body.element', 'w') {|f| f.puts ".</p>"}
+ File.open('intro.element', 'w') {|f| f.puts "<p>Intro" }
+ File.open('ellipsis.element', 'w') {|f| f.puts "...</p>" }
+ end
+ link_article url
+ index_comments url
+ puts "ok"
+ end
+
+ task :delete do
+ with_dir do
+ article = find_article
+ if agree("delete #{article}? ")
+ system("rm -rf #{article}")
+ end
+ end
+ Rake::Task[:index].invoke
+ end
+
+end
+
+task :new => [:'article:new']
+task :delete => [:'article:delete']
+task :index => [:'article:index']
+
+task :slogan do
+ with_dir do
+ slogans = open('../slogans.txt').readlines
+ File.open('slogan.element', 'w') do |f|
+ f.write slogans[rand(slogans.size)]
+ end
+ end
+end
+
+task :import do
+ load 'script/import.rb'
+end
+
+namespace :comments do
+ task :open do
+ allow_comments(find_article, true)
+ end
+
+ task :close do
+ allow_comments(find_article, false)
+ end
+
+ task :index do
+ index_comments find_article
+ end
+end
+
+task :open => [:'comments:open']
+task :close => [:'comments:close']
+
+task :dir do
+ puts find_article
+end
+
+task :default => [:dir]
+
12 TODO
@@ -0,0 +1,12 @@
+
+Definitely
+
+* task to delete/edit comments
+* task to push a local article to the server
+* pagination
+* tags
+* atom feed is still suspect
+
+Maybe
+
+* use date.elements instead of flastmod (except in atom feed)
54 httpd.conf
@@ -0,0 +1,54 @@
+
+ServerRoot "/Users/eweaver/p/fauna/bax/trunk"
+
+Listen 4040
+
+LoadModule auth_digest_module /opt/local/apache2/modules/mod_auth_digest.so
+LoadModule include_module /opt/local/apache2/modules/mod_include.so
+LoadModule vhost_alias_module /opt/local/apache2/modules/mod_vhost_alias.so
+LoadModule alias_module /opt/local/apache2/modules/mod_alias.so
+LoadModule rewrite_module /opt/local/apache2/modules/mod_rewrite.so
+LoadModule mime_module /opt/local/apache2/modules/mod_mime.so
+LoadModule dir_module /opt/local/apache2/modules/mod_dir.so
+LoadModule cgi_module /opt/local/apache2/modules/mod_cgi.so
+
+#User eweaver
+#Group eweaver
+
+ServerAdmin you@example.com
+DocumentRoot "/opt/local/apache2/htdocs"
+
+<Directory />
+ Options FollowSymLinks
+ AllowOverride None
+</Directory>
+
+LogLevel debug
+
+DefaultType text/plain
+TypesConfig /opt/local/apache2/conf/mime.types
+
+<VirtualHost *:4040>
+ ServerName localhost
+ DirectoryIndex index.html
+ DocumentRoot "/Users/eweaver/p/fauna/bax/trunk/public"
+
+ SSIErrorMsg "<strong>(Bax error)</strong>"
+ SSITimeFormat '%B %d, %Y'
+ AddType text/html .element
+ AddOutputFilter INCLUDES .html
+ AddOutputFilter INCLUDES .xml
+ AddOutputFilter INCLUDES .element
+
+ <Directory "/Users/eweaver/p/fauna/bax/trunk/public">
+ AllowOverride None
+ Options +Includes
+ </Directory>
+
+ ScriptAlias /script/ "/Users/eweaver/p/fauna/bax/trunkscript/"
+ <Directory "/Users/eweaver/p/fauna/bax/trunk/script">
+ AllowOverride None
+ Options +Includes
+ </Directory>
+
+</VirtualHost>
2  public/about/body.element
@@ -0,0 +1,2 @@
+<h2>about</h2>
+<p>I am a static page.</p>
1  public/about/content.element
1  public/about/index.html
1  public/about/title.element
@@ -0,0 +1 @@
+about
16 public/all/content.element
@@ -0,0 +1,16 @@
+
+<div class="post">
+ <div class="top">
+ <h2>
+ <!--#include virtual='title.element' -->
+ </h2>
+ </div>
+ <div class="mid">
+ <div class="body">
+ <!--#include virtual='body.element' -->
+ </div>
+ </div>
+ <div class="bottom">
+ </div>
+</div>
+
1  public/all/index.html
1  public/all/title.element
@@ -0,0 +1 @@
+all articles
2  public/closed.element
@@ -0,0 +1,2 @@
+
+<h2 id="comment-message">Comments are closed.</h2>
8 public/comment.element
@@ -0,0 +1,8 @@
+<div class="comment" id="comment-<!--#include virtual='index.element' -->">
+ <p class="author">
+ <!--#include virtual='author.element' -->
+ <a class="says" href="#comment-<!--#include virtual='index.element' -->" name="comment-<!--#include virtual='index.element' -->">says
+ <span class="date">(<!--#flastmod virtual='body.element' -->)</span></a>:
+ </p>
+ <!--#include virtual='body.element' -->
+</div>
3  public/css/bax.css
@@ -0,0 +1,3 @@
+
+
+
1  public/ellipsis.element
@@ -0,0 +1 @@
+..
BIN  public/images/rss.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 public/index.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <title><!--#include virtual='title.element' --> :: bax default blog</title>
+ <link rel="alternate" type="application/atom+xml" title="Atom" href="http://blog.yourhost.com/xml/atom.xml" />
+ <link rel="shortcut icon" href="/favicon.ico" >
+ <link rel="stylesheet" type="text/css" href="/yui/build/reset/reset.css">
+ <link rel="stylesheet" type="text/css" href="/yui/build/base/base.css">
+ <link rel="stylesheet" type="text/css" href="/yui/build/grids/grids.css">
+ <link rel="stylesheet" type="text/css" href="/yui/build/fonts/fonts.css">
+ <link rel="stylesheet" type="text/css" href="/css/bax.css">
+ <script src="/js/jquery.js" type="text/javascript"></script>
+ <script src="/js/bax.js" type="text/javascript"></script>
+</head>
+
+<body>
+
+ <div id="doc2">
+ <div id="bd">
+
+ <div id="hd" class="header">
+ <h1><a href="/">bax default blog</a></h1>
+ <h2><!--#include virtual='/slogan.element' --></h2>
+ </div>
+
+ <div class="yui-gf">
+ <div class="yui-u first sidebar">
+ <div class="mid">
+ <div class="content">
+
+ <ul>
+ <li><h3>sidebar 1</h3></li>
+ <li class="item"><a href="#">link 1</a></li>
+ <li class="item"><a href="#">link 2</a></li>
+ <li class="item"><a href="/about">a static page</a></li>
+ </ul>
+
+ <ul>
+ <li>
+ <h3>
+ <a href="/xml/atom.xml">
+ <img src="/images/rss.gif">
+ </a>
+ <a href="/xml/atom.xml">
+ subscribe
+ </a>
+ </h3>
+ </li>
+ </ul>
+
+ <ul>
+ <li><h3>sidebar 2</h3></li>
+ <li class="item"><a href="#">link 1</a></li>
+ <li class="item"><a href="#">link 2</a></li>
+ </ul>
+
+ <ul>
+ <li><h3>latest</h3></li>
+ <!--#include virtual='/recent.element' -->
+ <li id="all"><em><a href="/all">see all</a></em></li>
+ </ul>
+
+ </div>
+ </div>
+ </div>
+
+ <div class="yui-u content">
+ <!--#include virtual='content.element' -->
+ </div>
+ </div>
+ </div>
+
+ <div id="ft" class="footer">
+ <p>Copyright 2007 <a href="#">You</a></p>
+ </div>
+
+ </div>
+
+</body>
+</html>
25 public/js/bax.js
@@ -0,0 +1,25 @@
+
+function Submit(e) {
+
+ var params = {};
+ $('form').find("input, textarea").each(
+ function() {params[this.name] = this.value; }
+ );
+
+ if (e.name == "submit") params['article'] = document.location.href;
+
+ $.post("/script/bax.rb", /* send request */
+ params,
+ function(d) { /* preview callback */
+ $('#preview').html(d);
+ $('#preview').fadeIn();
+ if (params['article']) {
+ if ($('#preview[p.error]').size() == 0) {
+ /* disable form */
+ $('#comment-message').html("Thanks for your comment!");
+ $('form').fadeOut();
+ }
+ }
+ }
+ )
+}
11 public/js/jquery.js
@@ -0,0 +1,11 @@
+/*
+ * jQuery 1.1.3.1 - New Wave Javascript
+ *
+ * Copyright (c) 2007 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2007-07-05 00:43:24 -0400 (Thu, 05 Jul 2007) $
+ * $Rev: 2243 $
+ */
+eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7(1g 18.6=="I"){18.I=18.I;u 6=q(a,c){7(18==9||!9.3X)v 14 6(a,c);v 9.3X(a,c)};7(1g $!="I")6.1I$=$;u $=6;6.11=6.8r={3X:q(a,c){a=a||P;7(6.16(a))v 14 6(P)[6.11.1G?"1G":"1W"](a);7(1g a=="1s"){u m=/^[^<]*(<(.|\\s)+>)[^>]*$/.1V(a);7(m)a=6.31([m[1]]);B v 14 6(c).1L(a)}v 9.4E(a.15==2b&&a||(a.3C||a.C&&a!=18&&!a.1q&&a[0]!=I&&a[0].1q)&&6.2L(a)||[a])},3C:"1.1.3.1",7W:q(){v 9.C},C:0,1M:q(a){v a==I?6.2L(9):9[a]},1Z:q(a){u b=6(a);b.5q=9;v b},4E:q(a){9.C=0;[].R.O(9,a);v 9},F:q(a,b){v 6.F(9,a,b)},2p:q(a){u b=-1;9.F(q(i){7(9==a)b=i});v b},1b:q(f,d,e){u c=f;7(f.15==33)7(d==I)v 9.C&&6[e||"1b"](9[0],f)||I;B{c={};c[f]=d}v 9.F(q(a){E(u b V c)6.1b(e?9.T:9,b,6.4H(9,c[b],e,a,b))})},1f:q(b,a){v 9.1b(b,a,"2z")},2A:q(e){7(1g e=="1s")v 9.2Y().3e(P.66(e));u t="";6.F(e||9,q(){6.F(9.2S,q(){7(9.1q!=8)t+=9.1q!=1?9.5R:6.11.2A([9])})});v t},8b:q(){u a,1S=19;v 9.F(q(){7(!a)a=6.31(1S,9.2O);u b=a[0].3s(K);9.L.2K(b,9);1v(b.1d)b=b.1d;b.4g(9)})},3e:q(){v 9.2F(19,K,1,q(a){9.4g(a)})},5w:q(){v 9.2F(19,K,-1,q(a){9.2K(a,9.1d)})},5t:q(){v 9.2F(19,N,1,q(a){9.L.2K(a,9)})},5s:q(){v 9.2F(19,N,-1,q(a){9.L.2K(a,9.1X)})},2U:q(){v 9.5q||6([])},1L:q(t){u b=6.3k(9,q(a){v 6.1L(t,a)});v 9.1Z(/[^+>] [^+>]/.17(t)||t.J("..")>-1?6.5g(b):b)},7x:q(e){u d=9.1A(9.1L("*"));d.F(q(){9.1I$1a={};E(u a V 9.$1a)9.1I$1a[a]=6.1c({},9.$1a[a])}).3U();u r=9.1Z(6.3k(9,q(a){v a.3s(e!=I?e:K)}));d.F(q(){u b=9.1I$1a;E(u a V b)E(u c V b[a])6.S.1A(9,a,b[a][c],b[a][c].W);9.1I$1a=H});v r},1i:q(t){v 9.1Z(6.16(t)&&6.2s(9,q(b,a){v t.O(b,[a])})||6.2x(t,9))},4Y:q(t){v 9.1Z(t.15==33&&6.2x(t,9,K)||6.2s(9,q(a){v(t.15==2b||t.3C)?6.2w(a,t)<0:a!=t}))},1A:q(t){v 9.1Z(6.1T(9.1M(),t.15==33?6(t).1M():t.C!=I&&(!t.Q||t.Q=="6Z")?t:[t]))},37:q(a){v a?6.2x(a,9).C>0:N},6R:q(a){v a==I?(9.C?9[0].2v:H):9.1b("2v",a)},3F:q(a){v a==I?(9.C?9[0].27:H):9.2Y().3e(a)},2F:q(f,d,g,e){u c=9.C>1,a;v 9.F(q(){7(!a){a=6.31(f,9.2O);7(g<0)a.6E()}u b=9;7(d&&6.Q(9,"1r")&&6.Q(a[0],"2V"))b=9.3R("1z")[0]||9.4g(P.5h("1z"));6.F(a,q(){e.O(b,[c?9.3s(K):9])})})}};6.1c=6.11.1c=q(){u c=19[0],a=1;7(19.C==1){c=9;a=0}u b;1v((b=19[a++])!=H)E(u i V b)c[i]=b[i];v c};6.1c({6n:q(){7(6.1I$)$=6.1I$;v 6},16:q(a){v!!a&&1g a!="1s"&&!a.Q&&a.15!=2b&&/q/i.17(a+"")},40:q(a){v a.4z&&a.2O&&!a.2O.4y},Q:q(b,a){v b.Q&&b.Q.1D()==a.1D()},F:q(a,b,c){7(a.C==I)E(u i V a)b.O(a[i],c||[i,a[i]]);B E(u i=0,4x=a.C;i<4x;i++)7(b.O(a[i],c||[i,a[i]])===N)1F;v a},4H:q(c,b,d,e,a){7(6.16(b))b=b.3D(c,[e]);u f=/z-?2p|5Y-?8p|1e|5U|8i-?1u/i;v b&&b.15==3y&&d=="2z"&&!f.17(a)?b+"4o":b},12:{1A:q(b,c){6.F(c.2R(/\\s+/),q(i,a){7(!6.12.3w(b.12,a))b.12+=(b.12?" ":"")+a})},1E:q(b,c){b.12=c!=I?6.2s(b.12.2R(/\\s+/),q(a){v!6.12.3w(c,a)}).5M(" "):""},3w:q(t,c){v 6.2w(c,(t.12||t).3v().2R(/\\s+/))>-1}},4m:q(e,o,f){E(u i V o){e.T["2N"+i]=e.T[i];e.T[i]=o[i]}f.O(e,[]);E(u i V o)e.T[i]=e.T["2N"+i]},1f:q(e,p){7(p=="1u"||p=="29"){u b={},3r,3p,d=["83","81","80","7Y"];6.F(d,q(){b["7V"+9]=0;b["7T"+9+"7S"]=0});6.4m(e,b,q(){7(6(e).37(\':4f\')){3r=e.7Q;3p=e.7O}B{e=6(e.3s(K)).1L(":4b").5v("2B").2U().1f({48:"1y",3i:"7L",U:"2h",7K:"0",7I:"0"}).5o(e.L)[0];u a=6.1f(e.L,"3i")||"3n";7(a=="3n")e.L.T.3i="7G";3r=e.7E;3p=e.7D;7(a=="3n")e.L.T.3i="3n";e.L.3q(e)}});v p=="1u"?3r:3p}v 6.2z(e,p)},2z:q(e,a,d){u g;7(a=="1e"&&6.M.1h){g=6.1b(e.T,"1e");v g==""?"1":g}7(a.3t(/3x/i))a=6.1U;7(!d&&e.T[a])g=e.T[a];B 7(P.3f&&P.3f.3Y){7(a.3t(/3x/i))a="3x";a=a.1o(/([A-Z])/g,"-$1").2H();u b=P.3f.3Y(e,H);7(b)g=b.57(a);B 7(a=="U")g="1P";B 6.4m(e,{U:"2h"},q(){u c=P.3f.3Y(9,"");g=c&&c.57(a)||""})}B 7(e.3S){u f=a.1o(/\\-(\\w)/g,q(m,c){v c.1D()});g=e.3S[a]||e.3S[f]}v g},31:q(a,c){u r=[];c=c||P;6.F(a,q(i,b){7(!b)v;7(b.15==3y)b=b.3v();7(1g b=="1s"){u s=6.2C(b).2H(),1x=c.5h("1x"),1N=[];u a=!s.J("<1H")&&[1,"<2y>","</2y>"]||!s.J("<7g")&&[1,"<52>","</52>"]||(!s.J("<7c")||!s.J("<1z")||!s.J("<7a")||!s.J("<78"))&&[1,"<1r>","</1r>"]||!s.J("<2V")&&[2,"<1r><1z>","</1z></1r>"]||(!s.J("<75")||!s.J("<74"))&&[3,"<1r><1z><2V>","</2V></1z></1r>"]||!s.J("<73")&&[2,"<1r><4W>","</4W></1r>"]||[0,"",""];1x.27=a[1]+b+a[2];1v(a[0]--)1x=1x.1d;7(6.M.1h){7(!s.J("<1r")&&s.J("<1z")<0)1N=1x.1d&&1x.1d.2S;B 7(a[1]=="<1r>"&&s.J("<1z")<0)1N=1x.2S;E(u n=1N.C-1;n>=0;--n)7(6.Q(1N[n],"1z")&&!1N[n].2S.C)1N[n].L.3q(1N[n])}b=6.2L(1x.2S)}7(0===b.C&&(!6.Q(b,"34")&&!6.Q(b,"2y")))v;7(b[0]==I||6.Q(b,"34")||b.71)r.R(b);B r=6.1T(r,b)});v r},1b:q(c,d,a){u e=6.40(c)?{}:6.3H;7(e[d]){7(a!=I)c[e[d]]=a;v c[e[d]]}B 7(a==I&&6.M.1h&&6.Q(c,"34")&&(d=="70"||d=="6Y"))v c.6W(d).5R;B 7(c.4z){7(a!=I)c.6U(d,a);7(6.M.1h&&/4M|2u/.17(d)&&!6.40(c))v c.35(d,2);v c.35(d)}B{7(d=="1e"&&6.M.1h){7(a!=I){c.5U=1;c.1i=(c.1i||"").1o(/4L\\([^)]*\\)/,"")+(39(a).3v()=="6M"?"":"4L(1e="+a*4X+")")}v c.1i?(39(c.1i.3t(/1e=([^)]*)/)[1])/4X).3v():""}d=d.1o(/-([a-z])/6K,q(z,b){v b.1D()});7(a!=I)c[d]=a;v c[d]}},2C:q(t){v t.1o(/^\\s+|\\s+$/g,"")},2L:q(a){u r=[];7(1g a!="6I")E(u i=0,26=a.C;i<26;i++)r.R(a[i]);B r=a.51(0);v r},2w:q(b,a){E(u i=0,26=a.C;i<26;i++)7(a[i]==b)v i;v-1},1T:q(a,b){E(u i=0;b[i];i++)a.R(b[i]);v a},5g:q(a){u r=[],3P=6.1k++;E(u i=0,4G=a.C;i<4G;i++)7(3P!=a[i].1k){a[i].1k=3P;r.R(a[i])}v r},1k:0,2s:q(c,b,d){7(1g b=="1s")b=14 45("a","i","v "+b);u a=[];E(u i=0,30=c.C;i<30;i++)7(!d&&b(c[i],i)||d&&!b(c[i],i))a.R(c[i]);v a},3k:q(c,b){7(1g b=="1s")b=14 45("a","v "+b);u d=[];E(u i=0,30=c.C;i<30;i++){u a=b(c[i],i);7(a!==H&&a!=I){7(a.15!=2b)a=[a];d=d.6v(a)}}v d}});14 q(){u b=6u.6t.2H();6.M={4D:(b.3t(/.+(?:6s|6q|6o|6m)[\\/: ]([\\d.]+)/)||[])[1],20:/5l/.17(b),2a:/2a/.17(b),1h:/1h/.17(b)&&!/2a/.17(b),3j:/3j/.17(b)&&!/(6h|5l)/.17(b)};6.6g=!6.M.1h||P.6f=="6c";6.1U=6.M.1h?"1U":"5x",6.3H={"E":"68","67":"12","3x":6.1U,5x:6.1U,1U:6.1U,27:"27",12:"12",2v:"2v",2r:"2r",2B:"2B",65:"63",2T:"2T",62:"5Z"}};6.F({4v:"a.L",4p:"6.4p(a)",8o:"6.22(a,2,\'1X\')",8n:"6.22(a,2,\'4t\')",8k:"6.4q(a.L.1d,a)",8h:"6.4q(a.1d)"},q(i,n){6.11[i]=q(a){u b=6.3k(9,n);7(a&&1g a=="1s")b=6.2x(a,b);v 9.1Z(b)}});6.F({5o:"3e",8g:"5w",2K:"5t",8f:"5s"},q(i,n){6.11[i]=q(){u a=19;v 9.F(q(){E(u j=0,26=a.C;j<26;j++)6(a[j])[n](9)})}});6.F({5v:q(a){6.1b(9,a,"");9.8d(a)},8c:q(c){6.12.1A(9,c)},88:q(c){6.12.1E(9,c)},87:q(c){6.12[6.12.3w(9,c)?"1E":"1A"](9,c)},1E:q(a){7(!a||6.1i(a,[9]).r.C)9.L.3q(9)},2Y:q(){1v(9.1d)9.3q(9.1d)}},q(i,n){6.11[i]=q(){v 9.F(n,19)}});6.F(["5Q","5P","5O","5N"],q(i,n){6.11[n]=q(a,b){v 9.1i(":"+n+"("+a+")",b)}});6.F(["1u","29"],q(i,n){6.11[n]=q(h){v h==I?(9.C?6.1f(9[0],n):H):9.1f(n,h.15==33?h:h+"4o")}});6.1c({4n:{"":"m[2]==\'*\'||6.Q(a,m[2])","#":"a.35(\'2m\')==m[2]",":":{5P:"i<m[3]-0",5O:"i>m[3]-0",22:"m[3]-0==i",5Q:"m[3]-0==i",2Q:"i==0",2P:"i==r.C-1",5L:"i%2==0",5K:"i%2","2Q-3u":"a.L.3R(\'*\')[0]==a","2P-3u":"6.22(a.L.5J,1,\'4t\')==a","86-3u":"!6.22(a.L.5J,2,\'4t\')",4v:"a.1d",2Y:"!a.1d",5N:"(a.5H||a.85||\'\').J(m[3])>=0",4f:\'"1y"!=a.G&&6.1f(a,"U")!="1P"&&6.1f(a,"48")!="1y"\',1y:\'"1y"==a.G||6.1f(a,"U")=="1P"||6.1f(a,"48")=="1y"\',84:"!a.2r",2r:"a.2r",2B:"a.2B",2T:"a.2T||6.1b(a,\'2T\')",2A:"\'2A\'==a.G",4b:"\'4b\'==a.G",5F:"\'5F\'==a.G",4l:"\'4l\'==a.G",5E:"\'5E\'==a.G",4k:"\'4k\'==a.G",5D:"\'5D\'==a.G",5C:"\'5C\'==a.G",1J:\'"1J"==a.G||6.Q(a,"1J")\',5B:"/5B|2y|82|1J/i.17(a.Q)"},"[":"6.1L(m[2],a).C"},5A:[/^\\[ *(@)([\\w-]+) *([!*$^~=]*) *(\'?"?)(.*?)\\4 *\\]/,/^(\\[)\\s*(.*?(\\[.*?\\])?[^[]*?)\\s*\\]/,/^(:)([\\w-]+)\\("?\'?(.*?(\\(.*?\\))?[^(]*?)"?\'?\\)/,14 3o("^([:.#]*)("+(6.2J=6.M.20&&6.M.4D<"3.0.0"?"\\\\w":"(?:[\\\\w\\7Z-\\7X*1I-]|\\\\\\\\.)")+"+)")],2x:q(a,c,b){u d,1K=[];1v(a&&a!=d){d=a;u f=6.1i(a,c,b);a=f.t.1o(/^\\s*,\\s*/,"");1K=b?c=f.r:6.1T(1K,f.r)}v 1K},1L:q(t,l){7(1g t!="1s")v[t];7(l&&!l.1q)l=H;l=l||P;7(!t.J("//")){l=l.4h;t=t.2G(2,t.C)}B 7(!t.J("/")&&!l.2O){l=l.4h;t=t.2G(1,t.C);7(t.J("/")>=1)t=t.2G(t.J("/"),t.C)}u b=[l],2j=[],2P;1v(t&&2P!=t){u r=[];2P=t;t=6.2C(t).1o(/^\\/\\//,"");u k=N;u g=14 3o("^[/>]\\\\s*("+6.2J+"+)");u m=g.1V(t);7(m){u o=m[1].1D();E(u i=0;b[i];i++)E(u c=b[i].1d;c;c=c.1X)7(c.1q==1&&(o=="*"||c.Q.1D()==o.1D()))r.R(c);b=r;t=t.1o(g,"");7(t.J(" ")==0)7R;k=K}B{g=/^((\\/?\\.\\.)|([>\\/+~]))\\s*([a-z]*)/i;7((m=g.1V(t))!=H){r=[];u o=m[4],1k=6.1k++;m=m[1];E(u j=0,2e=b.C;j<2e;j++)7(m.J("..")<0){u n=m=="~"||m=="+"?b[j].1X:b[j].1d;E(;n;n=n.1X)7(n.1q==1){7(m=="~"&&n.1k==1k)1F;7(!o||n.Q.1D()==o.1D()){7(m=="~")n.1k=1k;r.R(n)}7(m=="+")1F}}B r.R(b[j].L);b=r;t=6.2C(t.1o(g,""));k=K}}7(t&&!k){7(!t.J(",")){7(l==b[0])b.4e();2j=6.1T(2j,b);r=b=[l];t=" "+t.2G(1,t.C)}B{u h=14 3o("^("+6.2J+"+)(#)("+6.2J+"+)");u m=h.1V(t);7(m){m=[0,m[2],m[3],m[1]]}B{h=14 3o("^([#.]?)("+6.2J+"*)");m=h.1V(t)}m[2]=m[2].1o(/\\\\/g,"");u f=b[b.C-1];7(m[1]=="#"&&f&&f.4d){u p=f.4d(m[2]);7((6.M.1h||6.M.2a)&&p&&1g p.2m=="1s"&&p.2m!=m[2])p=6(\'[@2m="\'+m[2]+\'"]\',f)[0];b=r=p&&(!m[3]||6.Q(p,m[3]))?[p]:[]}B{E(u i=0;b[i];i++){u a=m[1]!=""||m[0]==""?"*":m[2];7(a=="*"&&b[i].Q.2H()=="7P")a="2E";r=6.1T(r,b[i].3R(a))}7(m[1]==".")r=6.4c(r,m[2]);7(m[1]=="#"){u e=[];E(u i=0;r[i];i++)7(r[i].35("2m")==m[2]){e=[r[i]];1F}r=e}b=r}t=t.1o(h,"")}}7(t){u d=6.1i(t,r);b=r=d.r;t=6.2C(d.t)}}7(t)b=[];7(b&&l==b[0])b.4e();2j=6.1T(2j,b);v 2j},4c:q(r,m,a){m=" "+m+" ";u b=[];E(u i=0;r[i];i++){u c=(" "+r[i].12+" ").J(m)>=0;7(!a&&c||a&&!c)b.R(r[i])}v b},1i:q(t,r,h){u d;1v(t&&t!=d){d=t;u p=6.5A,m;E(u i=0;p[i];i++){m=p[i].1V(t);7(m){t=t.7N(m[0].C);m[2]=m[2].1o(/\\\\/g,"");1F}}7(!m)1F;7(m[1]==":"&&m[2]=="4Y")r=6.1i(m[3],r,K).r;B 7(m[1]==".")r=6.4c(r,m[2],h);B 7(m[1]=="@"){u g=[],G=m[3];E(u i=0,2e=r.C;i<2e;i++){u a=r[i],z=a[6.3H[m[2]]||m[2]];7(z==H||/4M|2u/.17(m[2]))z=6.1b(a,m[2])||\'\';7((G==""&&!!z||G=="="&&z==m[5]||G=="!="&&z!=m[5]||G=="^="&&z&&!z.J(m[5])||G=="$="&&z.2G(z.C-m[5].C)==m[5]||(G=="*="||G=="~=")&&z.J(m[5])>=0)^h)g.R(a)}r=g}B 7(m[1]==":"&&m[2]=="22-3u"){u e=6.1k++,g=[],17=/(\\d*)n\\+?(\\d*)/.1V(m[3]=="5L"&&"2n"||m[3]=="5K"&&"2n+1"||!/\\D/.17(m[3])&&"n+"+m[3]||m[3]),2Q=(17[1]||1)-0,d=17[2]-0;E(u i=0,2e=r.C;i<2e;i++){u j=r[i],L=j.L;7(e!=L.1k){u c=1;E(u n=L.1d;n;n=n.1X)7(n.1q==1)n.4a=c++;L.1k=e}u b=N;7(2Q==1){7(d==0||j.4a==d)b=K}B 7((j.4a+d)%2Q==0)b=K;7(b^h)g.R(j)}r=g}B{u f=6.4n[m[1]];7(1g f!="1s")f=6.4n[m[1]][m[2]];49("f = q(a,i){v "+f+"}");r=6.2s(r,f,h)}}v{r:r,t:t}},4p:q(c){u b=[];u a=c.L;1v(a&&a!=P){b.R(a);a=a.L}v b},22:q(a,e,c,b){e=e||1;u d=0;E(;a;a=a[c])7(a.1q==1&&++d==e)1F;v a},4q:q(n,a){u r=[];E(;n;n=n.1X){7(n.1q==1&&(!a||n!=a))r.R(n)}v r}});6.S={1A:q(d,e,c,b){7(6.M.1h&&d.3m!=I)d=18;7(!c.1Q)c.1Q=9.1Q++;7(b!=I){u f=c;c=q(){v f.O(9,19)};c.W=b;c.1Q=f.1Q}7(!d.$1a)d.$1a={};7(!d.$1p)d.$1p=q(){u a;7(1g 6=="I"||6.S.47)v a;a=6.S.1p.O(d,19);v a};u g=d.$1a[e];7(!g){g=d.$1a[e]={};7(d.46)d.46(e,d.$1p,N);B d.7M("5r"+e,d.$1p)}g[c.1Q]=c;7(!9.Y[e])9.Y[e]=[];7(6.2w(d,9.Y[e])==-1)9.Y[e].R(d)},1Q:1,Y:{},1E:q(b,c,a){u d=b.$1a,1Y,2p;7(d){7(c&&c.G){a=c.44;c=c.G}7(!c){E(c V d)9.1E(b,c)}B 7(d[c]){7(a)3l d[c][a.1Q];B E(a V b.$1a[c])3l d[c][a];E(1Y V d[c])1F;7(!1Y){7(b.43)b.43(c,b.$1p,N);B b.7J("5r"+c,b.$1p);1Y=H;3l d[c];1v(9.Y[c]&&((2p=6.2w(b,9.Y[c]))>=0))3l 9.Y[c][2p]}}E(1Y V d)1F;7(!1Y)b.$1p=b.$1a=H}},1t:q(c,b,d){b=6.2L(b||[]);7(!d)6.F(9.Y[c]||[],q(){6.S.1t(c,b,9)});B{u a,1Y,11=6.16(d[c]||H);b.5p(9.42({G:c,1O:d}));7(6.16(d.$1p)&&(a=d.$1p.O(d,b))!==N)9.47=K;7(11&&a!==N&&!6.Q(d,\'a\'))d[c]();9.47=N}},1p:q(b){u a;b=6.S.42(b||18.S||{});u c=9.$1a&&9.$1a[b.G],1S=[].51.3D(19,1);1S.5p(b);E(u j V c){1S[0].44=c[j];1S[0].W=c[j].W;7(c[j].O(9,1S)===N){b.2d();b.2D();a=N}}7(6.M.1h)b.1O=b.2d=b.2D=b.44=b.W=H;v a},42:q(c){u a=c;c=6.1c({},a);c.2d=q(){7(a.2d)v a.2d();a.7H=N};c.2D=q(){7(a.2D)v a.2D();a.7F=K};7(!c.1O&&c.5n)c.1O=c.5n;7(6.M.20&&c.1O.1q==3)c.1O=a.1O.L;7(!c.41&&c.4j)c.41=c.4j==c.1O?c.7C:c.4j;7(c.5k==H&&c.5j!=H){u e=P.4h,b=P.4y;c.5k=c.5j+(e&&e.5i||b.5i);c.7z=c.7y+(e&&e.5f||b.5f)}7(!c.3h&&(c.5e||c.5d))c.3h=c.5e||c.5d;7(!c.5c&&c.5b)c.5c=c.5b;7(!c.3h&&c.1J)c.3h=(c.1J&1?1:(c.1J&2?3:(c.1J&4?2:0)));v c}};6.11.1c({3g:q(c,a,b){v c=="3z"?9.3Z(c,a,b):9.F(q(){6.S.1A(9,c,b||a,b&&a)})},3Z:q(d,b,c){v 9.F(q(){6.S.1A(9,d,q(a){6(9).3U(a);v(c||b).O(9,19)},c&&b)})},3U:q(a,b){v 9.F(q(){6.S.1E(9,a,b)})},1t:q(a,b){v 9.F(q(){6.S.1t(a,b,9)})},1R:q(){u a=19;v 9.5a(q(e){9.4u=0==9.4u?1:0;e.2d();v a[9.4u].O(9,[e])||N})},7w:q(f,g){q 3W(e){u p=e.41;1v(p&&p!=9)2g{p=p.L}25(e){p=9};7(p==9)v N;v(e.G=="3V"?f:g).O(9,[e])}v 9.3V(3W).59(3W)},1G:q(f){7(6.3d)f.O(P,[6]);B 6.2q.R(q(){v f.O(9,[6])});v 9}});6.1c({3d:N,2q:[],1G:q(){7(!6.3d){6.3d=K;7(6.2q){6.F(6.2q,q(){9.O(P)});6.2q=H}7(6.M.3j||6.M.2a)P.43("58",6.1G,N);7(!18.7v.C)6(18).1W(q(){6("#3T").1E()})}}});14 q(){6.F(("7u,7t,1W,7s,7r,3z,5a,7q,"+"7p,7o,7n,3V,59,7m,2y,"+"4k,7l,7k,7j,2c").2R(","),q(i,o){6.11[o]=q(f){v f?9.3g(o,f):9.1t(o)}});7(6.M.3j||6.M.2a)P.46("58",6.1G,N);B 7(6.M.1h){P.7i("<7h"+"7f 2m=3T 7e=K "+"2u=//:><\\/3b>");u a=P.4d("3T");7(a)a.7d=q(){7(9.3a!="1n")v;6.1G()};a=H}B 7(6.M.20)6.3N=3m(q(){7(P.3a=="79"||P.3a=="1n"){3M(6.3N);6.3N=H;6.1G()}},10);6.S.1A(18,"1W",6.1G)};7(6.M.1h)6(18).3Z("3z",q(){u a=6.S.Y;E(u b V a){u c=a[b],i=c.C;7(i&&b!=\'3z\')77 c[i-1]&&6.S.1E(c[i-1],b);1v(--i)}});6.11.1c({76:q(c,b,a){9.1W(c,b,a,1)},1W:q(g,d,c,e){7(6.16(g))v 9.3g("1W",g);c=c||q(){};u f="3K";7(d)7(6.16(d)){c=d;d=H}B{d=6.2E(d);f="50"}u h=9;6.2Z({1C:g,G:f,W:d,2t:e,1n:q(a,b){7(b=="28"||!e&&b=="4V")h.1b("27",a.3c).3J().F(c,[a.3c,b,a]);B c.O(h,[a.3c,b,a])}});v 9},72:q(){v 6.2E(9)},3J:q(){v 9.1L("3b").F(q(){7(9.2u)6.4U(9.2u);B 6.3I(9.2A||9.5H||9.27||"")}).2U()}});6.F("4T,4I,4S,4R,4Q,4P".2R(","),q(i,o){6.11[o]=q(f){v 9.3g(o,f)}});6.1c({1M:q(e,c,a,d,b){7(6.16(c)){a=c;c=H}v 6.2Z({G:"3K",1C:e,W:c,28:a,3G:d,2t:b})},6X:q(d,b,a,c){v 6.1M(d,b,a,c,1)},4U:q(b,a){v 6.1M(b,H,a,"3b")},6V:q(c,b,a){v 6.1M(c,b,a,"4N")},6T:q(d,b,a,c){7(6.16(b)){a=b;b={}}v 6.2Z({G:"50",1C:d,W:b,28:a,3G:c})},6S:q(a){6.36.21=a},6Q:q(a){6.1c(6.36,a)},36:{Y:K,G:"3K",21:0,4O:"6P/x-6O-34-6N",4K:K,38:K,W:H},32:{},2Z:q(s){s=6.1c({},6.36,s);7(s.W){7(s.4K&&1g s.W!="1s")s.W=6.2E(s.W);7(s.G.2H()=="1M"){s.1C+=((s.1C.J("?")>-1)?"&":"?")+s.W;s.W=H}}7(s.Y&&!6.3L++)6.S.1t("4T");u f=N;u h=18.4Z?14 4Z("6L.6J"):14 4J();h.7b(s.G,s.1C,s.38);7(s.W)h.3Q("6H-6G",s.4O);7(s.2t)h.3Q("6F-3O-6D",6.32[s.1C]||"6C, 6B 6A 6z 4r:4r:4r 6y");h.3Q("X-6x-6w","4J");7(s.56)s.56(h);7(s.Y)6.S.1t("4P",[h,s]);u g=q(d){7(h&&(h.3a==4||d=="21")){f=K;7(i){3M(i);i=H}u c;2g{c=6.54(h)&&d!="21"?s.2t&&6.4F(h,s.1C)?"4V":"28":"2c";7(c!="2c"){u b;2g{b=h.3E("53-3O")}25(e){}7(s.2t&&b)6.32[s.1C]=b;u a=6.55(h,s.3G);7(s.28)s.28(a,c);7(s.Y)6.S.1t("4Q",[h,s])}B 6.2X(s,h,c)}25(e){c="2c";6.2X(s,h,c,e)}7(s.Y)6.S.1t("4S",[h,s]);7(s.Y&&!--6.3L)6.S.1t("4I");7(s.1n)s.1n(h,c);7(s.38)h=H}};u i=3m(g,13);7(s.21>0)4C(q(){7(h){h.6r();7(!f)g("21")}},s.21);2g{h.6p(s.W)}25(e){6.2X(s,h,H,e)}7(!s.38)g();v h},2X:q(s,a,b,e){7(s.2c)s.2c(a,b,e);7(s.Y)6.S.1t("4R",[a,s,e])},3L:0,54:q(r){2g{v!r.23&&7A.7B=="4l:"||(r.23>=5u&&r.23<6l)||r.23==5m||6.M.20&&r.23==I}25(e){}v N},4F:q(a,c){2g{u b=a.3E("53-3O");v a.23==5m||b==6.32[c]||6.M.20&&a.23==I}25(e){}v N},55:q(r,b){u c=r.3E("6k-G");u a=!b&&c&&c.J("4B")>=0;a=b=="4B"||a?r.6j:r.3c;7(b=="3b")6.3I(a);7(b=="4N")a=49("("+a+")");7(b=="3F")6("<1x>").3F(a).3J();v a},2E:q(a){u s=[];7(a.15==2b||a.3C)6.F(a,q(){s.R(2l(9.6i)+"="+2l(9.2v))});B E(u j V a)7(a[j]&&a[j].15==2b)6.F(a[j],q(){s.R(2l(j)+"="+2l(9))});B s.R(2l(j)+"="+2l(a[j]));v s.5M("&")},3I:q(a){7(18.4A)18.4A(a);B 7(6.M.20)18.4C(a,0);B 49.3D(18,a)}});6.11.1c({1m:q(b,a){v b?9.1w({1u:"1m",29:"1m",1e:"1m"},b,a):9.1i(":1y").F(q(){9.T.U=9.2i?9.2i:"";7(6.1f(9,"U")=="1P")9.T.U="2h"}).2U()},1j:q(b,a){v b?9.1w({1u:"1j",29:"1j",1e:"1j"},b,a):9.1i(":4f").F(q(){9.2i=9.2i||6.1f(9,"U");7(9.2i=="1P")9.2i="2h";9.T.U="1P"}).2U()},5G:6.11.1R,1R:q(a,b){v 6.16(a)&&6.16(b)?9.5G(a,b):a?9.1w({1u:"1R",29:"1R",1e:"1R"},a,b):9.F(q(){6(9)[6(9).37(":1y")?"1m":"1j"]()})},6e:q(b,a){v 9.1w({1u:"1m"},b,a)},6d:q(b,a){v 9.1w({1u:"1j"},b,a)},6b:q(b,a){v 9.1w({1u:"1R"},b,a)},6a:q(b,a){v 9.1w({1e:"1m"},b,a)},69:q(b,a){v 9.1w({1e:"1j"},b,a)},7U:q(c,a,b){v 9.1w({1e:a},c,b)},1w:q(d,h,f,g){v 9.1l(q(){u c=6(9).37(":1y"),1H=6.5z(h,f,g),5y=9;E(u p V d){7(d[p]=="1j"&&c||d[p]=="1m"&&!c)v 6.16(1H.1n)&&1H.1n.O(9);7(p=="1u"||p=="29"){1H.U=6.1f(9,"U");1H.2f=9.T.2f}}7(1H.2f!=H)9.T.2f="1y";9.2k=6.1c({},d);6.F(d,q(a,b){u e=14 6.2M(5y,1H,a);7(b.15==3y)e.2W(e.1K(),b);B e[b=="1R"?c?"1m":"1j":b](d)})})},1l:q(a,b){7(!b){b=a;a="2M"}v 9.F(q(){7(!9.1l)9.1l={};7(!9.1l[a])9.1l[a]=[];9.1l[a].R(b);7(9.1l[a].C==1)b.O(9)})}});6.1c({5z:q(b,a,c){u d=b&&b.15==64?b:{1n:c||!c&&a||6.16(b)&&b,1B:b,2I:c&&a||a&&a.15!=45&&a||(6.2I.4i?"4i":"4w")};d.1B=(d.1B&&d.1B.15==3y?d.1B:{61:60,89:5u}[d.1B])||8a;d.2N=d.1n;d.1n=q(){6.5I(9,"2M");7(6.16(d.2N))d.2N.O(9)};v d},2I:{4w:q(p,n,b,a){v b+a*p},4i:q(p,n,b,a){v((-5W.5X(p*5W.8e)/2)+0.5)*a+b}},1l:{},5I:q(b,a){a=a||"2M";7(b.1l&&b.1l[a]){b.1l[a].4e();u f=b.1l[a][0];7(f)f.O(b)}},3B:[],2M:q(f,e,g){u z=9;u y=f.T;z.a=q(){7(e.3A)e.3A.O(f,[z.2o]);7(g=="1e")6.1b(y,"1e",z.2o);B{y[g]=8m(z.2o)+"4o";y.U="2h"}};z.5V=q(){v 39(6.1f(f,g))};z.1K=q(){u r=39(6.2z(f,g));v r&&r>-8l?r:z.5V()};z.2W=q(c,b){z.4s=(14 5T()).5S();z.2o=c;z.a();6.3B.R(q(){v z.3A(c,b)});7(6.3B.C==1){u d=3m(q(){u a=6.3B;E(u i=0;i<a.C;i++)7(!a[i]())a.8j(i--,1);7(!a.C)3M(d)},13)}};z.1m=q(){7(!f.24)f.24={};f.24[g]=6.1b(f.T,g);e.1m=K;z.2W(0,9.1K());7(g!="1e")y[g]="8q";6(f).1m()};z.1j=q(){7(!f.24)f.24={};f.24[g]=6.1b(f.T,g);e.1j=K;z.2W(9.1K(),0)};z.3A=q(a,c){u t=(14 5T()).5S();7(t>e.1B+z.4s){z.2o=c;z.a();7(f.2k)f.2k[g]=K;u b=K;E(u i V f.2k)7(f.2k[i]!==K)b=N;7(b){7(e.U!=H){y.2f=e.2f;y.U=e.U;7(6.1f(f,"U")=="1P")y.U="2h"}7(e.1j)y.U="1P";7(e.1j||e.1m)E(u p V f.2k)6.1b(y,p,f.24[p])}7(b&&6.16(e.1n))e.1n.O(f);v N}B{u n=t-9.4s;u p=n/e.1B;z.2o=6.2I[e.2I](p,n,a,(c-a),e.1B);z.a()}v K}}})}',62,524,'||||||jQuery|if||this|||||||||||||||||function||||var|return||||||else|length||for|each|type|null|undefined|indexOf|true|parentNode|browser|false|apply|document|nodeName|push|event|style|display|in|data||global|||fn|className||new|constructor|isFunction|test|window|arguments|events|attr|extend|firstChild|opacity|css|typeof|msie|filter|hide|mergeNum|queue|show|complete|replace|handle|nodeType|table|string|trigger|height|while|animate|div|hidden|tbody|add|duration|url|toUpperCase|remove|break|ready|opt|_|button|cur|find|get|tb|target|none|guid|toggle|args|merge|styleFloat|exec|load|nextSibling|ret|pushStack|safari|timeout|nth|status|orig|catch|al|innerHTML|success|width|opera|Array|error|preventDefault|rl|overflow|try|block|oldblock|done|curAnim|encodeURIComponent|id||now|index|readyList|disabled|grep|ifModified|src|value|inArray|multiFilter|select|curCSS|text|checked|trim|stopPropagation|param|domManip|substr|toLowerCase|easing|chars|insertBefore|makeArray|fx|old|ownerDocument|last|first|split|childNodes|selected|end|tr|custom|handleError|empty|ajax|el|clean|lastModified|String|form|getAttribute|ajaxSettings|is|async|parseFloat|readyState|script|responseText|isReady|append|defaultView|bind|which|position|mozilla|map|delete|setInterval|static|RegExp|oWidth|removeChild|oHeight|cloneNode|match|child|toString|has|float|Number|unload|step|timers|jquery|call|getResponseHeader|html|dataType|props|globalEval|evalScripts|GET|active|clearInterval|safariTimer|Modified|num|setRequestHeader|getElementsByTagName|currentStyle|__ie_init|unbind|mouseover|handleHover|init|getComputedStyle|one|isXMLDoc|relatedTarget|fix|removeEventListener|handler|Function|addEventListener|triggered|visibility|eval|nodeIndex|radio|classFilter|getElementById|shift|visible|appendChild|documentElement|swing|fromElement|submit|file|swap|expr|px|parents|sibling|00|startTime|previousSibling|lastToggle|parent|linear|ol|body|tagName|execScript|xml|setTimeout|version|setArray|httpNotModified|fl|prop|ajaxStop|XMLHttpRequest|processData|alpha|href|json|contentType|ajaxSend|ajaxSuccess|ajaxError|ajaxComplete|ajaxStart|getScript|notmodified|colgroup|100|not|ActiveXObject|POST|slice|fieldset|Last|httpSuccess|httpData|beforeSend|getPropertyValue|DOMContentLoaded|mouseout|click|ctrlKey|metaKey|keyCode|charCode|scrollTop|unique|createElement|scrollLeft|clientX|pageX|webkit|304|srcElement|appendTo|unshift|prevObject|on|after|before|200|removeAttr|prepend|cssFloat|self|speed|parse|input|reset|image|password|checkbox|_toggle|textContent|dequeue|lastChild|odd|even|join|contains|gt|lt|eq|nodeValue|getTime|Date|zoom|max|Math|cos|font|maxLength|600|slow|maxlength|readOnly|Object|readonly|createTextNode|class|htmlFor|fadeOut|fadeIn|slideToggle|CSS1Compat|slideUp|slideDown|compatMode|boxModel|compatible|name|responseXML|content|300|ie|noConflict|ra|send|it|abort|rv|userAgent|navigator|concat|With|Requested|GMT|1970|Jan|01|Thu|Since|reverse|If|Type|Content|array|XMLHTTP|ig|Microsoft|NaN|urlencoded|www|application|ajaxSetup|val|ajaxTimeout|post|setAttribute|getJSON|getAttributeNode|getIfModified|method|FORM|action|options|serialize|col|th|td|loadIfModified|do|colg|loaded|tfoot|open|thead|onreadystatechange|defer|ipt|leg|scr|write|keyup|keypress|keydown|change|mousemove|mouseup|mousedown|dblclick|scroll|resize|focus|blur|frames|hover|clone|clientY|pageY|location|protocol|toElement|clientWidth|clientHeight|cancelBubble|relative|returnValue|left|detachEvent|right|absolute|attachEvent|substring|offsetWidth|object|offsetHeight|continue|Width|border|fadeTo|padding|size|uFFFF|Left|u0128|Right|Bottom|textarea|Top|enabled|innerText|only|toggleClass|removeClass|fast|400|wrap|addClass|removeAttribute|PI|insertAfter|prependTo|children|line|splice|siblings|10000|parseInt|prev|next|weight|1px|prototype'.split('|'),0,{}))
0  public/nothing.element
No changes.
34 public/open.element
@@ -0,0 +1,34 @@
+
+<div id="preview" style="display: none;">
+</div>
+
+<h2 id="comment-message">Add a comment</h2>
+
+<form name="comment" action="/script/submit.rb" method="post" onsubmit="return false;">
+ <p id="author">
+ <label for="author">Name:</label>
+ <input type="text" name="author">
+ </p>
+ <p id="email">
+ <label for="email">Email:</label>
+ <input type="text" name="email">
+ </p>
+ <p id="contact">
+ <label for="contact">E<span></span>mail:</label>
+ <input type="text" name="contact">
+ </p>
+ <p id="blog">
+ <label for="blog">Blog:</label>
+ <input type="text" name="blog">
+ </p>
+ <p id="body">
+ <textarea name="body"></textarea>
+ </p>
+ <p>Textile/Markdown enabled.</p>
+ <p id="submit">
+ <label for="preview"></label>
+ <input name="preview" type="submit" value="Preview" onclick="Submit(this);">
+ <input name="submit" type="submit" value="Submit" onclick="Submit(this);">
+ </p>
+
+</form>
31 public/post_full.element
@@ -0,0 +1,31 @@
+
+<div class="post">
+ <div class="top">
+ <h2>
+ <!--#include virtual='title.element' -->
+ </h2>
+ </div>
+ <div class="mid">
+ <div class="body">
+ <!--#include virtual='intro.element' -->
+ <!--#include virtual='body.element' -->
+ <p class="date"><!--#flastmod virtual='body.element' --></p>
+ </div>
+ </div>
+ <div class="bottom"></div>
+</div>
+<div class="post comments">
+ <div class="top">
+ <h2>
+ <a href="#comments" name="comments"><!--#include virtual='comments/count.element' --></a>
+ </h2>
+ </div>
+ <div class="mid">
+ <div class="body">
+ <!--#include virtual='comments/content.element' -->
+ <!--#include virtual='comments/form.element' -->
+ </div>
+ </div>
+ <div class="bottom"></div>
+</div>
+
7 public/post_index.element
@@ -0,0 +1,7 @@
+<h2 class="post_index">
+ <a href="<!--#include virtual='url.element' -->">
+ <!--#include virtual='title.element' -->
+ </a>
+</h2>
+<span class="date"><!--#flastmod virtual='body.element' --></span>
+<a href="<!--#include virtual='url.element' -->#comments"><!--#include virtual='comments/count.element' --></a>
23 public/post_short.element
@@ -0,0 +1,23 @@
+
+<div class="post">
+ <div class="top">
+ <h2>
+ <a href="<!--#include virtual='url.element' -->">
+ <!--#include virtual='title.element' -->
+ </a>
+ </h2>
+ </div>
+ <div class="mid">
+ <div class="body">
+ <!--#include virtual='intro.element' --><!--#include virtual='ellipsis.element' -->
+ <p class="date"><!--#flastmod virtual='body.element' --></p>
+ </div>
+ </div>
+ <div class="bottom">
+ <p>
+ <!--#include virtual='link.element' -->
+ <a href="<!--#include virtual='url.element' -->#comments"><!--#include virtual='comments/count.element' --></a>
+ </p>
+ </div>
+</div>
+
1  public/read_more.element
@@ -0,0 +1 @@
+<a href="<!--#include virtual='url.element' -->">Read more...</a><br>
17 public/static.element
@@ -0,0 +1,17 @@
+
+<div class="post">
+ <div class="top">
+ <h2>
+ <!--#include virtual='title.element' -->
+ </h2>
+ </div>
+ <div class="mid">
+ <div class="body">
+ <p class="date"><!--#flastmod virtual='body.element' --></p>
+ <!--#include virtual='body.element' -->
+ </div>
+ </div>
+ <div class="bottom">
+ </div>
+</div>
+
118 public/template.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <title>Snax</title>
+ <link rel="stylesheet" type="text/css" href="yui/build/reset/reset.css">
+ <link rel="stylesheet" type="text/css" href="yui/build/base/base.css">
+ <link rel="stylesheet" type="text/css" href="yui/build/grids/grids.css">
+ <link rel="stylesheet" type="text/css" href="yui/build/fonts/fonts.css">
+ <link rel="stylesheet" type="text/css" href="css/bax.css">
+ <script src="js/jquery-1.1.3.1.js" type="text/javascript"></script>
+</head>
+
+<body>
+<!-- the id on the containing div determines the page width. -->
+<!-- #doc = 750px; #doc2 = 950px; #doc3 = 100%; #doc4 = 974px -->
+<div id="leaves_cutout">
+<div id="top_left_leaves">
+<div id="top_right_leaves">
+<div id="bottom_left_leaves">
+<div id="bottom_right_leaves">
+
+ <div id="doc2">
+ <div id="hd" class="header">
+ <h1><a href="">snax</a></h1>
+ <h2>a ruby blog</h2>
+ </div>
+ <div id="bd">
+
+ <!-- Use Standard Nesting Grids and Special Nesting Grids to subdivid regions of your layout. -->
+ <!-- Special Nesting Grid F has two children, the first is 1/4, the second is 3/4 -->
+ <div class="yui-gf">
+
+ <!-- the first child of a Grid needs the "first" class -->
+ <div class="yui-u first sidebar">
+ <div class="mid">
+ <div class="content">
+
+ <ul>
+ <li><h3>meta</h3></li>
+ <li class="item"><a href="">about evan</a></li>
+ <li class="item"><a href="">cloudburst</a></li>
+ <li class="item"><a href="">hire me</a></li>
+ </ul>
+
+ <ul>
+ <li><h3><a href="">subscribe</a></h3></li>
+ </ul>
+
+ <ul>
+ <li><h3>projects</h3></li>
+ <li class="item"><a href="">polymorphs</a></li>
+ <li class="item"><a href="">allison</a></li>
+ <li class="item"><a href="">bleak_house</a></li>
+ <li class="item"><a href="">facebook_exceptions</a></li>
+ <li class="item"><a href="">ultrasphinx</a></li>
+ <li class="item"><a href="">kirby</a></li>
+ <li class="item"><a href="">echoe</a></li>
+ <li class="item"><a href="">cgi_multipart_eof_fix</a></li>
+ <li class="item"><a href="">rv</a></li>
+ <li class="item"><a href="">raspell</a></li>
+ <li class="item"><a href="">shadow</a></li>
+ </ul>
+
+ <ul>
+ <li><h3>recent articles</h3></li>
+ <li class="item"><a href=""> polymorphs tutorial</a></li>
+ <li class="item"><a href=""> svn branching best practices (in practice)</a></li>
+ <li class="item"><a href=""> desensitize your mac</a></li>
+ <li class="item"><a href=""> formulas</a></li>
+ <li class="item"><a href=""> attack of the beasts</a></li>
+ </ul>
+
+ </div>
+ </div>
+ </div>
+
+ <div class="yui-u content">
+
+ <div class="post">
+ <div class="top"><h2><a href="">polymorphs tutorial</a></h2></div>
+ <div class="mid">
+ <div class="body">
+ <p>Pratik Naik posted an introductory tutorial to has_many_polymorphs the other day. Looks good, and worth checking out if you're just getting started.
+ </p>
+ </div>
+ </div>
+ <div class="bottom"><p><a href="">Comments</a></p></div>
+ </div>
+
+ <div class="post">
+ <div class="top"><h2><a href="">svn branching best practices (in practice)</a></h2></div>
+ <div class="mid">
+ <div class="body">
+ <p>You want to make a Subversion branch, and merge it later. You read the branching section in the official book, but are still confused. What to do?</p>
+ <p>Get your step-by-step instructions here...</p>
+ </div>
+ </div>
+ <div class="bottom"><p><a href="">Read more</a><br><a href="">7 comments</a></p></div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+ <div id="ft" class="footer">
+ <p>&copy; 2007 <a href="http://cloudbur.st">Cloudburst, LLC</a></p>
+ </div>
+
+ </div>
+
+</div>
+</div>
+</div>
+</div>
+</div>
+
+</body>
+</html>
1  public/title.element
@@ -0,0 +1 @@
+home
1  public/url.element
@@ -0,0 +1 @@
+/articles/2007/08/15/svn-branching-best-practices-in-practice
15 public/xml/atom.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#160;"> ]>
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <title>Snax</title>
+ <link href="http://blog.evanweaver.com/xml/atom.xml" rel="self"/>
+ <link href="http://blog.evanweaver.com/"/>
+ <!--#config timefmt='%Y-%m-%dT%H:%M:%SZ'-->
+ <updated><!--#flastmod virtual='content.element' --></updated>
+ <author>
+ <name>Evan Weaver</name>
+ <email>snax@evanweaver.com</email>
+ </author>
+ <id>http://blog.evanweaver.com/</id>
+ <!--#include virtual='content.element' -->
+</feed>
1  public/xml/content.element
@@ -0,0 +1 @@
+<!--#include virtual='/articles/2007/08/21/sample-article/xml.post.element' -->
13 public/xml/post.element
@@ -0,0 +1,13 @@
+<entry>
+ <title><!--#include virtual='title.element' --></title>
+ <link href="http://blog.evanweaver.com<!--#include virtual='url.element' -->"/>
+ <id>http://blog.evanweaver.com<!--#include virtual='url.element' --></id>
+ <!--#config timefmt='%Y-%m-%dT%H:%M:%SZ'-->
+ <updated><!--#flastmod virtual='body.element' --></updated>
+ <content type="xhtml">
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <!--#include virtual='intro.element' -->
+ <!--#include virtual='body.element' -->
+ </div>
+ </content>
+</entry>
5 public/yui/build/base/README
@@ -0,0 +1,5 @@
+YUI Library - Base - Release Notes
+
+Version 2.3.0
+
+ * Initial release.
7 public/yui/build/base/base-min.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.3.0
+*/
+h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong{font-weight:bold;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;}em{font-style:italic;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style: decimal outside;}ul li{list-style: disc outside;}dl dd{margin-left:1em;}th,td {border:1px solid #000;padding:.5em;}th {font-weight:bold;text-align:center;}caption {margin-bottom:.5em;text-align:center;}p,fieldset,table {margin-bottom:1em;}
76 public/yui/build/base/base.css
@@ -0,0 +1,76 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.3.0
+*/
+/* base.css, part of YUI's CSS Foundation */
+h1 {
+ /*18px via YUI Fonts CSS foundation*/
+ font-size:138.5%;
+}
+h2 {
+ /*16px via YUI Fonts CSS foundation*/
+ font-size:123.1%;
+}
+h3 {
+ /*14px via YUI Fonts CSS foundation*/
+ font-size:108%;
+}
+h1,h2,h3 {
+ /* top & bottom margin based on font size */
+ margin:1em 0;
+}
+h1,h2,h3,h4,h5,h6,strong {
+ /*bringing boldness back to headers and the strong element*/
+ font-weight:bold;
+}
+abbr,acronym {
+ /*indicating to users that more info is available */
+ border-bottom:1px dotted #000;
+ cursor:help;
+}
+em {
+ /*bringing italics back to the em element*/
+ font-style:italic;
+}
+blockquote,ul,ol,dl {
+ /*giving blockquotes and lists room to breath*/
+ margin:1em;
+}
+ol,ul,dl {
+ /*bringing lists on to the page with breathing room */
+ margin-left:2em;
+}
+ol li {
+ /*giving OL's LIs generated numbers*/
+ list-style: decimal outside;
+}
+ul li {
+ /*giving UL's LIs generated disc markers*/
+ list-style: disc outside;
+}
+dl dd {
+ /*giving UL's LIs generated numbers*/
+ margin-left:1em;
+}
+th,td {
+ /*borders and padding to make the table readable*/
+ border:1px solid #000;
+ padding:.5em;
+}
+th {
+ /*distinguishing table headers from data cells*/
+ font-weight:bold;
+ text-align:center;
+}
+caption {
+ /*coordinated marking to match cell's padding*/
+ margin-bottom:.5em;
+ /*centered so it doesn't blend in to other content*/
+ text-align:center;
+}
+p,fieldset,table {
+ /*so things don't run into each other*/
+ margin-bottom:1em;
+}
25 public/yui/build/fonts/README
@@ -0,0 +1,25 @@
+YUI Library - Fonts - Release Notes
+
+Version 2.3.0
+
+ * Updated %-to-px conversion charted for increased accuracy
+ across browsers
+ * Move body line-height away from * wildcard selector. Now it is
+ part of the initial BODY rule set.
+ * Changed core lineheight from 1.22em to 1.231 (no units)
+ * Changed code,pre to not use font-property shorthand syntax
+ * Tweaked the sizing fix for monospace font size (pre,code...)
+ * Added "kbd,samp,tt" as new selectors for monospaced font stuff
+ * Added line-height:99% to keep monospaced font the right height
+
+Version 2.2.0 - 2.2.2
+
+ * No changes.
+
+Version 0.10.1 - 0.12.2
+
+ * No changes.
+
+Version 0.10.0
+
+ * Initial release.
7 public/yui/build/fonts/fonts-min.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.3.0
+*/
+body {font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}table {font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:99%;}
16 public/yui/build/fonts/fonts.css
@@ -0,0 +1,16 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.3.0
+*/
+/**
+ * Percents could work for IE, but for backCompat purposes, we are using keywords.
+ * x-small is for IE6/7 quirks mode.
+ */
+body {font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}
+table {font-size:inherit;font:100%;}
+/**
+ * Bump up IE to get to 13px equivalent
+ */
+pre,code,kbd,samp,tt {font-family:monospace;*font-size:108%;line-height:99%;}
55 public/yui/build/grids/README
@@ -0,0 +1,55 @@
+YUI Library - Grids - Release Notes
+
+Version 2.3.0
+
+ * Added more specific selectors to allow deeper nesting of both
+ grids inside special grids, special grids within special
+ grids, and special grids within grids.
+ * Added #doc4 which provides a 974px page width
+
+Version 2.2.0 - 2.2.2
+
+ * No changes.
+
+Version 0.12.1 - 0.12.2
+
+ * No changes.
+
+Version 0.12.0
+
+ * Removed redundant "text-align:left" from nodes below #doc.
+
+ * Removed small-font treatment on #ft.
+
+ * Removed margin-bottom from #hd,#bd.
+
+ * Added two new #doc definitions (#doc2 and #doc3) that provide
+ 950px centered and 100% page width presets to supplement the
+ original 750px centered page width.
+
+ * Made ".first" selectors more precise by binding to divs, to
+ make it less likely that our .first definitions will impact
+ nested content elements wishing to use this nomenclature.
+
+ * Fixed "nested yui-gc" bug.
+
+ * Fixed build-process whitespace bug with .yui-t2 and .yui-t5.
+
+ * Migrated .yui-t's technique to "negative margins" technique
+ from "floated ems".
+
+ * The "negative margins" technique caused z-index issues, so
+ position:relative was added to the secondary yui-b to enable
+ correct z-index.
+
+ * Make optimization benefits from inheritence wins via code
+ reorganizations.
+
+Version 0.11.0
+
+ * Removed line #43 because it set an already-set value.
+ Was: ".yui-t7 #main .yui-b{min-width:750px;}"
+
+Version 0.10.0
+
+ * Initial release.
7 public/yui/build/grids/grids-min.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.3.0
+*/
+body{text-align:center;}#ft{clear:both;}#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.301em;min-width:750px;}#doc2{width:73.074em;*width:71.313em;}#doc3{margin:auto 10px;width:auto;}#doc4{width:74.923em;*width:73.117em;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.3207em;*width:12.0106em;}.yui-t1 #yui-main .yui-b{margin-left:13.3207em;*margin-left:13.0106em;}.yui-t2 .yui-b{float:left;width:13.8456em;*width:13.512em;}.yui-t2 #yui-main .yui-b{margin-left:14.8456em;*margin-left:14.512em;}.yui-t3 .yui-b{float:left;width:23.0759em;*width:22.52em;}.yui-t3 #yui-main .yui-b{margin-left:24.0759em;*margin-left:23.52em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.512em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.512em;}.yui-t5 .yui-b{float:right;width:18.4608em;*width:18.016em;}.yui-t5 #yui-main .yui-b{margin-right:19.4608em;*margin-right:19.016em;}.yui-t6 .yui-b{float:right;width:23.0759em;*width:22.52em;}.yui-t6 #yui-main .yui-b{margin-right:24.0759em;*margin-right:23.52em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;margin-left:2%;width:32%;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{_margin-left:1.2%;*margin-left:1.7%;}.yui-g .yui-gb .yui-u{_margin-left:.8%;}.yui-gb .yui-u{margin-left:2%;width:32%;float:right;*width:31.8%;}.yui-gb div.first{margin-left:0;*margin-right:1px;float:left;}.yui-gb .yui-gb div.first{*margin-right:0;width:31.7%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-g .yui-gc div.first,.yui-g .yui-ge div.first{float:left;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-gb .yui-gc .yui-u,.yui-g .yui-gc .yui-u{_float:right;width:32%;_width:29.9%;}.yui-gb .yui-gc div.first{width:66%;*width:64.5%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;display:inline;}.yui-g div.first,.yui-g div.first,.yui-gc div.first,.yui-gc div.first div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first{float:left;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:48.1%;*margin-left:0;}.yui-g .yui-g div.first{*margin:0;}.yui-gb .yui-g div.first{*margin-right:1px;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{_width:48.1%;*width:48.1%;*margin-left:0;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-gc div.first,.yui-gc div.first,.yui-gd .yui-g,.yui-gd .yui-u{width:66%;}.yui-gd div.first,.yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf div.first{width:24%;}.yui-ge div.first,.yui-gf .yui-g,.yui-gf .yui-u{width:74.2%;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}#bd:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#bd,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;}
216 public/yui/build/grids/grids.css
@@ -0,0 +1,216 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.3.0
+*/
+/* for all templates and grids */
+body{text-align:center;}
+#ft{clear:both;}
+/**/
+/* 750 centered, and backward compatibility */
+#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7 {
+ margin:auto;text-align:left;
+ width:57.69em;*width:56.301em;min-width:750px;}
+/* 950 centered */
+#doc2 {
+ width:73.074em;*width:71.313em;}
+/* 100% with 10px viewport side matting */
+#doc3 {
+ margin:auto 10px; /* not for structure, but so content doesn't bleed to edge */
+ width:auto;}
+/* 974 centered */
+#doc4 {
+ width:74.923em;*width:73.117em;}
+
+/* below required for all fluid grids; adjust widths and margins above accordingly */
+
+ /* to preserve source-order independence for Gecko */
+ .yui-b{position:relative;}
+ .yui-b{_position:static;} /* for IE < 7 */
+ #yui-main .yui-b{position:static;}
+
+#yui-main {width:100%;}
+.yui-t1 #yui-main,
+.yui-t2 #yui-main,
+.yui-t3 #yui-main{float:right;margin-left:-25em;/* IE: preserve layout at narrow widths */}
+
+.yui-t4 #yui-main,
+.yui-t5 #yui-main,
+.yui-t6 #yui-main{float:left;margin-right:-25em;/* IE: preserve layout at narrow widths */}
+
+.yui-t1 .yui-b {
+ float:left;
+ width:12.3207em;*width:12.0106em;}
+.yui-t1 #yui-main .yui-b{
+ margin-left:13.3207em;*margin-left:13.0106em;
+}
+
+.yui-t2 .yui-b {
+ float:left;
+ width:13.8456em;*width:13.512em;}
+.yui-t2 #yui-main .yui-b {
+ margin-left:14.8456em;*margin-left:14.512em;
+}
+
+.yui-t3 .yui-b {
+ float:left;
+ width:23.0759em;*width:22.52em;}
+.yui-t3 #yui-main .yui-b {
+ margin-left:24.0759em;*margin-left:23.52em;
+}
+
+.yui-t4 .yui-b {
+ float:right;
+ width:13.8456em;*width:13.512em;}
+.yui-t4 #yui-main .yui-b {
+ margin-right:14.8456em;*margin-right:14.512em;
+}
+
+.yui-t5 .yui-b {
+ float:right;
+ width:18.4608em;*width:18.016em;}
+.yui-t5 #yui-main .yui-b {
+ margin-right:19.4608em;*margin-right:19.016em;
+}
+
+.yui-t6 .yui-b {
+ float:right;
+ width:23.0759em;*width:22.52em;}
+.yui-t6 #yui-main .yui-b {
+ margin-right:24.0759em;*margin-right:23.52em;
+}
+
+.yui-t7 #yui-main .yui-b {
+ display:block;margin:0 0 1em 0;
+}
+#yui-main .yui-b {float:none;width:auto;}
+/* GRIDS (not TEMPLATES) */
+
+.yui-gb .yui-g,/*new for nesting normal grids in special grids*/
+.yui-gb .yui-gb,/*new for nesting normal grids in special grids*/
+.yui-gb .yui-u,
+.yui-g .yui-gb .yui-u,
+.yui-gb .yui-gc,
+.yui-gb .yui-gd,
+.yui-gb .yui-ge,
+.yui-gb .yui-gf,
+.yui-gc .yui-u,
+.yui-gc .yui-g,
+.yui-gd .yui-u{float:left;margin-left:2%;width:32%;}
+
+.yui-gb .yui-gb .yui-u,
+.yui-gb .yui-gc .yui-u {_margin-left:1.2%;*margin-left:1.7%;}
+.yui-g .yui-gb .yui-u {_margin-left:.8%;} /* for #8-10 for IE6 */
+.yui-gb .yui-u {margin-left:2%;width:32%;float:right;*width:31.8%;}
+.yui-gb div.first {margin-left:0;*margin-right:1px;float:left;}
+.yui-gb .yui-gb div.first {*margin-right:0;width:31.7%;} /* for #29 for IE7*/
+.yui-gb .yui-gc div.first, /* for #41 for IE7 */
+.yui-gb .yui-gd div.first /* for #40 for IE7 */
+ {*margin-right:0;}
+
+.yui-g .yui-gc div.first,
+.yui-g .yui-ge div.first {float:left;}
+
+.yui-gb .yui-gd .yui-u {*width:66%;_width:61.2%;}
+.yui-gb .yui-gd div.first {*width:31%;_width:29.5%;}
+
+.yui-gb .yui-gc .yui-u,
+.yui-g .yui-gc .yui-u {_float:right;width:32%;_width:29.9%;}
+.yui-gb .yui-gc div.first {width:66%;*width:64.5%;*float:left;*margin-left:0;}
+.yui-gb .yui-ge .yui-u,
+.yui-gb .yui-gf .yui-u {margin:0;}
+
+ /*Float units (and sub grids) to the right */
+ .yui-g .yui-u,
+ .yui-g .yui-g,
+ .yui-g .yui-gb,
+ .yui-g .yui-gc,
+ .yui-g .yui-gd,
+ .yui-g .yui-ge,
+ .yui-g .yui-gf,
+ .yui-gc .yui-u,
+ .yui-gd .yui-g,
+ .yui-g .yui-gc .yui-u,
+ .yui-ge .yui-u,
+ .yui-ge .yui-g,
+ .yui-gf .yui-g,
+ .yui-gf .yui-u{float:right;display:inline;}
+
+ /*Float units (and sub grids) to the left */
+ .yui-g div.first,
+ .yui-g div.first,
+ .yui-gc div.first,
+ .yui-gc div.first div.first,
+ .yui-gd div.first,
+ .yui-ge div.first,
+ .yui-gf div.first{float:left;}
+
+.yui-g .yui-g .yui-u,
+.yui-gb .yui-g .yui-u,
+.yui-gc .yui-g .yui-u,
+.yui-gd .yui-g .yui-u,
+.yui-ge .yui-g .yui-u,
+.yui-gf .yui-g .yui-u {width:48.1%;*margin-left:0;}
+.yui-g .yui-g div.first {*margin:0;}
+.yui-gb .yui-g div.first {*margin-right:1px;}
+
+.yui-gb .yui-gb .yui-u {_margin-left:.7%;} /* for #23-32 for IE6*/
+.yui-gb .yui-g div.first, /* for #23 for IE6*/
+.yui-gb .yui-gb div.first {*margin-left:0;}
+
+.yui-gc .yui-g .yui-u,
+.yui-gd .yui-g .yui-u {_width:48.1%;*width:48.1%;*margin-left:0;}
+
+.yui-g .yui-u,
+.yui-g .yui-g,
+.yui-g .yui-gb,
+.yui-g .yui-gc,
+.yui-g .yui-gd,
+.yui-g .yui-ge,
+.yui-g .yui-gf {width:49.1%;}
+
+.yui-g .yui-gb div.first,
+ .yui-gb div.first,
+ .yui-gc div.first,
+ .yui-gd div.first {margin-left:0;}
+
+.yui-g .yui-gc div.first,
+.yui-gc div.first,
+.yui-gd .yui-g, /* for 056, 057 */
+.yui-gd .yui-u {width:66%;}
+
+.yui-gd div.first,
+.yui-gb .yui-gd div.first {width:32%;}
+.yui-g .yui-gd div.first {_width:29.9%;}
+
+.yui-ge .yui-u,
+.yui-ge .yui-g,
+.yui-gf div.first {width:24%;}
+
+.yui-ge div.first,
+.yui-gf .yui-g,
+.yui-gf .yui-u{width:74.2%;}
+
+/* narrower width in nexted contexts */
+.yui-gb .yui-ge .yui-u,
+.yui-gb .yui-gf div.first {*width:24%;_width:20%;}
+
+/* narrower width in nexted contexts */
+.yui-gb .yui-ge div.first,
+.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}
+
+#bd:after,
+.yui-g:after,
+.yui-gb:after,
+.yui-gc:after,
+.yui-gd:after,
+.yui-ge:after,
+.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}
+#bd,
+.yui-g,
+.yui-gb,
+.yui-gc,
+.yui-gd,
+.yui-ge,
+.yui-gf{zoom:1;}
34 public/yui/build/reset/README
@@ -0,0 +1,34 @@
+YUI Library - Reset - Release Notes
+
+Version 2.3.0
+
+ * Removed: ul,ol {list-style:none;}
+ * Added: li {list-style:none;} because it's less impactful (easier to rebuild) and shorter
+ * Added: acronym {font-variant:normal;} to reset the "small-caps" variant that Opera displays
+ * Added: body {color:#000;background:#FFF;}
+ * Added: sup,sub {line-height:-1px;vertical-align: text-top;}sub{vertical-align:text-bottom;}
+ * Added: input, textarea, select{font-family:inherit;font-size:inherit;font-weight:inherit;} (doesn't fix textareas in Opera/Win+Mac, IE6/7
+ * Added: padding:0;margin:0; for the legend element which wasn't fully reset before (has 2px lateral padding on some browsers)
+
+
+Version 2.2.0 - 2.2.2
+
+ * No changes.
+
+Version 0.12.1 - 0.12.2
+
+ * No changes.
+
+Version 0.12.0
+
+ * Added: h1,h2,h3,h4,h5,h6{font-weight:normal;}
+ * Added: abbr,acronym {border:0;}
+ * Added: textarea {padding:0;margin:0;}
+
+Version 0.11.0
+
+ * No changes.
+
+Version 0.10.0
+
+ * Initial release.
7 public/yui/build/reset/reset-min.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.3.0
+*/
+body{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup,sub{line-height:-1px;vertical-align:text-top;}sub{vertical-align:text-bottom;}input, textarea, select{font-family:inherit;font-size:inherit;font-weight:inherit;}
19 public/yui/build/reset/reset.css
@@ -0,0 +1,19 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.3.0
+*/
+body {color:#000;background:#FFF;}
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}
+table{border-collapse:collapse;border-spacing:0;}
+fieldset,img{border:0;}
+address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}
+li {list-style:none;}
+caption,th {text-align:left;}
+h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}
+q:before,q:after{content:'';}
+abbr,acronym {border:0;font-variant:normal;}
+sup,sub {line-height:-1px;vertical-align: text-top;}
+sub {vertical-align:text-bottom;}
+input, textarea, select{font-family:inherit;font-size:inherit;font-weight:inherit;}
65 script/bax.rb
@@ -0,0 +1,65 @@
+#!/usr/bin/env ruby
+
+require 'cgi'
+require 'support'
+$LOAD_PATH << 'superredcloth/lib'
+require 'superredcloth'
+require 'set'
+require 'html/tokenizer'
+require 'html/node'
+require 'white_list_helper'
+
+self.class.send(:include, WhiteListHelper)
+
+cgi = CGI.new
+params = Hash[*cgi.params.map do |key, values|
+ [key, white_list(key == 'body' ? SuperRedCloth.new(values.join).to_html : values.join)]
+end.flatten]
+
+old_author = params['author']
+
+# mangle some urls
+if !(blog = params['blog'].chomp('/')).empty?
+ blog = "http://" + blog unless blog =~ /http:\/\//
+ author = params['author']
+ author = "<a href=\"#{blog}\" rel=\"nofollow\">#{author}</a>"
+ if params['author'] =~ /OPTIONS['author']/ and blog == OPTIONS['url']
+ author = "<span class=\"me\">#{author}</span>"
+ end
+ params['author'] = author
+end
+
+if !params['article']
+ # preview... parse ourselves so we don't have to write to the filesystem
+ puts "Content-Type: text/html\n\n"
+ puts "<div id=\"preview-inner\">"
+ open("#{File.dirname(__FILE__)}/../public/comment.element").each do |line|
+ if line =~ /#(flastmod|include) virtual='(.*?).element'/
+ repl = ($1 == "flastmod" ? "preview" : params[$2]) || "nil"
+ line.gsub! /<!--.*?-->/, repl
+ end
+ puts line
+ end
+ puts "</div>"
+
+elsif missing = ['author', 'contact', 'body'].detect{|s| params[s].empty? }
+ missing = 'email' if missing == 'contact'
+ puts "Content-Type: text/html\n\n"
+ puts "<p class=\"error\">#{missing.capitalize} is required.</p>"
+
+elsif params['email'].empty? and !params['body'].empty?
+ # submitted, and not spam, we hope
+ params['article'] =~ /(.*)\/(articles\/.*?)\/($|\#|\?)/
+ base, url = $1, $2
+
+ comment_url = add_comment url, params
+ puts "Location: #{comment_url}\n\n"
+ STDOUT.close # don't make the user wait while we clean shit up
+
+ index_comments url
+ notify "New comment by #{old_author} on #{base}/#{url}#comments"
+else
+ # else spam
+ puts "Content-Type: text/plain\n\n"
+ puts "Ok." # lies
+end
65 script/html/document.rb
@@ -0,0 +1,65 @@
+require File.dirname(__FILE__) + '/tokenizer'
+require File.dirname(__FILE__) + '/node'
+require File.dirname(__FILE__) + '/selector'
+
+module HTML #:nodoc:
+
+ # A top-level HTMl document. You give it a body of text, and it will parse that
+ # text into a tree of nodes.
+ class Document #:nodoc:
+
+ # The root of the parsed document.
+ attr_reader :root
+
+ # Create a new Document from the given text.
+ def initialize(text, strict=false, xml=false)
+ tokenizer = Tokenizer.new(text)
+ @root = Node.new(nil)
+ node_stack = [ @root ]
+ while token = tokenizer.next
+ node = Node.parse(node_stack.last, tokenizer.line, tokenizer.position, token)
+
+ node_stack.last.children << node unless node.tag? && node.closing == :close
+ if node.tag?
+ if node_stack.length > 1 && node.closing == :close
+ if node_stack.last.name == node.name
+ node_stack.pop
+ else
+ open_start = node_stack.last.position - 20
+ open_start = 0 if open_start < 0
+ close_start = node.position - 20
+ close_start = 0 if close_start < 0
+ msg = <<EOF.strip
+ignoring attempt to close #{node_stack.last.name} with #{node.name}
+ opened at byte #{node_stack.last.position}, line #{node_stack.last.line}
+ closed at byte #{node.position}, line #{node.line}
+ attributes at open: #{node_stack.last.attributes.inspect}
+ text around open: #{text[open_start,40].inspect}
+ text around close: #{text[close_start,40].inspect}
+EOF
+ strict ? raise(msg) : warn(msg)
+ end
+ elsif !node.childless?(xml) && node.closing != :close
+ node_stack.push node
+ end
+ end
+ end
+ end
+
+ # Search the tree for (and return) the first node that matches the given
+ # conditions. The conditions are interpreted differently for different node
+ # types, see HTML::Text#find and HTML::Tag#find.
+ def find(conditions)
+ @root.find(conditions)
+ end
+
+ # Search the tree for (and return) all nodes that match the given
+ # conditions. The conditions are interpreted differently for different node
+ # types, see HTML::Text#find and HTML::Tag#find.
+ def find_all(conditions)
+ @root.find_all(conditions)
+ end
+
+ end
+
+end
530 script/html/node.rb
@@ -0,0 +1,530 @@
+require 'strscan'
+
+module HTML #:nodoc:
+
+ class Conditions < Hash #:nodoc:
+ def initialize(hash)
+ super()
+ hash = { :content => hash } unless Hash === hash
+ hash = keys_to_symbols(hash)
+ hash.each do |k,v|
+ case k
+ when :tag, :content then
+ # keys are valid, and require no further processing
+ when :attributes then
+ hash[k] = keys_to_strings(v)
+ when :parent, :child, :ancestor, :descendant, :sibling, :before,
+ :after
+ hash[k] = Conditions.new(v)
+ when :children
+ hash[k] = v = keys_to_symbols(v)
+ v.each do |k,v2|
+ case k
+ when :count, :greater_than, :less_than
+ # keys are valid, and require no further processing
+ when :only
+ v[k] = Conditions.new(v2)
+ else
+ raise "illegal key #{k.inspect} => #{v2.inspect}"
+ end
+ end
+ else
+ raise "illegal key #{k.inspect} => #{v.inspect}"
+ end
+ end
+ update hash
+ end
+
+ private
+
+ def keys_to_strings(hash)
+ hash.keys.inject({}) do |h,k|
+ h[k.to_s] = hash[k]
+ h
+ end
+ end
+
+ def keys_to_symbols(hash)
+ hash.keys.inject({}) do |h,k|
+ raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
+ h[k.to_sym] = hash[k]
+ h
+ end
+ end
+ end
+
+ # The base class of all nodes, textual and otherwise, in an HTML document.
+ class Node #:nodoc:
+ # The array of children of this node. Not all nodes have children.
+ attr_reader :children
+
+ # The parent node of this node. All nodes have a parent, except for the
+ # root node.
+ attr_reader :parent
+
+ # The line number of the input where this node was begun
+ attr_reader :line
+
+ # The byte position in the input where this node was begun
+ attr_reader :position
+
+ # Create a new node as a child of the given parent.
+ def initialize(parent, line=0, pos=0)
+ @parent = parent
+ @children = []
+ @line, @position = line, pos
+ end
+
+ # Return a textual representation of the node.
+ def to_s
+ s = ""
+ @children.each { |child| s << child.to_s }
+ s
+ end
+
+ # Return false (subclasses must override this to provide specific matching
+ # behavior.) +conditions+ may be of any type.
+ def match(conditions)
+ false
+ end
+
+ # Search the children of this node for the first node for which #find
+ # returns non +nil+. Returns the result of the #find call that succeeded.
+ def find(conditions)
+ conditions = validate_conditions(conditions)
+ @children.each do |child|
+ node = child.find(conditions)
+ return node if node
+ end
+ nil
+ end
+
+ # Search for all nodes that match the given conditions, and return them
+ # as an array.
+ def find_all(conditions)
+ conditions = validate_conditions(conditions)
+
+ matches = []
+ matches << self if match(conditions)
+ @children.each do |child|
+ matches.concat child.find_all(conditions)
+ end
+ matches
+ end
+
+ # Returns +false+. Subclasses may override this if they define a kind of
+ # tag.
+ def tag?
+ false
+ end
+
+ def validate_conditions(conditions)
+ Conditions === conditions ? conditions : Conditions.new(conditions)
+ end
+
+ def ==(node)
+ return false unless self.class == node.class && children.size == node.children.size
+
+ equivalent = true
+
+ children.size.times do |i|
+ equivalent &&= children[i] == node.children[i]
+ end
+
+ equivalent
+ end
+
+ class <<self
+ def parse(parent, line, pos, content, strict=true)
+ if content !~ /^<\S/
+ Text.new(parent, line, pos, content)
+ else
+ scanner = StringScanner.new(content)
+
+ unless scanner.skip(/</)
+ if strict
+ raise "expected <"
+ else
+ return Text.new(parent, line, pos, content)
+ end
+ end
+
+ if scanner.skip(/!\[CDATA\[/)
+ scanner.scan_until(/\]\]>/)
+ return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
+ end
+
+ closing = ( scanner.scan(/\//) ? :close : nil )
+ return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/)
+ name.downcase!
+
+ unless closing
+ scanner.skip(/\s*/)
+ attributes = {}
+ while attr = scanner.scan(/[-\w:]+/)
+ value = true
+ if scanner.scan(/\s*=\s*/)
+ if delim = scanner.scan(/['"]/)
+ value = ""
+ while text = scanner.scan(/[^#{delim}\\]+|./)
+ case text
+ when "\\" then
+ value << text
+ value << scanner.getch
+ when delim
+ break
+ else value << text
+ end
+ end
+ else
+ value = scanner.scan(/[^\s>\/]+/)
+ end
+ end
+ attributes[attr.downcase] = value
+ scanner.skip(/\s*/)
+ end
+
+ closing = ( scanner.scan(/\//) ? :self : nil )
+ end
+
+ unless scanner.scan(/\s*>/)
+ if strict
+ raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
+ else
+ # throw away all text until we find what we're looking for
+ scanner.skip_until(/>/) or scanner.terminate
+ end
+ end
+
+ Tag.new(parent, line, pos, name, attributes, closing)
+ end
+ end
+ end
+ end
+
+ # A node that represents text, rather than markup.
+ class Text < Node #:nodoc:
+
+ attr_reader :content
+
+ # Creates a new text node as a child of the given parent, with the given
+ # content.
+ def initialize(parent, line, pos, content)
+ super(parent, line, pos)
+ @content = content
+ end
+
+ # Returns the content of this node.
+ def to_s
+ @content
+ end
+
+ # Returns +self+ if this node meets the given conditions. Text nodes support
+ # conditions of the following kinds:
+ #
+ # * if +conditions+ is a string, it must be a substring of the node's
+ # content
+ # * if +conditions+ is a regular expression, it must match the node's
+ # content
+ # * if +conditions+ is a hash, it must contain a <tt>:content</tt> key that
+ # is either a string or a regexp, and which is interpreted as described
+ # above.
+ def find(conditions)
+ match(conditions) && self
+ end
+
+ # Returns non-+nil+ if this node meets the given conditions, or +nil+
+ # otherwise. See the discussion of #find for the valid conditions.
+ def match(conditions)
+ case conditions
+ when String
+ @content == conditions
+ when Regexp
+ @content =~ conditions
+ when Hash
+ conditions = validate_conditions(conditions)
+
+ # Text nodes only have :content, :parent, :ancestor
+ unless (conditions.keys - [:content, :parent, :ancestor]).empty?
+ return false
+ end
+
+ match(conditions[:content])
+ else
+ nil
+ end
+ end
+
+ def ==(node)
+ return false unless super
+ content == node.content
+ end
+ end
+
+ # A CDATA node is simply a text node with a specialized way of displaying
+ # itself.
+ class CDATA < Text #:nodoc:
+ def to_s
+ "<![CDATA[#{super}]>"
+ end
+ end
+
+ # A Tag is any node that represents markup. It may be an opening tag, a
+ # closing tag, or a self-closing tag. It has a name, and may have a hash of
+ # attributes.
+ class Tag < Node #:nodoc:
+
+ # Either +nil+, <tt>:close</tt>, or <tt>:self</tt>
+ attr_reader :closing
+
+ # Either +nil+, or a hash of attributes for this node.
+ attr_reader :attributes
+
+ # The name of this tag.
+ attr_reader :name
+
+ # Create a new node as a child of the given parent, using the given content
+ # to describe the node. It will be parsed and the node name, attributes and
+ # closing status extracted.
+ def initialize(parent, line, pos, name, attributes, closing)
+ super(parent, line, pos)
+ @name = name
+ @attributes = attributes
+ @closing = closing
+ end
+
+ # A convenience for obtaining an attribute of the node. Returns +nil+ if
+ # the node has no attributes.
+ def [](attr)
+ @attributes ? @attributes[attr] : nil
+ end
+
+ # Returns non-+nil+ if this tag can contain child nodes.
+ def childless?(xml = false)
+ return false if xml && @closing.nil?
+ !@closing.nil? ||
+ @name =~ /^(img|br|hr|link|meta|area|base|basefont|
+ col|frame|input|isindex|param)$/ox
+ end
+
+ # Returns a textual representation of the node
+ def to_s
+ if @closing == :close
+ "</#{@name}>"
+ else
+ s = "<#{@name}"
+ @attributes.each do |k,v|
+ s << " #{k}"
+ s << "=\"#{v}\"" if String === v
+ end
+ s << " /" if @closing == :self
+ s << ">"
+ @children.each { |child| s << child.to_s }
+ s << "</#{@name}>" if @closing != :self && !@children.empty?
+ s
+ end
+ end
+
+ # If either the node or any of its children meet the given conditions, the
+ # matching node is returned. Otherwise, +nil+ is returned. (See the
+ # description of the valid conditions in the +match+ method.)
+ def find(conditions)
+ match(conditions) && self || super
+ end
+
+ # Returns +true+, indicating that this node represents an HTML tag.
+ def tag?
+ true
+ end
+
+ # Returns +true+ if the node meets any of the given conditions. The
+ # +conditions+ parameter must be a hash of any of the following keys
+ # (all are optional):
+ #
+ # * <tt>:tag</tt>: the node name must match the corresponding value
+ # * <tt>:attributes</tt>: a hash. The node's values must match the
+ # corresponding values in the hash.
+ # * <tt>:parent</tt>: a hash. The node's parent must match the
+ # corresponding hash.
+ # * <tt>:child</tt>: a hash. At least one of the node's immediate children
+ # must meet the criteria described by the hash.
+ # * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
+ # meet the criteria described by the hash.
+ # * <tt>:descendant</tt>: a hash. At least one of the node's descendants
+ # must meet the criteria described by the hash.
+ # * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
+ # meet the criteria described by the hash.
+ # * <tt>:after</tt>: a hash. The node must be after any sibling meeting
+ # the criteria described by the hash, and at least one sibling must match.
+ # * <tt>:before</tt>: a hash. The node must be before any sibling meeting
+ # the criteria described by the hash, and at least one sibling must match.
+ # * <tt>:children</tt>: a hash, for counting children of a node. Accepts the
+ # keys:
+ # ** <tt>:count</tt>: either a number or a range which must equal (or
+ # include) the number of children that match.
+ # ** <tt>:less_than</tt>: the number of matching children must be less than
+ # this number.
+ # ** <tt>:greater_than</tt>: the number of matching children must be
+ # greater than this number.
+ # ** <tt>:only</tt>: another hash consisting of the keys to use
+ # to match on the children, and only matching children will be
+ # counted.
+ #
+ # Conditions are matched using the following algorithm:
+ #
+ # * if the condition is a string, it must be a substring of the value.
+ # * if the condition is a regexp, it must match the value.
+ # * if the condition is a number, the value must match number.to_s.
+ # * if the condition is +true+, the value must not be +nil+.
+ # * if the condition is +false+ or +nil+, the value must be +nil+.
+ #
+ # Usage:
+ #
+ # # test if the node is a "span" tag
+ # node.match :tag => "span"
+ #
+ # # test if the node's parent is a "div"
+ # node.match :parent => { :tag => "div" }
+ #
+ # # test if any of the node's ancestors are "table" tags
+ # node.match :ancestor => { :tag => "table" }
+ #
+ # # test if any of the node's immediate children are "em" tags
+ # node.match :child => { :tag => "em" }
+ #
+ # # test if any of the node's descendants are "strong" tags
+ # node.match :descendant => { :tag => "strong" }
+ #
+ # # test if the node has between 2 and 4 span tags as immediate children
+ # node.match :children => { :count => 2..4, :only => { :tag => "span" } }
+ #
+ # # get funky: test to see if the node is a "div", has a "ul" ancestor
+ # # and an "li" parent (with "class" = "enum"), and whether or not it has
+ # # a "span" descendant that contains # text matching /hello world/:
+ # node.match :tag => "div",
+ # :ancestor => { :tag => "ul" },
+ # :parent => { :tag => "li",
+ # :attributes => { :class => "enum" } },
+ # :descendant => { :tag => "span",
+ # :child => /hello world/ }
+ def match(conditions)
+ conditions = validate_conditions(conditions)
+ # check content of child nodes
+ if conditions[:content]
+ if children.empty?
+ return false unless match_condition("", conditions[:content])
+ else
+ return false unless children.find { |child| child.match(conditions[:content]) }
+ end
+ end
+
+ # test the name
+ return false unless match_condition(@name, conditions[:tag]) if conditions[:tag]
+
+ # test attributes
+ (conditions[:attributes] || {}).each do |key, value|
+ return false unless match_condition(self[key], value)
+ end
+
+ # test parent
+ return false unless parent.match(conditions[:parent]) if conditions[:parent]
+
+ # test children
+ return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
+
+ # test ancestors
+ if conditions[:ancestor]
+ return false unless catch :found do
+ p = self
+ throw :found, true if p.match(conditions[:ancestor]) while p = p.parent
+ end
+ end
+
+ # test descendants
+ if conditions[:descendant]
+ return false unless children.find do |child|
+ # test the child
+ child.match(conditions[:descendant]) ||
+ # test the child's descendants
+ child.match(:descendant => conditions[:descendant])
+ end
+ end
+
+ # count children
+ if opts = conditions[:children]
+ matches = children.select do |c|
+ (c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
+ end
+
+ matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
+ opts.each do |key, value|
+ next if key == :only
+ case key
+ when :count
+ if Integer === value
+ return false if matches.length != value
+ else
+ return false unless value.include?(matches.length)
+ end
+ when :less_than
+ return false unless matches.length < value
+ when :greater_than
+ return false unless matches.length > value
+ else raise "unknown count condition #{key}"
+ end
+ end
+ end
+
+ # test siblings
+ if conditions[:sibling] || conditions[:before] || conditions[:after]
+ siblings = parent ? parent.children : []
+ self_index = siblings.index(self)
+
+ if conditions[:sibling]
+ return false unless siblings.detect do |s|
+ s != self && s.match(conditions[:sibling])
+ end
+ end
+
+ if conditions[:before]
+ return false unless siblings[self_index+1..-1].detect do |s|
+ s != self && s.match(conditions[:before])
+ end
+ end
+
+ if conditions[:after]
+ return false unless siblings[0,self_index].detect do |s|
+ s != self && s.match(conditions[:after])
+ end
+ end
+ end
+
+ true
+ end
+
+ def ==(node)
+ return false unless super
+ return false unless closing == node.closing && self.name == node.name
+ attributes == node.attributes
+ end
+
+ private
+ # Match the given value to the given condition.
+ def match_condition(value, condition)
+ case condition
+ when String
+ value && value == condition
+ when Regexp
+ value && value.match(condition)
+ when Numeric
+ value == condition.to_s
+ when true
+ !value.nil?
+ when false, nil
+ value.nil?
+ else
+ false
+ end
+ end
+ end
+end
823 script/html/selector.rb
@@ -0,0 +1,823 @@
+#--
+# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
+# Under MIT and/or CC By license.
+#++
+
+module HTML
+
+ # Selects HTML elements using CSS 2 selectors.
+ #
+ # The +Selector+ class uses CSS selector expressions to match and select
+ # HTML elements.
+ #
+ # For example:
+ # selector = HTML::Selector.new "form.login[action=/login]"
+ # creates a new selector that matches any +form+ element with the class
+ # +login+ and an attribute +action+ with the value <tt>/login</tt>.
+ #
+ # === Matching Elements
+ #
+ # Use the #match method to determine if an element matches the selector.
+ #
+ # For simple selectors, the method returns an array with that element,
+ # or +nil+ if the element does not match. For complex selectors (see below)
+ # the method returns an array with all matched elements, of +nil+ if no
+ # match found.
+ #
+ # For example:
+ # if selector.match(element)
+ # puts "Element is a login form"
+ # end
+ #
+ # === Selecting Elements
+ #
+ # Use the #select method to select all matching elements starting with
+ # one element and going through all children in depth-first order.
+ #
+ # This method returns an array of all matching elements, an empty array
+ # if no match is found
+ #
+ # For example:
+ # selector = HTML::Selector.new "input[type=text]"
+ # matches = selector.select(element)
+ # matches.each do |match|
+ # puts "Found text field with name #{match.attributes['name']}"
+ # end
+ #
+ # === Expressions
+ #
+ # Selectors can match elements using any of the following criteria:
+ # * <tt>name</tt> -- Match an element based on its name (tag name).
+ # For example, <tt>p</tt> to match a paragraph. You can use <tt>*</tt>
+ # to match any element.
+ # * <tt>#</tt><tt>id</tt> -- Match an element based on its identifier (the
+ # <tt>id</tt> attribute). For example, <tt>#</tt><tt>page</tt>.
+ # * <tt>.class</tt> -- Match an element based on its class name, all
+ # class names if more than one specified.
+ # * <tt>[attr]</tt> -- Match an element that has the specified attribute.
+ # * <tt>[attr=value]</tt> -- Match an element that has the specified
+ # attribute and value. (More operators are supported see below)
+ # * <tt>:pseudo-class</tt> -- Match an element based on a pseudo class,
+ # such as <tt>:nth-child</tt> and <tt>:empty</tt>.
+ # * <tt>:not(expr)</tt> -- Match an element that does not match the
+ # negation expression.
+ #
+ # When using a combination of the above, the element name comes first
+ # followed by identifier, class names, attributes, pseudo classes and
+ # negation in any order. Do not seprate these parts with spaces!
+ # Space separation is used for descendant selectors.
+ #
+ # For example:
+ # selector = HTML::Selector.new "form.login[action=/login]"
+ # The matched element must be of type +form+ and have the class +login+.
+ # It may have other classes, but the class +login+ is required to match.
+ # It must also have an attribute called +action+ with the value
+ # <tt>/login</tt>.
+ #
+ # This selector will match the following element:
+ # <form class="login form" method="post" action="/login">
+ # but will not match the element:
+ # <form method="post" action="/logout">
+ #
+ # === Attribute Values
+ #
+ # Several operators are supported for matching attributes:
+ # * <tt>name</tt> -- The element must have an attribute with that name.
+ # * <tt>name=value</tt> -- The element must have an attribute with that
+ # name and value.
+ # * <tt>name^=value</tt> -- The attribute value must start with the
+ # specified value.
+ # * <tt>name$=value</tt> -- The attribute value must end with the
+ # specified value.
+ # * <tt>name*=value</tt> -- The attribute value must contain the
+ # specified value.
+ # * <tt>name~=word</tt> -- The attribute value must contain the specified
+ # word (space separated).
+ # * <tt>name|=word</tt> -- The attribute value must start with specified
+ # word.
+ #
+ # For example, the following two selectors match the same element:
+ # #my_id
+ # [id=my_id]
+ # and so do the following two selectors:
+ # .my_class
+ # [class~=my_class]
+ #
+ # === Alternatives, siblings, children
+ #
+ # Complex selectors use a combination of expressions to match elements:
+ # * <tt>expr1 expr2</tt> -- Match any element against the second expression
+ # if it has some parent element that matches the first expression.
+ # * <tt>expr1 > expr2</tt> -- Match any element against the second expression
+ # if it is the child of an element that matches the first expression.
+ # * <tt>expr1 + expr2</tt> -- Match any element against the second expression
+ # if it immediately follows an element that matches the first expression.
+ # * <tt>expr1 ~ expr2</tt> -- Match any element against the second expression
+ # that comes after an element that matches the first expression.
+ # * <tt>expr1, expr2</tt> -- Match any element against the first expression,
+ # or against the second expression.
+ #
+ # Since children and sibling selectors may match more than one element given
+ # the first element, the #match method may return more than one match.
+ #
+ # === Pseudo classes
+ #
+ # Pseudo classes were introduced in CSS 3. They are most often used to select
+ # elements in a given position:
+ # * <tt>:root</tt> -- Match the element only if it is the root element
+ # (no parent element).
+ # * <tt>:empty</tt> -- Match the element only if it has no child elements,
+ # and no text content.
+ # * <tt>:only-child</tt> -- Match the element if it is the only child (element)
+ # of its parent element.
+ # * <tt>:only-of-type</tt> -- Match the element if it is the only child (element)
+ # of its parent element and its type.
+ # * <tt>:first-child</tt> -- Match the element if it is the first child (element)
+ # of its parent element.
+ # * <tt>:first-of-type</tt> -- Match the element if it is the first child (element)
+ # of its parent element of its type.
+ # * <tt>:last-child</tt> -- Match the element if it is the last child (element)
+ # of its parent element.
+ # * <tt>:last-of-type</tt> -- Match the element if it is the last child (element)
+ # of its parent element of its type.
+ # * <tt>:nth-child(b)</tt> -- Match the element if it is the b-th child (element)
+ # of its parent element. The value <tt>b</tt> specifies its index, starting with 1.
+ # * <tt>:nth-child(an+b)</tt> -- Match the element if it is the b-th child (element)
+ # in each group of <tt>a</tt> child elements of its parent element.
+ # * <tt>:nth-child(-an+b)</tt> -- Match the element if it is the first child (element)
+ # in each group of <tt>a</tt> child elements, up to the first <tt>b</tt> child
+ # elements of its parent element.
+ # * <tt>:nth-child(odd)</tt> -- Match element in the odd position (i.e. first, third).
+ # Same as <tt>:nth-child(2n+1)</tt>.
+ # * <tt>:nth-child(even)</tt> -- Match element in the even position (i.e. second,
+ # fourth). Same as <tt>:nth-child(2n+2)</tt>.
+ # * <tt>:nth-of-type(..)</tt> -- As above, but only counts elements of its type.
+ # * <tt>:nth-last-child(..)</tt> -- As above, but counts from the last child.
+ # * <tt>:nth-last-of-type(..)</tt> -- As above, but counts from the last child and
+ # only elements of its type.
+ # * <tt>:not(selector)</tt> -- Match the element only if the element does not
+ # match the simple selector.
+ #
+ # As you can see, <tt>:nth-child<tt> pseudo class and its varient can get quite
+ # tricky and the CSS specification doesn't do a much better job explaining it.
+ # But after reading the examples and trying a few combinations, it's easy to
+ # figure out.
+ #
+ # For example:
+ # table tr:nth-child(odd)
+ # Selects every second row in the table starting with the first one.
+ #
+ # div p:nth-child(4)
+ # Selects the fourth paragraph in the +div+, but not if the +div+ contains
+ # other elements, since those are also counted.
+ #
+ # div p:nth-of-type(4)
+ # Selects the fourth paragraph in the +div+, counting only paragraphs, and
+ # ignoring all other elements.
+ #
+ # div p:nth-of-type(-n+4)
+ # Selects the first four paragraphs, ignoring all others.
+ #
+ # And you can always select an element that matches one set of rules but
+ # not another using <tt>:not</tt>. For example:
+ # p:not(.post)
+ # Matches all paragraphs that do not have the class <tt>.post</tt>.
+ #
+ # === Substitution Values
+ #
+ # You can use substitution with identifiers, class names and element values.
+ # A substitution takes the form of a question mark (<tt>?</tt>) and uses the
+ # next value in the argument list following the CSS expression.
+ #
+ # The substitution value may be a string or a regular expression. All other
+ # values are converted to strings.
+ #
+ # For example:
+ # selector = HTML::Selector.new "#?", /^\d+$/
+ # matches any element whose identifier consists of one or more digits.
+ #
+ # See http://www.w3.org/TR/css3-selectors/
+ class Selector
+
+
+ # An invalid selector.
+ class InvalidSelectorError < StandardError #:nodoc:
+ end
+
+
+ class << self
+
+ # :call-seq:
+ # Selector.for_class(cls) => selector
+ #
+ # Creates a new selector for the given class name.
+ def for_class(cls)
+ self.new([".?", cls])
+ end
+
+
+ # :call-seq:
+ # Selector.for_id(id) => selector
+ #
+ # Creates a new selector for the given id.
+ def for_id(id)
+ self.new(["#?", id])
+ end
+
+ end
+
+
+ # :call-seq:
+ # Selector.new(string, [values ...]) => selector
+ #
+ # Creates a new selector from a CSS 2 selector expression.
+ #
+ # The first argument is the selector expression. All other arguments
+ # are used for value substitution.
+ #
+ # Throws InvalidSelectorError is the selector expression is invalid.
+ def initialize(selector, *values)
+ raise ArgumentError, "CSS expression cannot be empty" if selector.empty?
+ @source = ""
+ values = values[0] if values.size == 1 && values[0].is_a?(Array)
+ # We need a copy to determine if we failed to parse, and also
+ # preserve the original pass by-ref statement.
+ statement = selector.strip.dup
+ # Create a simple selector, along with negation.
+ simple_selector(statement, values).each { |name, value| instance_variable_set("@#{name}", value) }
+
+ # Alternative selector.
+ if statement.sub!(/^\s*,\s*/, "")
+ second = Selector.new(statement, values)
+ (@alternates ||= []) << second
+ # If there are alternate selectors, we group them in the top selector.
+ if alternates = second.instance_variable_get(:@alternates)
+ second.instance_variable_set(:@alternates, nil)
+ @alternates.concat alternates
+ end
+ @source << " , " << second.to_s
+ # Sibling selector: create a dependency into second selector that will
+ # match element immediately following this one.
+ elsif statement.sub!(/^\s*\+\s*/, "")
+ second = next_selector(statement, values)
+ @depends = lambda do |element, first|
+ if element = next_element(element)
+ second.match(element, first)
+ end
+ end
+ @source << " + " << second.to_s
+ # Adjacent selector: create a dependency into second selector that will
+ # match all elements following this one.
+ elsif statement.sub!(/^\s*~\s*/, "")
+ second = next_selector(statement, values)
+ @depends = lambda do |element, first|
+ matches = []
+ while element = next_element(element)
+ if subset = second.match(element, first)
+