├── .gitignore
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── Rakefile
├── app
├── app.rb
├── helpers.rb
├── helpers
│ ├── pretty_printing.rb
│ └── sinatra.rb
├── libraries.rb
├── models
│ ├── db.rb
│ └── user.rb
├── templates
│ ├── 404.mustache
│ ├── 500.mustache
│ ├── about.mustache
│ ├── hurls.mustache
│ ├── index.mustache
│ ├── layout.mustache
│ ├── stats.mustache
│ └── view.mustache
└── views
│ ├── about.rb
│ ├── hurls.rb
│ ├── index.rb
│ ├── layout.rb
│ ├── stats.rb
│ └── view.rb
├── config.ru
├── docs
└── index.mustache
├── hurls.yaml
├── public
├── .gitignore
├── css
│ ├── facebox.css
│ ├── jquery.autocomplete.css
│ ├── pygment_trac.css
│ ├── reset.css
│ └── style.css
├── favicon.ico
├── img
│ ├── ajax-loader.gif
│ ├── autocomplete-loader.gif
│ ├── background.jpg
│ ├── buttonbg.png
│ ├── delete.png
│ ├── facebox
│ │ ├── b.png
│ │ ├── bl.png
│ │ ├── br.png
│ │ ├── closelabel.gif
│ │ ├── loading.gif
│ │ ├── logo.png
│ │ ├── shadow.gif
│ │ ├── tl.png
│ │ └── tr.png
│ ├── favicon.gif
│ ├── logo.png
│ ├── message-info.png
│ ├── message-warn.png
│ ├── pony-hurl.png
│ ├── pony.png
│ ├── rainbow.png
│ └── unicorn.png
├── js
│ ├── facebox.js
│ ├── hurl.headers.js
│ ├── hurl.js
│ ├── jquery.autocomplete.js
│ ├── jquery.form.js
│ ├── jquery.js
│ ├── jquery.relatize_date.js
│ └── json2.js
└── robots.txt
├── test
├── json
└── xml
└── tmp
└── .gitignore
/.gitignore:
--------------------------------------------------------------------------------
1 | db/
2 | .bundle/
3 | bin/
4 | env.rb
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source :rubygems
2 |
3 | gem 'sinatra', '~>1.0'
4 | gem 'yajl-ruby', '~>1.1.0'
5 | gem 'mustache', '~>0.11.2'
6 | gem 'curb', '~>0.7.8'
7 | gem 'coderay', '~>0.8.357'
8 |
9 | gem 'sinatra_auth_github'
10 |
11 | group :test do
12 | gem 'shotgun'
13 | end
14 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | specs:
4 | addressable (2.2.2)
5 | coderay (0.8.357)
6 | curb (0.7.8)
7 | faraday (0.4.6)
8 | addressable (>= 2.1.1)
9 | rack (>= 1.0.1)
10 | json (1.4.6)
11 | mime-types (1.16)
12 | multi_json (0.0.5)
13 | mustache (0.11.2)
14 | oauth2 (0.0.13)
15 | faraday (~> 0.4.1)
16 | multi_json (>= 0.0.4)
17 | rack (1.2.1)
18 | rest-client (1.5.1)
19 | mime-types (>= 1.16)
20 | shotgun (0.8)
21 | rack (>= 1.0)
22 | sinatra (1.1.0)
23 | rack (~> 1.1)
24 | tilt (~> 1.1)
25 | sinatra_auth_github (0.0.11)
26 | rest-client (~> 1.5.1)
27 | sinatra (~> 1.0)
28 | warden-github (~> 0.0.5)
29 | tilt (1.1)
30 | warden (0.10.7)
31 | rack (>= 1.0.0)
32 | warden-github (0.0.6)
33 | json (>= 1.0.0)
34 | oauth2 (~> 0.0.8)
35 | warden (~> 0.10)
36 | yajl-ruby (1.1.0)
37 |
38 | PLATFORMS
39 | ruby
40 |
41 | DEPENDENCIES
42 | coderay (~> 0.8.357)
43 | curb (~> 0.7.8)
44 | mustache (~> 0.11.2)
45 | shotgun
46 | sinatra (~> 1.0)
47 | sinatra_auth_github
48 | yajl-ruby (~> 1.1.0)
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 Chris Wanstrath and Leah Culver
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Hurl
2 | ====
3 |
4 | Hurl was created for the Rails Rumble 2009 in 48 hours.
5 | Now Hurl is an open source project for your enjoyment.
6 |
7 |
8 |
9 |
10 | Installation
11 | ------------
12 |
13 | Hurl requires Ruby 1.8.6+
14 |
15 | First download hurl and cd into the directory:
16 |
17 | git clone git://github.com/twilio/hurl
18 | cd hurl
19 |
20 | Or download [the zip](http://github.com/twilio/hurl/zipball/master).
21 |
22 | Next make sure you have [RubyGems](https://rubygems.org/pages/download) installed.
23 |
24 | Then install [Bundler](http://gembundler.com/):
25 |
26 | gem install bundler
27 |
28 | Now install Hurl's dependencies:
29 |
30 | bundle install
31 |
32 |
33 | Run Locally
34 | -----------
35 |
36 | bundle exec shotgun config.ru
37 |
38 | Now visit
39 |
40 |
41 | Issues
42 | ------
43 |
44 | Find a bug? Want a feature? Submit an [issue
45 | here](http://github.com/twilio/hurl/issues). Patches welcome!
46 |
47 |
48 | Screenshot
49 | ----------
50 |
51 | [](http://hurl.it)
52 |
53 |
54 | Original Authors
55 | ----------------
56 |
57 | * [Leah Culver][2]
58 | * [Chris Wanstrath][3]
59 |
60 |
61 | [1]: http://r09.railsrumble.com/
62 | [2]: http://github.com/leah
63 | [3]: http://github.com/defunkt
64 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | namespace :hurl do
2 | desc "Start Hurl for development"
3 | task :start do
4 | exec "bundle exec shotgun config.ru"
5 | end
6 |
7 | desc "Generate GitHub pages."
8 | task :pages => :check_dirty do
9 | require "mustache"
10 | require "rdiscount"
11 | view = Mustache.new
12 | view.template = File.read("docs/index.mustache")
13 | view[:content] = Markdown.new(File.read("README.md")).to_html
14 | File.open("new_index.html", "w") do |f|
15 | f.puts view.render
16 | end
17 | system "git checkout gh-pages"
18 | system "git pull origin gh-pages"
19 | system "mv new_index.html index.html"
20 | system "git commit -a -m 'auto update docs'"
21 | system "git push origin gh-pages"
22 | system "git checkout master"
23 | end
24 |
25 | task :check_dirty do
26 | if `git status -a` && $?.success?
27 | abort "dirty index - not publishing!"
28 | end
29 | end
30 |
31 | desc "Please pardon our dust."
32 | task :deploy do
33 | exec "ssh deploy@hurl.it 'cd /www/hurl && git fetch origin && git reset --hard origin/master && rake bundle && touch tmp/restart.txt'"
34 | end
35 | end
36 |
37 | # Needs bundler, uglifyjs, and uglifycss installed on the server.
38 | #
39 | # Bundler:
40 | # gem install bundler
41 | # uglifyjs:
42 | # npm install uglify-js
43 | # uglifycss:
44 | # curl https://github.com/fmarcia/UglifyCSS/raw/master/uglifycss > /usr/bin/uglifcss
45 | task :bundle do
46 | system "bundle install"
47 | rm "public/js/bundle.js" rescue nil # >:O
48 | rm "public/css/bundle.css" rescue nil
49 | system "cat $(ls -1 public/js/*.js | grep -v jquery.js) | uglifyjs -nc > public/js/bundle.js"
50 | system "uglifycss public/css/*.css > public/css/bundle.css"
51 | end
52 |
53 | desc "Start everything."
54 | multitask :start => [ 'hurl:start' ]
55 |
--------------------------------------------------------------------------------
/app/app.rb:
--------------------------------------------------------------------------------
1 | require 'app/libraries'
2 |
3 | module Hurl
4 | class App < Sinatra::Base
5 | register Mustache::Sinatra
6 | helpers Hurl::Helpers
7 |
8 | dir = File.dirname(File.expand_path(__FILE__))
9 |
10 | set :public, "#{dir}/public"
11 | set :root, RACK_ROOT
12 | set :app_file, __FILE__
13 | set :static, true
14 |
15 | set :views, "#{dir}/templates"
16 |
17 | set :mustache, {
18 | :namespace => Object,
19 | :views => "#{dir}/views",
20 | :templates => "#{dir}/templates"
21 | }
22 |
23 | enable :sessions
24 |
25 | set :github_options, { :client_id => ENV['HURL_CLIENT_ID'],
26 | :secret => ENV['HURL_SECRET'],
27 | :scopes => '',
28 | :callback_url => '/login/callback/' }
29 |
30 | register ::Sinatra::Auth::Github
31 |
32 | def initialize(*args)
33 | super
34 | @debug = ENV['DEBUG']
35 | @ga_code = ENV['GA_CODE']
36 | setup_default_hurls
37 | end
38 |
39 |
40 | #
41 | # routes
42 | #
43 |
44 | before do
45 | if authenticated?
46 | @user = User.new(github_user)
47 | end
48 |
49 | @flash = session.delete('flash')
50 | end
51 |
52 | get '/' do
53 | @hurl = params
54 | mustache :index
55 | end
56 |
57 | get '/hurls/?' do
58 | redirect('/') and return unless logged_in?
59 | @hurls = @user.hurls
60 | mustache :hurls
61 | end
62 |
63 | get '/hurls/:id/?' do
64 | @hurl = find_hurl_or_view(params[:id])
65 | @hurl ? mustache(:index) : not_found
66 | end
67 |
68 | delete '/hurls/:id/?' do
69 | redirect('/') and return unless logged_in?
70 |
71 | if @hurl = find_hurl_or_view(params[:id])
72 | @user.remove_hurl(@hurl['id'])
73 | end
74 | request.xhr? ? "ok" : redirect('/')
75 | end
76 |
77 | get '/hurls/:id/:view_id/?' do
78 | @hurl = find_hurl_or_view(params[:id])
79 | @view = find_hurl_or_view(params[:view_id])
80 | @view_id = params[:view_id]
81 | @hurl && @view ? mustache(:index) : not_found
82 | end
83 |
84 | get '/views/:id/?' do
85 | @view = find_hurl_or_view(params[:id])
86 | @view ? mustache(:view, :layout => false) : not_found
87 | end
88 |
89 | get '/test.json' do
90 | content_type 'application/json'
91 | File.read('test/json')
92 | end
93 |
94 | get '/test.xml' do
95 | content_type 'application/xml'
96 | File.read('test/xml')
97 | end
98 |
99 | get '/about/?' do
100 | mustache :about
101 | end
102 |
103 | get '/stats/?' do
104 | mustache :stats
105 | end
106 |
107 | get '/login/?' do
108 | authenticate!
109 | redirect '/'
110 | end
111 |
112 | get '/login/callback/?' do
113 | authenticate!
114 | redirect '/'
115 | end
116 |
117 | get '/logout/?' do
118 | logout!
119 | session['flash'] = 'see you later!'
120 | redirect '/'
121 | end
122 |
123 | post '/' do
124 | return json(:error => "Calm down and try my margarita!") if rate_limited?
125 |
126 | url, method, auth = params.values_at(:url, :method, :auth)
127 |
128 | return json(:error => "That's... wait.. what?!") if invalid_url?(url)
129 |
130 | curl = Curl::Easy.new(url)
131 |
132 | sent_headers = []
133 | curl.on_debug do |type, data|
134 | # track request headers
135 | sent_headers << data if type == Curl::CURLINFO_HEADER_OUT
136 | end
137 |
138 | curl.follow_location = true if params[:follow_redirects]
139 |
140 | # ensure a method is set
141 | method = (method.to_s.empty? ? 'GET' : method).upcase
142 |
143 | # update auth
144 | add_auth(auth, curl, params)
145 |
146 | # arbitrary headers
147 | add_headers_from_arrays(curl, params["header-keys"], params["header-vals"])
148 |
149 | # arbitrary post params
150 | if params['post-body'] && ['POST', 'PUT'].index(method)
151 | post_data = [params['post-body']]
152 | else
153 | post_data = make_fields(method, params["param-keys"], params["param-vals"])
154 | end
155 |
156 | begin
157 | debug { puts "#{method} #{url}" }
158 |
159 | if method == 'PUT'
160 | curl.http_put(stringify_data(post_data))
161 | else
162 | curl.send("http_#{method.downcase}", *post_data)
163 | end
164 |
165 | debug do
166 | puts sent_headers.join("\n")
167 | puts post_data.join('&') if post_data.any?
168 | puts curl.header_str
169 | end
170 |
171 | header = pretty_print_headers(curl.header_str)
172 | type = url =~ /(\.js)$/ ? 'js' : curl.content_type
173 | body = pretty_print(type, curl.body_str)
174 | request = pretty_print_requests(sent_headers, post_data)
175 |
176 | json :header => header,
177 | :body => body,
178 | :request => request,
179 | :hurl_id => save_hurl(params),
180 | :prev_hurl => @user ? @user.second_to_last_hurl_id : nil,
181 | :view_id => save_view(header, body, request)
182 | rescue => e
183 | json :error => CGI::escapeHTML(e.to_s)
184 | end
185 | end
186 |
187 |
188 | #
189 | # error handlers
190 | #
191 |
192 | not_found do
193 | mustache :"404"
194 | end
195 |
196 | error do
197 | mustache :"500"
198 | end
199 |
200 |
201 | #
202 | # route helpers
203 | #
204 |
205 | # is this a url hurl can handle. basically a spam check.
206 | def invalid_url?(url)
207 | valid_schemes = ['http', 'https']
208 | begin
209 | uri = URI.parse(url)
210 | raise URI::InvalidURIError if uri.host == 'hurl.it'
211 | raise URI::InvalidURIError if !valid_schemes.include? uri.scheme
212 | false
213 | rescue URI::InvalidURIError
214 | true
215 | end
216 | end
217 |
218 | # update auth based on auth type
219 | def add_auth(auth, curl, params)
220 | if auth == 'basic'
221 | username, password = params.values_at(:username, :password)
222 | encoded = Base64.encode64("#{username}:#{password}").gsub("\n",'')
223 | curl.headers['Authorization'] = "Basic #{encoded}"
224 | end
225 | end
226 |
227 | # headers from non-empty keys and values
228 | def add_headers_from_arrays(curl, keys, values)
229 | keys, values = Array(keys), Array(values)
230 |
231 | keys.each_with_index do |key, i|
232 | next if values[i].to_s.empty?
233 | curl.headers[key] = values[i]
234 | end
235 | end
236 |
237 | # post params from non-empty keys and values
238 | def make_fields(method, keys, values)
239 | return [] unless %w( POST PUT ).include? method
240 |
241 | fields = []
242 | keys, values = Array(keys), Array(values)
243 | keys.each_with_index do |name, i|
244 | value = values[i]
245 | next if name.to_s.empty? || value.to_s.empty?
246 | fields << Curl::PostField.content(name, value)
247 | end
248 | fields
249 | end
250 |
251 | def save_view(header, body, request)
252 | hash = { 'header' => header, 'body' => body, 'request' => request }
253 | id = sha(hash.to_s)
254 | DB.save(:views, id, hash)
255 | id
256 | end
257 |
258 | def save_hurl(params)
259 | id = sha(params.to_s)
260 | DB.save(:hurls, id, params.merge(:id => id))
261 | @user.add_hurl(id) if @user
262 | id
263 | end
264 |
265 | def find_hurl_or_view(id)
266 | DB.find(:hurls, id) || DB.find(:views, id)
267 | end
268 |
269 | # has this person made too many requests?
270 | def rate_limited?
271 | false
272 | end
273 |
274 | # turn post_data into a string for PUT requests
275 | def stringify_data(data)
276 | if data.is_a? String
277 | data
278 | elsif data.is_a? Array
279 | data.map { |x| stringify_data(x) }.join("&")
280 | elsif data.is_a? Curl::PostField
281 | data.to_s
282 | else
283 | raise "Cannot stringify #{data.inspect}"
284 | end
285 | end
286 | end
287 | end
288 |
--------------------------------------------------------------------------------
/app/helpers.rb:
--------------------------------------------------------------------------------
1 | require 'helpers/pretty_printing'
2 | require 'helpers/sinatra'
3 | require 'yaml'
4 |
5 | module Hurl
6 | module Helpers
7 | include Hurl::Helpers::PrettyPrinting
8 | include Hurl::Helpers::Sinatra
9 |
10 | #
11 | # helpers defined here are available to all views and sinatra routes
12 | #
13 |
14 | # debug { puts "hi!" }
15 | def debug
16 | yield if @debug
17 | end
18 |
19 | # sha(hash) => '01578ad840f1a7eba2bd202351119e635fde8e2a'
20 | def sha(thing)
21 | Digest::SHA1.hexdigest(thing.to_s)
22 | end
23 |
24 | def logged_in?
25 | !!@user
26 | end
27 |
28 | def ga_code
29 | @ga_code
30 | end
31 |
32 | def user
33 | @user
34 | end
35 |
36 | # for sorting hashes with symbol keys
37 | def sort_hash(hash)
38 | hash.to_a.sort_by { |a, b| a.to_s }
39 | end
40 |
41 | def next_hurl
42 | return unless logged_in?
43 |
44 | if @hurl
45 | hurls = @user.hurls
46 | hurls.each_with_index do |hurl, i|
47 | if hurl['id'] == @hurl['id']
48 | return i-1 >= 0 ? hurls[i-1]['id'] : nil
49 | end
50 | end
51 | nil
52 | end
53 | end
54 |
55 | def prev_hurl
56 | return unless logged_in?
57 |
58 | if @hurl.empty? && @user.hurls.any?
59 | @user.latest_hurl_id
60 | elsif @hurl.any?
61 | hurls = @user.hurls
62 | hurls.each_with_index do |hurl, i|
63 | if hurl['id'] == @hurl['id']
64 | return hurls[i+1] ? hurls[i+1]['id'] : nil
65 | end
66 | end
67 | nil
68 | end
69 | end
70 |
71 | # creates the hurls shown on the front page if they're not in the db
72 | def setup_default_hurls
73 | default_hurls.each do |name, params|
74 | save_hurl(params)
75 | end
76 | end
77 |
78 | def default_hurls
79 | return @default_hurls if @default_hurls
80 | path = File.expand_path(App.root + '/hurls.yaml')
81 | @default_hurls = YAML.load_file(path)
82 | end
83 | end
84 | end
85 |
--------------------------------------------------------------------------------
/app/helpers/pretty_printing.rb:
--------------------------------------------------------------------------------
1 | module Hurl
2 | module Helpers
3 | # Pretty printing of content types for the response div.
4 | module PrettyPrinting
5 | def pretty_print(type, content)
6 | type = type.to_s
7 |
8 | if type =~ /json|javascript/
9 | pretty_print_json(content)
10 | elsif type == 'js'
11 | pretty_print_js(content)
12 | elsif type.include? 'xml'
13 | pretty_print_xml(content)
14 | elsif type.include? 'html'
15 | colorize :html => content
16 | else
17 | CGI::escapeHTML(content.inspect)
18 | end
19 | end
20 |
21 | def pretty_print_json(content)
22 | json = Yajl::Parser.parse(content)
23 | pretty_print_js Yajl::Encoder.new(:pretty => true).encode(json)
24 | end
25 |
26 | def pretty_print_js(content)
27 | colorize :js => content
28 | end
29 |
30 | def pretty_print_xml(content)
31 | out = StringIO.new
32 | doc = REXML::Document.new(content)
33 | doc.write(out, 2)
34 | colorize :xml => out.string
35 | end
36 |
37 | def pretty_print_headers(content)
38 | content = CGI::escapeHTML(content)
39 | lines = content.split("\n").map do |line|
40 | if line =~ /^(.+?):(.+)$/
41 | "#{$1} :#{$2} "
42 | else
43 | "#{line} "
44 | end
45 | end
46 |
47 | ""
48 | end
49 |
50 | # accepts an array of request headers and formats them
51 | def pretty_print_requests(requests = [], fields = [])
52 | headers = requests.map do |request|
53 | pretty_print_headers request
54 | end
55 |
56 | headers.join + fields.join('&')
57 | end
58 | end
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/app/helpers/sinatra.rb:
--------------------------------------------------------------------------------
1 | module Hurl
2 | module Helpers
3 | # Random Sinatra DSL helpers.
4 | # Quite generic.
5 | module Sinatra
6 | # render a json response
7 | def json(hash = {})
8 | content_type 'application/json'
9 | Yajl::Encoder.encode(hash)
10 | end
11 |
12 | # colorize :js => '{ "blah": true }'
13 | def colorize(hash = {})
14 | tokens = CodeRay.scan(hash.values.first, hash.keys.first)
15 | colored = tokens.html.div.sub('CodeRay', 'highlight')
16 | colored.gsub(/(https?:\/\/[^< "']+)/, '\1 ')
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/app/libraries.rb:
--------------------------------------------------------------------------------
1 | RACK_ENV = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
2 | RACK_ROOT = File.expand_path(File.dirname(__FILE__) + '/..')
3 |
4 | # std lib
5 | require 'open3'
6 | require 'uri'
7 | require 'base64'
8 | require 'digest'
9 | require 'zlib'
10 | require "rexml/document"
11 |
12 | # bundled gems
13 | require 'sinatra/base'
14 | require 'yajl'
15 | require 'curb'
16 | require 'mustache/sinatra'
17 | require 'sinatra/auth/github'
18 | require 'coderay'
19 |
20 | $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
21 |
22 | # hurl
23 | require 'helpers'
24 |
25 | require 'models/db'
26 | require 'models/user'
27 |
28 | require 'views/layout'
29 |
--------------------------------------------------------------------------------
/app/models/db.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 |
3 | module Hurl
4 | class DB
5 | DIR = File.expand_path(ENV['HURL_DB_DIR'] || "db")
6 |
7 | def self.find(scope, id)
8 | decode File.read(dir(scope, id) + id) if id && id.is_a?(String)
9 | rescue Errno::ENOENT
10 | nil
11 | end
12 |
13 | def self.save(scope, id, content)
14 | File.open(dir(scope, id) + id, 'w') do |f|
15 | f.puts encode(content)
16 | end
17 |
18 | true
19 | end
20 |
21 | def self.dir(scope, id)
22 | path = FileUtils.mkdir_p "#{DIR}/#{scope}/#{id[0...2]}/#{id[2...4]}/"
23 |
24 | # In Ruby 1.9, mkdir_p always returns Array,
25 | # while in 1.8 it returns String when it has only one item to return.
26 | if path.is_a? Array
27 | path[0]
28 | else
29 | path
30 | end
31 | end
32 |
33 | private
34 | def self.encode(object)
35 | Zlib::Deflate.deflate Yajl::Encoder.encode(object)
36 | end
37 |
38 | def self.decode(object)
39 | Yajl::Parser.parse(Zlib::Inflate.inflate(object)) rescue nil
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | module Hurl
2 | class User
3 | attr_accessor :id, :login, :github_user
4 |
5 | def initialize(github_user)
6 | @github_user = github_user
7 | @login = github_user.login
8 | @id = github_user.attribs['id']
9 | end
10 |
11 | #
12 | # each user has an associated list
13 | # of hurls
14 | #
15 |
16 | def latest_hurl
17 | hurls(0, 1).first
18 | end
19 |
20 | def second_to_last_hurl_id
21 | hurls.any? and hurls(0, 2).size == 2 and hurls(0, 2)[1]['id']
22 | end
23 |
24 | def latest_hurl_id
25 | hurls.any? and latest_hurl['id']
26 | end
27 |
28 | def add_hurl(id)
29 | hurl_ids = DB.find(:users, db_id) || {}
30 | hurl_ids[id] = Time.now
31 | DB.save(:users, db_id, hurl_ids)
32 | end
33 |
34 | def remove_hurl(id)
35 | hurl_ids = DB.find(:users, db_id) || {}
36 | hurl_ids.delete(id)
37 | DB.save(:users, db_id, hurl_ids)
38 | end
39 |
40 | def db_id
41 | Digest::MD5.hexdigest(id.to_s)
42 | end
43 |
44 | def unsorted_hurls(start = 0, limit = 100)
45 | Array(DB.find(:users, db_id)).map do |id, date|
46 | DB.find(:hurls, id).merge('date' => Time.parse(date)) if id
47 | end.compact
48 | end
49 |
50 | def hurls(start = 0, limit = 100)
51 | unsorted_hurls(start, limit).sort_by { |h| -h['date'].to_i }
52 | end
53 |
54 | #
55 | # instance methods
56 | #
57 |
58 | def to_s
59 | login
60 | end
61 |
62 | def gravatar_url
63 | "http://www.gravatar.com/avatar/%s" % github_user.attribs['gravatar_id']
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/app/templates/404.mustache:
--------------------------------------------------------------------------------
1 | 404 not found
2 |
3 |
4 |
This is not the hurl you are looking for.
5 |
Go home.
6 |
--------------------------------------------------------------------------------
/app/templates/500.mustache:
--------------------------------------------------------------------------------
1 | 500 error
2 |
3 |
4 |
Something went very wrong.
5 |
Go home.
6 |
--------------------------------------------------------------------------------
/app/templates/about.mustache:
--------------------------------------------------------------------------------
1 | Hurl?
2 |
3 |
Hurl makes HTTP requests.
4 |
Choose the request method, customize headers and POST parameters, add basic authorization, and even follow redirects.
5 | Then view the nicely formatted request and response.
6 |
It's the perfect tool for testing APIs. Just enter a URL and click send.
7 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------
/app/templates/hurls.mustache:
--------------------------------------------------------------------------------
1 | Your hurls
2 | {{# any_hurls?}}
3 |
4 | {{# hurls}}
5 |
6 | {{ url }}
7 | {{ method }}
8 | {{ auth }}
9 | {{ date }}
10 |
11 |
12 | {{/ hurls}}
13 |
14 | {{/ any_hurls?}}
15 |
16 | {{^ any_hurls?}}
17 |
18 |
19 |
We found some hurls, but none are yours.
20 |
21 | {{/ any_hurls?}}
22 |
--------------------------------------------------------------------------------
/app/templates/index.mustache:
--------------------------------------------------------------------------------
1 |
67 |
68 |
69 |
70 | no auth
71 |
72 |
73 |
78 | HTTP basic
79 |
80 |
81 |
82 |
83 |
84 |
85 |
88 |
89 | {{# hurl_header_keys }}
90 |
95 | {{/ hurl_header_keys }}
96 |
97 | {{# no_hurl_header_keys }}
98 |
103 | {{/ no_hurl_header_keys }}
104 |
105 |
106 | Send
107 |
108 |
109 |
110 |
111 |
112 |
Hurl makes HTTP requests.
113 |
Enter a URL, set some headers, view the response, then share it with others.
114 |
Perfect for demoing and debugging APIs.
115 |
116 |
117 |
118 |
119 |
Try it out:
120 |
121 | {{# default_hurls }}
122 | {{ name }}
123 | {{/ default_hurls }}
124 |
125 |
126 |
127 |
128 |
132 |
133 |
134 | request response
135 |
136 |
137 | request response
138 |
139 |
140 |
141 |
142 | {{{ view_request }}}
143 |
144 |
145 |
146 |
147 | {{# view }}
148 |
{{{ header }}} {{{ body }}}
149 | {{/ view }}
150 |
151 |
152 |
156 |
157 |
--------------------------------------------------------------------------------
/app/templates/layout.mustache:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | hurl.it - Test HTTP API requests - Twilio Labs
5 |
6 |
7 | {{# bundled?}}
8 |
9 |
10 |
11 | {{/ bundled?}}
12 |
13 | {{^ bundled?}}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{/ bundled?}}
29 |
30 |
31 |
32 |
33 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | {{ flash }}
59 |
60 | {{{ yield }}}
61 |
62 |
69 |
70 | {{# ga_code }}
71 |
82 | {{/ ga_code }}
83 |
84 |
--------------------------------------------------------------------------------
/app/templates/stats.mustache:
--------------------------------------------------------------------------------
1 |
2 |
Stats
3 |
4 |
5 | {{# hurl_stats}}
6 |
7 | {{ stat }}
8 | {{ value }}
9 |
10 | {{/ hurl_stats}}
11 |
12 |
13 |
Disk
14 |
15 |
16 | {{# disk_stats}}
17 |
18 | {{ stat }}
19 | {{ value }}
20 |
21 | {{/ disk_stats}}
22 |
23 |
24 |
25 | Deployed SHA:
26 | {{ deployed_sha }}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/templates/view.mustache:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | hurl
6 |
7 |
8 |
16 |
17 |
18 | {{{ view_request }}}
19 | {{{ view_header }}}
20 | {{{ view_body }}}
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/views/about.rb:
--------------------------------------------------------------------------------
1 | module Views
2 | class About < Layout
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/hurls.rb:
--------------------------------------------------------------------------------
1 | module Views
2 | class Hurls < Layout
3 | def hurls
4 | @user.hurls.map do |hurl|
5 | hurl['auth'] = hurl['auth'] == 'none' ? 'no auth' : 'HTTP basic'
6 | hurl
7 | end
8 | end
9 |
10 | def any_hurls?
11 | @user.hurls.any?
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/app/views/index.rb:
--------------------------------------------------------------------------------
1 | module Views
2 | class Index < Layout
3 | def previous_hurl
4 | if prev = prev_hurl
5 | [ :hurl => prev.to_s ]
6 | end
7 | end
8 |
9 | def next_hurl
10 | if nxt = super
11 | [ :hurl => nxt.to_s ]
12 | end
13 | end
14 |
15 | def help_blurb_hidden?
16 | logged_in? or not @hurl.empty?
17 | end
18 |
19 | def try_it_hidden?
20 | not @hurl.empty?
21 | end
22 |
23 | def default_hurls
24 | super.sort.map do |name, params|
25 | dname = name.downcase
26 | { :name => name, :sha => sha(params), :class => dname.split(' ')[0] }
27 | end
28 | end
29 |
30 | def hide_request_and_response?
31 | @view.nil?
32 | end
33 |
34 |
35 | #
36 | # @hurl related
37 | #
38 |
39 | def hurl?
40 | @hurl && @hurl.any?
41 | end
42 |
43 | def hurl_post_body
44 | @hurl['post-body'] if @hurl
45 | end
46 |
47 | def hurl_url
48 | @hurl['url'] if @hurl
49 | end
50 |
51 | def method_is_GET?
52 | @hurl['method'] == 'GET'
53 | end
54 |
55 | def method_is_HEAD?
56 | @hurl['method'] == 'HEAD'
57 | end
58 |
59 | def method_is_POST?
60 | @hurl['method'] == 'POST'
61 | end
62 |
63 | def method_is_PUT?
64 | @hurl['method'] == 'PUT'
65 | end
66 |
67 | def method_is_DELETE?
68 | @hurl['method'] == 'DELETE'
69 | end
70 |
71 | def hurl_param_keys
72 | return if @hurl['param-keys'].nil?
73 | arr = []
74 | @hurl['param-keys'].each_with_index do |name, i|
75 | arr << { :name => name, :value => @hurl['param-vals'][i] }
76 | end
77 | arr
78 | end
79 |
80 | def no_hurl_param_keys
81 | hurl_param_keys.nil? || hurl_param_keys.empty?
82 | end
83 |
84 | def hurl_header_keys
85 | return if @hurl['header-keys'].nil?
86 | arr = []
87 | @hurl['header-keys'].each_with_index do |name, i|
88 | arr << { :name => name, :value => @hurl['header-vals'][i] }
89 | end
90 | arr
91 | end
92 |
93 | def no_hurl_header_keys
94 | hurl_header_keys.nil? || hurl_header_keys.empty?
95 | end
96 |
97 | def hurl_basic_auth?
98 | @hurl['auth'] == 'basic'
99 | end
100 |
101 | def hurl_username
102 | @hurl['username']
103 | end
104 |
105 | def hurl_password
106 | @hurl['password']
107 | end
108 |
109 | def hurl_permalink
110 | @view_id ? "/hurls/#{@hurl['id']}/#{@view_id}" : "#"
111 | end
112 |
113 | def follows_redirects?
114 | @hurl['follows_redirects']
115 | end
116 |
117 |
118 | #
119 | # view related
120 | #
121 |
122 | def view_permalink
123 | @view_id ? "/views/#{@view_id}" : "#"
124 | end
125 |
126 | def view_request
127 | @view['request'] if @view
128 | end
129 |
130 | def view
131 | return unless @view
132 | [ :header => @view['header'], :body => @view['body'] ]
133 | end
134 | end
135 | end
136 |
--------------------------------------------------------------------------------
/app/views/layout.rb:
--------------------------------------------------------------------------------
1 | module Views
2 | class Layout < Mustache
3 | include Hurl::Helpers
4 |
5 | def no_flash
6 | not @flash
7 | end
8 |
9 | def flash
10 | @flash
11 | end
12 |
13 | def deployed_sha
14 | @deployed_sha ||= `git rev-parse --short HEAD`.chomp
15 | end
16 |
17 | def bundled?
18 | File.exist?("#{RACK_ROOT}/public/css/bundle.css") &&
19 | File.exist?("#{RACK_ROOT}/public/js/bundle.js")
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/views/stats.rb:
--------------------------------------------------------------------------------
1 | module Views
2 | class Stats < Layout
3 | def hurl_stats
4 | return [
5 | count(:users),
6 | count(:views),
7 | count(:hurls)
8 | ]
9 | end
10 |
11 | def count(thing)
12 | files = Dir["#{Hurl::DB::DIR}/#{thing}/**/**"].reject do |file|
13 | File.directory?(file)
14 | end
15 |
16 | { :stat => thing, :value => files.size }
17 | end
18 |
19 | def disk_stats
20 | [ :stat => 'db-size', :value => `du -sh db`.split(' ')[0] ]
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/app/views/view.rb:
--------------------------------------------------------------------------------
1 | module Views
2 | class View < Layout
3 | def view_request
4 | @view['request']
5 | end
6 |
7 | def view_header
8 | @view['header']
9 | end
10 |
11 | def view_body
12 | @view['body']
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | unless $LOAD_PATH.include? "."
2 | $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
3 | end
4 |
5 | begin
6 | require './env'
7 | rescue LoadError
8 | nil
9 | end
10 |
11 | require 'app/app'
12 |
13 | run Hurl::App.new
14 |
--------------------------------------------------------------------------------
/docs/index.mustache:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 | hurl
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 | Hurl makes HTTP requests.
24 |
25 |
26 | {{{ content }}}
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/hurls.yaml:
--------------------------------------------------------------------------------
1 | # The default Hurls shown on the home page
2 | Twilio API (view account):
3 | url: https://api.twilio.com/2010-04-01/Accounts
4 | method: GET
5 | auth: basic
6 | username: account_sid_here
7 | password: auth_token_here
8 |
9 | Twitter API (public timeline):
10 | url: http://twitter.com/statuses/public_timeline.json
11 | method: GET
12 | auth: none
13 |
14 | Vimeo API (video):
15 | url: http://vimeo.com/api/v2/video/6238577.xml
16 | method: GET
17 | auth: none
18 |
19 | GitHub API (repository):
20 | url: http://github.com/api/v2/json/repos/show/twilio/hurl
21 | method: GET
22 | auth: none
--------------------------------------------------------------------------------
/public/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/.gitignore
--------------------------------------------------------------------------------
/public/css/facebox.css:
--------------------------------------------------------------------------------
1 | #facebox .b {
2 | background:url(/img/facebox/b.png);
3 | }
4 |
5 | #facebox .tl {
6 | background:url(/img/facebox/tl.png);
7 | }
8 |
9 | #facebox .tr {
10 | background:url(/img/facebox/tr.png);
11 | }
12 |
13 | #facebox .bl {
14 | background:url(/img/facebox/bl.png);
15 | }
16 |
17 | #facebox .br {
18 | background:url(/img/facebox/br.png);
19 | }
20 |
21 | #facebox {
22 | position: absolute;
23 | top: 0;
24 | left: 0;
25 | z-index: 100;
26 | text-align: left;
27 | }
28 |
29 | #facebox .popup {
30 | position: relative;
31 | }
32 |
33 | #facebox table {
34 | border-collapse: collapse;
35 | }
36 |
37 | #facebox td {
38 | border-bottom: 0;
39 | padding: 0;
40 | }
41 |
42 | #facebox .body {
43 | padding: 10px;
44 | background: #fff;
45 | width: 370px;
46 | }
47 |
48 | #facebox .loading {
49 | text-align: center;
50 | }
51 |
52 | #facebox .image {
53 | text-align: center;
54 | }
55 |
56 | #facebox img {
57 | border: 0;
58 | margin: 0;
59 | }
60 |
61 | #facebox .footer {
62 | border-top: 1px solid #DDDDDD;
63 | padding-top: 5px;
64 | margin-top: 10px;
65 | text-align: right;
66 | }
67 |
68 | #facebox .footer img {
69 | vertical-align: middle;
70 | }
71 |
72 | #facebox .tl, #facebox .tr, #facebox .bl, #facebox .br {
73 | height: 10px;
74 | width: 10px;
75 | overflow: hidden;
76 | padding: 0;
77 | }
78 |
79 | #facebox_overlay {
80 | position: fixed;
81 | top: 0px;
82 | left: 0px;
83 | height:100%;
84 | width:100%;
85 | }
86 |
87 | .facebox_hide {
88 | z-index:-100;
89 | }
90 |
91 | .facebox_overlayBG {
92 | background-color: #000;
93 | z-index: 99;
94 | }
95 |
96 | * html #facebox_overlay { /* ie6 hack */
97 | position: absolute;
98 | height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
99 | }
--------------------------------------------------------------------------------
/public/css/jquery.autocomplete.css:
--------------------------------------------------------------------------------
1 | .ac_results {
2 | padding: 0px;
3 | border: 1px solid WindowFrame;
4 | background-color: Window;
5 | overflow: hidden;
6 | }
7 |
8 | .ac_results ul {
9 | width: 100%;
10 | list-style-position: outside;
11 | list-style: none;
12 | padding: 0;
13 | margin: 0;
14 | }
15 |
16 | .ac_results iframe {
17 | display:none;/*sorry for IE5*/
18 | display/**/:block;/*sorry for IE5*/
19 | position:absolute;
20 | top:0;
21 | left:0;
22 | z-index:-1;
23 | filter:mask();
24 | width:3000px;
25 | height:3000px;
26 | }
27 |
28 | .ac_results li {
29 | margin: 0px;
30 | padding: 2px 5px;
31 | cursor: pointer;
32 | display: block;
33 | width: 100%;
34 | font: menu;
35 | font-size: 12px;
36 | overflow: hidden;
37 | }
38 |
39 | .ac_loading {
40 | background : Window url('/img/autocomplete-loader.gif') right center no-repeat;
41 | }
42 |
43 | .ac_over {
44 | background-color: Highlight;
45 | color: HighlightText;
46 | }
47 |
--------------------------------------------------------------------------------
/public/css/pygment_trac.css:
--------------------------------------------------------------------------------
1 | /* .highlight { background: #ffffff; } */
2 | .highlight .c { color: #999988; font-style: italic } /* Comment */
3 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
4 | .highlight .kw { font-weight: bold } /* Keyword */
5 | .highlight .o { font-weight: bold } /* Operator */
6 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
7 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
8 | .highlight .pp { color: #999999; font-weight: bold } /* Comment.Preproc */
9 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
10 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
11 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
12 | .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
13 | .highlight .ge { font-style: italic } /* Generic.Emph */
14 | .highlight .gr { color: #aa0000 } /* Generic.Error */
15 | .highlight .gh { color: #999999 } /* Generic.Heading */
16 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
17 | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
18 | .highlight .go { color: #888888 } /* Generic.Output */
19 | .highlight .gp { color: #555555 } /* Generic.Prompt */
20 | .highlight .gs { font-weight: bold } /* Generic.Strong */
21 | .highlight .gu { color: #aaaaaa } /* Generic.Subheading */
22 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */
23 | .highlight .kc { font-weight: bold } /* Keyword.Constant */
24 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */
25 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */
26 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */
27 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
28 | .highlight .m { color: #009999 } /* Literal.Number */
29 | .highlight .s { color: #d14 } /* Literal.String */
30 | .highlight .na { color: #008080 } /* Name.Attribute */
31 | .highlight .nb { color: #0086B3 } /* Name.Builtin */
32 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
33 | .highlight .no { color: #008080 } /* Name.Constant */
34 | .highlight .ni { color: #800080 } /* Name.Entity */
35 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
36 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
37 | .highlight .nn { color: #555555 } /* Name.Namespace */
38 | .highlight .nt { color: #000080 } /* Name.Tag */
39 | .highlight .ta { color: #000080 } /* Name.Tag */
40 | .highlight .nv { color: #008080 } /* Name.Variable */
41 | .highlight .ow { font-weight: bold } /* Operator.Word */
42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */
43 | .highlight .mf { color: #009999 } /* Literal.Number.Float */
44 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */
45 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */
46 | .highlight .i { color: #009999 } /* Literal.Number.Integer */
47 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */
48 | .highlight .sb { color: #d14 } /* Literal.String.Backtick */
49 | .highlight .sc { color: #d14 } /* Literal.String.Char */
50 | .highlight .sd { color: #d14 } /* Literal.String.Doc */
51 | .highlight .s2 { color: #d14 } /* Literal.String.Double */
52 | .highlight .k { color: #d14 } /* Literal.String.Double */
53 | .highlight .se { color: #d14 } /* Literal.String.Escape */
54 | .highlight .sh { color: #d14 } /* Literal.String.Heredoc */
55 | .highlight .si { color: #d14 } /* Literal.String.Interpol */
56 | .highlight .sx { color: #d14 } /* Literal.String.Other */
57 | .highlight .sr { color: #009926 } /* Literal.String.Regex */
58 | .highlight .s1 { color: #d14 } /* Literal.String.Single */
59 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */
60 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
61 | .highlight .vc { color: #008080 } /* Name.Variable.Class */
62 | .highlight .vg { color: #008080 } /* Name.Variable.Global */
63 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */
64 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
65 |
--------------------------------------------------------------------------------
/public/css/reset.css:
--------------------------------------------------------------------------------
1 | /* reset */
2 | html, body, div, span, applet, object, iframe,
3 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
4 | a, abbr, acronym, address, big, cite, code,
5 | del, dfn, em, font, img, ins, kbd, q, s, samp,
6 | small, strike, strong, sub, sup, tt, var,
7 | dl, dt, dd, ul, li,
8 | form, label, legend,
9 | table, caption, tbody, tfoot, thead, tr, th, td {
10 | margin: 0;
11 | padding: 0;
12 | border: 0;
13 | outline: 0;
14 | font-weight: inherit;
15 | font-style: normal;
16 | font-size: 100%;
17 | font-family: inherit;
18 | vertical-align: baseline;
19 | }
20 | :focus {
21 | outline: 0;
22 | }
23 | body {
24 | line-height: 1;
25 | color: black;
26 | background: white;
27 | }
28 | ul {
29 | list-style: none;
30 | }
31 | table {
32 | border-collapse: collapse;
33 | border-spacing: 0;
34 | }
35 | caption, th, td {
36 | text-align: left;
37 | font-weight: normal;
38 | }
39 | blockquote:before, blockquote:after,
40 | q:before, q:after {
41 | content: "";
42 | }
43 | blockquote, q {
44 | quotes: "" "";
45 | }
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | /* general */
2 | body {
3 | font: 1em "lucida Grande",arial,sans-serif;
4 | background: #fff url(/img/rainbow.png) repeat;
5 | color: #333;
6 | }
7 | p {
8 | margin: 6px 0;
9 | }
10 | a {
11 | text-decoration: none;
12 | color: #26B3CF;
13 | }
14 | a:hover {
15 | text-decoration: underline;
16 | }
17 | h2 {
18 | font-size: 1.5em;
19 | margin-bottom: 20px;
20 | }
21 | .clearwrap {
22 | overflow: hidden;
23 | }
24 | img.gravatar {
25 | width: 18px;
26 | height: 18px;
27 | padding: 1px;
28 | border: 1px solid #c0c0c0;
29 | }
30 | /* forms */
31 | label {
32 | font-size: 16px;
33 | }
34 | select,
35 | input[type=text],
36 | input[type=password],
37 | textarea {
38 | -x-system-font:none;
39 | color:#333;
40 | font: 1em "lucida Grande",arial,sans-serif;
41 | font-size: 16px;
42 | font-size-adjust:none;
43 | font-stretch: normal;
44 | font-style: normal;
45 | font-variant: normal;
46 | font-weight: normal;
47 | line-height: normal;
48 | padding: 5px;
49 | border: 1px solid #bbb;
50 | width: 748px;
51 | margin: 2px 0;
52 | }
53 | select.form-alpha {
54 | width: 290px;
55 | }
56 | input[type=text].form-alpha {
57 | width: 280px;
58 | }
59 | .facebox input[type=text],
60 | .facebox input[type=password] {
61 | width: 380px;
62 | }
63 | input[type=text].form-beta,
64 | input[type=password].form-beta {
65 | width: 444px;
66 | }
67 | .form-beta {
68 | margin-left: 6px !important;
69 | }
70 | /* buttons */
71 | button {
72 | font: 1em "Helvetica Neue", Arial, sans-serif;
73 | color: #333;
74 | padding: 0.365em 10px;
75 | line-height: 1.4em;
76 | font-weight: bold;
77 | cursor: pointer;
78 | background: #e9eaea url(/img/buttonbg.png) repeat-x;
79 | border: 1px solid #ccc;
80 | text-shadow: 1px 1px 0 #fff;
81 | -moz-border-radius: 2px;
82 | -webkit-border-radius: 2px;
83 | }
84 | button:hover {
85 | opacity: 0.8;
86 | }
87 | button:active {
88 | opacity: 1;
89 | }
90 | #send-wrap {
91 | height: 40px;
92 | margin-top: 16px;
93 | }
94 | #send-wrap img {
95 | padding: 0.8em 10px;
96 | line-height: 1.4em;
97 | }
98 | #spinner {
99 | padding-right: 9px 1px;
100 | display: none;
101 | }
102 | /* close buttons */
103 | img.delete {
104 | opacity: 0.5;
105 | }
106 | .header-delete,
107 | .param-delete {
108 | float: right;
109 | margin-right: -20px;
110 | padding-top: 7px;
111 | }
112 | .flash-close {
113 | float: right;
114 | margin-right: -26px;
115 | }
116 | /* layout */
117 | #inset {
118 | background-color: #fff;
119 | width: 860px;
120 | margin: 0 auto;
121 | padding-bottom: 40px;
122 | }
123 | #content {
124 | margin: 0 50px;
125 | }
126 | #header {
127 | padding: 20px 50px 5px 50px;
128 | border-bottom: 5px solid #26B3CF;
129 | margin-bottom: 30px;
130 | }
131 | #nav {
132 | float: right;
133 | margin-top: 34px;
134 | }
135 | a.nav-item {
136 | margin-right: 12px;
137 | }
138 | #footer {
139 | margin-top: 60px;
140 | background-color: #26B3CF;
141 | padding: 4px 50px;
142 | text-align: right;
143 | }
144 | #footer a {
145 | color: #fff;
146 | }
147 | /* homepage */
148 | #page-prev,
149 | #page-next {
150 | font-weight: bold;
151 | margin-top: 7px;
152 | }
153 | #page-prev {
154 | float: left;
155 | margin-left: -30px;
156 | }
157 | #page-next {
158 | float: right;
159 | margin-right: -30px;
160 | }
161 | #page-prev:hover,
162 | #page-next:hover {
163 | text-decoration: none;
164 | }
165 | #hurl-form {
166 | margin: 20px 0;
167 | }
168 | #auth-selection,
169 | #header-selection {
170 | margin-top: 10px;
171 | }
172 | .utils {
173 | float: right;
174 | margin-top: 6px;
175 | }
176 | .link-icon {
177 | font-weight: bold;
178 | }
179 | /* flash */
180 | .flash-error,
181 | .error-msg {
182 | padding: 6px 6px 6px 36px;
183 | background: #ef95a1 url(/img/message-warn.png) 6px 50% no-repeat;
184 | }
185 | .flash-notice {
186 | padding: 6px 6px 6px 36px;
187 | background: #86EF9E url(/img/message-info.png) 6px 50% no-repeat;
188 | }
189 | /* help text */
190 | #help-text {
191 | text-align: center;
192 | background-color: #eee;
193 | margin: 20px 0;
194 | -moz-border-radius: 5px;
195 | -webkit-border-radius: 5px;
196 | padding: 10px 0;
197 | }
198 | ul.examples li {
199 | padding-left: 24px;
200 | margin: 3px 0;
201 | }
202 | ul.examples li.twitter {
203 | background: #fff url(http://twitter.com/favicon.ico) 0 40% no-repeat;
204 | }
205 | ul.examples li.vimeo {
206 | background: #fff url(http://vimeo.com/favicon.ico) 0 40% no-repeat;
207 | }
208 | ul.examples li.github {
209 | background: #fff url(http://github.com/favicon.ico) 0 40% no-repeat;
210 | }
211 | ul.examples li.twilio {
212 | background: #fff url(http://www.twilio.com/favicon.ico) 0 40% no-repeat;
213 | background-size: 16px 16px;
214 | }
215 | ul.examples li.baconfile {
216 | background: #fff url(http://baconfile.com/favicon.ico) 0 40% no-repeat;
217 | }
218 | /* request/response area */
219 | .tab-current,
220 | .toggle-reqres-link {
221 | padding: 6px;
222 | margin-right: 4px;
223 | }
224 | .tab-current,
225 | .toggle-reqres-link:hover {
226 | background-color: #26B3CF;
227 | color: #fff;
228 | -moz-border-radius: 5px;
229 | -webkit-border-radius: 5px;
230 | text-decoration: none;
231 | }
232 | .code-wrap {
233 | margin-top: 14px;
234 | background-color: #eee;
235 | padding: 8px 16px;
236 | -moz-border-radius: 5px;
237 | -webkit-border-radius: 5px;
238 | }
239 | .code-text {
240 | padding: 2px;
241 | min-height: 60px;
242 | overflow-y: auto;
243 | font-family:'Bitstream Vera Sans Mono','Courier',monospace;
244 | font-size: 14px;
245 | }
246 | /* saved archives page */
247 | table td {
248 | padding-right: 12px;
249 | padding-bottom: 6px;
250 | }
251 | td.date {
252 | color: #aaa;
253 | }
254 | /* about */
255 | .blurb {
256 | margin: 20px 0;
257 | }
258 | .blurb p {
259 | margin: 10px 0;
260 | }
261 | #about-video {
262 | text-align: center;
263 | margin-bottom: 20px;
264 | }
265 | /* stats */
266 | #stats h2 {
267 | color: #26B3CF;
268 | margin-bottom: 6px;
269 | }
270 | #stats table {
271 | margin-bottom: 15px;
272 | }
273 | /* error */
274 | .unicorn {
275 | text-align: center;
276 | }
277 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/favicon.ico
--------------------------------------------------------------------------------
/public/img/ajax-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/ajax-loader.gif
--------------------------------------------------------------------------------
/public/img/autocomplete-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/autocomplete-loader.gif
--------------------------------------------------------------------------------
/public/img/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/background.jpg
--------------------------------------------------------------------------------
/public/img/buttonbg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/buttonbg.png
--------------------------------------------------------------------------------
/public/img/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/delete.png
--------------------------------------------------------------------------------
/public/img/facebox/b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/facebox/b.png
--------------------------------------------------------------------------------
/public/img/facebox/bl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/facebox/bl.png
--------------------------------------------------------------------------------
/public/img/facebox/br.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/facebox/br.png
--------------------------------------------------------------------------------
/public/img/facebox/closelabel.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/facebox/closelabel.gif
--------------------------------------------------------------------------------
/public/img/facebox/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/facebox/loading.gif
--------------------------------------------------------------------------------
/public/img/facebox/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/facebox/logo.png
--------------------------------------------------------------------------------
/public/img/facebox/shadow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/facebox/shadow.gif
--------------------------------------------------------------------------------
/public/img/facebox/tl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/facebox/tl.png
--------------------------------------------------------------------------------
/public/img/facebox/tr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/facebox/tr.png
--------------------------------------------------------------------------------
/public/img/favicon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/favicon.gif
--------------------------------------------------------------------------------
/public/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/logo.png
--------------------------------------------------------------------------------
/public/img/message-info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/message-info.png
--------------------------------------------------------------------------------
/public/img/message-warn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/message-warn.png
--------------------------------------------------------------------------------
/public/img/pony-hurl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/pony-hurl.png
--------------------------------------------------------------------------------
/public/img/pony.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/pony.png
--------------------------------------------------------------------------------
/public/img/rainbow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/rainbow.png
--------------------------------------------------------------------------------
/public/img/unicorn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/twilio/hurl2/e28d7f58d38f70dccaca1a32d3771d44fdb9a597/public/img/unicorn.png
--------------------------------------------------------------------------------
/public/js/facebox.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Facebox (for jQuery)
3 | * version: 1.2 (05/05/2008)
4 | * @requires jQuery v1.2 or later
5 | *
6 | * Examples at http://famspam.com/facebox/
7 | *
8 | * Licensed under the MIT:
9 | * http://www.opensource.org/licenses/mit-license.php
10 | *
11 | * Copyright 2007, 2008 Chris Wanstrath [ chris@ozmm.org ]
12 | *
13 | * Usage:
14 | *
15 | * jQuery(document).ready(function() {
16 | * jQuery('a[rel*=facebox]').facebox()
17 | * })
18 | *
19 | * Terms
20 | * Loads the #terms div in the box
21 | *
22 | * Terms
23 | * Loads the terms.html page in the box
24 | *
25 | * Terms
26 | * Loads the terms.png image in the box
27 | *
28 | *
29 | * You can also use it programmatically:
30 | *
31 | * jQuery.facebox('some html')
32 | * jQuery.facebox('some html', 'my-groovy-style')
33 | *
34 | * The above will open a facebox with "some html" as the content.
35 | *
36 | * jQuery.facebox(function($) {
37 | * $.get('blah.html', function(data) { $.facebox(data) })
38 | * })
39 | *
40 | * The above will show a loading screen before the passed function is called,
41 | * allowing for a better ajaxy experience.
42 | *
43 | * The facebox function can also display an ajax page, an image, or the contents of a div:
44 | *
45 | * jQuery.facebox({ ajax: 'remote.html' })
46 | * jQuery.facebox({ ajax: 'remote.html' }, 'my-groovy-style')
47 | * jQuery.facebox({ image: 'stairs.jpg' })
48 | * jQuery.facebox({ image: 'stairs.jpg' }, 'my-groovy-style')
49 | * jQuery.facebox({ div: '#box' })
50 | * jQuery.facebox({ div: '#box' }, 'my-groovy-style')
51 | *
52 | * Want to close the facebox? Trigger the 'close.facebox' document event:
53 | *
54 | * jQuery(document).trigger('close.facebox')
55 | *
56 | * Facebox also has a bunch of other hooks:
57 | *
58 | * loading.facebox
59 | * beforeReveal.facebox
60 | * reveal.facebox (aliased as 'afterReveal.facebox')
61 | * init.facebox
62 | *
63 | * Simply bind a function to any of these hooks:
64 | *
65 | * $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... })
66 | *
67 | */
68 | (function($) {
69 | $.facebox = function(data, klass) {
70 | $.facebox.loading()
71 |
72 | if (data.ajax) fillFaceboxFromAjax(data.ajax, klass)
73 | else if (data.image) fillFaceboxFromImage(data.image, klass)
74 | else if (data.div) fillFaceboxFromHref(data.div, klass)
75 | else if ($.isFunction(data)) data.call($)
76 | else $.facebox.reveal(data, klass)
77 | }
78 |
79 | /*
80 | * Public, $.facebox methods
81 | */
82 |
83 | $.extend($.facebox, {
84 | settings: {
85 | opacity : 0,
86 | overlay : true,
87 | loadingImage : '/facebox/loading.gif',
88 | closeImage : '/facebox/closelabel.gif',
89 | imageTypes : [ 'png', 'jpg', 'jpeg', 'gif' ],
90 | faceboxHtml : '\
91 | \
92 | \
117 |
'
118 | },
119 |
120 | loading: function() {
121 | init()
122 | if ($('#facebox .loading').length == 1) return true
123 | showOverlay()
124 |
125 | $('#facebox .content').empty()
126 | $('#facebox .body').children().hide().end().
127 | append('')
128 |
129 | $('#facebox').css({
130 | top: getPageScroll()[1] + (getPageHeight() / 10),
131 | left: $(window).width() / 2 - 205
132 | }).show()
133 |
134 | $(document).bind('keydown.facebox', function(e) {
135 | if (e.keyCode == 27) $.facebox.close()
136 | return true
137 | })
138 | $(document).trigger('loading.facebox')
139 | },
140 |
141 | reveal: function(data, klass) {
142 | $(document).trigger('beforeReveal.facebox')
143 | if (klass) $('#facebox .content').addClass(klass)
144 | $('#facebox .content').append(data)
145 | $('#facebox .loading').remove()
146 | $('#facebox .body').children().fadeIn('normal')
147 | $('#facebox').css('left', $(window).width() / 2 - ($('#facebox table').width() / 2))
148 | $(document).trigger('reveal.facebox').trigger('afterReveal.facebox')
149 | },
150 |
151 | close: function() {
152 | $(document).trigger('close.facebox')
153 | return false
154 | }
155 | })
156 |
157 | /*
158 | * Public, $.fn methods
159 | */
160 |
161 | $.fn.facebox = function(settings) {
162 | if ($(this).length == 0) return
163 |
164 | init(settings)
165 |
166 | function clickHandler() {
167 | $.facebox.loading(true)
168 |
169 | // support for rel="facebox.inline_popup" syntax, to add a class
170 | // also supports deprecated "facebox[.inline_popup]" syntax
171 | var klass = this.rel.match(/facebox\[?\.(\w+)\]?/)
172 | if (klass) klass = klass[1]
173 |
174 | fillFaceboxFromHref(this.href, klass)
175 | return false
176 | }
177 |
178 | return this.bind('click.facebox', clickHandler)
179 | }
180 |
181 | /*
182 | * Private methods
183 | */
184 |
185 | // called one time to setup facebox on this page
186 | function init(settings) {
187 | if ($.facebox.settings.inited) return true
188 | else $.facebox.settings.inited = true
189 |
190 | $(document).trigger('init.facebox')
191 | makeCompatible()
192 |
193 | var imageTypes = $.facebox.settings.imageTypes.join('|')
194 | $.facebox.settings.imageTypesRegexp = new RegExp('\.(' + imageTypes + ')$', 'i')
195 |
196 | if (settings) $.extend($.facebox.settings, settings)
197 | $('body').append($.facebox.settings.faceboxHtml)
198 |
199 | var preload = [ new Image(), new Image() ]
200 | preload[0].src = $.facebox.settings.closeImage
201 | preload[1].src = $.facebox.settings.loadingImage
202 |
203 | $('#facebox').find('.b:first, .bl, .br, .tl, .tr').each(function() {
204 | preload.push(new Image())
205 | preload.slice(-1).src = $(this).css('background-image').replace(/url\((.+)\)/, '$1')
206 | })
207 |
208 | $('#facebox .close').click($.facebox.close)
209 | $('#facebox .close_image').attr('src', $.facebox.settings.closeImage)
210 | }
211 |
212 | // getPageScroll() by quirksmode.com
213 | function getPageScroll() {
214 | var xScroll, yScroll;
215 | if (self.pageYOffset) {
216 | yScroll = self.pageYOffset;
217 | xScroll = self.pageXOffset;
218 | } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
219 | yScroll = document.documentElement.scrollTop;
220 | xScroll = document.documentElement.scrollLeft;
221 | } else if (document.body) {// all other Explorers
222 | yScroll = document.body.scrollTop;
223 | xScroll = document.body.scrollLeft;
224 | }
225 | return new Array(xScroll,yScroll)
226 | }
227 |
228 | // Adapted from getPageSize() by quirksmode.com
229 | function getPageHeight() {
230 | var windowHeight
231 | if (self.innerHeight) { // all except Explorer
232 | windowHeight = self.innerHeight;
233 | } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
234 | windowHeight = document.documentElement.clientHeight;
235 | } else if (document.body) { // other Explorers
236 | windowHeight = document.body.clientHeight;
237 | }
238 | return windowHeight
239 | }
240 |
241 | // Backwards compatibility
242 | function makeCompatible() {
243 | var $s = $.facebox.settings
244 |
245 | $s.loadingImage = $s.loading_image || $s.loadingImage
246 | $s.closeImage = $s.close_image || $s.closeImage
247 | $s.imageTypes = $s.image_types || $s.imageTypes
248 | $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml
249 | }
250 |
251 | // Figures out what you want to display and displays it
252 | // formats are:
253 | // div: #id
254 | // image: blah.extension
255 | // ajax: anything else
256 | function fillFaceboxFromHref(href, klass) {
257 | // div
258 | if (href.match(/#/)) {
259 | var url = window.location.href.split('#')[0]
260 | var target = href.replace(url,'')
261 | if (target == '#') return
262 | $.facebox.reveal($(target).html(), klass)
263 |
264 | // image
265 | } else if (href.match($.facebox.settings.imageTypesRegexp)) {
266 | fillFaceboxFromImage(href, klass)
267 | // ajax
268 | } else {
269 | fillFaceboxFromAjax(href, klass)
270 | }
271 | }
272 |
273 | function fillFaceboxFromImage(href, klass) {
274 | var image = new Image()
275 | image.onload = function() {
276 | $.facebox.reveal('', klass)
277 | }
278 | image.src = href
279 | }
280 |
281 | function fillFaceboxFromAjax(href, klass) {
282 | $.get(href, function(data) { $.facebox.reveal(data, klass) })
283 | }
284 |
285 | function skipOverlay() {
286 | return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null
287 | }
288 |
289 | function showOverlay() {
290 | if (skipOverlay()) return
291 |
292 | if ($('#facebox_overlay').length == 0)
293 | $("body").append('
')
294 |
295 | $('#facebox_overlay').hide().addClass("facebox_overlayBG")
296 | .css('opacity', $.facebox.settings.opacity)
297 | .click(function() { $(document).trigger('close.facebox') })
298 | .fadeIn(200)
299 | return false
300 | }
301 |
302 | function hideOverlay() {
303 | if (skipOverlay()) return
304 |
305 | $('#facebox_overlay').fadeOut(200, function(){
306 | $("#facebox_overlay").removeClass("facebox_overlayBG")
307 | $("#facebox_overlay").addClass("facebox_hide")
308 | $("#facebox_overlay").remove()
309 | })
310 |
311 | return false
312 | }
313 |
314 | /*
315 | * Bindings
316 | */
317 |
318 | $(document).bind('close.facebox', function() {
319 | $(document).unbind('keydown.facebox')
320 | $('#facebox').fadeOut(function() {
321 | $('#facebox .content').removeClass().addClass('content')
322 | hideOverlay()
323 | $('#facebox .loading').remove()
324 | })
325 | })
326 |
327 | })(jQuery);
--------------------------------------------------------------------------------
/public/js/hurl.headers.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | $.fn.hurlHeaders = function(el) {
3 | $(el).autocompleteArray(keyNames( Headers ), {
4 | delay: 40,
5 | onItemSelect: function(e) {
6 | var header = $(e).text(), next = $(el).siblings('input')
7 | var more = Headers[header]
8 |
9 | if ( header == "User-Agent" ) {
10 | next.autocompleteArray(keyNames( Headers['User-Agent'] ), {
11 | delay: 40,
12 | onItemSelect: function(row) {
13 | next.val( Headers['User-Agent'][$(row).text()] )
14 | }
15 | })
16 | } else if ( more == "date" ) {
17 | next.focus().val( GetRFC822Date(new Date) )
18 | } else if ( more ) {
19 | next.autocompleteArray( more, { delay: 40 } )
20 | }
21 |
22 | next.focus()
23 | }
24 | })
25 | }
26 |
27 | function keyNames(obj) {
28 | var names = []
29 | for (name in obj) {
30 | names.push(name)
31 | }
32 | return names
33 | }
34 |
35 | var Headers = {
36 | "Accept": ["*/*", "text/plain", "text/html, text/plain", "application/xml", "application/json"],
37 | "Accept-Encoding": [ "compress", "deflate", "gzip", "compress, gzip", "gzip, deflate"],
38 | "Accept-Language": [ "en", "es", "de", "fr", "*" ],
39 | "Cache-Control": [ "cache", "no-cache" ],
40 | "Connection": [ "close", "keep-alive" ],
41 | "Cookie": null,
42 | "Content-Length": null,
43 | "Content-Type": [ "application/octet-stream", "application/x-www-form-urlencoded", "application/xml", "application/json", "text/html", "text/plain", "text/xml" ],
44 | "From": null,
45 | "Host": null,
46 | "If-Match": [ "*" ],
47 | "If-Modified-Since": "date",
48 | "If-None-Match": [ "*" ],
49 | "If-Range": "date",
50 | "If-Unmodified-Since": "date",
51 | "Max-Forwards": null,
52 | "Pragma": [ "cache", "no-cache" ],
53 | "User-Agent": {
54 | "Firefox 1.5.0.12 - Mac": "Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12",
55 | "Firefox 1.5.0.12 - Windows": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.12) Gecko/20070508 Firefox/1.5.0.12",
56 | "Firefox 2.0.0.12 - Mac": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12",
57 | "Firefox 2.0.0.12 - Windows": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12",
58 | "Firefox 3.0.4 - Mac": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4",
59 | "Firefox 3.0.4 - Windows": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.12) Gecko/2008102920 Firefox/3.0.4",
60 | "Firefox 3.5.2 - Mac": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2",
61 | "Firefox 3.5.2 - Windows": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2",
62 | "Internet Explorer 5.2.3 – Mac": "Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC)",
63 | "Internet Explorer 5.5": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.1)",
64 | "Internet Explorer 6.0": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)",
65 | "Internet Explorer 7.0": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
66 | "Internet Explorer 8.0": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.2; Trident/4.0)",
67 | "Lynx 2.8.4rel.1 on Linux": "Lynx/2.8.4rel.1 libwww-FM/2.14",
68 | "MobileSafari 1.1.3 - iPhone": "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A93 Safari/419.3",
69 | "MobileSafari 1.1.3 - iPod touch": "Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A93 Safari/419.3",
70 | "Opera 9.25 - Mac": "Opera/9.25 (Macintosh; Intel Mac OS X; U; en)",
71 | "Opera 9.25 - Windows": "Opera/9.25 (Windows NT 5.1; U; en)",
72 | "Safari 1.2.4 - Mac": "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.5.7 (KHTML, like Gecko) Safari/125.12",
73 | "Safari 1.3.2 - Mac": "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.8 (KHTML, like Gecko) Safari/312.6",
74 | "Safari 2.0.4 - Mac": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3",
75 | "Safari 3.0.4 - Mac": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-us) AppleWebKit/523.10.3 (KHTML, like Gecko) Version/3.0.4 Safari/523.10",
76 | "Safari 3.1.2 - Mac": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_2; en-us) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13",
77 | "Safari 3.1.2 - Windows": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-us) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13",
78 | "Safari 3.2.1 - Mac": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1",
79 | "Safari 3.2.1 - Windows": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1",
80 | "Safari 4.0.2 - Mac": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19",
81 | "Safari 4.0.2 - Windows": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/4.0.2 Safari/530.19.1"
82 | }
83 | }
84 |
85 |
86 | /*
87 | * Stolen without mercy nor remorse from
88 | * http://www.sanctumvoid.net/jsexamples/rfc822datetime/rfc822datetime.html
89 | */
90 |
91 | /*Accepts a Javascript Date object as the parameter;
92 | outputs an RFC822-formatted datetime string. */
93 | function GetRFC822Date(oDate)
94 | {
95 | var aMonths = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun",
96 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
97 |
98 | var aDays = new Array( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
99 | var dtm = new String();
100 |
101 | dtm = aDays[oDate.getDay()] + ", ";
102 | dtm += padWithZero(oDate.getDate()) + " ";
103 | dtm += aMonths[oDate.getMonth()] + " ";
104 | dtm += oDate.getFullYear() + " ";
105 | dtm += padWithZero(oDate.getHours()) + ":";
106 | dtm += padWithZero(oDate.getMinutes()) + ":";
107 | dtm += padWithZero(oDate.getSeconds()) + " " ;
108 | dtm += getTZOString(oDate.getTimezoneOffset());
109 | return dtm;
110 | }
111 |
112 | //Pads numbers with a preceding 0 if the number is less than 10.
113 | function padWithZero(val)
114 | {
115 | if (parseInt(val) < 10)
116 | {
117 | return "0" + val;
118 | }
119 | return val;
120 | }
121 |
122 | /* accepts the client's time zone offset from GMT in minutes as a parameter.
123 | returns the timezone offset in the format [+|-}DDDD */
124 | function getTZOString(timezoneOffset)
125 | {
126 | var hours = Math.floor(timezoneOffset/60);
127 | var modMin = Math.abs(timezoneOffset%60);
128 | var s = new String();
129 | s += (hours > 0) ? "-" : "+";
130 | var absHours = Math.abs(hours)
131 | s += (absHours < 10) ? "0" + absHours :absHours;
132 | s += ((modMin == 0) ? "00" : modMin);
133 | return(s);
134 | }
135 | })();
--------------------------------------------------------------------------------
/public/js/hurl.js:
--------------------------------------------------------------------------------
1 | var Hurl = {
2 | // apply label hints to inputs based on their
3 | // title attribute
4 | labelHints: function(el) {
5 | $(el).each(function() {
6 | var self = $(this), title = self.attr('title')
7 |
8 | // indicate inputs using defaults
9 | self.addClass('defaulted')
10 |
11 | if (self.val() === '' || self.val() === title) {
12 | self.val(title).css('color', '#E9EAEA')
13 | } else {
14 | self.addClass('focused')
15 | }
16 |
17 | self.focus(function() {
18 | if (self.val() === title) {
19 | self.val('').addClass('focused').css('color', '#333')
20 | }
21 | })
22 |
23 | self.blur(function() {
24 | if (self.val() === '') {
25 | self.val(title).removeClass('focused').css('color', '#E9EAEA')
26 | }
27 | })
28 | })
29 | },
30 |
31 | removeEmptyData: function(data) {
32 | var keepers = [], value
33 |
34 | // remove empty arrays and any default titular data
35 | for (key in data) {
36 | if (value = data[key].value) {
37 | if ($('input[name=' + data[key].name +'].defaulted:not(.focused)').val() != value) {
38 | keepers.push(data[key])
39 | }
40 | }
41 | }
42 |
43 | data.splice(0, data.length)
44 |
45 | for (key in keepers)
46 | data.push( keepers[key] )
47 |
48 | return true
49 | },
50 |
51 | pony: function() {
52 | if (!this.ponyLoaded) return this.loadPony()
53 | if (this.ponying) return
54 | this.ponying = true
55 |
56 | var width = 668
57 |
58 | var pony = $("
").css({
59 | width: width,
60 | height: 422,
61 | background: 'url(/img/pony.png) top center',
62 | position: 'fixed',
63 | bottom: 0,
64 | right: 0-width,
65 | "z-index": 1000,
66 | cursor: 'pointer'
67 | }).appendTo($("body"))
68 |
69 | pony.show().animate({right: 0}, 1500, function() {
70 | setTimeout(function() {
71 | pony.css('background', 'url(/img/pony-hurl.png) top center')
72 | setTimeout(function() {
73 | pony.animate({right: 0-width}, 1500, function() {
74 | Hurl.ponying = false
75 | })
76 | }, 500)
77 | }, 1000)
78 | })
79 | },
80 |
81 | loadPony: function() {
82 | $(new Image()).load(function() {
83 | Hurl.loadOtherPony()
84 | }).attr('src', '/img/pony.png');
85 | },
86 |
87 | loadOtherPony: function() {
88 | $(new Image()).load(function() {
89 | Hurl.ponyLoaded = true
90 | Hurl.pony()
91 | }).attr('src', '/img/pony-hurl.png');
92 | }
93 | }
94 |
95 | $.fn.hurlAjaxSubmit = function(callback) {
96 | return $(this).ajaxSubmit({
97 | beforeSubmit: Hurl.removeEmptyData,
98 | success: callback
99 | })
100 | }
101 |
102 | $(document).ready(function() {
103 | // select method
104 | $('#select-method').change(function() {
105 | $('#select-method option:selected').each(function() {
106 | var method = $(this).attr('value')
107 | if (method == 'POST' || method == 'PUT'){
108 | $('#post-params').show()
109 | } else {
110 | $('#post-params').hide()
111 | }
112 | })
113 | })
114 | $('#select-method').change()
115 |
116 | // add auth
117 | $('input[name=auth]').change(function() {
118 | if ($(this).attr('value') == 'basic') {
119 | $('#basic-auth-fields').show()
120 | $('#basic-auth-fields .form-alpha').focus()
121 | } else {
122 | $('#basic-auth-fields').hide()
123 | }
124 | })
125 | $('#auth-selection :checked').change()
126 |
127 | // add post param
128 | $('#add-param').click(function() {
129 | // toggle if post body is being shown
130 | if ( $('#set-post-body .link-icon').text() == '-' ) {
131 | $('#set-post-body').click()
132 | return false
133 | }
134 |
135 | var newField = $('#param-fields').clone()
136 | newField.toggle().attr('id', '')
137 | newField.addClass('param-field')
138 | newField.find('.form-alpha').attr('title', 'name')
139 | newField.find('.form-beta').attr('title', 'value')
140 | Hurl.labelHints( newField.find('input[title]') )
141 | registerRemoveHandlers( newField, '.param-delete' )
142 | $(this).parent().append( newField )
143 | return false
144 | })
145 |
146 | // set post body
147 | $('#set-post-body').click(function() {
148 | var icon = $(this).find('.link-icon')
149 |
150 | if ( icon.text() == '+' ) {
151 | icon.text('-')
152 | $('.param-field').hide()
153 | $('#post-body').show().find('textarea').attr('disabled', false)
154 | } else {
155 | icon.text('+')
156 | $('.param-field').show()
157 | $('#post-body').hide().find('textarea').attr('disabled', true)
158 | }
159 |
160 | return false
161 | })
162 |
163 | if ( $('#post-body').is(':visible') ) {
164 | $('#set-post-body').click()
165 | }
166 |
167 | // add header
168 | $('#add-header').click(function() {
169 | var newField = $('#header-fields').clone()
170 | newField.toggle().attr('id', '')
171 | newField.find('.form-alpha').hurlHeaders()
172 | newField.find('.form-alpha').attr('title', 'name')
173 | newField.find('.form-beta').attr('title', 'value')
174 | Hurl.labelHints( newField.find('input[title]') )
175 | registerRemoveHandlers( newField, '.header-delete' )
176 | $(this).parent().append( newField )
177 | return false
178 | })
179 |
180 | // remove header / param
181 | function registerRemoveHandlers(el, klass) {
182 | $(el).find(klass).click(function() {
183 | $(this).parents('p:first').remove()
184 | return false
185 | })
186 | }
187 |
188 | registerRemoveHandlers( document, '.header-delete' )
189 | registerRemoveHandlers( document, '.param-delete' )
190 |
191 | // hurl it!
192 | $('#hurl-form').submit(function() {
193 | $('#send-wrap').children().toggle()
194 | $('.flash-error, .flash-notice').fadeOut()
195 | $('#request-and-response').hide()
196 |
197 | $(this).hurlAjaxSubmit(function(res) {
198 | var data = JSON.parse(res)
199 |
200 | if (data.error) {
201 | $('#flash-error-msg').html(data.error)
202 | $('.flash-error').show()
203 | } else if (/hurl/.test(location.pathname) && data.hurl_id && data.view_id) {
204 | window.location = '/hurls/' + data.hurl_id + '/' + data.view_id
205 | } else if (data.header && data.body && data.request) {
206 | if ( /railsrumble/.test($('input[name=url]').val()) ) Hurl.pony()
207 | if (data.prev_hurl) {
208 | $('#page-prev').attr('href', '/hurls/' + data.prev_hurl).show()
209 | $('#page-next').attr('href', '/').show()
210 | }
211 | $('.permalink').attr('href', '/hurls/'+data.hurl_id+'/'+data.view_id)
212 | $('.full-size-link').attr('href', '/views/' + data.view_id)
213 | $('#request').html(data.request)
214 | $('#response').html('' + data.header + ' ' + data.body)
215 | $('.help-blurb').hide()
216 | $('#request-and-response').show()
217 | } else {
218 | $('#flash-error-msg').html("Weird response. Sorry.")
219 | $('.flash-error').show()
220 | }
221 |
222 | $('#send-wrap').children().toggle()
223 | })
224 |
225 | return false
226 | })
227 |
228 | // delete hurl
229 | $('.hurl-delete').click(function() {
230 | $(this).parents('tr:first').remove()
231 | $.ajax({type: 'DELETE', url: $(this).attr('href')})
232 | return false
233 | })
234 |
235 | // toggle request/response display
236 | $('.toggle-reqres-link').click(function(){
237 | $('.toggle-reqres').toggle()
238 | $('#code-request').toggle()
239 | $('#code-response').toggle()
240 | return false
241 | })
242 |
243 | // flash close
244 | $('.flash-close').click(function (){
245 | $(this).parent().fadeOut()
246 | return false
247 | })
248 |
249 | // facebox
250 | $('a[rel*=facebox]').facebox({ opacity: 0.4 })
251 | $(document).bind('reveal.facebox', function() {
252 | Hurl.labelHints('#facebox input[title]')
253 | $('#facebox .footer').remove()
254 | })
255 |
256 | // in-field labels
257 | Hurl.labelHints('input[title]')
258 |
259 | // relatize dates
260 | $('.relatize').relatizeDate()
261 | });
262 |
--------------------------------------------------------------------------------
/public/js/jquery.autocomplete.js:
--------------------------------------------------------------------------------
1 | jQuery.autocomplete = function(input, options) {
2 | // Create a link to self
3 | var me = this;
4 |
5 | // Create jQuery object for input element
6 | var $input = $(input).attr("autocomplete", "off");
7 |
8 | // Apply inputClass if necessary
9 | if (options.inputClass) $input.addClass(options.inputClass);
10 |
11 | // Create results
12 | var results = document.createElement("div");
13 | // Create jQuery object for results
14 | var $results = $(results);
15 | $results.hide().addClass(options.resultsClass).css("position", "absolute");
16 | if( options.width > 0 ) $results.css("width", options.width);
17 |
18 | // Add to body element
19 | $("body").append(results);
20 |
21 | input.autocompleter = me;
22 |
23 | var timeout = null;
24 | var prev = "";
25 | var active = -1;
26 | var cache = {};
27 | var keyb = false;
28 | var hasFocus = false;
29 | var lastKeyPressCode = null;
30 |
31 | // flush cache
32 | function flushCache(){
33 | cache = {};
34 | cache.data = {};
35 | cache.length = 0;
36 | };
37 |
38 | // flush cache
39 | flushCache();
40 |
41 | // if there is a data array supplied
42 | if( options.data != null ){
43 | var sFirstChar = "", stMatchSets = {}, row = [];
44 |
45 | // no url was specified, we need to adjust the cache length to make sure it fits the local data store
46 | if( typeof options.url != "string" ) options.cacheLength = 1;
47 |
48 | // loop through the array and create a lookup structure
49 | for( var i=0; i < options.data.length; i++ ){
50 | // if row is a string, make an array otherwise just reference the array
51 | row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);
52 |
53 | // if the length is zero, don't add to list
54 | if( row[0].length > 0 ){
55 | // get the first character
56 | sFirstChar = row[0].substring(0, 1).toLowerCase();
57 | // if no lookup array for this character exists, look it up now
58 | if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];
59 | // if the match is a string
60 | stMatchSets[sFirstChar].push(row);
61 | }
62 | }
63 |
64 | // add the data items to the cache
65 | for( var k in stMatchSets ){
66 | // increase the cache size
67 | options.cacheLength++;
68 | // add to the cache
69 | addToCache(k, stMatchSets[k]);
70 | }
71 | }
72 |
73 | $input
74 | .keydown(function(e) {
75 | // track last key pressed
76 | lastKeyPressCode = e.keyCode;
77 | switch(e.keyCode) {
78 | case 38: // up
79 | e.preventDefault();
80 | moveSelect(-1);
81 | break;
82 | case 40: // down
83 | e.preventDefault();
84 | moveSelect(1);
85 | break;
86 | case 9: // tab
87 | case 13: // return
88 | if( selectCurrent() ){
89 | // make sure to blur off the current field
90 | $input.get(0).blur();
91 | e.preventDefault();
92 | }
93 | break;
94 | default:
95 | active = -1;
96 | if (timeout) clearTimeout(timeout);
97 | timeout = setTimeout(function(){onChange();}, options.delay);
98 | break;
99 | }
100 | })
101 | .focus(function(){
102 | // track whether the field has focus, we shouldn't process any results if the field no longer has focus
103 | hasFocus = true;
104 | })
105 | .blur(function() {
106 | // track whether the field has focus
107 | hasFocus = false;
108 | hideResults();
109 | });
110 |
111 | hideResultsNow();
112 |
113 | function onChange() {
114 | // ignore if the following keys are pressed: [del] [shift] [capslock]
115 | if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();
116 | var v = $input.val();
117 | if (v == prev) return;
118 | prev = v;
119 | if (v.length >= options.minChars) {
120 | $input.addClass(options.loadingClass);
121 | requestData(v);
122 | } else {
123 | $input.removeClass(options.loadingClass);
124 | $results.hide();
125 | }
126 | };
127 |
128 | function moveSelect(step) {
129 |
130 | var lis = $("li", results);
131 | if (!lis) return;
132 |
133 | active += step;
134 |
135 | if (active < 0) {
136 | active = 0;
137 | } else if (active >= lis.size()) {
138 | active = lis.size() - 1;
139 | }
140 |
141 | lis.removeClass("ac_over");
142 |
143 | $(lis[active]).addClass("ac_over");
144 |
145 | // Weird behaviour in IE
146 | // if (lis[active] && lis[active].scrollIntoView) {
147 | // lis[active].scrollIntoView(false);
148 | // }
149 |
150 | };
151 |
152 | function selectCurrent() {
153 | var li = $("li.ac_over", results)[0];
154 | if (!li) {
155 | var $li = $("li", results);
156 | if (options.selectOnly) {
157 | if ($li.length == 1) li = $li[0];
158 | } else if (options.selectFirst) {
159 | li = $li[0];
160 | }
161 | }
162 | if (li) {
163 | selectItem(li);
164 | return true;
165 | } else {
166 | return false;
167 | }
168 | };
169 |
170 | function selectItem(li) {
171 | if (!li) {
172 | li = document.createElement("li");
173 | li.extra = [];
174 | li.selectValue = "";
175 | }
176 | var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
177 | input.lastSelected = v;
178 | prev = v;
179 | $results.html("");
180 | $input.val(v);
181 | hideResultsNow();
182 | if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li) }, 1);
183 | };
184 |
185 | // selects a portion of the input string
186 | function createSelection(start, end){
187 | // get a reference to the input element
188 | var field = $input.get(0);
189 | if( field.createTextRange ){
190 | var selRange = field.createTextRange();
191 | selRange.collapse(true);
192 | selRange.moveStart("character", start);
193 | selRange.moveEnd("character", end);
194 | selRange.select();
195 | } else if( field.setSelectionRange ){
196 | field.setSelectionRange(start, end);
197 | } else {
198 | if( field.selectionStart ){
199 | field.selectionStart = start;
200 | field.selectionEnd = end;
201 | }
202 | }
203 | field.focus();
204 | };
205 |
206 | // fills in the input box w/the first match (assumed to be the best match)
207 | function autoFill(sValue){
208 | // if the last user key pressed was backspace, don't autofill
209 | if( lastKeyPressCode != 8 ){
210 | // fill in the value (keep the case the user has typed)
211 | $input.val($input.val() + sValue.substring(prev.length));
212 | // select the portion of the value not typed by the user (so the next character will erase)
213 | createSelection(prev.length, sValue.length);
214 | }
215 | };
216 |
217 | function showResults() {
218 | // get the position of the input field right now (in case the DOM is shifted)
219 | var pos = findPos(input);
220 | // either use the specified width, or autocalculate based on form element
221 | var iWidth = (options.width > 0) ? options.width : $input.width();
222 | // reposition
223 | $results.css({
224 | width: parseInt(iWidth) + "px",
225 | top: (pos.y + input.offsetHeight) + "px",
226 | left: pos.x + "px"
227 | }).show();
228 | };
229 |
230 | function hideResults() {
231 | if (timeout) clearTimeout(timeout);
232 | timeout = setTimeout(hideResultsNow, 200);
233 | };
234 |
235 | function hideResultsNow() {
236 | if (timeout) clearTimeout(timeout);
237 | $input.removeClass(options.loadingClass);
238 | if ($results.is(":visible")) {
239 | $results.hide();
240 | }
241 | if (options.mustMatch) {
242 | var v = $input.val();
243 | if (v != input.lastSelected) {
244 | selectItem(null);
245 | }
246 | }
247 | };
248 |
249 | function receiveData(q, data) {
250 | if (data) {
251 | $input.removeClass(options.loadingClass);
252 | results.innerHTML = "";
253 |
254 | // if the field no longer has focus or if there are no matches, do not display the drop down
255 | if( !hasFocus || data.length == 0 ) return hideResultsNow();
256 |
257 | if ($.browser.msie) {
258 | // we put a styled iframe behind the calendar so HTML SELECT elements don't show through
259 | $results.append(document.createElement('iframe'));
260 | }
261 | results.appendChild(dataToDom(data));
262 | // autofill in the complete box w/the first match as long as the user hasn't entered in more data
263 | if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);
264 | showResults();
265 | } else {
266 | hideResultsNow();
267 | }
268 | };
269 |
270 | function parseData(data) {
271 | if (!data) return null;
272 | var parsed = [];
273 | var rows = data.split(options.lineSeparator);
274 | for (var i=0; i < rows.length; i++) {
275 | var row = $.trim(rows[i]);
276 | if (row) {
277 | parsed[parsed.length] = row.split(options.cellSeparator);
278 | }
279 | }
280 | return parsed;
281 | };
282 |
283 | function dataToDom(data) {
284 | var ul = document.createElement("ul");
285 | var num = data.length;
286 |
287 | // limited results to a max number
288 | if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;
289 |
290 | for (var i=0; i < num; i++) {
291 | var row = data[i];
292 | if (!row) continue;
293 | var li = document.createElement("li");
294 | if (options.formatItem) {
295 | li.innerHTML = options.formatItem(row, i, num);
296 | li.selectValue = row[0];
297 | } else {
298 | li.innerHTML = row[0];
299 | li.selectValue = row[0];
300 | }
301 | var extra = null;
302 | if (row.length > 1) {
303 | extra = [];
304 | for (var j=1; j < row.length; j++) {
305 | extra[extra.length] = row[j];
306 | }
307 | }
308 | li.extra = extra;
309 | ul.appendChild(li);
310 | $(li).hover(
311 | function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); },
312 | function() { $(this).removeClass("ac_over"); }
313 | ).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) });
314 | }
315 | return ul;
316 | };
317 |
318 | function requestData(q) {
319 | if (!options.matchCase) q = q.toLowerCase();
320 | var data = options.cacheLength ? loadFromCache(q) : null;
321 | // recieve the cached data
322 | if (data) {
323 | receiveData(q, data);
324 | // if an AJAX url has been supplied, try loading the data now
325 | } else if( (typeof options.url == "string") && (options.url.length > 0) ){
326 | $.get(makeUrl(q), function(data) {
327 | data = parseData(data);
328 | addToCache(q, data);
329 | receiveData(q, data);
330 | });
331 | // if there's been no data found, remove the loading class
332 | } else {
333 | $input.removeClass(options.loadingClass);
334 | }
335 | };
336 |
337 | function makeUrl(q) {
338 | var url = options.url + "?q=" + encodeURI(q);
339 | for (var i in options.extraParams) {
340 | url += "&" + i + "=" + encodeURI(options.extraParams[i]);
341 | }
342 | return url;
343 | };
344 |
345 | function loadFromCache(q) {
346 | if (!q) return null;
347 | if (cache.data[q]) return cache.data[q];
348 | if (options.matchSubset) {
349 | for (var i = q.length - 1; i >= options.minChars; i--) {
350 | var qs = q.substr(0, i);
351 | var c = cache.data[qs];
352 | if (c) {
353 | var csub = [];
354 | for (var j = 0; j < c.length; j++) {
355 | var x = c[j];
356 | var x0 = x[0];
357 | if (matchSubset(x0, q)) {
358 | csub[csub.length] = x;
359 | }
360 | }
361 | return csub;
362 | }
363 | }
364 | }
365 | return null;
366 | };
367 |
368 | function matchSubset(s, sub) {
369 | if (!options.matchCase) s = s.toLowerCase();
370 | var i = s.indexOf(sub);
371 | if (i == -1) return false;
372 | return i == 0 || options.matchContains;
373 | };
374 |
375 | this.flushCache = function() {
376 | flushCache();
377 | };
378 |
379 | this.setExtraParams = function(p) {
380 | options.extraParams = p;
381 | };
382 |
383 | this.findValue = function(){
384 | var q = $input.val();
385 |
386 | if (!options.matchCase) q = q.toLowerCase();
387 | var data = options.cacheLength ? loadFromCache(q) : null;
388 | if (data) {
389 | findValueCallback(q, data);
390 | } else if( (typeof options.url == "string") && (options.url.length > 0) ){
391 | $.get(makeUrl(q), function(data) {
392 | data = parseData(data)
393 | addToCache(q, data);
394 | findValueCallback(q, data);
395 | });
396 | } else {
397 | // no matches
398 | findValueCallback(q, null);
399 | }
400 | }
401 |
402 | function findValueCallback(q, data){
403 | if (data) $input.removeClass(options.loadingClass);
404 |
405 | var num = (data) ? data.length : 0;
406 | var li = null;
407 |
408 | for (var i=0; i < num; i++) {
409 | var row = data[i];
410 |
411 | if( row[0].toLowerCase() == q.toLowerCase() ){
412 | li = document.createElement("li");
413 | if (options.formatItem) {
414 | li.innerHTML = options.formatItem(row, i, num);
415 | li.selectValue = row[0];
416 | } else {
417 | li.innerHTML = row[0];
418 | li.selectValue = row[0];
419 | }
420 | var extra = null;
421 | if( row.length > 1 ){
422 | extra = [];
423 | for (var j=1; j < row.length; j++) {
424 | extra[extra.length] = row[j];
425 | }
426 | }
427 | li.extra = extra;
428 | }
429 | }
430 |
431 | if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);
432 | }
433 |
434 | function addToCache(q, data) {
435 | if (!data || !q || !options.cacheLength) return;
436 | if (!cache.length || cache.length > options.cacheLength) {
437 | flushCache();
438 | cache.length++;
439 | } else if (!cache[q]) {
440 | cache.length++;
441 | }
442 | cache.data[q] = data;
443 | };
444 |
445 | function findPos(obj) {
446 | var curleft = obj.offsetLeft || 0;
447 | var curtop = obj.offsetTop || 0;
448 | while (obj = obj.offsetParent) {
449 | curleft += obj.offsetLeft
450 | curtop += obj.offsetTop
451 | }
452 | return {x:curleft,y:curtop};
453 | }
454 | }
455 |
456 | jQuery.fn.autocomplete = function(url, options, data) {
457 | // Make sure options exists
458 | options = options || {};
459 | // Set url as option
460 | options.url = url;
461 | // set some bulk local data
462 | options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;
463 |
464 | // Set default values for required options
465 | options.inputClass = options.inputClass || "ac_input";
466 | options.resultsClass = options.resultsClass || "ac_results";
467 | options.lineSeparator = options.lineSeparator || "\n";
468 | options.cellSeparator = options.cellSeparator || "|";
469 | options.minChars = options.minChars || 1;
470 | options.delay = options.delay || 400;
471 | options.matchCase = options.matchCase || 0;
472 | options.matchSubset = options.matchSubset || 1;
473 | options.matchContains = options.matchContains || 0;
474 | options.cacheLength = options.cacheLength || 1;
475 | options.mustMatch = options.mustMatch || 0;
476 | options.extraParams = options.extraParams || {};
477 | options.loadingClass = options.loadingClass || "ac_loading";
478 | options.selectFirst = options.selectFirst || false;
479 | options.selectOnly = options.selectOnly || false;
480 | options.maxItemsToShow = options.maxItemsToShow || -1;
481 | options.autoFill = options.autoFill || false;
482 | options.width = parseInt(options.width, 10) || 0;
483 |
484 | this.each(function() {
485 | var input = this;
486 | new jQuery.autocomplete(input, options);
487 | });
488 |
489 | // Don't break the chain
490 | return this;
491 | }
492 |
493 | jQuery.fn.autocompleteArray = function(data, options) {
494 | return this.autocomplete(null, options, data);
495 | }
496 |
497 | jQuery.fn.indexOf = function(e){
498 | for( var i=0; i= 0 ? '&' : '?') + q;
113 | options.data = null; // data is null for 'get'
114 | }
115 | else
116 | options.data = q; // data is the query string for 'post'
117 |
118 | var $form = this, callbacks = [];
119 | if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
120 | if (options.clearForm) callbacks.push(function() { $form.clearForm(); });
121 |
122 | // perform a load on the target only if dataType is not provided
123 | if (!options.dataType && options.target) {
124 | var oldSuccess = options.success || function(){};
125 | callbacks.push(function(data) {
126 | $(options.target).html(data).each(oldSuccess, arguments);
127 | });
128 | }
129 | else if (options.success)
130 | callbacks.push(options.success);
131 |
132 | options.success = function(data, status) {
133 | for (var i=0, max=callbacks.length; i < max; i++)
134 | callbacks[i].apply(options, [data, status, $form]);
135 | };
136 |
137 | // are there files to upload?
138 | var files = $('input:file', this).fieldValue();
139 | var found = false;
140 | for (var j=0; j < files.length; j++)
141 | if (files[j])
142 | found = true;
143 |
144 | var multipart = false;
145 | // var mp = 'multipart/form-data';
146 | // multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
147 |
148 | // options.iframe allows user to force iframe mode
149 | if (options.iframe || found || multipart) {
150 | // hack to fix Safari hang (thanks to Tim Molendijk for this)
151 | // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
152 | if (options.closeKeepAlive)
153 | $.get(options.closeKeepAlive, fileUpload);
154 | else
155 | fileUpload();
156 | }
157 | else
158 | $.ajax(options);
159 |
160 | // fire 'notify' event
161 | this.trigger('form-submit-notify', [this, options]);
162 | return this;
163 |
164 |
165 | // private function for handling file uploads (hat tip to YAHOO!)
166 | function fileUpload() {
167 | var form = $form[0];
168 |
169 | if ($(':input[name=submit]', form).length) {
170 | alert('Error: Form elements must not be named "submit".');
171 | return;
172 | }
173 |
174 | var opts = $.extend({}, $.ajaxSettings, options);
175 | var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);
176 |
177 | var id = 'jqFormIO' + (new Date().getTime());
178 | var $io = $('');
179 | var io = $io[0];
180 |
181 | $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
182 |
183 | var xhr = { // mock object
184 | aborted: 0,
185 | responseText: null,
186 | responseXML: null,
187 | status: 0,
188 | statusText: 'n/a',
189 | getAllResponseHeaders: function() {},
190 | getResponseHeader: function() {},
191 | setRequestHeader: function() {},
192 | abort: function() {
193 | this.aborted = 1;
194 | $io.attr('src','about:blank'); // abort op in progress
195 | }
196 | };
197 |
198 | var g = opts.global;
199 | // trigger ajax global events so that activity/block indicators work like normal
200 | if (g && ! $.active++) $.event.trigger("ajaxStart");
201 | if (g) $.event.trigger("ajaxSend", [xhr, opts]);
202 |
203 | if (s.beforeSend && s.beforeSend(xhr, s) === false) {
204 | s.global && $.active--;
205 | return;
206 | }
207 | if (xhr.aborted)
208 | return;
209 |
210 | var cbInvoked = 0;
211 | var timedOut = 0;
212 |
213 | // add submitting element to data if we know it
214 | var sub = form.clk;
215 | if (sub) {
216 | var n = sub.name;
217 | if (n && !sub.disabled) {
218 | options.extraData = options.extraData || {};
219 | options.extraData[n] = sub.value;
220 | if (sub.type == "image") {
221 | options.extraData[name+'.x'] = form.clk_x;
222 | options.extraData[name+'.y'] = form.clk_y;
223 | }
224 | }
225 | }
226 |
227 | // take a breath so that pending repaints get some cpu time before the upload starts
228 | setTimeout(function() {
229 | // make sure form attrs are set
230 | var t = $form.attr('target'), a = $form.attr('action');
231 |
232 | // update form attrs in IE friendly way
233 | form.setAttribute('target',id);
234 | if (form.getAttribute('method') != 'POST')
235 | form.setAttribute('method', 'POST');
236 | if (form.getAttribute('action') != opts.url)
237 | form.setAttribute('action', opts.url);
238 |
239 | // ie borks in some cases when setting encoding
240 | if (! options.skipEncodingOverride) {
241 | $form.attr({
242 | encoding: 'multipart/form-data',
243 | enctype: 'multipart/form-data'
244 | });
245 | }
246 |
247 | // support timout
248 | if (opts.timeout)
249 | setTimeout(function() { timedOut = true; cb(); }, opts.timeout);
250 |
251 | // add "extra" data to form if provided in options
252 | var extraInputs = [];
253 | try {
254 | if (options.extraData)
255 | for (var n in options.extraData)
256 | extraInputs.push(
257 | $(' ')
258 | .appendTo(form)[0]);
259 |
260 | // add iframe to doc and submit the form
261 | $io.appendTo('body');
262 | io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
263 | form.submit();
264 | }
265 | finally {
266 | // reset attrs and remove "extra" input elements
267 | form.setAttribute('action',a);
268 | t ? form.setAttribute('target', t) : $form.removeAttr('target');
269 | $(extraInputs).remove();
270 | }
271 | }, 10);
272 |
273 | var nullCheckFlag = 0;
274 |
275 | function cb() {
276 | if (cbInvoked++) return;
277 |
278 | io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
279 |
280 | var ok = true;
281 | try {
282 | if (timedOut) throw 'timeout';
283 | // extract the server response from the iframe
284 | var data, doc;
285 |
286 | doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
287 |
288 | if ((doc.body == null || doc.body.innerHTML == '') && !nullCheckFlag) {
289 | // in some browsers (cough, Opera 9.2.x) the iframe DOM is not always traversable when
290 | // the onload callback fires, so we give them a 2nd chance
291 | nullCheckFlag = 1;
292 | cbInvoked--;
293 | setTimeout(cb, 100);
294 | return;
295 | }
296 |
297 | xhr.responseText = doc.body ? doc.body.innerHTML : null;
298 | xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
299 | xhr.getResponseHeader = function(header){
300 | var headers = {'content-type': opts.dataType};
301 | return headers[header];
302 | };
303 |
304 | if (opts.dataType == 'json' || opts.dataType == 'script') {
305 | var ta = doc.getElementsByTagName('textarea')[0];
306 | xhr.responseText = ta ? ta.value : xhr.responseText;
307 | }
308 | else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
309 | xhr.responseXML = toXml(xhr.responseText);
310 | }
311 | data = $.httpData(xhr, opts.dataType);
312 | }
313 | catch(e){
314 | ok = false;
315 | $.handleError(opts, xhr, 'error', e);
316 | }
317 |
318 | // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
319 | if (ok) {
320 | opts.success(data, 'success');
321 | if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
322 | }
323 | if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
324 | if (g && ! --$.active) $.event.trigger("ajaxStop");
325 | if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');
326 |
327 | // clean up
328 | setTimeout(function() {
329 | $io.remove();
330 | xhr.responseXML = null;
331 | }, 100);
332 | };
333 |
334 | function toXml(s, doc) {
335 | if (window.ActiveXObject) {
336 | doc = new ActiveXObject('Microsoft.XMLDOM');
337 | doc.async = 'false';
338 | doc.loadXML(s);
339 | }
340 | else
341 | doc = (new DOMParser()).parseFromString(s, 'text/xml');
342 | return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
343 | };
344 | };
345 | };
346 |
347 | /**
348 | * ajaxForm() provides a mechanism for fully automating form submission.
349 | *
350 | * The advantages of using this method instead of ajaxSubmit() are:
351 | *
352 | * 1: This method will include coordinates for elements (if the element
353 | * is used to submit the form).
354 | * 2. This method will include the submit element's name/value data (for the element that was
355 | * used to submit the form).
356 | * 3. This method binds the submit() method to the form for you.
357 | *
358 | * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
359 | * passes the options argument along after properly binding events for submit elements and
360 | * the form itself.
361 | */
362 | $.fn.ajaxForm = function(options) {
363 | return this.ajaxFormUnbind().bind('submit.form-plugin',function() {
364 | $(this).ajaxSubmit(options);
365 | return false;
366 | }).each(function() {
367 | // store options in hash
368 | $(":submit,input:image", this).bind('click.form-plugin',function(e) {
369 | var form = this.form;
370 | form.clk = this;
371 | if (this.type == 'image') {
372 | if (e.offsetX != undefined) {
373 | form.clk_x = e.offsetX;
374 | form.clk_y = e.offsetY;
375 | } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
376 | var offset = $(this).offset();
377 | form.clk_x = e.pageX - offset.left;
378 | form.clk_y = e.pageY - offset.top;
379 | } else {
380 | form.clk_x = e.pageX - this.offsetLeft;
381 | form.clk_y = e.pageY - this.offsetTop;
382 | }
383 | }
384 | // clear form vars
385 | setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 10);
386 | });
387 | });
388 | };
389 |
390 | // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
391 | $.fn.ajaxFormUnbind = function() {
392 | this.unbind('submit.form-plugin');
393 | return this.each(function() {
394 | $(":submit,input:image", this).unbind('click.form-plugin');
395 | });
396 |
397 | };
398 |
399 | /**
400 | * formToArray() gathers form element data into an array of objects that can
401 | * be passed to any of the following ajax functions: $.get, $.post, or load.
402 | * Each object in the array has both a 'name' and 'value' property. An example of
403 | * an array for a simple login form might be:
404 | *
405 | * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
406 | *
407 | * It is this array that is passed to pre-submit callback functions provided to the
408 | * ajaxSubmit() and ajaxForm() methods.
409 | */
410 | $.fn.formToArray = function(semantic) {
411 | var a = [];
412 | if (this.length == 0) return a;
413 |
414 | var form = this[0];
415 | var els = semantic ? form.getElementsByTagName('*') : form.elements;
416 | if (!els) return a;
417 | for(var i=0, max=els.length; i < max; i++) {
418 | var el = els[i];
419 | var n = el.name;
420 | if (!n) continue;
421 |
422 | if (semantic && form.clk && el.type == "image") {
423 | // handle image inputs on the fly when semantic == true
424 | if(!el.disabled && form.clk == el) {
425 | a.push({name: n, value: $(el).val()});
426 | a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
427 | }
428 | continue;
429 | }
430 |
431 | var v = $.fieldValue(el, true);
432 | if (v && v.constructor == Array) {
433 | for(var j=0, jmax=v.length; j < jmax; j++)
434 | a.push({name: n, value: v[j]});
435 | }
436 | else if (v !== null && typeof v != 'undefined')
437 | a.push({name: n, value: v});
438 | }
439 |
440 | if (!semantic && form.clk) {
441 | // input type=='image' are not found in elements array! handle it here
442 | var $input = $(form.clk), input = $input[0], n = input.name;
443 | if (n && !input.disabled && input.type == 'image') {
444 | a.push({name: n, value: $input.val()});
445 | a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
446 | }
447 | }
448 | return a;
449 | };
450 |
451 | /**
452 | * Serializes form data into a 'submittable' string. This method will return a string
453 | * in the format: name1=value1&name2=value2
454 | */
455 | $.fn.formSerialize = function(semantic) {
456 | //hand off to jQuery.param for proper encoding
457 | return $.param(this.formToArray(semantic));
458 | };
459 |
460 | /**
461 | * Serializes all field elements in the jQuery object into a query string.
462 | * This method will return a string in the format: name1=value1&name2=value2
463 | */
464 | $.fn.fieldSerialize = function(successful) {
465 | var a = [];
466 | this.each(function() {
467 | var n = this.name;
468 | if (!n) return;
469 | var v = $.fieldValue(this, successful);
470 | if (v && v.constructor == Array) {
471 | for (var i=0,max=v.length; i < max; i++)
472 | a.push({name: n, value: v[i]});
473 | }
474 | else if (v !== null && typeof v != 'undefined')
475 | a.push({name: this.name, value: v});
476 | });
477 | //hand off to jQuery.param for proper encoding
478 | return $.param(a);
479 | };
480 |
481 | /**
482 | * Returns the value(s) of the element in the matched set. For example, consider the following form:
483 | *
484 | *
485 | *
486 | *
487 | *
488 | *
489 | *
490 | *
491 | *
492 | *
493 | * var v = $(':text').fieldValue();
494 | * // if no values are entered into the text inputs
495 | * v == ['','']
496 | * // if values entered into the text inputs are 'foo' and 'bar'
497 | * v == ['foo','bar']
498 | *
499 | * var v = $(':checkbox').fieldValue();
500 | * // if neither checkbox is checked
501 | * v === undefined
502 | * // if both checkboxes are checked
503 | * v == ['B1', 'B2']
504 | *
505 | * var v = $(':radio').fieldValue();
506 | * // if neither radio is checked
507 | * v === undefined
508 | * // if first radio is checked
509 | * v == ['C1']
510 | *
511 | * The successful argument controls whether or not the field element must be 'successful'
512 | * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
513 | * The default value of the successful argument is true. If this value is false the value(s)
514 | * for each element is returned.
515 | *
516 | * Note: This method *always* returns an array. If no valid value can be determined the
517 | * array will be empty, otherwise it will contain one or more values.
518 | */
519 | $.fn.fieldValue = function(successful) {
520 | for (var val=[], i=0, max=this.length; i < max; i++) {
521 | var el = this[i];
522 | var v = $.fieldValue(el, successful);
523 | if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
524 | continue;
525 | v.constructor == Array ? $.merge(val, v) : val.push(v);
526 | }
527 | return val;
528 | };
529 |
530 | /**
531 | * Returns the value of the field element.
532 | */
533 | $.fieldValue = function(el, successful) {
534 | var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
535 | if (typeof successful == 'undefined') successful = true;
536 |
537 | if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
538 | (t == 'checkbox' || t == 'radio') && !el.checked ||
539 | (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
540 | tag == 'select' && el.selectedIndex == -1))
541 | return null;
542 |
543 | if (tag == 'select') {
544 | var index = el.selectedIndex;
545 | if (index < 0) return null;
546 | var a = [], ops = el.options;
547 | var one = (t == 'select-one');
548 | var max = (one ? index+1 : ops.length);
549 | for(var i=(one ? index : 0); i < max; i++) {
550 | var op = ops[i];
551 | if (op.selected) {
552 | var v = op.value;
553 | if (!v) // extra pain for IE...
554 | v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
555 | if (one) return v;
556 | a.push(v);
557 | }
558 | }
559 | return a;
560 | }
561 | return el.value;
562 | };
563 |
564 | /**
565 | * Clears the form data. Takes the following actions on the form's input fields:
566 | * - input text fields will have their 'value' property set to the empty string
567 | * - select elements will have their 'selectedIndex' property set to -1
568 | * - checkbox and radio inputs will have their 'checked' property set to false
569 | * - inputs of type submit, button, reset, and hidden will *not* be effected
570 | * - button elements will *not* be effected
571 | */
572 | $.fn.clearForm = function() {
573 | return this.each(function() {
574 | $('input,select,textarea', this).clearFields();
575 | });
576 | };
577 |
578 | /**
579 | * Clears the selected form elements.
580 | */
581 | $.fn.clearFields = $.fn.clearInputs = function() {
582 | return this.each(function() {
583 | var t = this.type, tag = this.tagName.toLowerCase();
584 | if (t == 'text' || t == 'password' || tag == 'textarea')
585 | this.value = '';
586 | else if (t == 'checkbox' || t == 'radio')
587 | this.checked = false;
588 | else if (tag == 'select')
589 | this.selectedIndex = -1;
590 | });
591 | };
592 |
593 | /**
594 | * Resets the form data. Causes all form elements to be reset to their original value.
595 | */
596 | $.fn.resetForm = function() {
597 | return this.each(function() {
598 | // guard against an input with the name of 'reset'
599 | // note that IE reports the reset function as an 'object'
600 | if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
601 | this.reset();
602 | });
603 | };
604 |
605 | /**
606 | * Enables or disables any matching elements.
607 | */
608 | $.fn.enable = function(b) {
609 | if (b == undefined) b = true;
610 | return this.each(function() {
611 | this.disabled = !b;
612 | });
613 | };
614 |
615 | /**
616 | * Checks/unchecks any matching checkboxes or radio buttons and
617 | * selects/deselects and matching option elements.
618 | */
619 | $.fn.selected = function(select) {
620 | if (select == undefined) select = true;
621 | return this.each(function() {
622 | var t = this.type;
623 | if (t == 'checkbox' || t == 'radio')
624 | this.checked = select;
625 | else if (this.tagName.toLowerCase() == 'option') {
626 | var $sel = $(this).parent('select');
627 | if (select && $sel[0] && $sel[0].type == 'select-one') {
628 | // deselect all other options
629 | $sel.find('option').selected(false);
630 | }
631 | this.selected = select;
632 | }
633 | });
634 | };
635 |
636 | // helper fn for console logging
637 | // set $.fn.ajaxSubmit.debug to true to enable debug logging
638 | function log() {
639 | if ($.fn.ajaxSubmit.debug && window.console && window.console.log)
640 | window.console.log('[jquery.form] ' + Array.prototype.join.call(arguments,''));
641 | };
642 |
643 | })(jQuery);
--------------------------------------------------------------------------------
/public/js/jquery.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery JavaScript Library v1.3.2
3 | * http://jquery.com/
4 | *
5 | * Copyright (c) 2009 John Resig
6 | * Dual licensed under the MIT and GPL licenses.
7 | * http://docs.jquery.com/License
8 | *
9 | * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009)
10 | * Revision: 6246
11 | */
12 | (function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"+T+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,""]||!O.indexOf(""," "]||(!O.indexOf(""," "]||!O.indexOf(""," "]||!o.support.htmlSerialize&&[1,"div","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}});
13 | /*
14 | * Sizzle CSS Selector Engine - v0.9.3
15 | * Copyright 2009, The Dojo Foundation
16 | * Released under the MIT, BSD, and GPL Licenses.
17 | * More information: http://sizzlejs.com/
18 | */
19 | (function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V ";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML=" ";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="
";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(Fa text ';var H=K.getElementsByTagName("*"),E=K.getElementsByTagName("a")[0];if(!H||!H.length||!E){return}o.support={leadingWhitespace:K.firstChild.nodeType==3,tbody:!K.getElementsByTagName("tbody").length,objectAll:!!K.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!K.getElementsByTagName("link").length,style:/red/.test(E.getAttribute("style")),hrefNormalized:E.getAttribute("href")==="/a",opacity:E.style.opacity==="0.5",cssFloat:!!E.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};G.type="text/javascript";try{G.appendChild(document.createTextNode("window."+J+"=1;"))}catch(I){}F.insertBefore(G,F.firstChild);if(l[J]){o.support.scriptEval=true;delete l[J]}F.removeChild(G);if(K.attachEvent&&K.fireEvent){K.attachEvent("onclick",function(){o.support.noCloneEvent=false;K.detachEvent("onclick",arguments.callee)});K.cloneNode(true).fireEvent("onclick")}o(function(){var L=document.createElement("div");L.style.width=L.style.paddingLeft="1px";document.body.appendChild(L);o.boxModel=o.support.boxModel=L.offsetWidth===2;document.body.removeChild(L).style.display="none"})})();var w=o.support.cssFloat?"cssFloat":"styleFloat";o.props={"for":"htmlFor","class":"className","float":w,cssFloat:w,styleFloat:w,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};o.fn.extend({_load:o.fn.load,load:function(G,J,K){if(typeof G!=="string"){return this._load(G)}var I=G.indexOf(" ");if(I>=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(/