aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/public_body_controller.rb12
-rw-r--r--app/controllers/request_controller.rb29
-rw-r--r--app/controllers/user_controller.rb18
-rw-r--r--app/helpers/config_helper.rb4
-rw-r--r--app/models/info_request.rb7
-rw-r--r--app/models/info_request_event.rb32
-rw-r--r--app/views/general/_orglink.rhtml2
-rw-r--r--app/views/general/frontpage.rhtml2
-rw-r--r--app/views/layouts/default.rhtml33
-rw-r--r--app/views/layouts/no_chrome.rhtml39
-rw-r--r--app/views/public_body/_search_ahead.rhtml18
-rw-r--r--app/views/public_body/show.rhtml4
-rw-r--r--app/views/request/_search_ahead.rhtml13
-rw-r--r--app/views/request/new.rhtml203
-rw-r--r--app/views/request/preview.rhtml10
-rw-r--r--app/views/request/select_authority.rhtml50
-rw-r--r--app/views/user/_signin.rhtml1
-rw-r--r--app/views/user/_signup.rhtml1
-rw-r--r--app/views/user/signin_successful.rhtml9
-rw-r--r--config/environment.rb2
-rw-r--r--config/general.yml-example3
-rw-r--r--config/routes.rb6
l---------public/asktheeu-theme1
-rw-r--r--public/javascripts/ba-throttle-debounce.js9
-rwxr-xr-xpublic/javascripts/jquery.fancybox-1.3.4.pack.js46
-rw-r--r--public/javascripts/jquery.form.js11
-rwxr-xr-xpublic/stylesheets/fancybox-x.pngbin0 -> 203 bytes
-rwxr-xr-xpublic/stylesheets/fancybox-y.pngbin0 -> 176 bytes
-rwxr-xr-xpublic/stylesheets/fancybox.pngbin0 -> 15287 bytes
-rwxr-xr-xpublic/stylesheets/jquery.fancybox-1.3.4.css359
-rw-r--r--public/stylesheets/main.css64
-rw-r--r--public/stylesheets/theme.css2
-rw-r--r--spec/controllers/public_body_controller_spec.rb35
-rw-r--r--spec/controllers/request_controller_spec.rb56
-rw-r--r--spec/models/customstates.rb20
36 files changed, 917 insertions, 188 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index a2a628f10..3794043fb 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -211,7 +211,9 @@ class ApplicationController < ActionController::Base
post_redirect = PostRedirect.new(:uri => request.request_uri, :post_params => params,
:reason_params => reason_params)
post_redirect.save!
- redirect_to signin_url(:token => post_redirect.token)
+ # 'modal' controls whether the sign-in form will be displayed in the typical full-blown
+ # page or on its own, useful for pop-ups
+ redirect_to signin_url(:token => post_redirect.token, :modal => params[:modal])
return false
end
return true
diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb
index 378dd58e3..0e58b7055 100644
--- a/app/controllers/public_body_controller.rb
+++ b/app/controllers/public_body_controller.rb
@@ -176,5 +176,17 @@ class PublicBodyController < ApplicationController
:filename => 'all-authorities.csv',
:disposition =>'attachment', :encoding => 'utf8')
end
+
+ # Type ahead search
+ def search_typeahead
+ # Since acts_as_xapian doesn't support the Partial match flag, we work around it
+ # by making the last work a wildcard, which is quite the same
+ query = params[:q] + '*'
+
+ query = query.split(' ').join(' OR ') # XXX: HACK for OR instead of default AND!
+ @xapian_requests = perform_search([PublicBody], query, 'relevant', 'request_collapse', 5)
+
+ render :partial => "public_body/search_ahead"
+ end
end
diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb
index 10c0917c8..e3f431cc7 100644
--- a/app/controllers/request_controller.rb
+++ b/app/controllers/request_controller.rb
@@ -22,6 +22,21 @@ class RequestController < ApplicationController
rescue MissingSourceFile, NameError
end
+ def select_authority
+ # Check whether we force the user to sign in right at the start, or we allow her
+ # to start filling the request anonymously
+ if force_registration_on_new_request && !authenticated?(
+ :web => _("To send your FOI request"),
+ :email => _("Then you'll be allowed to send FOI requests."),
+ :email_subject => _("Confirm your email address")
+ )
+ # do nothing - as "authenticated?" has done the redirect to signin page for us
+ return
+ end
+
+ medium_cache
+ end
+
def show
medium_cache
@locale = self.locale_from_params()
@@ -66,7 +81,7 @@ class RequestController < ApplicationController
@last_info_request_event_id = @info_request.last_event_id_needing_description
@new_responses_count = @info_request.events_needing_description.select {|i| i.event_type == 'response'}.size
-1
+
# Sidebar stuff
# ... requests that have similar imporant terms
behavior_cache :tag => ['similar', @info_request.id] do
@@ -727,5 +742,17 @@ class RequestController < ApplicationController
return
end
end
+
+ # Type ahead search
+ def search_typeahead
+ # Since acts_as_xapian doesn't support the Partial match flag, we work around it
+ # by making the last work a wildcard, which is quite the same
+ query = params[:q] + '*'
+
+ query = query.split(' ').join(' OR ') # XXX: HACK for OR instead of default AND!
+ @xapian_requests = perform_search([InfoRequestEvent], query, 'relevant', 'request_collapse', 5)
+
+ render :partial => "request/search_ahead.rhtml"
+ end
end
diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb
index 47a133135..96dbfba74 100644
--- a/app/controllers/user_controller.rb
+++ b/app/controllers/user_controller.rb
@@ -8,6 +8,8 @@
class UserController < ApplicationController
+ layout :select_layout
+
protect_from_forgery :only => [ :contact,
:set_profile_photo,
:signchangeemail,
@@ -113,7 +115,12 @@ class UserController < ApplicationController
session[:user_id] = @user_signin.id
session[:user_circumstance] = nil
session[:remember_me] = params[:remember_me] ? true : false
- do_post_redirect @post_redirect
+
+ if is_modal_dialog
+ render :action => 'signin_successful'
+ else
+ do_post_redirect @post_redirect
+ end
else
send_confirmation_mail @user_signin
end
@@ -511,6 +518,15 @@ class UserController < ApplicationController
private
+ def is_modal_dialog
+ (params[:modal].to_i != 0)
+ end
+
+ # when logging in through a modal iframe, don't display chrome around the content
+ def select_layout
+ is_modal_dialog ? 'no_chrome' : 'default'
+ end
+
# Decide where we are going to redirect back to after signin/signup, and record that
def work_out_post_redirect
# Redirect to front page later if nothing else specified
diff --git a/app/helpers/config_helper.rb b/app/helpers/config_helper.rb
index 80f2deed2..b0381a2f5 100644
--- a/app/helpers/config_helper.rb
+++ b/app/helpers/config_helper.rb
@@ -2,4 +2,8 @@ module ConfigHelper
def site_name
MySociety::Config.get('SITE_NAME', 'Alaveteli')
end
+
+ def force_registration_on_new_request
+ MySociety::Config.get('FORCE_REGISTRATION_ON_NEW_REQUEST', false)
+ end
end \ No newline at end of file
diff --git a/app/models/info_request.rb b/app/models/info_request.rb
index 9b0f1047b..978e2cd08 100644
--- a/app/models/info_request.rb
+++ b/app/models/info_request.rb
@@ -780,8 +780,7 @@ public
# Display version of status
- def display_status
- status = self.calculate_status
+ def InfoRequest.get_status_description(status)
if status == 'waiting_classification'
_("Awaiting classification.")
elsif status == 'waiting_response'
@@ -819,6 +818,10 @@ public
end
end
+ def display_status
+ InfoRequest.get_status_description(self.calculate_status)
+ end
+
# Completely delete this request and all objects depending on it
def fully_destroy
self.track_things.each do |track_thing|
diff --git a/app/models/info_request_event.rb b/app/models/info_request_event.rb
index 4003217b0..4ea89bf81 100644
--- a/app/models/info_request_event.rb
+++ b/app/models/info_request_event.rb
@@ -303,37 +303,7 @@ class InfoRequestEvent < ActiveRecord::Base
def display_status
if is_incoming_message?
status = self.calculated_state
- if !status.nil?
- if status == 'waiting_response'
- return _("Acknowledgement")
- elsif status == 'waiting_clarification'
- return _("Clarification required")
- elsif status == 'gone_postal'
- return _("Handled by post")
- elsif status == 'deadline_extended'
- return _("Deadline Extended")
- elsif status == 'wrong_response'
- return _("Wrong Response")
- elsif status == 'not_held'
- return _("Information not held")
- elsif status == 'rejected'
- return _("Refused")
- elsif status == 'partially_successful'
- return _("Some information sent")
- elsif status == 'successful'
- return _("All information sent")
- elsif status == 'internal_review'
- return _("Internal review acknowledgement")
- elsif status == 'user_withdrawn'
- return _("Withdrawn by requester")
- elsif status == 'error_message'
- return _("Delivery error")
- elsif status == 'requires_admin'
- return _("Unusual response")
- end
- raise "unknown status " + status
- end
- return "Response"
+ return status.nil? ? _("Response") : InfoRequest.get_status_description(status)
end
if is_outgoing_message?
diff --git a/app/views/general/_orglink.rhtml b/app/views/general/_orglink.rhtml
index 20d0d6ce4..7d74dbaac 100644
--- a/app/views/general/_orglink.rhtml
+++ b/app/views/general/_orglink.rhtml
@@ -1,2 +1,2 @@
<%-# Put the link to your organisation here, or leave blank -%>
-<a href="http://www.alaveteli.org">an Alaveteli site</a>
+<%= link_to image_tag('logo.png'), frontpage_url, :id=>'logo' %>
diff --git a/app/views/general/frontpage.rhtml b/app/views/general/frontpage.rhtml
index 3b1088411..55b00b753 100644
--- a/app/views/general/frontpage.rhtml
+++ b/app/views/general/frontpage.rhtml
@@ -7,7 +7,7 @@
Information<br/>
request</strong>") %>
</h1>
- <a class="link_button_green_large" href="/en/new/tgq"><%= _("Start now &raquo;") %></a>
+ <a class="link_button_green_large" href="/select_authority"><%= _("Start now &raquo;") %></a>
</div>
<div id="right_column">
<div id="frontpage_search_box">
diff --git a/app/views/layouts/default.rhtml b/app/views/layouts/default.rhtml
index e21553d0e..75ba167e8 100644
--- a/app/views/layouts/default.rhtml
+++ b/app/views/layouts/default.rhtml
@@ -1,8 +1,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="<%= I18n.locale %>">
<head>
+ <script type="text/javascript" src="/javascripts/jquery.js"></script>
<% if @profile_photo_javascript %>
- <script type="text/javascript" src="/javascripts/jquery.js"></script>
<script type="text/javascript" src="/javascripts/jquery.Jcrop.js"></script>
<script type="text/javascript" src="/javascripts/profile_photo.js"></script>
<link rel="stylesheet" href="/stylesheets/jquery.Jcrop.css" type="text/css" >
@@ -32,6 +32,8 @@
<style type="text/css">@import url("/stylesheets/ie7.css");</style>
<![endif]-->
<%= stylesheet_link_tag 'custom', :title => "Main", :rel => "stylesheet" %>
+ <!-- XXX: add conditional include -->
+ <%= stylesheet_link_tag 'jquery.fancybox-1.3.4', :rel => "stylesheet" %>
<% if @feed_autodetect %>
<% for feed in @feed_autodetect %>
@@ -53,6 +55,28 @@
</head>
<body <%= "class='front'" if params[:action] == 'frontpage' %>>
+ <!-- XXX: move to a separate file -->
+ <% if force_registration_on_new_request && !@user %>
+ <%= javascript_include_tag 'jquery.fancybox-1.3.4.pack' %>
+ <script>
+ $(document).ready(function() {
+ $("#make-request-link").fancybox({
+ 'modal': false,
+ 'width': 800,
+ 'height': 500,
+ 'type': 'iframe',
+ 'href': '/en/profile/sign_in?modal=1',
+ 'onClosed': function() {
+ // modal_signin_successful variable set by modal dialog box
+ if (typeof modal_signin_successful != 'undefined' ) {
+ window.location.href = '<%= select_authority_url %>';
+ }
+ }
+ });
+ });
+ </script>
+ <% end %>
+
<% # code for popup advert for a campaign etc.
=begin
<div id="everypage" class="jshide">
@@ -93,12 +117,13 @@
<% end %>
</div>
- <%= link_to image_tag('logo.png'), frontpage_url, :id=>'logo' %>
+ <%= render :partial => 'general/orglink' %>
<div id="topnav">
<ul id="navigation">
- <li class="<%= 'selected' if params[:controller] == 'general' and params[:action] != 'blog' %>"><%= link_to _("Make a request"), frontpage_url %></li>
- <li class="<%= 'selected' if params[:controller] == 'request' %>"><%= link_to _("View requests"), request_list_successful_url %></li>
+ <li class="<%= 'selected' if params[:controller] == 'general' and params[:action] != 'blog' %>"><%= link_to _("Home"), frontpage_url %></li>
+ <li class="<%= 'selected' if params[:controller] == 'request' and ['new', 'select_authority'].include?(params[:action]) %>"><%= link_to _("Make a request"), select_authority_url, :id => 'make-request-link' %></li>
+ <li class="<%= 'selected' if params[:controller] == 'request' and !['new', 'select_authority'].include?(params[:action]) %>"><%= link_to _("View requests"), request_list_successful_url %></li>
<li class="<%= 'selected' if params[:controller] == 'public_body' %>"><%= link_to _("View authorities"), list_public_bodies_default %></li>
<li class="<%= 'selected' if params[:controller] == 'general' and params[:action] == 'blog' %>"><%= link_to _("Read blog"), blog_url %></li>
<li class="<%= 'selected' if params[:controller] == 'help' %>"><%= link_to _("Help"), help_about_url %></li>
diff --git a/app/views/layouts/no_chrome.rhtml b/app/views/layouts/no_chrome.rhtml
new file mode 100644
index 000000000..c314dfbd3
--- /dev/null
+++ b/app/views/layouts/no_chrome.rhtml
@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="<%= I18n.locale %>">
+ <head>
+ <title>
+ <% if @title %>
+ <%=@title%> - <%= site_name %>
+ <% else %>
+ <%= site_name %> - <%= _('Make and browse Freedom of Information (FOI) requests') %>
+ <% end %>
+ </title>
+
+ <script type="text/javascript" src="/javascripts/jquery.js"></script>
+
+ <%= stylesheet_link_tag 'main', :title => "Main", :rel => "stylesheet" %>
+ <!--[if LT IE 7]>
+ <style type="text/css">@import url("/stylesheets/ie6.css");</style>
+ <![endif]-->
+ <!--[if LT IE 7]>
+ <style type="text/css">@import url("/stylesheets/ie6-custom.css");</style>
+ <![endif]-->
+ <%= stylesheet_link_tag 'custom', :title => "Main", :rel => "stylesheet" %>
+ </head>
+ <body>
+ <div class="entirebody">
+ <div id="content">
+ <% if flash[:notice] %>
+ <div id="notice"><%= flash[:notice] %></div>
+ <% end %>
+ <% if flash[:error] %>
+ <div id="error"><%= flash[:error] %></div>
+ <% end %>
+
+ <div id="<%= controller.controller_name + "_" + controller.action_name %>" class="controller_<%= controller.controller_name %>">
+ <%= yield :layout %>
+ </div>
+ </div>
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/app/views/public_body/_search_ahead.rhtml b/app/views/public_body/_search_ahead.rhtml
new file mode 100644
index 000000000..19c7eb4e8
--- /dev/null
+++ b/app/views/public_body/_search_ahead.rhtml
@@ -0,0 +1,18 @@
+<p>
+ <% if @xapian_requests.results.size > 0 %>
+ <h3><%= _('Top search results:') %></h3>
+ <p>
+ <%= _('Select one to see more information about the authority.')%>
+ </p>
+ <% else %>
+ <h3><%= _('No results found.') %></h3>
+ <% end %>
+ <div id="authority_search_ahead_results">
+ <% for result in @xapian_requests.results %>
+ <%= render :partial => 'body_listing_single', :locals => { :public_body => result[:model] } %>
+ <% end %>
+ </div>
+</p>
+
+
+
diff --git a/app/views/public_body/show.rhtml b/app/views/public_body/show.rhtml
index 85506a1bd..32e96294a 100644
--- a/app/views/public_body/show.rhtml
+++ b/app/views/public_body/show.rhtml
@@ -1,4 +1,5 @@
<% @title = h(@public_body.name) + " - view and make Freedom of Information requests" %>
+<div id="main_content">
<div>
<div id="header_right">
<h2><%= _('Track this authority')%></h2>
@@ -106,4 +107,5 @@
<% end %>
<p> <%= _('The search index is currently offline, so we can\'t show the Freedom of Information requests that have been made to this authority.')%></p>
<% end %>
-
+</div>
+</div> \ No newline at end of file
diff --git a/app/views/request/_search_ahead.rhtml b/app/views/request/_search_ahead.rhtml
new file mode 100644
index 000000000..053b10146
--- /dev/null
+++ b/app/views/request/_search_ahead.rhtml
@@ -0,0 +1,13 @@
+<p>
+ <div id="request_search_ahead_results">
+ <% if @xapian_requests.results.size > 0 %>
+ <h3>Possibly related requests:</h3>
+ <% end %>
+ <% for result in @xapian_requests.results %>
+ <%= render :partial => 'request/request_listing_short_via_event', :locals => { :event => result[:model], :info_request => result[:model].info_request } %>
+ <% end %>
+ </div>
+</p>
+
+
+
diff --git a/app/views/request/new.rhtml b/app/views/request/new.rhtml
index f58cbdd94..cd748b6c2 100644
--- a/app/views/request/new.rhtml
+++ b/app/views/request/new.rhtml
@@ -1,3 +1,14 @@
+<script type="text/javascript" src="/javascripts/ba-throttle-debounce.js"></script>
+<script>
+ $(document).ready(function(){
+ // Avoid triggering too often (on each keystroke) by using the debounce jQuery plugin:
+ // http://benalman.com/projects/jquery-throttle-debounce-plugin/
+ $("#typeahead_search").keypress($.debounce( 300, function() {
+ $("#typeahead_response").load("<%=search_ahead_url%>?q="+encodeURI(this.value));
+ }));
+ });
+</script>
+
<% @title = _("Make an {{law_used_short}} request to '{{public_body_name}}'",:law_used_short=>h(@info_request.law_used_short),:public_body_name=>h(@info_request.public_body.name)) %>
<% if @existing_request %>
@@ -10,109 +21,89 @@
</ul></div>
<% end %>
-<h1>Make a new request</h1>
+ <%= foi_error_messages_for :info_request, :outgoing_message %>
-<%= foi_error_messages_for :info_request, :outgoing_message %>
+ <h1><%= _('2. Ask for Information') %></h1>
-<div id="request_advice">
- <h2><%= _('Read this before writing your {{info_request_law_used_full}} request', :info_request_law_used_full=>h(@info_request.law_used_full)) %></h2>
- <ol>
- <li>
- <% form_tag("http://www.google.co.uk/search", {:id => "search_body_website_form", :method => "get"} ) do %>
- <p>
- <%= _('First,') %>
- <% if !@info_request.public_body.publication_scheme.empty? %>
- <%= _('<strong>browse</strong> the authority\'s <a href="%s">publication scheme</a> or <strong>search</strong> their web site ...') % [@info_request.public_body.publication_scheme] %>
+ <% form_for(:info_request, @info_request, :html => { :id => 'write_form' } ) do |f| %>
+
+ <div id="request_header">
+ <p>
+ <label class="form_label" for="info_request_public_body_id"><%= _('To:') %></label>
+ <span id="to_public_body"><%=h(@info_request.public_body.name)%></span>
+ <div class="form_item_note">
+ <% if @info_request.public_body.info_requests.size > 0 %>
+ <%= _("Browse <a href='{{url}}'>other requests</a> to '{{public_body_name}}' for examples of how to word your request.", :public_body_name=>h(@info_request.public_body.name), :url=>public_body_url(@info_request.public_body)) %>
<% else %>
- <%= _('<strong>search</strong> the authority\'s web site ...') %>
+ <%= _("Browse <a href='{{url}}'>other requests</a> for examples of how to word your request.", :url=>request_list_url) %>
<% end %>
- </p>
- <% if !@info_request.public_body.calculated_home_page.nil? %>
- <p>
- &nbsp; &nbsp; &nbsp; &nbsp; <%= text_field_tag 'q', params[:q], { :size => 20 } %>
- <%= hidden_field_tag 'as_sitesearch', @info_request.public_body.calculated_home_page %>
- <%= submit_tag _("Search") %>
- </p>
- <% end %>
- <p>
- ... <%= _('to check that the info isn\'t already published.') %>
- </p>
- <% end %>
- </li>
+ </div>
+ </p>
- <li>
- <% if @info_request.public_body.info_requests.size > 0 %>
- <%= _("Browse <a href='{{url}}'>other requests</a> to '{{public_body_name}}' for examples of how to word your request.", :public_body_name=>h(@info_request.public_body.name), :url=>public_body_url(@info_request.public_body)) %>
- <% else %>
- <%= _('Browse <a href="%s">other requests</a> for examples of how to word your request.') % [request_list_url] %>
+ <div id="request_header_text">
+ <% if @info_request.public_body.has_notes? %>
+ <h3><%= _('Special note for this authority!') %></h3>
+ <p><%= @info_request.public_body.notes_as_html %></p>
<% end %>
- </li>
- <li><%= _('Write your request in <strong>simple, precise language</strong>.') %></li>
- <li><%= _('Ask for <strong>specific</strong> documents or information, this site is not suitable for general enquiries.') %></li>
- <li><%= _('Keep it <strong>focused</strong>, you\'ll be more likely to get what you want (<a href="%s">why?</a>).') % [help_requesting_path + '#focused'] %></li>
- <li><%= _('This site is <strong>public</strong>. Everything you type and any response will be published.') %></li>
- </ol>
-
- <% if @info_request.public_body.has_notes? %>
- <h1><%= _('Special note for this authority!') %></h1>
- <ul>
- <li><p><%= @info_request.public_body.notes_as_html %></p></li>
- </ul>
- <% end %>
- <% if @info_request.public_body.eir_only? %>
- <h1><%= _('Please ask for environmental information only') %></h1>
-
- <p><%= _('The Freedom of Information Act <strong>does not apply</strong> to') %> <%=h(@info_request.public_body.name)%>.
- <%= _('However, you have the right to request environmental
- information under a different law') %> (<a href="/help/requesting#eir">explanation</a>).
- <%= _('This covers a very wide spectrum of information about the state of
- the <strong>natural and built environment</strong>, such as:') %>
+ <% if @info_request.public_body.eir_only? %>
+ <h3><%= _('Please ask for environmental information only') %></h3>
+
+ <p><%= _('The Freedom of Information Act <strong>does not apply</strong> to') %> <%=h(@info_request.public_body.name)%>.
+ <%= _('However, you have the right to request environmental
+ information under a different law') %> (<a href="/help/requesting#eir">explanation</a>).
+ <%= _('This covers a very wide spectrum of information about the state of
+ the <strong>natural and built environment</strong>, such as:') %>
+ <ul>
+ <li><%= _('Air, water, soil, land, flora and fauna (including how these effect
+ human beings)') %></li>
+ <li><%= _('Information on emissions and discharges (e.g. noise, energy,
+ radiation, waste materials)') %></li>
+ <li><%= _('Human health and safety') %></li>
+ <li><%= _('Cultural sites and built structures (as they may be affected by the
+ environmental factors listed above)') %></li>
+ <li><%= _('Plans and administrative measures that affect these matters') %></li>
+ </ul>
+
+ <p><%= _('Please only request information that comes under those categories, <strong>do not waste your
+ time</strong> or the time of the public authority by requesting unrelated information.') %></p>
+ <% end %>
+ </div>
+
+ <p>
+ <label class="form_label" for="info_request_title"><%= _('Summary:') %></label>
+ <%= f.text_field :title, :size => 50, :id =>"typeahead_search" %>
+ </p>
+ <div class="form_item_note">
+ (<%= _("a one line summary of the information you are requesting, \n\t\t\te.g.") %>
+ <% if @info_request.law_used == 'eir' %>
+ <%= _("'Pollution levels over time for the River Tyne'") %>
+ <% else %>
+ <%= _("'Crime statistics by ward level for Wales'") %>
+ <% end %>
+ )
+ </div>
+ <div id="typeahead_response">
+ </div>
+ </div>
+
+ <div id="request_advice">
<ul>
- <li><%= _('Air, water, soil, land, flora and fauna (including how these effect
- human beings)') %></li>
- <li><%= _('Information on emissions and discharges (e.g. noise, energy,
- radiation, waste materials)') %></li>
- <li><%= _('Human health and safety') %></li>
- <li><%= _('Cultural sites and built structures (as they may be affected by the
- environmental factors listed above)') %></li>
- <li><%= _('Plans and administrative measures that affect these matters') %></li>
+ <li><%= _('Write your request in <strong>simple, precise language</strong>.') %></li>
+ <li><%= _('Ask for <strong>specific</strong> documents or information, this site is not suitable for general enquiries.') %></li>
+ <li><%= _('Keep it <strong>focused</strong>, you\'ll be more likely to get what you want (<a href="%s">why?</a>).') % [help_requesting_path + '#focused'] %></li>
</ul>
+ </div>
- <p><%= _('Please only request information that comes under those categories, <strong>do not waste your
- time</strong> or the time of the public authority by requesting unrelated information.') %></p>
- <% end %>
-</div>
-
-<% form_for(:info_request, @info_request, :html => { :id => 'write_form' } ) do |f| %>
-
- <div id="request_form">
- <label class="form_label" for="info_request_public_body_id"><%= _('To:') %></label>
- <span id="to_public_body"><%=h(@info_request.public_body.name)%></span>
-
- <p>
- <label class="form_label" for="info_request_title"><%= _('Summary:') %></label>
- <%= f.text_field :title, :size => 50 %>
- </p>
- <div class="form_item_note">
- (<%= _('a one line summary of the information you are requesting,
- e.g.') %>
- <% if @info_request.law_used == 'eir' %>
- <%= _("'Pollution levels over time for the River Tyne'") %>
- <% else %>
- <%= _("'Crime statistics by ward level for Wales'") %>
- <% end %>
- )
- </div>
-
- <% fields_for :outgoing_message do |o| %>
- <p>
- <label class="form_label" for="outgoing_message_body"><%= _('Your request:') %></label>
- <%= o.text_area :body, :rows => 20, :cols => 60 %>
- </p>
- <% end %>
-
+ <div id="request_form">
+ <% fields_for :outgoing_message do |o| %>
+ <p>
+ <label class="form_label" for="outgoing_message_body"><%= _('Your request:') %></label>
+ <%= o.text_area :body, :rows => 20, :cols => 60 %>
+ </p>
+ <% end %>
+
<% if !@user %>
<p class="form_note">
<%= _('Everything that you enter on this page, including <strong>your name</strong>,
@@ -128,18 +119,18 @@
this website forever (<a href="%s">why?</a>).') % [help_privacy_path+"#public_request"] %>
</p>
<% end %>
-
- <p class="form_note">
- <%= _('<strong> Can I request information about myself?</strong>
- <a href="%s">No! (Click here for details)</a>') % [help_requesting_path+"#data_protection"] %>
- </p>
-
- <div class="form_button">
- <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %>
- <%= hidden_field_tag(:submitted_new_request, 1 ) %>
- <%= hidden_field_tag(:preview, 1 ) %>
- <%= submit_tag _("Preview your public request") %>
- </div>
+
+ <p class="form_note">
+ <%= _("<strong> Can I request information about myself?</strong>\n" +
+ "\t\t\t<a href=\"%s\">No! (Click here for details)</a>") % [help_requesting_path+"#data_protection"] %>
+ </p>
+
+ <div class="form_button">
+ <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %>
+ <%= hidden_field_tag(:submitted_new_request, 1 ) %>
+ <%= hidden_field_tag(:preview, 1 ) %>
+ <%= submit_tag _("Preview your public request") %>
+ </div>
<% if !@info_request.tag_string.empty? %>
<p class="form_note">
@@ -150,8 +141,8 @@
<strong>Tags:</strong> <%=h @info_request.tag_string %>
</p>
<% end %>
-
- </div>
+
+ </div>
<% end %>
diff --git a/app/views/request/preview.rhtml b/app/views/request/preview.rhtml
index 6f6ecb2f9..45b6a3dc1 100644
--- a/app/views/request/preview.rhtml
+++ b/app/views/request/preview.rhtml
@@ -2,7 +2,7 @@
<% form_for(:info_request, @info_request, :html => { :id => 'preview_form' } ) do |f| %>
- <h1><%= _('Now preview your request') %></h1>
+ <h1><%= _('3. Now check your request') %></h1>
<ul>
<li><%= _('Check you haven\'t included any <strong>personal information</strong>.') %></li>
<li><%= _('Your name, request and any responses will appear in <strong>search engines</strong>
@@ -37,14 +37,12 @@
<%= f.hidden_field(:tag_string) %>
<%= hidden_field_tag(:submitted_new_request, 1) %>
<%= hidden_field_tag(:preview, 0 ) %>
- <%= submit_tag _("Re-edit this request"), :name => 'reedit' %>
- <%= submit_tag _("Send public ") + h(@info_request.law_used_full) + " request", :name => 'submit' %>
+ <%= submit_tag _("Edit this request"), :name => 'reedit', :id => 'reedit_button' %>
+ <%= submit_tag _("Send request"), :name => 'submit', :id => 'submit_button' %>
</p>
<% if !@info_request.tag_string.empty? %>
<p><strong><%= _('Tags:') %></strong> <%=h @info_request.tag_string %></p>
<% end %>
-<% end %>
-
-
+<% end %> \ No newline at end of file
diff --git a/app/views/request/select_authority.rhtml b/app/views/request/select_authority.rhtml
new file mode 100644
index 000000000..802fefec6
--- /dev/null
+++ b/app/views/request/select_authority.rhtml
@@ -0,0 +1,50 @@
+<script type="text/javascript" src="/javascripts/ba-throttle-debounce.js"></script>
+<script>
+ $(document).ready(function(){
+ $("#authority_preview").hide();
+
+ // Avoid triggering too often (on each keystroke) by using the debounce jQuery plugin:
+ // http://benalman.com/projects/jquery-throttle-debounce-plugin/
+ $("#query").keypress($.debounce( 300, function() {
+ // Do a type ahead search and display results
+ $("#typeahead_response").load("<%=search_ahead_bodies_url%>?q="+encodeURI(this.value), function() {
+ $("#authority_preview").hide(); // Hide the preview, since results have changed
+
+ // We're using the existing body list: we intercept the clicks on the titles to
+ // display a preview on the right hand side of the screen
+ $("#typeahead_response a").click(function() {
+ $("#authority_preview").load(this.href+" #main_content", function() {
+ $("#authority_preview").show();
+ $("#authority_preview #header_right").hide();
+ });
+ return false;
+ });
+ });
+ }));
+ });
+</script>
+
+<% @title = _("Select the authority to write to") %>
+
+ <h1 style="clear: left"><%= _('1. Select an authority') %></h1>
+
+ <div id="authority_selection">
+ <% form_tag({:controller => "general", :action => "search_redirect"}, {:id => "search_form"}) do %>
+ <p>
+ <p>
+ <%= _('First, type in the <strong>name of the UK public authority</strong> you\'d
+ <br>like information from. <strong>By law, they have to respond</strong>
+ (<a href="%s">why?</a>).') % help_about_url %>
+ </p>
+ <%= text_field_tag 'query', params[:query], { :size => 30 } %>
+ <%= hidden_field_tag 'bodies', 1 %>
+ <%= submit_tag _('Search') %>
+ </p>
+ <% end %>
+ <div id="typeahead_response">
+ </div>
+ </div>
+
+ <div id="authority_preview">
+ </div>
+ \ No newline at end of file
diff --git a/app/views/user/_signin.rhtml b/app/views/user/_signin.rhtml
index 6baed3c25..a7cfdf28a 100644
--- a/app/views/user/_signin.rhtml
+++ b/app/views/user/_signin.rhtml
@@ -28,6 +28,7 @@
<div class="form_button">
<%= hidden_field_tag 'token', params[:token], { :id => 'signin_token' } %>
+ <%= hidden_field_tag :modal, params[:modal] %>
<%= submit_tag _('Sign in') %>
</div>
<% end %>
diff --git a/app/views/user/_signup.rhtml b/app/views/user/_signup.rhtml
index f183ac1f1..791226586 100644
--- a/app/views/user/_signup.rhtml
+++ b/app/views/user/_signup.rhtml
@@ -42,6 +42,7 @@
<div class="form_button">
<%= hidden_field_tag 'token', params[:token], { :id => 'signup_token' } %>
+ <%= hidden_field_tag :modal, params[:modal] %>
<%= submit_tag _('Sign up') %>
</div>
diff --git a/app/views/user/signin_successful.rhtml b/app/views/user/signin_successful.rhtml
new file mode 100644
index 000000000..762bfa763
--- /dev/null
+++ b/app/views/user/signin_successful.rhtml
@@ -0,0 +1,9 @@
+<%= _("You're in. <a href=\"#\" id=\"send-request\">Continue sending your request</a>") %>
+
+<script>
+ parent.modal_signin_successful = true;
+
+ $("#send-request").click(function() {
+ parent.$.fancybox.close(); return false;
+ });
+</script> \ No newline at end of file
diff --git a/config/environment.rb b/config/environment.rb
index c363e7fae..0c49e5e3c 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -61,7 +61,7 @@ Rails::Initializer.run do |config|
config.gem "gettext", :version => '>=1.9.3'
config.gem "fast_gettext", :version => '>=0.4.8'
config.gem "rack", :version => '1.1.0'
- config.gem "rdoc", :version => '2.4.3'
+ config.gem "rdoc", :version => '>=2.4.3'
config.gem "recaptcha", :lib => "recaptcha/rails"
config.gem 'rspec', :lib => false, :version => '1.3.1'
config.gem 'rspec-rails', :lib => false, :version => '1.3.3'
diff --git a/config/general.yml-example b/config/general.yml-example
index 48e8523ef..ae50afc5a 100644
--- a/config/general.yml-example
+++ b/config/general.yml-example
@@ -37,6 +37,9 @@ FRONTPAGE_PUBLICBODY_EXAMPLES: 'tgq'
# URL of theme to install (when running rails-post-deploy script)
THEME_URL: 'git://github.com/mysociety/whatdotheyknow-theme.git'
+# Whether a user needs to sign in to start the New Request process
+FORCE_REGISTRATION_ON_NEW_REQUEST: false
+
## Incoming email
# Your email domain, e.g. 'foifa.com'
diff --git a/config/routes.rb b/config/routes.rb
index c1bbfc3b3..6f13c7bc3 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -40,9 +40,13 @@ ActionController::Routing::Routes.draw do |map|
request.request_list_awaiting '/list/awaiting', :action => 'list', :view => 'awaiting'
request.request_list '/list', :action => 'list'
+ request.select_authority '/select_authority', :action => 'select_authority'
+
request.new_request '/new', :action => 'new'
request.new_request_to_body '/new/:url_name', :action => 'new'
+ request.search_ahead '/request/search_ahead', :action => 'search_typeahead'
+
request.show_request '/request/:url_title.:format', :action => 'show'
request.show_new_request '/request/:url_title/new', :action => 'show'
request.details_request '/details/request/:url_title', :action => 'details'
@@ -57,7 +61,6 @@ ActionController::Routing::Routes.draw do |map|
request.info_request_event '/request_event/:info_request_event_id', :action => 'show_request_event'
request.upload_response "/upload/request/:url_title", :action => 'upload_response'
-
end
# Use /profile for things to do with the currently signed in user.
@@ -85,6 +88,7 @@ ActionController::Routing::Routes.draw do |map|
end
map.with_options :controller => 'public_body' do |body|
+ body.search_ahead_bodies '/body/search_ahead', :action => 'search_typeahead'
body.list_public_bodies "/body", :action => 'list'
body.list_public_bodies_default "/body/list/all", :action => 'list'
body.list_public_bodies "/body/list/:tag", :action => 'list'
diff --git a/public/asktheeu-theme b/public/asktheeu-theme
new file mode 120000
index 000000000..108ef0abe
--- /dev/null
+++ b/public/asktheeu-theme
@@ -0,0 +1 @@
+/Users/David/src/asktheeu/asktheeu/vendor/plugins/asktheeu-theme/public \ No newline at end of file
diff --git a/public/javascripts/ba-throttle-debounce.js b/public/javascripts/ba-throttle-debounce.js
new file mode 100644
index 000000000..07205508e
--- /dev/null
+++ b/public/javascripts/ba-throttle-debounce.js
@@ -0,0 +1,9 @@
+/*
+ * jQuery throttle / debounce - v1.1 - 3/7/2010
+ * http://benalman.com/projects/jquery-throttle-debounce-plugin/
+ *
+ * Copyright (c) 2010 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ */
+(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this); \ No newline at end of file
diff --git a/public/javascripts/jquery.fancybox-1.3.4.pack.js b/public/javascripts/jquery.fancybox-1.3.4.pack.js
new file mode 100755
index 000000000..1373ed083
--- /dev/null
+++ b/public/javascripts/jquery.fancybox-1.3.4.pack.js
@@ -0,0 +1,46 @@
+/*
+ * FancyBox - jQuery Plugin
+ * Simple and fancy lightbox alternative
+ *
+ * Examples and documentation at: http://fancybox.net
+ *
+ * Copyright (c) 2008 - 2010 Janis Skarnelis
+ * That said, it is hardly a one-person project. Many people have submitted bugs, code, and offered their advice freely. Their support is greatly appreciated.
+ *
+ * Version: 1.3.4 (11/11/2010)
+ * Requires: jQuery v1.3+
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+;(function(b){var m,t,u,f,D,j,E,n,z,A,q=0,e={},o=[],p=0,d={},l=[],G=null,v=new Image,J=/\.(jpg|gif|png|bmp|jpeg)(.*)?$/i,W=/[^\.]\.(swf)\s*$/i,K,L=1,y=0,s="",r,i,h=false,B=b.extend(b("<div/>")[0],{prop:0}),M=b.browser.msie&&b.browser.version<7&&!window.XMLHttpRequest,N=function(){t.hide();v.onerror=v.onload=null;G&&G.abort();m.empty()},O=function(){if(false===e.onError(o,q,e)){t.hide();h=false}else{e.titleShow=false;e.width="auto";e.height="auto";m.html('<p id="fancybox-error">The requested content cannot be loaded.<br />Please try again later.</p>');
+F()}},I=function(){var a=o[q],c,g,k,C,P,w;N();e=b.extend({},b.fn.fancybox.defaults,typeof b(a).data("fancybox")=="undefined"?e:b(a).data("fancybox"));w=e.onStart(o,q,e);if(w===false)h=false;else{if(typeof w=="object")e=b.extend(e,w);k=e.title||(a.nodeName?b(a).attr("title"):a.title)||"";if(a.nodeName&&!e.orig)e.orig=b(a).children("img:first").length?b(a).children("img:first"):b(a);if(k===""&&e.orig&&e.titleFromAlt)k=e.orig.attr("alt");c=e.href||(a.nodeName?b(a).attr("href"):a.href)||null;if(/^(?:javascript)/i.test(c)||
+c=="#")c=null;if(e.type){g=e.type;if(!c)c=e.content}else if(e.content)g="html";else if(c)g=c.match(J)?"image":c.match(W)?"swf":b(a).hasClass("iframe")?"iframe":c.indexOf("#")===0?"inline":"ajax";if(g){if(g=="inline"){a=c.substr(c.indexOf("#"));g=b(a).length>0?"inline":"ajax"}e.type=g;e.href=c;e.title=k;if(e.autoDimensions)if(e.type=="html"||e.type=="inline"||e.type=="ajax"){e.width="auto";e.height="auto"}else e.autoDimensions=false;if(e.modal){e.overlayShow=true;e.hideOnOverlayClick=false;e.hideOnContentClick=
+false;e.enableEscapeButton=false;e.showCloseButton=false}e.padding=parseInt(e.padding,10);e.margin=parseInt(e.margin,10);m.css("padding",e.padding+e.margin);b(".fancybox-inline-tmp").unbind("fancybox-cancel").bind("fancybox-change",function(){b(this).replaceWith(j.children())});switch(g){case "html":m.html(e.content);F();break;case "inline":if(b(a).parent().is("#fancybox-content")===true){h=false;break}b('<div class="fancybox-inline-tmp" />').hide().insertBefore(b(a)).bind("fancybox-cleanup",function(){b(this).replaceWith(j.children())}).bind("fancybox-cancel",
+function(){b(this).replaceWith(m.children())});b(a).appendTo(m);F();break;case "image":h=false;b.fancybox.showActivity();v=new Image;v.onerror=function(){O()};v.onload=function(){h=true;v.onerror=v.onload=null;e.width=v.width;e.height=v.height;b("<img />").attr({id:"fancybox-img",src:v.src,alt:e.title}).appendTo(m);Q()};v.src=c;break;case "swf":e.scrolling="no";C='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+e.width+'" height="'+e.height+'"><param name="movie" value="'+c+
+'"></param>';P="";b.each(e.swf,function(x,H){C+='<param name="'+x+'" value="'+H+'"></param>';P+=" "+x+'="'+H+'"'});C+='<embed src="'+c+'" type="application/x-shockwave-flash" width="'+e.width+'" height="'+e.height+'"'+P+"></embed></object>";m.html(C);F();break;case "ajax":h=false;b.fancybox.showActivity();e.ajax.win=e.ajax.success;G=b.ajax(b.extend({},e.ajax,{url:c,data:e.ajax.data||{},error:function(x){x.status>0&&O()},success:function(x,H,R){if((typeof R=="object"?R:G).status==200){if(typeof e.ajax.win==
+"function"){w=e.ajax.win(c,x,H,R);if(w===false){t.hide();return}else if(typeof w=="string"||typeof w=="object")x=w}m.html(x);F()}}}));break;case "iframe":Q()}}else O()}},F=function(){var a=e.width,c=e.height;a=a.toString().indexOf("%")>-1?parseInt((b(window).width()-e.margin*2)*parseFloat(a)/100,10)+"px":a=="auto"?"auto":a+"px";c=c.toString().indexOf("%")>-1?parseInt((b(window).height()-e.margin*2)*parseFloat(c)/100,10)+"px":c=="auto"?"auto":c+"px";m.wrapInner('<div style="width:'+a+";height:"+c+
+";overflow: "+(e.scrolling=="auto"?"auto":e.scrolling=="yes"?"scroll":"hidden")+';position:relative;"></div>');e.width=m.width();e.height=m.height();Q()},Q=function(){var a,c;t.hide();if(f.is(":visible")&&false===d.onCleanup(l,p,d)){b.event.trigger("fancybox-cancel");h=false}else{h=true;b(j.add(u)).unbind();b(window).unbind("resize.fb scroll.fb");b(document).unbind("keydown.fb");f.is(":visible")&&d.titlePosition!=="outside"&&f.css("height",f.height());l=o;p=q;d=e;if(d.overlayShow){u.css({"background-color":d.overlayColor,
+opacity:d.overlayOpacity,cursor:d.hideOnOverlayClick?"pointer":"auto",height:b(document).height()});if(!u.is(":visible")){M&&b("select:not(#fancybox-tmp select)").filter(function(){return this.style.visibility!=="hidden"}).css({visibility:"hidden"}).one("fancybox-cleanup",function(){this.style.visibility="inherit"});u.show()}}else u.hide();i=X();s=d.title||"";y=0;n.empty().removeAttr("style").removeClass();if(d.titleShow!==false){if(b.isFunction(d.titleFormat))a=d.titleFormat(s,l,p,d);else a=s&&s.length?
+d.titlePosition=="float"?'<table id="fancybox-title-float-wrap" cellpadding="0" cellspacing="0"><tr><td id="fancybox-title-float-left"></td><td id="fancybox-title-float-main">'+s+'</td><td id="fancybox-title-float-right"></td></tr></table>':'<div id="fancybox-title-'+d.titlePosition+'">'+s+"</div>":false;s=a;if(!(!s||s==="")){n.addClass("fancybox-title-"+d.titlePosition).html(s).appendTo("body").show();switch(d.titlePosition){case "inside":n.css({width:i.width-d.padding*2,marginLeft:d.padding,marginRight:d.padding});
+y=n.outerHeight(true);n.appendTo(D);i.height+=y;break;case "over":n.css({marginLeft:d.padding,width:i.width-d.padding*2,bottom:d.padding}).appendTo(D);break;case "float":n.css("left",parseInt((n.width()-i.width-40)/2,10)*-1).appendTo(f);break;default:n.css({width:i.width-d.padding*2,paddingLeft:d.padding,paddingRight:d.padding}).appendTo(f)}}}n.hide();if(f.is(":visible")){b(E.add(z).add(A)).hide();a=f.position();r={top:a.top,left:a.left,width:f.width(),height:f.height()};c=r.width==i.width&&r.height==
+i.height;j.fadeTo(d.changeFade,0.3,function(){var g=function(){j.html(m.contents()).fadeTo(d.changeFade,1,S)};b.event.trigger("fancybox-change");j.empty().removeAttr("filter").css({"border-width":d.padding,width:i.width-d.padding*2,height:e.autoDimensions?"auto":i.height-y-d.padding*2});if(c)g();else{B.prop=0;b(B).animate({prop:1},{duration:d.changeSpeed,easing:d.easingChange,step:T,complete:g})}})}else{f.removeAttr("style");j.css("border-width",d.padding);if(d.transitionIn=="elastic"){r=V();j.html(m.contents());
+f.show();if(d.opacity)i.opacity=0;B.prop=0;b(B).animate({prop:1},{duration:d.speedIn,easing:d.easingIn,step:T,complete:S})}else{d.titlePosition=="inside"&&y>0&&n.show();j.css({width:i.width-d.padding*2,height:e.autoDimensions?"auto":i.height-y-d.padding*2}).html(m.contents());f.css(i).fadeIn(d.transitionIn=="none"?0:d.speedIn,S)}}}},Y=function(){if(d.enableEscapeButton||d.enableKeyboardNav)b(document).bind("keydown.fb",function(a){if(a.keyCode==27&&d.enableEscapeButton){a.preventDefault();b.fancybox.close()}else if((a.keyCode==
+37||a.keyCode==39)&&d.enableKeyboardNav&&a.target.tagName!=="INPUT"&&a.target.tagName!=="TEXTAREA"&&a.target.tagName!=="SELECT"){a.preventDefault();b.fancybox[a.keyCode==37?"prev":"next"]()}});if(d.showNavArrows){if(d.cyclic&&l.length>1||p!==0)z.show();if(d.cyclic&&l.length>1||p!=l.length-1)A.show()}else{z.hide();A.hide()}},S=function(){if(!b.support.opacity){j.get(0).style.removeAttribute("filter");f.get(0).style.removeAttribute("filter")}e.autoDimensions&&j.css("height","auto");f.css("height","auto");
+s&&s.length&&n.show();d.showCloseButton&&E.show();Y();d.hideOnContentClick&&j.bind("click",b.fancybox.close);d.hideOnOverlayClick&&u.bind("click",b.fancybox.close);b(window).bind("resize.fb",b.fancybox.resize);d.centerOnScroll&&b(window).bind("scroll.fb",b.fancybox.center);if(d.type=="iframe")b('<iframe id="fancybox-frame" name="fancybox-frame'+(new Date).getTime()+'" frameborder="0" hspace="0" '+(b.browser.msie?'allowtransparency="true""':"")+' scrolling="'+e.scrolling+'" src="'+d.href+'"></iframe>').appendTo(j);
+f.show();h=false;b.fancybox.center();d.onComplete(l,p,d);var a,c;if(l.length-1>p){a=l[p+1].href;if(typeof a!=="undefined"&&a.match(J)){c=new Image;c.src=a}}if(p>0){a=l[p-1].href;if(typeof a!=="undefined"&&a.match(J)){c=new Image;c.src=a}}},T=function(a){var c={width:parseInt(r.width+(i.width-r.width)*a,10),height:parseInt(r.height+(i.height-r.height)*a,10),top:parseInt(r.top+(i.top-r.top)*a,10),left:parseInt(r.left+(i.left-r.left)*a,10)};if(typeof i.opacity!=="undefined")c.opacity=a<0.5?0.5:a;f.css(c);
+j.css({width:c.width-d.padding*2,height:c.height-y*a-d.padding*2})},U=function(){return[b(window).width()-d.margin*2,b(window).height()-d.margin*2,b(document).scrollLeft()+d.margin,b(document).scrollTop()+d.margin]},X=function(){var a=U(),c={},g=d.autoScale,k=d.padding*2;c.width=d.width.toString().indexOf("%")>-1?parseInt(a[0]*parseFloat(d.width)/100,10):d.width+k;c.height=d.height.toString().indexOf("%")>-1?parseInt(a[1]*parseFloat(d.height)/100,10):d.height+k;if(g&&(c.width>a[0]||c.height>a[1]))if(e.type==
+"image"||e.type=="swf"){g=d.width/d.height;if(c.width>a[0]){c.width=a[0];c.height=parseInt((c.width-k)/g+k,10)}if(c.height>a[1]){c.height=a[1];c.width=parseInt((c.height-k)*g+k,10)}}else{c.width=Math.min(c.width,a[0]);c.height=Math.min(c.height,a[1])}c.top=parseInt(Math.max(a[3]-20,a[3]+(a[1]-c.height-40)*0.5),10);c.left=parseInt(Math.max(a[2]-20,a[2]+(a[0]-c.width-40)*0.5),10);return c},V=function(){var a=e.orig?b(e.orig):false,c={};if(a&&a.length){c=a.offset();c.top+=parseInt(a.css("paddingTop"),
+10)||0;c.left+=parseInt(a.css("paddingLeft"),10)||0;c.top+=parseInt(a.css("border-top-width"),10)||0;c.left+=parseInt(a.css("border-left-width"),10)||0;c.width=a.width();c.height=a.height();c={width:c.width+d.padding*2,height:c.height+d.padding*2,top:c.top-d.padding-20,left:c.left-d.padding-20}}else{a=U();c={width:d.padding*2,height:d.padding*2,top:parseInt(a[3]+a[1]*0.5,10),left:parseInt(a[2]+a[0]*0.5,10)}}return c},Z=function(){if(t.is(":visible")){b("div",t).css("top",L*-40+"px");L=(L+1)%12}else clearInterval(K)};
+b.fn.fancybox=function(a){if(!b(this).length)return this;b(this).data("fancybox",b.extend({},a,b.metadata?b(this).metadata():{})).unbind("click.fb").bind("click.fb",function(c){c.preventDefault();if(!h){h=true;b(this).blur();o=[];q=0;c=b(this).attr("rel")||"";if(!c||c==""||c==="nofollow")o.push(this);else{o=b("a[rel="+c+"], area[rel="+c+"]");q=o.index(this)}I()}});return this};b.fancybox=function(a,c){var g;if(!h){h=true;g=typeof c!=="undefined"?c:{};o=[];q=parseInt(g.index,10)||0;if(b.isArray(a)){for(var k=
+0,C=a.length;k<C;k++)if(typeof a[k]=="object")b(a[k]).data("fancybox",b.extend({},g,a[k]));else a[k]=b({}).data("fancybox",b.extend({content:a[k]},g));o=jQuery.merge(o,a)}else{if(typeof a=="object")b(a).data("fancybox",b.extend({},g,a));else a=b({}).data("fancybox",b.extend({content:a},g));o.push(a)}if(q>o.length||q<0)q=0;I()}};b.fancybox.showActivity=function(){clearInterval(K);t.show();K=setInterval(Z,66)};b.fancybox.hideActivity=function(){t.hide()};b.fancybox.next=function(){return b.fancybox.pos(p+
+1)};b.fancybox.prev=function(){return b.fancybox.pos(p-1)};b.fancybox.pos=function(a){if(!h){a=parseInt(a);o=l;if(a>-1&&a<l.length){q=a;I()}else if(d.cyclic&&l.length>1){q=a>=l.length?0:l.length-1;I()}}};b.fancybox.cancel=function(){if(!h){h=true;b.event.trigger("fancybox-cancel");N();e.onCancel(o,q,e);h=false}};b.fancybox.close=function(){function a(){u.fadeOut("fast");n.empty().hide();f.hide();b.event.trigger("fancybox-cleanup");j.empty();d.onClosed(l,p,d);l=e=[];p=q=0;d=e={};h=false}if(!(h||f.is(":hidden"))){h=
+true;if(d&&false===d.onCleanup(l,p,d))h=false;else{N();b(E.add(z).add(A)).hide();b(j.add(u)).unbind();b(window).unbind("resize.fb scroll.fb");b(document).unbind("keydown.fb");j.find("iframe").attr("src",M&&/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank");d.titlePosition!=="inside"&&n.empty();f.stop();if(d.transitionOut=="elastic"){r=V();var c=f.position();i={top:c.top,left:c.left,width:f.width(),height:f.height()};if(d.opacity)i.opacity=1;n.empty().hide();B.prop=1;
+b(B).animate({prop:0},{duration:d.speedOut,easing:d.easingOut,step:T,complete:a})}else f.fadeOut(d.transitionOut=="none"?0:d.speedOut,a)}}};b.fancybox.resize=function(){u.is(":visible")&&u.css("height",b(document).height());b.fancybox.center(true)};b.fancybox.center=function(a){var c,g;if(!h){g=a===true?1:0;c=U();!g&&(f.width()>c[0]||f.height()>c[1])||f.stop().animate({top:parseInt(Math.max(c[3]-20,c[3]+(c[1]-j.height()-40)*0.5-d.padding)),left:parseInt(Math.max(c[2]-20,c[2]+(c[0]-j.width()-40)*0.5-
+d.padding))},typeof a=="number"?a:200)}};b.fancybox.init=function(){if(!b("#fancybox-wrap").length){b("body").append(m=b('<div id="fancybox-tmp"></div>'),t=b('<div id="fancybox-loading"><div></div></div>'),u=b('<div id="fancybox-overlay"></div>'),f=b('<div id="fancybox-wrap"></div>'));D=b('<div id="fancybox-outer"></div>').append('<div class="fancybox-bg" id="fancybox-bg-n"></div><div class="fancybox-bg" id="fancybox-bg-ne"></div><div class="fancybox-bg" id="fancybox-bg-e"></div><div class="fancybox-bg" id="fancybox-bg-se"></div><div class="fancybox-bg" id="fancybox-bg-s"></div><div class="fancybox-bg" id="fancybox-bg-sw"></div><div class="fancybox-bg" id="fancybox-bg-w"></div><div class="fancybox-bg" id="fancybox-bg-nw"></div>').appendTo(f);
+D.append(j=b('<div id="fancybox-content"></div>'),E=b('<a id="fancybox-close"></a>'),n=b('<div id="fancybox-title"></div>'),z=b('<a href="javascript:;" id="fancybox-left"><span class="fancy-ico" id="fancybox-left-ico"></span></a>'),A=b('<a href="javascript:;" id="fancybox-right"><span class="fancy-ico" id="fancybox-right-ico"></span></a>'));E.click(b.fancybox.close);t.click(b.fancybox.cancel);z.click(function(a){a.preventDefault();b.fancybox.prev()});A.click(function(a){a.preventDefault();b.fancybox.next()});
+b.fn.mousewheel&&f.bind("mousewheel.fb",function(a,c){if(h)a.preventDefault();else if(b(a.target).get(0).clientHeight==0||b(a.target).get(0).scrollHeight===b(a.target).get(0).clientHeight){a.preventDefault();b.fancybox[c>0?"prev":"next"]()}});b.support.opacity||f.addClass("fancybox-ie");if(M){t.addClass("fancybox-ie6");f.addClass("fancybox-ie6");b('<iframe id="fancybox-hide-sel-frame" src="'+(/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank")+'" scrolling="no" border="0" frameborder="0" tabindex="-1"></iframe>').prependTo(D)}}};
+b.fn.fancybox.defaults={padding:10,margin:40,opacity:false,modal:false,cyclic:false,scrolling:"auto",width:560,height:340,autoScale:true,autoDimensions:true,centerOnScroll:false,ajax:{},swf:{wmode:"transparent"},hideOnOverlayClick:true,hideOnContentClick:false,overlayShow:true,overlayOpacity:0.7,overlayColor:"#777",titleShow:true,titlePosition:"float",titleFormat:null,titleFromAlt:false,transitionIn:"fade",transitionOut:"fade",speedIn:300,speedOut:300,changeSpeed:300,changeFade:"fast",easingIn:"swing",
+easingOut:"swing",showCloseButton:true,showNavArrows:true,enableEscapeButton:true,enableKeyboardNav:true,onStart:function(){},onCancel:function(){},onComplete:function(){},onCleanup:function(){},onClosed:function(){},onError:function(){}};b(document).ready(function(){b.fancybox.init()})})(jQuery); \ No newline at end of file
diff --git a/public/javascripts/jquery.form.js b/public/javascripts/jquery.form.js
new file mode 100644
index 000000000..bc0061418
--- /dev/null
+++ b/public/javascripts/jquery.form.js
@@ -0,0 +1,11 @@
+/*!
+ * jQuery Form Plugin
+ * version: 2.83 (11-JUL-2011)
+ * @requires jQuery v1.3.2 or later
+ *
+ * Examples and documentation at: http://malsup.com/jquery/form/
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+(function(a){function b(){var a="[jquery.form] "+Array.prototype.join.call(arguments,"");if(window.console&&window.console.log){window.console.log(a)}else if(window.opera&&window.opera.postError){window.opera.postError(a)}}a.fn.ajaxSubmit=function(c){function t(e){function C(c){if(o.aborted||B){return}try{z=w(n)}catch(d){b("cannot access response document: ",d);c=v}if(c===u&&o){o.abort("timeout");return}else if(c==v&&o){o.abort("server abort");return}if(!z||z.location.href==j.iframeSrc){if(!r)return}n.detachEvent?n.detachEvent("onload",C):n.removeEventListener("load",C,false);var e="success",f;try{if(r){throw"timeout"}var g=j.dataType=="xml"||z.XMLDocument||a.isXMLDoc(z);b("isXml="+g);if(!g&&window.opera&&(z.body==null||z.body.innerHTML=="")){if(--A){b("requeing onLoad callback, DOM not available");setTimeout(C,250);return}}var h=z.body?z.body:z.documentElement;o.responseText=h?h.innerHTML:null;o.responseXML=z.XMLDocument?z.XMLDocument:z;if(g)j.dataType="xml";o.getResponseHeader=function(a){var b={"content-type":j.dataType};return b[a]};if(h){o.status=Number(h.getAttribute("status"))||o.status;o.statusText=h.getAttribute("statusText")||o.statusText}var i=j.dataType||"";var l=/(json|script|text)/.test(i.toLowerCase());if(l||j.textarea){var p=z.getElementsByTagName("textarea")[0];if(p){o.responseText=p.value;o.status=Number(p.getAttribute("status"))||o.status;o.statusText=p.getAttribute("statusText")||o.statusText}else if(l){var q=z.getElementsByTagName("pre")[0];var t=z.getElementsByTagName("body")[0];if(q){o.responseText=q.textContent?q.textContent:q.innerHTML}else if(t){o.responseText=t.innerHTML}}}else if(j.dataType=="xml"&&!o.responseXML&&o.responseText!=null){o.responseXML=D(o.responseText)}try{y=F(o,j.dataType,j)}catch(c){e="parsererror";o.error=f=c||e}}catch(c){b("error caught: ",c);e="error";o.error=f=c||e}if(o.aborted){b("upload aborted");e=null}if(o.status){e=o.status>=200&&o.status<300||o.status===304?"success":"error"}if(e==="success"){j.success&&j.success.call(j.context,y,"success",o);k&&a.event.trigger("ajaxSuccess",[o,j])}else if(e){if(f==undefined)f=o.statusText;j.error&&j.error.call(j.context,o,e,f);k&&a.event.trigger("ajaxError",[o,j,f])}k&&a.event.trigger("ajaxComplete",[o,j]);if(k&&!--a.active){a.event.trigger("ajaxStop")}j.complete&&j.complete.call(j.context,o,e);B=true;if(j.timeout)clearTimeout(s);setTimeout(function(){if(!j.iframeTarget)m.remove();o.responseXML=null},100)}function x(){function h(){try{var a=w(n).readyState;b("state = "+a);if(a.toLowerCase()=="uninitialized")setTimeout(h,50)}catch(c){b("Server abort: ",c," (",c.name,")");C(v);s&&clearTimeout(s);s=undefined}}var c=g.attr("target"),e=g.attr("action");f.setAttribute("target",l);if(!d){f.setAttribute("method","POST")}if(e!=j.url){f.setAttribute("action",j.url)}if(!j.skipEncodingOverride&&(!d||/post/i.test(d))){g.attr({encoding:"multipart/form-data",enctype:"multipart/form-data"})}if(j.timeout){s=setTimeout(function(){r=true;C(u)},j.timeout)}var i=[];try{if(j.extraData){for(var k in j.extraData){i.push(a('<input type="hidden" name="'+k+'" />').attr("value",j.extraData[k]).appendTo(f)[0])}}if(!j.iframeTarget){m.appendTo("body");n.attachEvent?n.attachEvent("onload",C):n.addEventListener("load",C,false)}setTimeout(h,15);f.submit()}finally{f.setAttribute("action",e);if(c){f.setAttribute("target",c)}else{g.removeAttr("target")}a(i).remove()}}function w(a){var b=a.contentWindow?a.contentWindow.document:a.contentDocument?a.contentDocument:a.document;return b}var f=g[0],h,i,j,k,l,m,n,o,p,q,r,s;var t=!!a.fn.prop;if(e){for(i=0;i<e.length;i++){h=a(f[e[i].name]);h[t?"prop":"attr"]("disabled",false)}}if(a(":input[name=submit],:input[id=submit]",f).length){alert('Error: Form elements must not have name or id of "submit".');return}j=a.extend(true,{},a.ajaxSettings,c);j.context=j.context||j;l="jqFormIO"+(new Date).getTime();if(j.iframeTarget){m=a(j.iframeTarget);q=m.attr("name");if(q==null)m.attr("name",l);else l=q}else{m=a('<iframe name="'+l+'" src="'+j.iframeSrc+'" />');m.css({position:"absolute",top:"-1000px",left:"-1000px"})}n=m[0];o={aborted:0,responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(c){var d=c==="timeout"?"timeout":"aborted";b("aborting upload... "+d);this.aborted=1;m.attr("src",j.iframeSrc);o.error=d;j.error&&j.error.call(j.context,o,d,c);k&&a.event.trigger("ajaxError",[o,j,d]);j.complete&&j.complete.call(j.context,o,d)}};k=j.global;if(k&&!(a.active++)){a.event.trigger("ajaxStart")}if(k){a.event.trigger("ajaxSend",[o,j])}if(j.beforeSend&&j.beforeSend.call(j.context,o,j)===false){if(j.global){a.active--}return}if(o.aborted){return}p=f.clk;if(p){q=p.name;if(q&&!p.disabled){j.extraData=j.extraData||{};j.extraData[q]=p.value;if(p.type=="image"){j.extraData[q+".x"]=f.clk_x;j.extraData[q+".y"]=f.clk_y}}}var u=1;var v=2;if(j.forceSync){x()}else{setTimeout(x,10)}var y,z,A=50,B;var D=a.parseXML||function(a,b){if(window.ActiveXObject){b=new ActiveXObject("Microsoft.XMLDOM");b.async="false";b.loadXML(a)}else{b=(new DOMParser).parseFromString(a,"text/xml")}return b&&b.documentElement&&b.documentElement.nodeName!="parsererror"?b:null};var E=a.parseJSON||function(a){return window["eval"]("("+a+")")};var F=function(b,c,d){var e=b.getResponseHeader("content-type")||"",f=c==="xml"||!c&&e.indexOf("xml")>=0,g=f?b.responseXML:b.responseText;if(f&&g.documentElement.nodeName==="parsererror"){a.error&&a.error("parsererror")}if(d&&d.dataFilter){g=d.dataFilter(g,c)}if(typeof g==="string"){if(c==="json"||!c&&e.indexOf("json")>=0){g=E(g)}else if(c==="script"||!c&&e.indexOf("javascript")>=0){a.globalEval(g)}}return g}}if(!this.length){b("ajaxSubmit: skipping submit process - no element selected");return this}var d,e,f,g=this;if(typeof c=="function"){c={success:c}}d=this.attr("method");e=this.attr("action");f=typeof e==="string"?a.trim(e):"";f=f||window.location.href||"";if(f){f=(f.match(/^([^#]+)/)||[])[1]}c=a.extend(true,{url:f,success:a.ajaxSettings.success,type:d||"GET",iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank"},c);var h={};this.trigger("form-pre-serialize",[this,c,h]);if(h.veto){b("ajaxSubmit: submit vetoed via form-pre-serialize trigger");return this}if(c.beforeSerialize&&c.beforeSerialize(this,c)===false){b("ajaxSubmit: submit aborted via beforeSerialize callback");return this}var i,j,k=this.formToArray(c.semantic);if(c.data){c.extraData=c.data;for(i in c.data){if(c.data[i]instanceof Array){for(var l in c.data[i]){k.push({name:i,value:c.data[i][l]})}}else{j=c.data[i];j=a.isFunction(j)?j():j;k.push({name:i,value:j})}}}if(c.beforeSubmit&&c.beforeSubmit(k,this,c)===false){b("ajaxSubmit: submit aborted via beforeSubmit callback");return this}this.trigger("form-submit-validate",[k,this,c,h]);if(h.veto){b("ajaxSubmit: submit vetoed via form-submit-validate trigger");return this}var m=a.param(k);if(c.type.toUpperCase()=="GET"){c.url+=(c.url.indexOf("?")>=0?"&":"?")+m;c.data=null}else{c.data=m}var n=[];if(c.resetForm){n.push(function(){g.resetForm()})}if(c.clearForm){n.push(function(){g.clearForm()})}if(!c.dataType&&c.target){var o=c.success||function(){};n.push(function(b){var d=c.replaceTarget?"replaceWith":"html";a(c.target)[d](b).each(o,arguments)})}else if(c.success){n.push(c.success)}c.success=function(a,b,d){var e=c.context||c;for(var f=0,h=n.length;f<h;f++){n[f].apply(e,[a,b,d||g,g])}};var p=a("input:file",this).length>0;var q="multipart/form-data";var r=g.attr("enctype")==q||g.attr("encoding")==q;if(c.iframe!==false&&(p||c.iframe||r)){if(c.closeKeepAlive){a.get(c.closeKeepAlive,function(){t(k)})}else{t(k)}}else{if(a.browser.msie&&d=="get"){var s=g[0].getAttribute("method");if(typeof s==="string")c.type=s}a.ajax(c)}this.trigger("form-submit-notify",[this,c]);return this};a.fn.ajaxForm=function(c){if(this.length===0){var d={s:this.selector,c:this.context};if(!a.isReady&&d.s){b("DOM not ready, queuing ajaxForm");a(function(){a(d.s,d.c).ajaxForm(c)});return this}b("terminating; zero elements found by selector"+(a.isReady?"":" (DOM not ready)"));return this}return this.ajaxFormUnbind().bind("submit.form-plugin",function(b){if(!b.isDefaultPrevented()){b.preventDefault();a(this).ajaxSubmit(c)}}).bind("click.form-plugin",function(b){var c=b.target;var d=a(c);if(!d.is(":submit,input:image")){var e=d.closest(":submit");if(e.length==0){return}c=e[0]}var f=this;f.clk=c;if(c.type=="image"){if(b.offsetX!=undefined){f.clk_x=b.offsetX;f.clk_y=b.offsetY}else if(typeof a.fn.offset=="function"){var g=d.offset();f.clk_x=b.pageX-g.left;f.clk_y=b.pageY-g.top}else{f.clk_x=b.pageX-c.offsetLeft;f.clk_y=b.pageY-c.offsetTop}}setTimeout(function(){f.clk=f.clk_x=f.clk_y=null},100)})};a.fn.ajaxFormUnbind=function(){return this.unbind("submit.form-plugin click.form-plugin")};a.fn.formToArray=function(b){var c=[];if(this.length===0){return c}var d=this[0];var e=b?d.getElementsByTagName("*"):d.elements;if(!e){return c}var f,g,h,i,j,k,l;for(f=0,k=e.length;f<k;f++){j=e[f];h=j.name;if(!h){continue}if(b&&d.clk&&j.type=="image"){if(!j.disabled&&d.clk==j){c.push({name:h,value:a(j).val()});c.push({name:h+".x",value:d.clk_x},{name:h+".y",value:d.clk_y})}continue}i=a.fieldValue(j,true);if(i&&i.constructor==Array){for(g=0,l=i.length;g<l;g++){c.push({name:h,value:i[g]})}}else if(i!==null&&typeof i!="undefined"){c.push({name:h,value:i})}}if(!b&&d.clk){var m=a(d.clk),n=m[0];h=n.name;if(h&&!n.disabled&&n.type=="image"){c.push({name:h,value:m.val()});c.push({name:h+".x",value:d.clk_x},{name:h+".y",value:d.clk_y})}}return c};a.fn.formSerialize=function(b){return a.param(this.formToArray(b))};a.fn.fieldSerialize=function(b){var c=[];this.each(function(){var d=this.name;if(!d){return}var e=a.fieldValue(this,b);if(e&&e.constructor==Array){for(var f=0,g=e.length;f<g;f++){c.push({name:d,value:e[f]})}}else if(e!==null&&typeof e!="undefined"){c.push({name:this.name,value:e})}});return a.param(c)};a.fn.fieldValue=function(b){for(var c=[],d=0,e=this.length;d<e;d++){var f=this[d];var g=a.fieldValue(f,b);if(g===null||typeof g=="undefined"||g.constructor==Array&&!g.length){continue}g.constructor==Array?a.merge(c,g):c.push(g)}return c};a.fieldValue=function(b,c){var d=b.name,e=b.type,f=b.tagName.toLowerCase();if(c===undefined){c=true}if(c&&(!d||b.disabled||e=="reset"||e=="button"||(e=="checkbox"||e=="radio")&&!b.checked||(e=="submit"||e=="image")&&b.form&&b.form.clk!=b||f=="select"&&b.selectedIndex==-1)){return null}if(f=="select"){var g=b.selectedIndex;if(g<0){return null}var h=[],i=b.options;var j=e=="select-one";var k=j?g+1:i.length;for(var l=j?g:0;l<k;l++){var m=i[l];if(m.selected){var n=m.value;if(!n){n=m.attributes&&m.attributes["value"]&&!m.attributes["value"].specified?m.text:m.value}if(j){return n}h.push(n)}}return h}return a(b).val()};a.fn.clearForm=function(){return this.each(function(){a("input,select,textarea",this).clearFields()})};a.fn.clearFields=a.fn.clearInputs=function(){var a=/^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i;return this.each(function(){var b=this.type,c=this.tagName.toLowerCase();if(a.test(b)||c=="textarea"){this.value=""}else if(b=="checkbox"||b=="radio"){this.checked=false}else if(c=="select"){this.selectedIndex=-1}})};a.fn.resetForm=function(){return this.each(function(){if(typeof this.reset=="function"||typeof this.reset=="object"&&!this.reset.nodeType){this.reset()}})};a.fn.enable=function(a){if(a===undefined){a=true}return this.each(function(){this.disabled=!a})};a.fn.selected=function(b){if(b===undefined){b=true}return this.each(function(){var c=this.type;if(c=="checkbox"||c=="radio"){this.checked=b}else if(this.tagName.toLowerCase()=="option"){var d=a(this).parent("select");if(b&&d[0]&&d[0].type=="select-one"){d.find("option").selected(false)}this.selected=b}})};})(jQuery) \ No newline at end of file
diff --git a/public/stylesheets/fancybox-x.png b/public/stylesheets/fancybox-x.png
new file mode 100755
index 000000000..c2130f869
--- /dev/null
+++ b/public/stylesheets/fancybox-x.png
Binary files differ
diff --git a/public/stylesheets/fancybox-y.png b/public/stylesheets/fancybox-y.png
new file mode 100755
index 000000000..7ef399b99
--- /dev/null
+++ b/public/stylesheets/fancybox-y.png
Binary files differ
diff --git a/public/stylesheets/fancybox.png b/public/stylesheets/fancybox.png
new file mode 100755
index 000000000..65e14f68f
--- /dev/null
+++ b/public/stylesheets/fancybox.png
Binary files differ
diff --git a/public/stylesheets/jquery.fancybox-1.3.4.css b/public/stylesheets/jquery.fancybox-1.3.4.css
new file mode 100755
index 000000000..6f53d8f4a
--- /dev/null
+++ b/public/stylesheets/jquery.fancybox-1.3.4.css
@@ -0,0 +1,359 @@
+/*
+ * FancyBox - jQuery Plugin
+ * Simple and fancy lightbox alternative
+ *
+ * Examples and documentation at: http://fancybox.net
+ *
+ * Copyright (c) 2008 - 2010 Janis Skarnelis
+ * That said, it is hardly a one-person project. Many people have submitted bugs, code, and offered their advice freely. Their support is greatly appreciated.
+ *
+ * Version: 1.3.4 (11/11/2010)
+ * Requires: jQuery v1.3+
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+#fancybox-loading {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ width: 40px;
+ height: 40px;
+ margin-top: -20px;
+ margin-left: -20px;
+ cursor: pointer;
+ overflow: hidden;
+ z-index: 1104;
+ display: none;
+}
+
+#fancybox-loading div {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 40px;
+ height: 480px;
+ background-image: url('fancybox.png');
+}
+
+#fancybox-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ z-index: 1100;
+ display: none;
+}
+
+#fancybox-tmp {
+ padding: 0;
+ margin: 0;
+ border: 0;
+ overflow: auto;
+ display: none;
+}
+
+#fancybox-wrap {
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: 20px;
+ z-index: 1101;
+ outline: none;
+ display: none;
+}
+
+#fancybox-outer {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background: #fff;
+}
+
+#fancybox-content {
+ width: 0;
+ height: 0;
+ padding: 0;
+ outline: none;
+ position: relative;
+ overflow: hidden;
+ z-index: 1102;
+ border: 0px solid #fff;
+}
+
+#fancybox-hide-sel-frame {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: transparent;
+ z-index: 1101;
+}
+
+#fancybox-close {
+ position: absolute;
+ top: -15px;
+ right: -15px;
+ width: 30px;
+ height: 30px;
+ background: transparent url('fancybox.png') -40px 0px;
+ cursor: pointer;
+ z-index: 1103;
+ display: none;
+}
+
+#fancybox-error {
+ color: #444;
+ font: normal 12px/20px Arial;
+ padding: 14px;
+ margin: 0;
+}
+
+#fancybox-img {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ border: none;
+ outline: none;
+ line-height: 0;
+ vertical-align: top;
+}
+
+#fancybox-frame {
+ width: 100%;
+ height: 100%;
+ border: none;
+ display: block;
+}
+
+#fancybox-left, #fancybox-right {
+ position: absolute;
+ bottom: 0px;
+ height: 100%;
+ width: 35%;
+ cursor: pointer;
+ outline: none;
+ background: transparent url('blank.gif');
+ z-index: 1102;
+ display: none;
+}
+
+#fancybox-left {
+ left: 0px;
+}
+
+#fancybox-right {
+ right: 0px;
+}
+
+#fancybox-left-ico, #fancybox-right-ico {
+ position: absolute;
+ top: 50%;
+ left: -9999px;
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ cursor: pointer;
+ z-index: 1102;
+ display: block;
+}
+
+#fancybox-left-ico {
+ background-image: url('fancybox.png');
+ background-position: -40px -30px;
+}
+
+#fancybox-right-ico {
+ background-image: url('fancybox.png');
+ background-position: -40px -60px;
+}
+
+#fancybox-left:hover, #fancybox-right:hover {
+ visibility: visible; /* IE6 */
+}
+
+#fancybox-left:hover span {
+ left: 20px;
+}
+
+#fancybox-right:hover span {
+ left: auto;
+ right: 20px;
+}
+
+.fancybox-bg {
+ position: absolute;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ width: 20px;
+ height: 20px;
+ z-index: 1001;
+}
+
+#fancybox-bg-n {
+ top: -20px;
+ left: 0;
+ width: 100%;
+ background-image: url('fancybox-x.png');
+}
+
+#fancybox-bg-ne {
+ top: -20px;
+ right: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -162px;
+}
+
+#fancybox-bg-e {
+ top: 0;
+ right: -20px;
+ height: 100%;
+ background-image: url('fancybox-y.png');
+ background-position: -20px 0px;
+}
+
+#fancybox-bg-se {
+ bottom: -20px;
+ right: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -182px;
+}
+
+#fancybox-bg-s {
+ bottom: -20px;
+ left: 0;
+ width: 100%;
+ background-image: url('fancybox-x.png');
+ background-position: 0px -20px;
+}
+
+#fancybox-bg-sw {
+ bottom: -20px;
+ left: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -142px;
+}
+
+#fancybox-bg-w {
+ top: 0;
+ left: -20px;
+ height: 100%;
+ background-image: url('fancybox-y.png');
+}
+
+#fancybox-bg-nw {
+ top: -20px;
+ left: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -122px;
+}
+
+#fancybox-title {
+ font-family: Helvetica;
+ font-size: 12px;
+ z-index: 1102;
+}
+
+.fancybox-title-inside {
+ padding-bottom: 10px;
+ text-align: center;
+ color: #333;
+ background: #fff;
+ position: relative;
+}
+
+.fancybox-title-outside {
+ padding-top: 10px;
+ color: #fff;
+}
+
+.fancybox-title-over {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ color: #FFF;
+ text-align: left;
+}
+
+#fancybox-title-over {
+ padding: 10px;
+ background-image: url('fancy_title_over.png');
+ display: block;
+}
+
+.fancybox-title-float {
+ position: absolute;
+ left: 0;
+ bottom: -20px;
+ height: 32px;
+}
+
+#fancybox-title-float-wrap {
+ border: none;
+ border-collapse: collapse;
+ width: auto;
+}
+
+#fancybox-title-float-wrap td {
+ border: none;
+ white-space: nowrap;
+}
+
+#fancybox-title-float-left {
+ padding: 0 0 0 15px;
+ background: url('fancybox.png') -40px -90px no-repeat;
+}
+
+#fancybox-title-float-main {
+ color: #FFF;
+ line-height: 29px;
+ font-weight: bold;
+ padding: 0 0 3px 0;
+ background: url('fancybox-x.png') 0px -40px;
+}
+
+#fancybox-title-float-right {
+ padding: 0 0 0 15px;
+ background: url('fancybox.png') -55px -90px no-repeat;
+}
+
+/* IE6 */
+
+.fancybox-ie6 #fancybox-close { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_close.png', sizingMethod='scale'); }
+
+.fancybox-ie6 #fancybox-left-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_nav_left.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-right-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_nav_right.png', sizingMethod='scale'); }
+
+.fancybox-ie6 #fancybox-title-over { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_over.png', sizingMethod='scale'); zoom: 1; }
+.fancybox-ie6 #fancybox-title-float-left { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_left.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-title-float-main { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_main.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-title-float-right { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_right.png', sizingMethod='scale'); }
+
+.fancybox-ie6 #fancybox-bg-w, .fancybox-ie6 #fancybox-bg-e, .fancybox-ie6 #fancybox-left, .fancybox-ie6 #fancybox-right, #fancybox-hide-sel-frame {
+ height: expression(this.parentNode.clientHeight + "px");
+}
+
+#fancybox-loading.fancybox-ie6 {
+ position: absolute; margin-top: 0;
+ top: expression( (-20 + (document.documentElement.clientHeight ? document.documentElement.clientHeight/2 : document.body.clientHeight/2 ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop )) + 'px');
+}
+
+#fancybox-loading.fancybox-ie6 div { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_loading.png', sizingMethod='scale'); }
+
+/* IE6, IE7, IE8 */
+
+.fancybox-ie .fancybox-bg { background: transparent !important; }
+
+.fancybox-ie #fancybox-bg-n { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_n.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-ne { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_ne.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-e { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_e.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-se { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_se.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-s { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_s.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-sw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_sw.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-w { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_w.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-nw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_nw.png', sizingMethod='scale'); } \ No newline at end of file
diff --git a/public/stylesheets/main.css b/public/stylesheets/main.css
index e746a559f..d9bdd25a8 100644
--- a/public/stylesheets/main.css
+++ b/public/stylesheets/main.css
@@ -312,6 +312,7 @@ h1, h2, h3
line-height: 1em;
letter-spacing: 0em;
color: #555;
+ clear: left;
}
h1 { font-size: 1.8em;}
h2 { font-size: 1.4em;}
@@ -931,19 +932,72 @@ a img.attachment_image {
#body_sidebar a { text-decoration: none; }
+/*------------------------------------------------ selecting an authority */
+
+#authority_selection
+{
+ float: left;
+ width: 40%;
+}
+
+#authority_search_ahead_results
+{
+ width: 26em;
+}
+
+#authority_preview
+{
+ width: 45%;
+ float: right;
+ background-color: #FFFFE0;
+ padding-left: 1em;
+ padding-right: 1em;
+ overflow: hidden;
+}
+
+ #authority_preview #header_left,
+ #authority_preview.request_left,
+ #authority_preview #stepwise_make_request
+ {
+ width: 95%;
+ }
+
/*------------------------------------------------ making a request / sign up / sign in */
#request_advice
-{}
+{
+ float: right;
+ width: 250px;
+ margin-top: 1em;
+}
+
#request_advice ul
{
- width: 26em;
margin: 0 auto 0 auto;
}
#request_advice ul li { margin: 0 0 1em 0; }
+#request_header
+{
+ background-color: #FFFFE0;
+ padding-top: 0.5em;
+ padding-bottom: 1em;
+}
+
+#request_header_text
+{
+ font-size: 0.8em;
+ margin-left: 11em;
+}
+
+#request_search_ahead_results
+{
+ font-size: 0.8em;
+ margin-left: 11em;
+}
+
#request_form
-{ margin-top: 4em;}
+{ margin-top: 1em;}
#request_form label,
label.form_label
@@ -956,10 +1010,6 @@ label.form_label
padding: 0 10px 0 0;
margin: 0 0 0 0;
}
- #request_form h1 label
- {
- font-size: 0.55em;
- }
.form_item_note, .form_note
{
diff --git a/public/stylesheets/theme.css b/public/stylesheets/theme.css
index 7fcb823c4..43042fa75 100644
--- a/public/stylesheets/theme.css
+++ b/public/stylesheets/theme.css
@@ -127,8 +127,6 @@ div.lang {
#topnav {
background: transparent;
top: 120px;
- left: 115px;
- font-family:'DeliciousRoman', Arial, sans-serif;
font-size: 18px;
}
diff --git a/spec/controllers/public_body_controller_spec.rb b/spec/controllers/public_body_controller_spec.rb
index ba9b49f31..c5c9d60e1 100644
--- a/spec/controllers/public_body_controller_spec.rb
+++ b/spec/controllers/public_body_controller_spec.rb
@@ -156,6 +156,41 @@ describe PublicBodyController, "when showing JSON version for API" do
end
+describe PublicBodyController, "when doing type ahead searches" do
+ fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments
+ it "should return nothing for the empty query string" do
+ get :search_typeahead, :q => ""
+ response.should render_template('public_body/_search_ahead')
+ assigns[:xapian_requests].results.size.should == 0
+ end
+
+ it "should return a body matching the given keyword, but not users with a matching description" do
+ get :search_typeahead, :q => "Geraldine"
+ response.should render_template('public_body/_search_ahead')
+ assigns[:xapian_requests].results.size.should == 1
+ assigns[:xapian_requests].results[0][:model].name.should == public_bodies(:geraldine_public_body).name
+ end
+ it "should return all requests matching any of the given keywords" do
+ get :search_typeahead, :q => "Geraldine Humpadinking"
+ response.should render_template('public_body/_search_ahead')
+ assigns[:xapian_requests].results.size.should == 2
+ assigns[:xapian_requests].results[0][:model].name.should == public_bodies(:humpadink_public_body).name
+ assigns[:xapian_requests].results[1][:model].name.should == public_bodies(:geraldine_public_body).name
+ end
+ it "should return requests matching the given keywords in any of their locales" do
+ get :search_typeahead, :q => "baguette" # part of the spanish notes
+ response.should render_template('public_body/_search_ahead')
+ assigns[:xapian_requests].results.size.should == 1
+ assigns[:xapian_requests].results[0][:model].name.should == public_bodies(:humpadink_public_body).name
+ end
+
+ it "should return partial matches" do
+ get :search_typeahead, :q => "geral" # 'geral' for 'Geraldine'
+ response.should render_template('public_body/_search_ahead')
+ assigns[:xapian_requests].results.size.should == 1
+ assigns[:xapian_requests].results[0][:model].name.should == public_bodies(:geraldine_public_body).name
+ end
+end
diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb
index 55b5fc5e3..63e86b525 100644
--- a/spec/controllers/request_controller_spec.rb
+++ b/spec/controllers/request_controller_spec.rb
@@ -349,7 +349,20 @@ describe RequestController, "when creating a new request" do
response.should render_template('new')
end
+ it "should redirect to sign in page when input is good and nobody is logged in" do
+ params = { :info_request => { :public_body_id => @body.id,
+ :title => "Why is your quango called Geraldine?", :tag_string => "" },
+ :outgoing_message => { :body => "This is a silly letter. It is too short to be interesting." },
+ :submitted_new_request => 1, :preview => 0
+ }
+ post :new, params
+ post_redirect = PostRedirect.get_last_post_redirect
+ response.should redirect_to(:controller => 'user', :action => 'signin', :token => post_redirect.token)
+ # post_redirect.post_params.should == params # XXX get this working. there's a : vs '' problem amongst others
+ end
+
it "should show preview when input is good" do
+ session[:user_id] = @user.id
post :new, { :info_request => { :public_body_id => @body.id,
:title => "Why is your quango called Geraldine?", :tag_string => "" },
:outgoing_message => { :body => "This is a silly letter. It is too short to be interesting." },
@@ -367,18 +380,6 @@ describe RequestController, "when creating a new request" do
response.should render_template('new')
end
- it "should redirect to sign in page when input is good and nobody is logged in" do
- params = { :info_request => { :public_body_id => @body.id,
- :title => "Why is your quango called Geraldine?", :tag_string => "" },
- :outgoing_message => { :body => "This is a silly letter. It is too short to be interesting." },
- :submitted_new_request => 1, :preview => 0
- }
- post :new, params
- post_redirect = PostRedirect.get_last_post_redirect
- response.should redirect_to(:controller => 'user', :action => 'signin', :token => post_redirect.token)
- # post_redirect.post_params.should == params # XXX get this working. there's a : vs '' problem amongst others
- end
-
it "should create the request and outgoing message, and send the outgoing message by email, and redirect to request page when input is good and somebody is logged in" do
session[:user_id] = @user.id
post :new, :info_request => { :public_body_id => @body.id,
@@ -1320,5 +1321,36 @@ describe RequestController, "when showing JSON version for API" do
end
+describe RequestController, "when doing type ahead searches" do
+ fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments
+
+ it "should return nothing for the empty query string" do
+ get :search_typeahead, :q => ""
+ response.should render_template('request/_search_ahead.rhtml')
+ assigns[:xapian_requests].results.size.should == 0
+ end
+
+ it "should return a request matching the given keyword, but not users with a matching description" do
+ get :search_typeahead, :q => "chicken"
+ response.should render_template('request/_search_ahead.rhtml')
+ assigns[:xapian_requests].results.size.should == 1
+ assigns[:xapian_requests].results[0][:model].title.should == info_requests(:naughty_chicken_request).title
+ end
+
+ it "should return all requests matching any of the given keywords" do
+ get :search_typeahead, :q => "money dog"
+ response.should render_template('request/_search_ahead.rhtml')
+ assigns[:xapian_requests].results.size.should == 2
+ assigns[:xapian_requests].results[0][:model].title.should == info_requests(:fancy_dog_request).title
+ assigns[:xapian_requests].results[1][:model].title.should == info_requests(:naughty_chicken_request).title
+ end
+
+ it "should return partial matches" do
+ get :search_typeahead, :q => "chick" # 'chick' for 'chicken'
+ response.should render_template('request/_search_ahead.rhtml')
+ assigns[:xapian_requests].results.size.should == 1
+ assigns[:xapian_requests].results[0][:model].title.should == info_requests(:naughty_chicken_request).title
+ end
+end
diff --git a/spec/models/customstates.rb b/spec/models/customstates.rb
index 406d4ead9..3488e6730 100644
--- a/spec/models/customstates.rb
+++ b/spec/models/customstates.rb
@@ -5,16 +5,6 @@ module InfoRequestCustomStates
end
# Mixin methods for InfoRequest
- def theme_display_status(status)
- if status == 'deadline_extended'
- _("Deadline extended.")
- elsif status == 'wrong_response'
- _("Wrong Response.")
- else
- raise _("unknown status ") + status
- end
- end
-
def theme_calculate_status
return 'waiting_classification' if self.awaiting_description
waiting_response = self.described_state == "waiting_response" || self.described_state == "deadline_extended"
@@ -41,6 +31,16 @@ module InfoRequestCustomStates
end
module ClassMethods
+ def theme_display_status(status)
+ if status == 'deadline_extended'
+ _("Deadline extended.")
+ elsif status == 'wrong_response'
+ _("Wrong Response.")
+ else
+ raise _("unknown status ") + status
+ end
+ end
+
def theme_extra_states
return ['deadline_extended',
'wrong_response']