Merge branch 'master' of git://github.com/bsag/tracks

This commit is contained in:
Reinier Balt 2008-12-03 14:07:33 +01:00
commit e743dd79c3
1641 changed files with 90344 additions and 58968 deletions

2
.gitignore vendored
View file

@ -9,3 +9,5 @@ nbproject
vendor/plugins/query_trace/ vendor/plugins/query_trace/
db/schema.rb db/schema.rb
.dotest .dotest
public/javascripts/cache
public/stylesheets/cache

View file

@ -32,13 +32,13 @@ class ApplicationController < ActionController::Base
before_filter :set_time_zone before_filter :set_time_zone
prepend_before_filter :login_required prepend_before_filter :login_required
prepend_before_filter :enable_mobile_content_negotiation prepend_before_filter :enable_mobile_content_negotiation
after_filter :restore_content_type_for_mobile
after_filter :set_charset after_filter :set_charset
include ActionView::Helpers::TextHelper include ActionView::Helpers::TextHelper
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::SanitizeHelper
extend ActionView::Helpers::SanitizeHelper::ClassMethods
helper_method :format_date, :markdown helper_method :format_date, :markdown
# By default, sets the charset to UTF-8 if it isn't already set # By default, sets the charset to UTF-8 if it isn't already set
@ -148,18 +148,12 @@ class ApplicationController < ActionController::Base
# during the processing and then setting it to 'text/html' in an # during the processing and then setting it to 'text/html' in an
# 'after_filter' -LKM 2007-04-01 # 'after_filter' -LKM 2007-04-01
def mobile? def mobile?
return params[:format] == 'm' || response.content_type == MOBILE_CONTENT_TYPE return params[:format] == 'm'
end end
def enable_mobile_content_negotiation def enable_mobile_content_negotiation
if mobile? if mobile?
request.accepts.unshift(Mime::Type::lookup(MOBILE_CONTENT_TYPE)) request.format = :m
end
end
def restore_content_type_for_mobile
if mobile?
response.content_type = 'text/html'
end end
end end
@ -234,8 +228,13 @@ class ApplicationController < ActionController::Base
end end
def init_data_for_sidebar def init_data_for_sidebar
@projects = @projects || current_user.projects.find(:all, :include => [:default_context ]) @completed_projects = current_user.projects.completed
@contexts = @contexts || current_user.contexts @hidden_projects = current_user.projects.hidden
@active_projects = current_user.projects.active
@active_contexts = current_user.contexts.active
@hidden_contexts = current_user.contexts.hidden
init_not_done_counts init_not_done_counts
if prefs.show_hidden_projects_in_sidebar if prefs.show_hidden_projects_in_sidebar
init_project_hidden_todo_counts(['project']) init_project_hidden_todo_counts(['project'])
@ -244,13 +243,13 @@ class ApplicationController < ActionController::Base
def init_not_done_counts(parents = ['project','context']) def init_not_done_counts(parents = ['project','context'])
parents.each do |parent| parents.each do |parent|
eval("@#{parent}_not_done_counts = @#{parent}_not_done_counts || Todo.count(:conditions => ['user_id = ? and state = ?', current_user.id, 'active'], :group => :#{parent}_id)") eval("@#{parent}_not_done_counts = @#{parent}_not_done_counts || current_user.todos.active.count(:group => :#{parent}_id)")
end end
end end
def init_project_hidden_todo_counts(parents = ['project','context']) def init_project_hidden_todo_counts(parents = ['project','context'])
parents.each do |parent| parents.each do |parent|
eval("@#{parent}_project_hidden_todo_counts = @#{parent}_project_hidden_todo_counts || Todo.count(:conditions => ['user_id = ? and (state = ? or state = ?)', current_user.id, 'project_hidden', 'active'], :group => :#{parent}_id)") eval("@#{parent}_project_hidden_todo_counts = @#{parent}_project_hidden_todo_counts || current_user.todos.count(:conditions => ['state = ? or state = ?', 'project_hidden', 'active'], :group => :#{parent}_id)")
end end
end end

View file

@ -201,6 +201,9 @@ class ContextsController < ApplicationController
:conditions => ['todos.state = ? AND (todos.project_id IS ? OR projects.state = ?)', 'active', nil, 'active'], :conditions => ['todos.state = ? AND (todos.project_id IS ? OR projects.state = ?)', 'active', nil, 'active'],
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC", :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
:include => [:project, :tags]) :include => [:project, :tags])
@projects = current_user.projects
@count = @not_done_todos.size @count = @not_done_todos.size
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json @default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
end end

View file

@ -76,7 +76,7 @@ class TodosController < ApplicationController
format.m do format.m do
@return_path=cookies[:mobile_url] @return_path=cookies[:mobile_url]
# todo: use function for this fixed path # todo: use function for this fixed path
@return_path='/mobile' if @return_path.nil? @return_path='/m' if @return_path.nil?
if @saved if @saved
redirect_to @return_path redirect_to @return_path
else else
@ -400,7 +400,8 @@ class TodosController < ApplicationController
:conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'completed'], :conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'completed'],
:order => 'todos.completed_at DESC') :order => 'todos.completed_at DESC')
@contexts = current_user.contexts.find(:all) @projects = current_user.projects
@contexts = current_user.contexts
@contexts_to_show = @contexts.reject {|x| x.hide? } @contexts_to_show = @contexts.reject {|x| x.hide? }
# Set count badge to number of items with this tag # Set count badge to number of items with this tag

View file

@ -1,5 +1,5 @@
module NotesHelper module NotesHelper
def truncated_note(note, characters = 50) def truncated_note(note, characters = 50)
sanitize(textilize_without_paragraph(truncate(note.body, characters, "..."))) sanitize(textilize_without_paragraph(truncate(note.body, :length => characters, :omission => "...")))
end end
end end

View file

@ -20,12 +20,12 @@ module ProjectsHelper
def project_next_prev def project_next_prev
html = '' html = ''
unless @previous_project.nil? unless @previous_project.nil?
project_name = truncate(@previous_project.name, 40, "...") project_name = truncate(@previous_project.name, :length => 40, :omission => "...")
html << link_to_project(@previous_project, "&laquo; #{project_name}") html << link_to_project(@previous_project, "&laquo; #{project_name}")
end end
html << ' | ' if @previous_project && @next_project html << ' | ' if @previous_project && @next_project
unless @next_project.nil? unless @next_project.nil?
project_name = truncate(@next_project.name, 40, "...") project_name = truncate(@next_project.name, :length => 40, :omission => "...")
html << link_to_project(@next_project, "#{project_name} &raquo;") html << link_to_project(@next_project, "#{project_name} &raquo;")
end end
html html
@ -34,12 +34,12 @@ module ProjectsHelper
def project_next_prev_mobile def project_next_prev_mobile
html = '' html = ''
unless @previous_project.nil? unless @previous_project.nil?
project_name = truncate(@previous_project.name, 40, "...") project_name = truncate(@previous_project.name, :length => 40, :omission => "...")
html << link_to_project_mobile(@previous_project, "5", "&laquo; 5-#{project_name}") html << link_to_project_mobile(@previous_project, "5", "&laquo; 5-#{project_name}")
end end
html << ' | ' if @previous_project && @next_project html << ' | ' if @previous_project && @next_project
unless @next_project.nil? unless @next_project.nil?
project_name = truncate(@next_project.name, 40, "...") project_name = truncate(@next_project.name, :length => 40, :omission => "...")
html << link_to_project_mobile(@next_project, "6", "6-#{project_name} &raquo;") html << link_to_project_mobile(@next_project, "6", "6-#{project_name} &raquo;")
end end
html html

View file

@ -117,7 +117,7 @@ module TodosHelper
"<span class=\"tag\">" + "<span class=\"tag\">" +
link_to(t.name, {:action => "tag", :controller => "todos", :id => t.name+".m"}) + link_to(t.name, {:action => "tag", :controller => "todos", :id => t.name+".m"}) +
"</span>"}.join('') "</span>"}.join('')
"<span class=\"tags\">#{tag_list}</span>" if not tag_list.empty? then "<span class=\"tags\">#{tag_list}</span>" end
end end
def deferred_due_date def deferred_due_date
@ -242,13 +242,13 @@ module TodosHelper
end end
def project_names_for_autocomplete def project_names_for_autocomplete
array_or_string_for_javascript( ['None'] + @projects.select{ |p| p.active? }.collect{|p| escape_javascript(p.name) } ) array_or_string_for_javascript( ['None'] + current_user.projects.active.collect{|p| escape_javascript(p.name) } )
end end
def context_names_for_autocomplete def context_names_for_autocomplete
# #return array_or_string_for_javascript(['Create a new context']) if # #return array_or_string_for_javascript(['Create a new context']) if
# @contexts.empty? # @contexts.empty?
array_or_string_for_javascript( @contexts.collect{|c| escape_javascript(c.name) } ) array_or_string_for_javascript( current_user.contexts.collect{|c| escape_javascript(c.name) } )
end end
def format_ical_notes(notes) def format_ical_notes(notes)

View file

@ -1,5 +1,7 @@
class MessageGateway < ActionMailer::Base class MessageGateway < ActionMailer::Base
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::SanitizeHelper
extend ActionView::Helpers::SanitizeHelper::ClassMethods
def receive(email) def receive(email)
user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip]) user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip])
if user.nil? if user.nil?

View file

@ -188,7 +188,7 @@ class User < ActiveRecord::Base
end end
def at_midnight(date) def at_midnight(date)
return TimeZone[prefs.time_zone].local(date.year, date.month, date.day, 0, 0, 0) return ActiveSupport::TimeZone[prefs.time_zone].local(date.year, date.month, date.day, 0, 0, 0)
end end
def generate_token def generate_token

View file

@ -6,7 +6,7 @@ if not @not_done.empty?
# only show a context when there are actions in it # only show a context when there are actions in it
-%> -%>
<h2><%=mobile_context.name%></h2> <h2><%=mobile_context.name%></h2>
<table cellpadding="0" cellspacing="0" border="0"> <table cellpadding="0" cellspacing="0" border="0" class="c">
<%= render :partial => "todos/mobile_todo", <%= render :partial => "todos/mobile_todo",
:collection => @not_done, :collection => @not_done,
:locals => { :parent_container_type => "context" }-%> :locals => { :parent_container_type => "context" }-%>

View file

@ -8,5 +8,5 @@
<div id="input_box"> <div id="input_box">
<%= render :partial => "shared/add_new_item_form" %> <%= render :partial => "shared/add_new_item_form" %>
<%= render "sidebar/sidebar" %> <%= render :template => "sidebar/sidebar" %>
</div><!-- End of input box --> </div><!-- End of input box -->

View file

@ -123,7 +123,7 @@
</div><!-- End of display_box --> </div><!-- End of display_box -->
<div id="input_box"> <div id="input_box">
<%= render "sidebar/sidebar" %> <%= render :template => "sidebar/sidebar" %>
</div><!-- End of input box --> </div><!-- End of input box -->
<script type="text/javascript"> <script type="text/javascript">

View file

@ -5,9 +5,16 @@
<% if @prefs.refresh != 0 -%> <% if @prefs.refresh != 0 -%>
<meta http-equiv="Refresh" content="<%= @prefs["refresh"].to_i*60 %>;url=<%= request.request_uri %>"> <meta http-equiv="Refresh" content="<%= @prefs["refresh"].to_i*60 %>;url=<%= request.request_uri %>">
<% end -%> <% end -%>
<%= javascript_include_merged :tracks %> <% bundle do %>
<%= javascript_include_tag *%w[
prototype effects dragdrop controls application
calendar calendar-en calendar-setup
accesskey-hints todo-items niftycube
protoload flashobject lowpro
] %>
<%= stylesheet_link_tag *%w[ standard calendar-system niftyCorners] %>
<% end %>
<%= javascript_include_tag :unobtrusive %> <%= javascript_include_tag :unobtrusive %>
<%= stylesheet_link_merged :tracks %>
<%= stylesheet_link_tag "print", :media => "print" %> <%= stylesheet_link_tag "print", :media => "print" %>
<link rel="shortcut icon" href="<%= url_for(:controller => 'favicon.ico') %>" /> <link rel="shortcut icon" href="<%= url_for(:controller => 'favicon.ico') %>" />

View file

@ -1,6 +1,6 @@
<div class="page_name_auto_complete" id="default_context_list" style="display:none;z-index:9999"></div> <div class="page_name_auto_complete" id="default_context_list" style="display:none;z-index:9999"></div>
<script type="text/javascript"> <script type="text/javascript">
defaultContextAutoCompleter = new Autocompleter.Local('project[default_context_name]', 'default_context_list', <%= context_names_for_autocomplete %>, {choices:100,autoSelect:false}); defaultContextAutoCompleter = new Autocompleter.Local('project_default_context_name', 'default_context_list', <%= context_names_for_autocomplete %>, {choices:100,autoSelect:false});
Event.observe($('project[default_context_name]'), "focus", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter)); Event.observe($('project_default_context_name'), "focus", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter));
Event.observe($('project[default_context_name]'), "click", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter)); Event.observe($('project_default_context_name'), "click", defaultContextAutoCompleter.activate.bind(defaultContextAutoCompleter));
</script> </script>

View file

@ -74,5 +74,5 @@
<div id="input_box"> <div id="input_box">
<%= render :partial => "shared/add_new_item_form" %> <%= render :partial => "shared/add_new_item_form" %>
<%= render "sidebar/sidebar" %> <%= render :template => "sidebar/sidebar" %>
</div><!-- End of input box --> </div><!-- End of input box -->

View file

@ -13,7 +13,7 @@ if @recurring_saved
page.hide 'recurring-todos-empty-nd' page.hide 'recurring-todos-empty-nd'
page.insert_html :bottom, page.insert_html :bottom,
'recurring_todos_container', 'recurring_todos_container',
:partial => 'recurring_todos/recurring_todo' :partial => 'recurring_todos/recurring_todo', :locals => { :recurring_todo => @recurring_todo}
page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3 page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
# update badge count # update badge count
page['badge_count'].replace_html @count page['badge_count'].replace_html @count

View file

@ -4,7 +4,7 @@ if @saved
if @recurring_todo.completed? if @recurring_todo.completed?
# show completed recurring todo # show completed recurring todo
page.insert_html :top, "completed_recurring_todos_container", :partial => 'recurring_todos/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 page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
# set empty messages # set empty messages
@ -14,7 +14,7 @@ if @saved
# recurring_todo is activated # recurring_todo is activated
# show completed recurring todo # show completed recurring todo
page.insert_html :top, "recurring_todos_container", :partial => 'recurring_todos/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 page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
# inform user if a new todo has been created because of the activation # inform user if a new todo has been created because of the activation

View file

@ -13,7 +13,7 @@ if @saved
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}; projectAutoCompleter.changed = true" if @new_project_created page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}; projectAutoCompleter.changed = true" if @new_project_created
# replace old recurring todo with updated todo # replace old recurring todo with updated todo
page.replace dom_id(@recurring_todo), :partial => 'recurring_todos/recurring_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 page.visual_effect :highlight, dom_id(@recurring_todo), :duration => 3
else else

View file

@ -2,7 +2,7 @@
@todo = nil @todo = nil
@initial_context_name = @context.name unless @context.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 ||= @project.default_context.name unless @project.nil? || @project.default_context.nil?
@initial_context_name ||= @contexts[0].name unless @contexts[0].nil? @initial_context_name ||= current_user.contexts.first.name unless current_user.contexts.first.nil?
@initial_project_name = @project.name unless @project.nil? @initial_project_name = @project.name unless @project.nil?
%> %>
<div id="todo_new_action_container"> <div id="todo_new_action_container">

View file

@ -3,28 +3,28 @@
<%= render :partial => "sidebar/project_list", <%= render :partial => "sidebar/project_list",
:locals => { :list_name => 'Active Projects', :locals => { :list_name => 'Active Projects',
:projects => @projects.select{|p| p.active? } } -%> :projects => @active_projects } -%>
<%= render :partial => "sidebar/context_list", <%= render :partial => "sidebar/context_list",
:locals => { :list_name => 'Active Contexts', :locals => { :list_name => 'Active Contexts',
:contexts => @contexts.reject{|c| c.hide? } } -%> :contexts => @active_contexts } -%>
<% if prefs.show_hidden_projects_in_sidebar -%> <% if prefs.show_hidden_projects_in_sidebar -%>
<%= render :partial => "sidebar/project_list", <%= render :partial => "sidebar/project_list",
:locals => { :list_name => 'Hidden Projects', :locals => { :list_name => 'Hidden Projects',
:projects => @projects.select{|p| p.hidden? } } -%> :projects => @hidden_projects } -%>
<% end -%> <% end -%>
<% if prefs.show_completed_projects_in_sidebar -%> <% if prefs.show_completed_projects_in_sidebar -%>
<%= render :partial => "sidebar/project_list", <%= render :partial => "sidebar/project_list",
:locals => { :list_name => 'Completed Projects', :locals => { :list_name => 'Completed Projects',
:projects => @projects.select{|p| p.completed? } } -%> :projects => @completed_projects } -%>
<% end -%> <% end -%>
<% if prefs.show_hidden_contexts_in_sidebar -%> <% if prefs.show_hidden_contexts_in_sidebar -%>
<%= render :partial => "sidebar/context_list", <%= render :partial => "sidebar/context_list",
:locals => { :list_name => 'Hidden Contexts', :locals => { :list_name => 'Hidden Contexts',
:contexts => @contexts.select{|c| c.hide? } } -%> :contexts => @hidden_contexts } -%>
<% end -%> <% end -%>
<div class="integrations-link"><ul> <div class="integrations-link"><ul>

View file

@ -8,9 +8,9 @@ end
-%><%=@actions_per_context[@actions_per_context.size()-1]['total'].to_i*100/@sum%>& -%><%=@actions_per_context[@actions_per_context.size()-1]['total'].to_i*100/@sum%>&
&pie_labels=<% &pie_labels=<%
0.upto @actions_per_context.size()-2 do | i | 0.upto @actions_per_context.size()-2 do | i |
%><%=truncate(@actions_per_context[i]['name'],@truncate_chars, '...')%>,<% %><%=truncate(@actions_per_context[i]['name'], :length => @truncate_chars, :omission => '...')%>,<%
end end
-%><%=truncate(@actions_per_context[@actions_per_context.size()-1]['name'],@truncate_chars,'...') %>& -%><%=truncate(@actions_per_context[@actions_per_context.size()-1]['name'], :legnth => @truncate_chars, :omission => '...') %>&
&links=<% &links=<%
0.upto @actions_per_context.size()-2 do | i | 0.upto @actions_per_context.size()-2 do | i |
%><%=url_for :controller => "contexts", :action => "show", :id=>@actions_per_context[i]['id']%>,<% %><%=url_for :controller => "contexts", :action => "show", :id=>@actions_per_context[i]['id']%>,<%

View file

@ -8,9 +8,9 @@ end
-%><%=@actions_per_context[@actions_per_context.size()-1]['total'].to_i*100/@sum%>& -%><%=@actions_per_context[@actions_per_context.size()-1]['total'].to_i*100/@sum%>&
&pie_labels=<% &pie_labels=<%
0.upto @actions_per_context.size()-2 do | i | 0.upto @actions_per_context.size()-2 do | i |
%><%=truncate(@actions_per_context[i]['name'], @truncate_chars, '...') %>,<% %><%=truncate(@actions_per_context[i]['name'], :length => @truncate_chars, :omission => '...') %>,<%
end end
-%><%=truncate(@actions_per_context[@actions_per_context.size()-1]['name'], @truncate_chars, '...') %>& -%><%=truncate(@actions_per_context[@actions_per_context.size()-1]['name'], :length => @truncate_chars, :omission => '...') %>&
&links=<% &links=<%
0.upto @actions_per_context.size()-2 do | i | 0.upto @actions_per_context.size()-2 do | i |
%><%=url_for :controller => "contexts", :action => "show", :id=>@actions_per_context[i]['id']%>,<% %><%=url_for :controller => "contexts", :action => "show", :id=>@actions_per_context[i]['id']%>,<%

View file

@ -4,8 +4,7 @@ if mobile_todo.starred?
else else
bullet = "<span class=r>&raquo;&nbsp;</span>" bullet = "<span class=r>&raquo;&nbsp;</span>"
end -%> end -%>
<div class="t" id="<%= dom_id(mobile_todo) %>"> <tr class="t" id="<%= dom_id(mobile_todo) %>" valign="top"><td><%= bullet %></td><td><%
<tr valign="top"><td><%= bullet %></td><td><%
if mobile_todo.completed? if mobile_todo.completed?
-%><span class="m_t_d"> -%><span class="m_t_d">
<% else <% else
@ -23,4 +22,4 @@ end -%>
")</span>" -%> ")</span>" -%>
<% end -%> <% end -%>
<%= tag_list_mobile -%> <%= tag_list_mobile -%>
</span></td></tr></div> </span></td></tr>

View file

@ -16,7 +16,7 @@ if @saved
page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @todo.context, :collapsible => true } page.insert_html :top, 'display_box', :partial => 'contexts/context', :locals => { :context => @todo.context, :collapsible => true }
else else
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" if source_view_is_one_of(:todo, :deferred) page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" if source_view_is_one_of(:todo, :deferred)
page.insert_html :bottom, item_container_id(@todo) + 'items', :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type, :source_view => @source_view } page.insert_html :bottom, item_container_id(@todo) + 'items', :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.visual_effect :highlight, dom_id(@todo), :duration => 3
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil? page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
end end

View file

@ -10,5 +10,7 @@
<div id="input_box"> <div id="input_box">
<%= render :partial => "shared/add_new_item_form" %> <%= render :partial => "shared/add_new_item_form" %>
<%= render "sidebar/sidebar" %> <%- # 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" %>
</div><!-- End of input box --> </div><!-- End of input box -->

View file

@ -11,5 +11,5 @@
<div id="input_box"> <div id="input_box">
<%= render :partial => "shared/add_new_item_form" %> <%= render :partial => "shared/add_new_item_form" %>
<%= render "sidebar/sidebar" %> <%= render :template => "sidebar/sidebar" %>
</div><!-- End of input box --> </div><!-- End of input box -->

View file

@ -23,5 +23,5 @@
<div id="input_box"> <div id="input_box">
<%= render :partial => "shared/add_new_item_form" %> <%= render :partial => "shared/add_new_item_form" %>
<%= render "sidebar/sidebar" %> <%= render :template => "sidebar/sidebar" %>
</div><!-- End of input box --> </div><!-- End of input box -->

View file

@ -5,7 +5,7 @@ if @saved
# completed todos move from their context to the completed container # completed todos move from their context to the completed container
unless @prefs.hide_completed_actions? unless @prefs.hide_completed_actions?
page.insert_html :top, "completed_containeritems", :partial => 'todos/todo', :locals => { :parent_container_type => "completed" } 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.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[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 page.show 'tickler-empty-nd' if source_view_is(:project) && @deferred_count == 0
@ -33,7 +33,7 @@ if @saved
else else
# todo is activated from completed container # todo is activated from completed container
page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@todo) page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@todo)
page.insert_html :bottom, item_container_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type } 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.visual_effect :highlight, dom_id(@todo, 'line'), {'startcolor' => "'#99ff99'"}
page.show "empty-d" if @completed_count == 0 page.show "empty-d" if @completed_count == 0
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 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

View file

@ -36,7 +36,7 @@ if @saved
page.call "todoItems.expandNextActionListingByContext", "c#{@todo.context_id}items", true 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[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
# show all todos in context # show all todos in context
page.insert_html :bottom, "c#{@todo.context_id}items", :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type } page.insert_html :bottom, "c#{@todo.context_id}items", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
end end
# update badge count # update badge count
@ -52,7 +52,7 @@ if @saved
end end
end end
else else
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type } 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 page.visual_effect :highlight, dom_id(@todo), :duration => 3
end end
elsif source_view_is :project elsif source_view_is :project
@ -63,17 +63,17 @@ if @saved
elsif @todo.deferred? elsif @todo.deferred?
page[@todo].remove page[@todo].remove
page.show("p#{@original_item_project_id}empty-nd") if (@remaining_undone_in_project == 0) page.show("p#{@original_item_project_id}empty-nd") if (@remaining_undone_in_project == 0)
page.insert_html :bottom, "tickler", :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type } page.insert_html :bottom, "tickler", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
page['tickler-empty-nd'].hide page['tickler-empty-nd'].hide
page.replace_html "badge_count", @down_count page.replace_html "badge_count", @down_count
elsif @todo_was_activated_from_deferred_state elsif @todo_was_activated_from_deferred_state
page[@todo].remove page[@todo].remove
page['tickler-empty-nd'].show if (@deferred_count == 0) page['tickler-empty-nd'].show if (@deferred_count == 0)
page.insert_html :bottom, "p#{@todo.project_id}", :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type } page.insert_html :bottom, "p#{@todo.project_id}", :partial => 'todos/todo', :locals => { :todo => @todo, :parent_container_type => parent_container_type }
page["p#{@todo.project_id}empty-nd"].hide page["p#{@todo.project_id}empty-nd"].hide
page.replace_html "badge_count", @down_count page.replace_html "badge_count", @down_count
else else
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type } 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 page.visual_effect :highlight, dom_id(@todo), :duration => 3
end end
elsif source_view_is :deferred elsif source_view_is :deferred
@ -87,7 +87,7 @@ if @saved
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}" page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@todo.context_id}"
page.call "todoItems.expandNextActionListingByContext", "c#{@todo.context_id}items", true 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[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 => { :parent_container_type => parent_container_type } 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.replace_html("badge_count", @down_count)
page.delay(0.5) do page.delay(0.5) do
page.call "todoItems.ensureContainerHeight", "c#{@original_item_context_id}items" page.call "todoItems.ensureContainerHeight", "c#{@original_item_context_id}items"
@ -95,18 +95,18 @@ if @saved
page.visual_effect :highlight, dom_id(@todo), :duration => 3 page.visual_effect :highlight, dom_id(@todo), :duration => 3
end end
else else
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type } 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 page.visual_effect :highlight, dom_id(@todo), :duration => 3
end end
elsif source_view_is :stats elsif source_view_is :stats
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type } 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 page.visual_effect :highlight, dom_id(@todo), :duration => 3
elsif source_view_is :calendar elsif source_view_is :calendar
if @due_date_changed if @due_date_changed
page[@todo].remove page[@todo].remove
page.show "empty_"+@original_item_due_id if @old_due_empty page.show "empty_"+@original_item_due_id if @old_due_empty
page.hide "empty_"+@new_due_id page.hide "empty_"+@new_due_id
page.insert_html :bottom, @new_due_id, :partial => 'todos/todo' page.insert_html :bottom, @new_due_id, :partial => 'todos/todo', :locals => {:todo => @todo}
page.visual_effect :highlight, dom_id(@todo), :duration => 3 page.visual_effect :highlight, dom_id(@todo), :duration => 3
else else
if @todo.due.nil? if @todo.due.nil?
@ -114,7 +114,7 @@ if @saved
page[@todo].remove page[@todo].remove
page.show "empty_"+@original_item_due_id if @old_due_empty page.show "empty_"+@original_item_due_id if @old_due_empty
else else
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type } 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 page.visual_effect :highlight, dom_id(@todo), :duration => 3
end end
end end

View file

@ -1,22 +0,0 @@
---
javascripts:
- tracks:
- prototype
- effects
- dragdrop
- controls
- application
- calendar
- calendar-en
- calendar-setup
- accesskey-hints
- todo-items
- niftycube
- protoload
- flashobject
- lowpro
stylesheets:
- tracks:
- standard
- calendar-system
- niftyCorners

View file

@ -67,7 +67,7 @@ module Rails
class << self class << self
def rubygems_version def rubygems_version
Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion Gem::RubyGemsVersion rescue nil
end end
def gem_version def gem_version
@ -82,14 +82,14 @@ module Rails
def load_rubygems def load_rubygems
require 'rubygems' require 'rubygems'
min_version = '1.3.1'
unless rubygems_version >= '0.9.4' unless rubygems_version >= min_version
$stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.) $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
exit 1 exit 1
end end
rescue LoadError rescue LoadError
$stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org) $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
exit 1 exit 1
end end

View file

@ -15,6 +15,10 @@ class Rails::Configuration
attr_accessor :action_web_service attr_accessor :action_web_service
end end
# Leave this alone or set it to one or more of ['database', 'ldap', 'open_id'].
# If you choose ldap, see the additional configuration options further down.
AUTHENTICATION_SCHEMES = ['database']
Rails::Initializer.run do |config| Rails::Initializer.run do |config|
# Skip frameworks you're not going to use # Skip frameworks you're not going to use
# config.frameworks -= [ :action_web_service, :action_mailer ] # config.frameworks -= [ :action_web_service, :action_mailer ]
@ -22,6 +26,8 @@ Rails::Initializer.run do |config|
config.action_web_service = Rails::OrderedOptions.new config.action_web_service = Rails::OrderedOptions.new
config.load_paths += %W( #{RAILS_ROOT}/app/apis ) config.load_paths += %W( #{RAILS_ROOT}/app/apis )
config.action_controller.use_accept_header = true
# Add additional load paths for your own custom dirs # Add additional load paths for your own custom dirs
# config.load_paths += %W( #{RAILS_ROOT}/app/services ) # config.load_paths += %W( #{RAILS_ROOT}/app/services )
@ -33,10 +39,10 @@ Rails::Initializer.run do |config|
# (create the session table with 'rake create_sessions_table') # (create the session table with 'rake create_sessions_table')
config.action_controller.session_store = :active_record_store config.action_controller.session_store = :active_record_store
# config.action_controller.session = { config.action_controller.session = {
# :session_key => '_tracks_session_id', :session_key => '_tracks_session_id',
# :secret => SALT * (30.0 / SALT.length).ceil #must be at least 30 characters :secret => SALT * (30.0 / SALT.length).ceil #must be at least 30 characters
# } }
# Enable page/fragment caching by setting a file-based store # Enable page/fragment caching by setting a file-based store
# (remember to create the caching directory and make it readable to the application) # (remember to create the caching directory and make it readable to the application)
@ -70,9 +76,6 @@ end
# Include your application configuration below # Include your application configuration below
# Leave this alone or set it to one or more of ['database', 'ldap', 'open_id'].
# If you choose ldap, see the additional configuration options further down.
AUTHENTICATION_SCHEMES = ['database']
require 'name_part_finder' require 'name_part_finder'
require 'tracks/todo_list' require 'tracks/todo_list'
@ -96,9 +99,6 @@ end
# setting this to true will make the cookies only available over HTTPS # setting this to true will make the cookies only available over HTTPS
TRACKS_COOKIES_SECURE = false TRACKS_COOKIES_SECURE = false
MOBILE_CONTENT_TYPE = 'tracks/mobile'
Mime::Type.register(MOBILE_CONTENT_TYPE, :m)
tracks_version='1.7-devel' tracks_version='1.7-devel'
# comment out next two lines if you do not want (or can not) the date of the # comment out next two lines if you do not want (or can not) the date of the

View file

@ -1,3 +1,4 @@
# Add new mime types for use in respond_to blocks: # Add new mime types for use in respond_to blocks:
# Mime::Type.register "text/richtext", :rtf # Mime::Type.register "text/richtext", :rtf
# Mime::Type.register "application/x-mobile", :mobile # Mime::Type.register "application/x-mobile", :mobile
Mime::Type.register_alias "text/html", :m

View file

@ -2,7 +2,7 @@ namespace :tracks do
desc 'Replace the password of USER with a new one.' desc 'Replace the password of USER with a new one.'
task :password => :environment do task :password => :environment do
Dependencies.load_paths.unshift(File.dirname(__FILE__) + "/..../vendor/gems/highline-1.4.0/lib") Dependencies.load_paths.unshift(File.dirname(__FILE__) + "/../../vendor/gems/highline-1.5.0/lib")
require "highline/import" require "highline/import"
user = User.find_by_login(ENV['USER']) user = User.find_by_login(ENV['USER'])

View file

@ -1,10 +1,12 @@
raise "To avoid rake task loading problems: run 'rake clobber' in vendor/plugins/rspec" if File.directory?(File.join(File.dirname(__FILE__), *%w[.. .. vendor plugins rspec pkg]))
raise "To avoid rake task loading problems: run 'rake clobber' in vendor/plugins/rspec-rails" if File.directory?(File.join(File.dirname(__FILE__), *%w[.. .. vendor plugins rspec-rails pkg]))
# In rails 1.2, plugins aren't available in the path until they're loaded. # In rails 1.2, plugins aren't available in the path until they're loaded.
# Check to see if the rspec plugin is installed first and require # Check to see if the rspec plugin is installed first and require
# it if it is. If not, use the gem version. # it if it is. If not, use the gem version.
rspec_base = File.expand_path(File.dirname(__FILE__) + '/../../rspec/lib') rspec_base = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec/lib')
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base) $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
require 'spec/rake/spectask' require 'spec/rake/spectask'
require 'spec/translator'
spec_prereq = File.exist?(File.join(RAILS_ROOT, 'config', 'database.yml')) ? "db:test:prepare" : :noop spec_prereq = File.exist?(File.join(RAILS_ROOT, 'config', 'database.yml')) ? "db:test:prepare" : :noop
task :noop do task :noop do
@ -64,13 +66,6 @@ namespace :spec do
end end
end end
desc "Translate/upgrade specs using the built-in translator"
task :translate do
translator = ::Spec::Translator.new
dir = RAILS_ROOT + '/spec'
translator.translate(dir, dir)
end
# Setup specs for stats # Setup specs for stats
task :statsetup do task :statsetup do
require 'code_statistics' require 'code_statistics'

View file

@ -1,6 +1,6 @@
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan) // (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com) // (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
// Contributors: // Contributors:
// Richard Livsey // Richard Livsey
// Rahul Bhargava // Rahul Bhargava
@ -37,10 +37,10 @@
if(typeof Effect == 'undefined') if(typeof Effect == 'undefined')
throw("controls.js requires including script.aculo.us' effects.js library"); throw("controls.js requires including script.aculo.us' effects.js library");
var Autocompleter = { } var Autocompleter = { };
Autocompleter.Base = Class.create({ Autocompleter.Base = Class.create({
baseInitialize: function(element, update, options) { baseInitialize: function(element, update, options) {
element = $(element) element = $(element);
this.element = element; this.element = element;
this.update = $(update); this.update = $(update);
this.hasFocus = false; this.hasFocus = false;
@ -209,13 +209,13 @@ Autocompleter.Base = Class.create({
}, },
markPrevious: function() { markPrevious: function() {
if(this.index > 0) this.index-- if(this.index > 0) this.index--;
else this.index = this.entryCount-1; else this.index = this.entryCount-1;
this.getEntry(this.index).scrollIntoView(false); this.getEntry(this.index).scrollIntoView(true);
}, },
markNext: function() { markNext: function() {
if(this.index < this.entryCount-1) this.index++ if(this.index < this.entryCount-1) this.index++;
else this.index = 0; else this.index = 0;
this.getEntry(this.index).scrollIntoView(false); this.getEntry(this.index).scrollIntoView(false);
}, },
@ -457,7 +457,7 @@ Autocompleter.Local = Class.create(Autocompleter.Base, {
} }
} }
if (partial.length) if (partial.length)
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
return "<ul>" + ret.join('') + "</ul>"; return "<ul>" + ret.join('') + "</ul>";
} }
}, options || { }); }, options || { });
@ -474,7 +474,7 @@ Field.scrollFreeActivate = function(field) {
setTimeout(function() { setTimeout(function() {
Field.activate(field); Field.activate(field);
}, 1); }, 1);
} };
Ajax.InPlaceEditor = Class.create({ Ajax.InPlaceEditor = Class.create({
initialize: function(element, url, options) { initialize: function(element, url, options) {
@ -604,7 +604,7 @@ Ajax.InPlaceEditor = Class.create({
this.triggerCallback('onEnterHover'); this.triggerCallback('onEnterHover');
}, },
getText: function() { getText: function() {
return this.element.innerHTML; return this.element.innerHTML.unescapeHTML();
}, },
handleAJAXFailure: function(transport) { handleAJAXFailure: function(transport) {
this.triggerCallback('onFailure', transport); this.triggerCallback('onFailure', transport);
@ -780,7 +780,7 @@ Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
onSuccess: function(transport) { onSuccess: function(transport) {
var js = transport.responseText.strip(); var js = transport.responseText.strip();
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
throw 'Server returned an invalid collection representation.'; throw('Server returned an invalid collection representation.');
this._collection = eval(js); this._collection = eval(js);
this.checkForExternalText(); this.checkForExternalText();
}.bind(this), }.bind(this),

View file

@ -1,5 +1,5 @@
// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) // Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) // (c) 2005-2008 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. // 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/ // For details, see the script.aculo.us web site: http://script.aculo.us/
@ -121,7 +121,7 @@ var Droppables = {
if(this.last_active) if(this.last_active)
this.deactivate(this.last_active); this.deactivate(this.last_active);
} }
} };
var Draggables = { var Draggables = {
drags: [], drags: [],
@ -218,7 +218,7 @@ var Draggables = {
).length; ).length;
}); });
} }
} };
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@ -331,8 +331,8 @@ var Draggable = Class.create({
if(this.options.ghosting) { if(this.options.ghosting) {
this._clone = this.element.cloneNode(true); this._clone = this.element.cloneNode(true);
this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
if (!this.element._originallyAbsolute) if (!this._originallyAbsolute)
Position.absolutize(this.element); Position.absolutize(this.element);
this.element.parentNode.insertBefore(this._clone, this.element); this.element.parentNode.insertBefore(this._clone, this.element);
} }
@ -403,9 +403,9 @@ var Draggable = Class.create({
} }
if(this.options.ghosting) { if(this.options.ghosting) {
if (!this.element._originallyAbsolute) if (!this._originallyAbsolute)
Position.relativize(this.element); Position.relativize(this.element);
delete this.element._originallyAbsolute; delete this._originallyAbsolute;
Element.remove(this._clone); Element.remove(this._clone);
this._clone = null; this._clone = null;
} }
@ -478,10 +478,10 @@ var Draggable = Class.create({
} else { } else {
if(Object.isArray(this.options.snap)) { if(Object.isArray(this.options.snap)) {
p = p.map( function(v, i) { p = p.map( function(v, i) {
return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)) return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
} else { } else {
p = p.map( function(v) { p = p.map( function(v) {
return (v/this.options.snap).round()*this.options.snap }.bind(this)) return (v/this.options.snap).round()*this.options.snap }.bind(this));
} }
}} }}
@ -560,7 +560,7 @@ var Draggable = Class.create({
H = documentElement.clientHeight; H = documentElement.clientHeight;
} else { } else {
W = body.offsetWidth; W = body.offsetWidth;
H = body.offsetHeight H = body.offsetHeight;
} }
} }
return { top: T, left: L, width: W, height: H }; return { top: T, left: L, width: W, height: H };
@ -608,7 +608,8 @@ var Sortable = {
}, },
destroy: function(element){ destroy: function(element){
var s = Sortable.options(element); element = $(element);
var s = Sortable.sortables[element.id];
if(s) { if(s) {
Draggables.removeObserver(s.element); Draggables.removeObserver(s.element);
@ -689,14 +690,14 @@ var Sortable = {
tree: options.tree, tree: options.tree,
hoverclass: options.hoverclass, hoverclass: options.hoverclass,
onHover: Sortable.onHover onHover: Sortable.onHover
} };
var options_for_tree = { var options_for_tree = {
onHover: Sortable.onEmptyHover, onHover: Sortable.onEmptyHover,
overlap: options.overlap, overlap: options.overlap,
containment: options.containment, containment: options.containment,
hoverclass: options.hoverclass hoverclass: options.hoverclass
} };
// fix for gecko engine // fix for gecko engine
Element.cleanWhitespace(element); Element.cleanWhitespace(element);
@ -851,11 +852,11 @@ var Sortable = {
children: [], children: [],
position: parent.children.length, position: parent.children.length,
container: $(children[i]).down(options.treeTag) container: $(children[i]).down(options.treeTag)
} };
/* Get the element containing the children and recurse over it */ /* Get the element containing the children and recurse over it */
if (child.container) if (child.container)
this._tree(child.container, options, child) this._tree(child.container, options, child);
parent.children.push (child); parent.children.push (child);
} }
@ -880,7 +881,7 @@ var Sortable = {
children: [], children: [],
container: element, container: element,
position: 0 position: 0
} };
return Sortable._tree(element, options, root); return Sortable._tree(element, options, root);
}, },
@ -940,14 +941,14 @@ var Sortable = {
}).join('&'); }).join('&');
} }
} }
} };
// Returns true if child is contained within element // Returns true if child is contained within element
Element.isParent = function(child, element) { Element.isParent = function(child, element) {
if (!child.parentNode || child == element) return false; if (!child.parentNode || child == element) return false;
if (child.parentNode == element) return true; if (child.parentNode == element) return true;
return Element.isParent(child.parentNode, element); return Element.isParent(child.parentNode, element);
} };
Element.findChildren = function(element, only, recursive, tagName) { Element.findChildren = function(element, only, recursive, tagName) {
if(!element.hasChildNodes()) return null; if(!element.hasChildNodes()) return null;
@ -965,8 +966,8 @@ Element.findChildren = function(element, only, recursive, tagName) {
}); });
return (elements.length>0 ? elements.flatten() : []); return (elements.length>0 ? elements.flatten() : []);
} };
Element.offsetSize = function (element, type) { Element.offsetSize = function (element, type) {
return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
} };

View file

@ -70,25 +70,20 @@ var Effect = {
Transitions: { Transitions: {
linear: Prototype.K, linear: Prototype.K,
sinoidal: function(pos) { sinoidal: function(pos) {
return (-Math.cos(pos*Math.PI)/2) + 0.5; return (-Math.cos(pos*Math.PI)/2) + .5;
}, },
reverse: function(pos) { reverse: function(pos) {
return 1-pos; return 1-pos;
}, },
flicker: function(pos) { flicker: function(pos) {
var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
return pos > 1 ? 1 : pos; return pos > 1 ? 1 : pos;
}, },
wobble: function(pos) { wobble: function(pos) {
return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
}, },
pulse: function(pos, pulses) { pulse: function(pos, pulses) {
pulses = pulses || 5; return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
return (
((pos % (1/pulses)) * pulses).round() == 0 ?
((pos * pulses * 2) - (pos * pulses * 2).floor()) :
1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
);
}, },
spring: function(pos) { spring: function(pos) {
return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
@ -249,18 +244,30 @@ Effect.Base = Class.create({
this.totalTime = this.finishOn-this.startOn; this.totalTime = this.finishOn-this.startOn;
this.totalFrames = this.options.fps*this.options.duration; this.totalFrames = this.options.fps*this.options.duration;
eval('this.render = function(pos){ '+ this.render = (function() {
'if (this.state=="idle"){this.state="running";'+ function dispatch(effect, eventName) {
codeForEvent(this.options,'beforeSetup')+ if (effect.options[eventName + 'Internal'])
(this.setup ? 'this.setup();':'')+ effect.options[eventName + 'Internal'](effect);
codeForEvent(this.options,'afterSetup')+ if (effect.options[eventName])
'};if (this.state=="running"){'+ effect.options[eventName](effect);
'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+ }
'this.position=pos;'+
codeForEvent(this.options,'beforeUpdate')+ return function(pos) {
(this.update ? 'this.update(pos);':'')+ if (this.state === "idle") {
codeForEvent(this.options,'afterUpdate')+ this.state = "running";
'}}'); dispatch(this, 'beforeSetup');
if (this.setup) this.setup();
dispatch(this, 'afterSetup');
}
if (this.state === "running") {
pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
this.position = pos;
dispatch(this, 'beforeUpdate');
if (this.update) this.update(pos);
dispatch(this, 'afterUpdate');
}
};
})();
this.event('beforeStart'); this.event('beforeStart');
if (!this.options.sync) if (!this.options.sync)
@ -508,16 +515,15 @@ Effect.Highlight = Class.create(Effect.Base, {
Effect.ScrollTo = function(element) { Effect.ScrollTo = function(element) {
var options = arguments[1] || { }, var options = arguments[1] || { },
scrollOffsets = document.viewport.getScrollOffsets(), scrollOffsets = document.viewport.getScrollOffsets(),
elementOffsets = $(element).cumulativeOffset(), elementOffsets = $(element).cumulativeOffset();
max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
if (options.offset) elementOffsets[1] += options.offset; if (options.offset) elementOffsets[1] += options.offset;
return new Effect.Tween(null, return new Effect.Tween(null,
scrollOffsets.top, scrollOffsets.top,
elementOffsets[1] > max ? max : elementOffsets[1], elementOffsets[1],
options, options,
function(p){ scrollTo(scrollOffsets.left, p.round()) } function(p){ scrollTo(scrollOffsets.left, p.round()); }
); );
}; };
@ -568,7 +574,7 @@ Effect.Puff = function(element) {
new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
Object.extend({ duration: 1.0, Object.extend({ duration: 1.0,
beforeSetupInternal: function(effect) { beforeSetupInternal: function(effect) {
Position.absolutize(effect.effects[0].element) Position.absolutize(effect.effects[0].element);
}, },
afterFinishInternal: function(effect) { afterFinishInternal: function(effect) {
effect.effects[0].element.hide().setStyle(oldStyle); } effect.effects[0].element.hide().setStyle(oldStyle); }
@ -625,7 +631,7 @@ Effect.SwitchOff = function(element) {
afterFinishInternal: function(effect) { afterFinishInternal: function(effect) {
effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
} }
}) });
} }
}, arguments[1] || { })); }, arguments[1] || { }));
}; };
@ -674,7 +680,7 @@ Effect.Shake = function(element) {
new Effect.Move(effect.element, new Effect.Move(effect.element,
{ x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
effect.element.undoPositioned().setStyle(oldStyle); effect.element.undoPositioned().setStyle(oldStyle);
}}) }}) }}) }}) }}) }}); }}); }}); }}); }}); }}); }});
}; };
Effect.SlideDown = function(element) { Effect.SlideDown = function(element) {
@ -816,7 +822,7 @@ Effect.Grow = function(element) {
effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
} }
}, options) }, options)
) );
} }
}); });
}; };
@ -877,11 +883,13 @@ Effect.Shrink = function(element) {
Effect.Pulsate = function(element) { Effect.Pulsate = function(element) {
element = $(element); element = $(element);
var options = arguments[1] || { }; var options = arguments[1] || { },
var oldOpacity = element.getInlineOpacity(); oldOpacity = element.getInlineOpacity(),
var transition = options.transition || Effect.Transitions.sinoidal; transition = options.transition || Effect.Transitions.linear,
var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; reverser = function(pos){
reverser.bind(transition); return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
};
return new Effect.Opacity(element, return new Effect.Opacity(element,
Object.extend(Object.extend({ duration: 2.0, from: 0, Object.extend(Object.extend({ duration: 2.0, from: 0,
afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
@ -934,7 +942,7 @@ Effect.Morph = Class.create(Effect.Base, {
effect.transforms.each(function(transform) { effect.transforms.each(function(transform) {
effect.element.style[transform.style] = ''; effect.element.style[transform.style] = '';
}); });
} };
} }
} }
this.start(options); this.start(options);
@ -945,7 +953,7 @@ Effect.Morph = Class.create(Effect.Base, {
if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
color = color.parseColor(); color = color.parseColor();
return $R(0,2).map(function(i){ return $R(0,2).map(function(i){
return parseInt( color.slice(i*2+1,i*2+3), 16 ) return parseInt( color.slice(i*2+1,i*2+3), 16 );
}); });
} }
this.transforms = this.style.map(function(pair){ this.transforms = this.style.map(function(pair){
@ -978,7 +986,7 @@ Effect.Morph = Class.create(Effect.Base, {
transform.unit != 'color' && transform.unit != 'color' &&
(isNaN(transform.originalValue) || isNaN(transform.targetValue)) (isNaN(transform.originalValue) || isNaN(transform.targetValue))
) )
) );
}); });
}, },
update: function(position) { update: function(position) {
@ -1074,14 +1082,14 @@ if (document.defaultView && document.defaultView.getComputedStyle) {
Element.getStyles = function(element) { Element.getStyles = function(element) {
element = $(element); element = $(element);
var css = element.currentStyle, styles; var css = element.currentStyle, styles;
styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) { styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
hash.set(property, css[property]); results[property] = css[property];
return hash; return results;
}); });
if (!styles.opacity) styles.set('opacity', element.getOpacity()); if (!styles.opacity) styles.opacity = element.getOpacity();
return styles; return styles;
}; };
}; }
Effect.Methods = { Effect.Methods = {
morph: function(element, style) { morph: function(element, style) {
@ -1090,7 +1098,7 @@ Effect.Methods = {
return element; return element;
}, },
visualEffect: function(element, effect, options) { visualEffect: function(element, effect, options) {
element = $(element) element = $(element);
var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
new Effect[klass](element, options); new Effect[klass](element, options);
return element; return element;
@ -1109,7 +1117,7 @@ $w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
element = $(element); element = $(element);
Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
return element; return element;
} };
} }
); );

File diff suppressed because it is too large Load diff

View file

@ -60,10 +60,6 @@ h4.notice {
color: #007E00; color: #007E00;
} }
div.t {
padding-left:5px;
}
span.tag { span.tag {
font-size: XX-small; font-size: XX-small;
background-color: #CCE7FF; background-color: #CCE7FF;
@ -126,3 +122,7 @@ span.prj, span.ctx{
.errors { .errors {
background: #FFC2C2; background: #FFC2C2;
} }
table.c {
margin-left: 5px;
}

4
script/autospec Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/env ruby
ENV['RSPEC'] = 'true' # allows autotest to discover rspec
ENV['AUTOTEST'] = 'true' # allows autotest to run w/ color on linux
system (RUBY_PLATFORM =~ /mswin|mingw/ ? 'autotest.bat' : 'autotest'), *ARGV

View file

@ -1,4 +1,5 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../vendor/plugins/rspec/lib")) $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../vendor/plugins/rspec/lib"))
require 'rubygems'
require 'spec' require 'spec'
exit ::Spec::Runner::CommandLine.run(::Spec::Runner::OptionParser.parse(ARGV, STDERR, STDOUT)) exit ::Spec::Runner::CommandLine.run(::Spec::Runner::OptionParser.parse(ARGV, STDERR, STDOUT))

View file

@ -18,14 +18,14 @@ module LuckySneaks
it "should not be valid if #{attribute} length is more than #{maximum}" do it "should not be valid if #{attribute} length is more than #{maximum}" do
instance.send "#{attribute}=", 'x'*(maximum+1) instance.send "#{attribute}=", 'x'*(maximum+1)
instance.errors_on(attribute).should include( instance.errors_on(attribute).should include(
options[:message_too_long] || ActiveRecord::Errors.default_error_messages[:too_long] % maximum options[:message_too_long] || I18n.t('activerecord.errors.messages.too_long', :count => maximum)
) )
end if maximum end if maximum
it "should not be valid if #{attribute} length is less than #{minimum}" do it "should not be valid if #{attribute} length is less than #{minimum}" do
instance.send "#{attribute}=", 'x'*(minimum-1) instance.send "#{attribute}=", 'x'*(minimum-1)
instance.errors_on(attribute).should include( instance.errors_on(attribute).should include(
options[:message_to_short] || ActiveRecord::Errors.default_error_messages[:too_short] % minimum options[:message_to_short] || I18n.t('activerecord.errors.messages.too_short', :count => minimum)
) )
end if minimum end if minimum
end end

View file

@ -3,32 +3,36 @@ require File.dirname(__FILE__) + '/../../spec_helper'
describe "/notes/_notes.rhtml" do describe "/notes/_notes.rhtml" do
before :each do before :each do
@project = mock_model(Project, :name => "a project") @project = mock_model(Project, :name => "a project")
@note = mock_model(Note, :body => "this is a note", :project => @project, @note = mock_model(Note, :body => "this is a note", :project => @project, :project_id => @project.id,
:created_at => Time.now, :updated_at? => false) :created_at => Time.now, :updated_at? => false)
@controller.template.stub!(:apply_behavior) # @controller.template.stub!(:apply_behavior)
@controller.template.stub!(:format_date) @controller.template.stub!(:format_date)
@controller.template.stub!(:render) # @controller.template.stub!(:render)
@controller.template.stub!(:form_remote_tag) # @controller.template.stub!(:form_remote_tag)
end end
it "should render" do it "should render" do
pending "figure out how to mock or work with with UJS"
render :partial => "/notes/notes", :locals => {:notes => @note} render :partial => "/notes/notes", :locals => {:notes => @note}
response.should have_tag("div.note_footer") response.should have_tag("div.note_footer")
end end
it "should auto-link URLs" do it "should auto-link URLs" do
pending "figure out how to mock or work with with UJS"
@note.stub!(:body).and_return("http://www.google.com/") @note.stub!(:body).and_return("http://www.google.com/")
render :partial => "/notes/notes", :locals => {:notes => @note} render :partial => "/notes/notes", :locals => {:notes => @note}
response.should have_tag("a[href=\"http://www.google.com/\"]") response.should have_tag("a[href=\"http://www.google.com/\"]")
end end
it "should auto-link embedded URLs" do it "should auto-link embedded URLs" do
pending "figure out how to mock or work with with UJS"
@note.stub!(:body).and_return("this is cool: http://www.google.com/") @note.stub!(:body).and_return("this is cool: http://www.google.com/")
render :partial => "/notes/notes", :locals => {:notes => @note} render :partial => "/notes/notes", :locals => {:notes => @note}
response.should have_tag("a[href=\"http://www.google.com/\"]") response.should have_tag("a[href=\"http://www.google.com/\"]")
end end
it "should parse Textile links correctly" do it "should parse Textile links correctly" do
pending "figure out how to mock or work with with UJS"
@note.stub!(:body).and_return("\"link\":http://www.google.com/") @note.stub!(:body).and_return("\"link\":http://www.google.com/")
render :partial => "/notes/notes", :locals => {:notes => @note} render :partial => "/notes/notes", :locals => {:notes => @note}
response.should have_tag("a[href=\"http://www.google.com/\"]") response.should have_tag("a[href=\"http://www.google.com/\"]")

4
stories/all.rb Normal file
View file

@ -0,0 +1,4 @@
dir = File.dirname(__FILE__)
Dir[File.expand_path("#{dir}/**/*.rb")].uniq.each do |file|
require file
end

View file

@ -1,136 +1,3 @@
ENV["RAILS_ENV"] = "test" ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment") require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
$:.unshift File.join(File.dirname(__FILE__), *%w[.. vendor plugings rspec lib]) require 'spec/rails/story_adapter'
require 'test_help'
require 'test/unit/testresult'
require 'spec'
require 'spec/rails'
require 'spec/story'
require 'webrat/selenium'
require 'action_controller/test_process'
module Spec
module Story
class StepGroup
def include_steps_for(name)
require File.expand_path(File.dirname(__FILE__) + "/steps/#{name}")
step_matchers = rspec_story_steps[name.to_sym]
warn "WARNING: 0 step matchers found for include_steps_for(:#{name}). Are you missing an include?" if step_matchers.empty?
self << step_matchers
end
end
end
end
Test::Unit.run = true
class SeleniumRailsStory < Test::Unit::TestCase
include Spec::Matchers
include Spec::Rails::Matchers
def initialize #:nodoc:
# TODO - eliminate this hack, which is here to stop
# Rails Stories from dumping the example summary.
Spec::Runner::Options.class_eval do
def examples_should_be_run?
false
end
end
@_result = Test::Unit::TestResult.new
end
def should_see(text_or_regexp)
if text_or_regexp.is_a?(Regexp)
response.should have_tag("*", text_or_regexp)
else
response.should have_tag("*", /#{Regexp.escape(text_or_regexp)}/i)
end
end
def should_not_see(text_or_regexp)
if text_or_regexp.is_a?(Regexp)
response.should_not have_tag("*", text_or_regexp)
else
response.should_not have_tag("*", /#{Regexp.escape(text_or_regexp)}/i)
end
end
def response
webrat_session.response_body
end
def logged_in_as(user)
visits("/selenium_helper/login?as=#{user.login}")
end
def selenium
SeleniumDriverManager.instance.running_selenium_driver
end
def badge_count_should_show(count)
response.should have_tag('#badge_count', count.to_s)
end
def method_missing(name, *args)
if webrat_session.respond_to?(name)
webrat_session.send(name, *args)
else
super
end
end
protected
def webrat_session
@webrat_session ||= begin
Webrat::SeleniumSession.new(SeleniumDriverManager.instance.running_selenium_driver)
end
end
end
class DatabaseResetListener
include Singleton
def scenario_started(*args)
if defined?(ActiveRecord::Base)
connection = ActiveRecord::Base.connection
%w[users].each do |table|
connection.execute "DELETE FROM #{table}"
end
end
end
def method_missing sym, *args, &block
# ignore all messages you don't care about
end
end
class CookieResetListener
include Singleton
def scenario_started(*args)
%w[tracks_login auth_token _session_id].each do |cookie_name|
SeleniumDriverManager.instance.running_selenium_driver.get_eval("window.document.cookie = '#{cookie_name}=;expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/';")
end
end
def method_missing sym, *args, &block
# ignore all messages you don't care about
end
end
class Spec::Story::Runner::ScenarioRunner
def initialize
@listeners = [DatabaseResetListener.instance, CookieResetListener.instance]
end
end
class Spec::Story::GivenScenario
def perform(instance, name = nil)
scenario = Spec::Story::Runner::StoryRunner.scenario_from_current_story @name
runner = Spec::Story::Runner::ScenarioRunner.new
runner.instance_variable_set(:@listeners,[])
runner.run(scenario, instance)
end
end

View file

@ -223,8 +223,8 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
login_as :admin_user login_as :admin_user
u = users(:admin_user) u = users(:admin_user)
post :actionize, :state => "active", :format => 'js' post :actionize, :state => "active", :format => 'js'
assert_equal 1, projects(:gardenclean).position assert_equal 2, projects(:gardenclean).position
assert_equal 2, projects(:moremoney).position assert_equal 1, projects(:moremoney).position
assert_equal 3, projects(:timemachine).position assert_equal 3, projects(:timemachine).position
end end

View file

@ -184,7 +184,7 @@ class TodosControllerTest < Test::Rails::TestCase
assert_xml_select 'rss[version="2.0"]' do assert_xml_select 'rss[version="2.0"]' do
assert_select 'channel' do assert_select 'channel' do
assert_select '>title', 'Tracks Actions' assert_select '>title', 'Actions'
assert_select '>description', "Actions for #{users(:admin_user).display_name}" assert_select '>description', "Actions for #{users(:admin_user).display_name}"
assert_select 'language', 'en-us' assert_select 'language', 'en-us'
assert_select 'ttl', '40' assert_select 'ttl', '40'
@ -205,7 +205,7 @@ class TodosControllerTest < Test::Rails::TestCase
assert_xml_select 'rss[version="2.0"]' do assert_xml_select 'rss[version="2.0"]' do
assert_select 'channel' do assert_select 'channel' do
assert_select '>title', 'Tracks Actions' assert_select '>title', 'Actions'
assert_select '>description', "Actions for #{users(:admin_user).display_name}" assert_select '>description', "Actions for #{users(:admin_user).display_name}"
assert_select 'item', 5 do assert_select 'item', 5 do
assert_select 'title', /.+/ assert_select 'title', /.+/
@ -240,7 +240,7 @@ class TodosControllerTest < Test::Rails::TestCase
# #puts @response.body # #puts @response.body
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
assert_xml_select '>title', 'Tracks Actions' assert_xml_select '>title', 'Actions'
assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}" assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}"
assert_xml_select 'entry', 11 do assert_xml_select 'entry', 11 do
assert_xml_select 'title', /.+/ assert_xml_select 'title', /.+/
@ -450,7 +450,7 @@ class TodosControllerTest < Test::Rails::TestCase
# check that the new_todo is in the tickler to show next month # check that the new_todo is in the tickler to show next month
assert !new_todo.show_from.nil? assert !new_todo.show_from.nil?
assert_equal Time.utc(today.year, today.month+1, today.day), new_todo.show_from assert_equal Time.utc(today.year, today.month, today.day)+1.month, new_todo.show_from
end end
def test_check_for_next_todo def test_check_for_next_todo

View file

@ -20,8 +20,9 @@ class ContextXmlApiTest < ActionController::IntegrationTest
end end
def test_fails_with_invalid_xml_format def test_fails_with_invalid_xml_format
authenticated_post_xml_to_context_create "<foo></bar>" # Fails too hard for test to catch
assert_equal 500, @integration_session.status # authenticated_post_xml_to_context_create "<foo></bar>"
# assert_equal 500, @integration_session.status
end end
def test_fails_with_invalid_xml_format2 def test_fails_with_invalid_xml_format2

View file

@ -20,8 +20,9 @@ class ProjectXmlApiTest < ActionController::IntegrationTest
end end
def test_fails_with_invalid_xml_format def test_fails_with_invalid_xml_format
authenticated_post_xml_to_project_create "<foo></bar>" #Fails too hard for test to catch
assert_equal 500, @integration_session.status # authenticated_post_xml_to_project_create "<foo></bar>"
# assert_equal 500, @integration_session.status
end end
def test_fails_with_invalid_xml_format2 def test_fails_with_invalid_xml_format2

View file

@ -29,10 +29,11 @@ class UsersXmlApiTest < ActionController::IntegrationTest
assert_404_invalid_xml assert_404_invalid_xml
end end
def test_fails_with_invalid_xml_format # Fails too hard for test to catch
authenticated_post_xml_to_user_create "<foo></bar>" # def test_fails_with_invalid_xml_format
assert_equal 500, @integration_session.status # authenticated_post_xml_to_user_create "<foo></bar>"
end # assert_equal 500, @integration_session.status
# end
def test_fails_with_invalid_xml_format2 def test_fails_with_invalid_xml_format2
authenticated_post_xml_to_user_create "<request><username>foo</username></request>" authenticated_post_xml_to_user_create "<request><username>foo</username></request>"

View file

@ -19,3 +19,6 @@ wait_for_not_visible "todo_9"
# check that newly added action is not there # check that newly added action is not there
wait_for_not_visible "xpath=//span[text()='a new action']" wait_for_not_visible "xpath=//span[text()='a new action']"
# toggle c1 back. Selenium does not reset cookies
click "css=#toggle_c1"

View file

@ -3,7 +3,7 @@ login :as => 'admin'
open "/" open "/"
# we should start with 10 actions on home page # we should start with 10 actions on home page
assert_text 'badge_count', '10' assert_text 'badge_count', '11'
# set project to hidden state # set project to hidden state
open "/projects/2" open "/projects/2"

View file

@ -6,4 +6,4 @@ wait_for_element_present "show_from_todo_9"
type "show_from_todo_9", "1/1/2030" type "show_from_todo_9", "1/1/2030"
click "css=#submit_todo_9" click "css=#submit_todo_9"
wait_for_element_not_present "todo_9" wait_for_element_not_present "todo_9"
assert_text 'badge_count', '9' assert_text 'badge_count', '10'

View file

@ -6,5 +6,5 @@ wait_for_element_present "show_from_todo_5"
type "show_from_todo_5", "1/1/2030" type "show_from_todo_5", "1/1/2030"
click "css=#submit_todo_5" click "css=#submit_todo_5"
wait_for_element_not_present "todo_5" wait_for_element_not_present "todo_5"
assert_text 'badge_count', '9' assert_text 'badge_count', '10'
wait_for_not_visible "c5" wait_for_not_visible "c5"

View file

@ -1,8 +1,8 @@
setup :fixtures => :all setup :fixtures => :all
login :as => 'admin' login :as => 'admin'
open "/" open "/"
wait_for_element_present '//div[@id="line_todo_5"]//img[@alt="Defer_1"]/..' wait_for_element_present '//div[@id="line_todo_5"]//img[@alt="Defer 1 day"]/..'
click '//div[@id="line_todo_5"]//img[@alt="Defer_1"]/..' click '//div[@id="line_todo_5"]//img[@alt="Defer 1 day"]/..'
wait_for_element_not_present "todo_5" wait_for_element_not_present "todo_5"
assert_text 'badge_count', '9' assert_text 'badge_count', '10'
wait_for_not_visible "c5" wait_for_not_visible "c5"

View file

@ -2,7 +2,7 @@ setup :fixtures => :all
login :as => 'admin' login :as => 'admin'
open '/m' open '/m'
wait_for_text 'css=h1 span.count', '10' wait_for_text 'css=h1 span.count', '11'
click_and_wait "link=0-Add new action" click_and_wait "link=0-Add new action"
@ -15,4 +15,4 @@ select "todo_due_2i", "label=January"
select "todo_due_1i", "label=2009" select "todo_due_1i", "label=2009"
click_and_wait "//input[@value='Create']" click_and_wait "//input[@value='Create']"
wait_for_text 'css=h1 span.count', '11' wait_for_text 'css=h1 span.count', '12'

View file

@ -2,10 +2,12 @@ setup :fixtures => :all
login :as => 'admin' login :as => 'admin'
open '/m' open '/m'
wait_for_text 'css=h1 span.count', '10' wait_for_text 'css=h1 span.count', '11'
open '/todos/6.m'
wait_for_page_to_load 3000
open_and_wait '/todos/6.m'
click "done" click "done"
click_and_wait "//input[@value='Update']" click_and_wait "//input[@value='Update']"
wait_for_text 'css=h1 span.count', '9' wait_for_text 'css=h1 span.count', '10'

View file

@ -4,14 +4,14 @@ login :as => 'admin'
# open home page # open home page
open '/m' open '/m'
wait_for_title "All actions" wait_for_title "All actions"
wait_for_text 'css=h1 span.count', '10' wait_for_text 'css=h1 span.count', '11'
# open context page # open context page
click_and_wait "link=2-Contexts" click_and_wait "link=2-Contexts"
# verify_title "All actions in context agenda" # verify_title "All actions in context agenda"
# choose agenda context # choose agenda context
click_and_wait "link=agenda" click_and_wait "link=agenda"
wait_for_text 'css=h1 span.count', '5' wait_for_text 'css=h1 span.count', '6'
# click on tag foo to go to tag page # click on tag foo to go to tag page
click_and_wait "link=foo" click_and_wait "link=foo"

View file

@ -1,7 +1,7 @@
setup :fixtures => :all setup :fixtures => :all
login :as => 'admin' login :as => 'admin'
open '/projects/1' open '/projects/1'
assert_checked 'project_state_active', 'ignored' assert_checked 'project_state_active'
assert_attribute 'css=#project_status .active span', 'class', 'active_state' assert_attribute 'css=#project_status .active span', 'class', 'active_state'
assert_attribute 'css=#project_status .hidden span', 'class', 'inactive_state' assert_attribute 'css=#project_status .hidden span', 'class', 'inactive_state'
assert_text 'badge_count', '2' assert_text 'badge_count', '2'
@ -11,4 +11,4 @@ wait_for_attribute 'css=#project_status .hidden span', 'class', 'active_state'
assert_text 'badge_count', '2' assert_text 'badge_count', '2'
open '/projects/1' open '/projects/1'
assert_text 'badge_count', '2' assert_text 'badge_count', '2'
assert_checked 'project_state_hidden', 'ignored' assert_checked 'project_state_hidden'

View file

@ -20,4 +20,4 @@ assert_text 'badge_count', '4'
open '/projects/1' open '/projects/1'
assert_text 'badge_count', '4' assert_text 'badge_count', '4'
assert_checked 'project_state_hidden', 'ignored' assert_checked 'project_state_hidden'

View file

@ -51,4 +51,10 @@ module SeleniumOnRails::TestBuilderAccessors
yield yield
wait_for_context_count "${expected_context_count}" wait_for_context_count "${expected_context_count}"
end end
def click_and_wait clickable
click clickable
wait_for_page_to_load 3000
end
end end

View file

@ -68,5 +68,6 @@ class PrototypeHelperExtensionsTest < Test::Unit::TestCase
false false
end end
attr_accessor :output_buffer
end end

View file

@ -2,6 +2,13 @@
Below is a complete listing of changes for each revision of HighLine. Below is a complete listing of changes for each revision of HighLine.
== 1.5.0
* Fixed a bug that would prevent Readline from showing all completions.
(reported by Yaohan Chen)
* Added the ability to pass a block to HighLine#agree().
(patch by Yaohan Chen)
== 1.4.0 == 1.4.0
* Made the code grabbing terminal size a little more cross-platform by * Made the code grabbing terminal size a little more cross-platform by

View file

@ -9,7 +9,7 @@ require "rubygems"
require "highline/import" require "highline/import"
loop do loop do
cmd = ask("Enter command: ", %w{save load reset quit}) do |q| cmd = ask("Enter command: ", %w{save sample load reset quit}) do |q|
q.readline = true q.readline = true
end end
say("Executing \"#{cmd}\"...") say("Executing \"#{cmd}\"...")

View file

@ -29,7 +29,7 @@ require "abbrev"
# #
class HighLine class HighLine
# The version of the installed library. # The version of the installed library.
VERSION = "1.4.0".freeze VERSION = "1.5.0".freeze
# An internal HighLine error. User code does not need to trap this. # An internal HighLine error. User code does not need to trap this.
class QuestionError < StandardError class QuestionError < StandardError
@ -173,7 +173,8 @@ class HighLine
# A shortcut to HighLine.ask() a question that only accepts "yes" or "no" # A shortcut to HighLine.ask() a question that only accepts "yes" or "no"
# answers ("y" and "n" are allowed) and returns +true+ or +false+ # answers ("y" and "n" are allowed) and returns +true+ or +false+
# (+true+ for "yes"). If provided a +true+ value, _character_ will cause # (+true+ for "yes"). If provided a +true+ value, _character_ will cause
# HighLine to fetch a single character response. # HighLine to fetch a single character response. A block can be provided
# to further configure the question as in HighLine.ask()
# #
# Raises EOFError if input is exhausted. # Raises EOFError if input is exhausted.
# #
@ -183,6 +184,8 @@ class HighLine
q.responses[:not_valid] = 'Please enter "yes" or "no".' q.responses[:not_valid] = 'Please enter "yes" or "no".'
q.responses[:ask_on_error] = :question q.responses[:ask_on_error] = :question
q.character = character q.character = character
yield q if block_given?
end end
end end
@ -577,8 +580,9 @@ class HighLine
@output = old_output @output = old_output
# prep auto-completion # prep auto-completion
completions = @question.selection.abbrev Readline.completion_proc = lambda do |string|
Readline.completion_proc = lambda { |string| completions[string] } @question.selection.grep(/\A#{Regexp.escape(string)}/)
end
# work-around ugly readline() warnings # work-around ugly readline() warnings
old_verbose = $VERBOSE old_verbose = $VERBOSE

View file

@ -44,6 +44,14 @@ class TestHighLine < Test::Unit::TestCase
assert_equal(true, @terminal.agree("Yes or no? ", :getc)) assert_equal(true, @terminal.agree("Yes or no? ", :getc))
end end
def test_agree_with_block
@input << "\n\n"
@input.rewind
assert_equal(true, @terminal.agree("Yes or no? ") { |q| q.default = "y" })
assert_equal(false, @terminal.agree("Yes or no? ") { |q| q.default = "n" })
end
def test_ask def test_ask
name = "James Edward Gray II" name = "James Edward Gray II"
@input << name << "\n" @input << name << "\n"

View file

@ -38,7 +38,7 @@ module Arts
raise "Invalid content type" raise "Invalid content type"
end end
else else
assert_match Regexp.new("new Insertion\.#{position.to_s.camelize}(.*#{item_id}.*,.*?);"), assert_match /Element\.insert\("#{item_id}", \{.*#{position.to_s.downcase}.*\}.*\)\;/,
@response.body @response.body
end end
end end
@ -130,4 +130,12 @@ module Arts
return create_generator.send(:arguments_for_call, args) return create_generator.send(:arguments_for_call, args)
end end
end end
public
# hack for rails 2.2.2
def with_output_buffer(lines=[], &block)
block.call
end
end end

View file

@ -1,122 +0,0 @@
------------------------------------------------------------------------
r52 | sbecker | 2007-11-04 01:38:21 -0400 (Sun, 04 Nov 2007) | 3 lines
* Allow configuration of which environments the helpers should merge scripts with the Synthesis::AssetPackage.merge_environments variable.
* Refactored tests so they can all run together, and not depend on what the RAILS_ENV constant is.
* Only add file extension if it was explicitly passed in, fixes other helpers in rails.
------------------------------------------------------------------------
r51 | sbecker | 2007-10-26 16:24:48 -0400 (Fri, 26 Oct 2007) | 1 line
* Updated jsmin.rb to latest version from 2007-07-20
------------------------------------------------------------------------
r50 | sbecker | 2007-10-23 23:16:07 -0400 (Tue, 23 Oct 2007) | 1 line
Updated CHANGELOG
------------------------------------------------------------------------
r49 | sbecker | 2007-10-23 23:13:27 -0400 (Tue, 23 Oct 2007) | 1 line
* Finally committed the subdirectory patch. (Thanks James Coglan!)
------------------------------------------------------------------------
r48 | sbecker | 2007-10-15 15:10:43 -0400 (Mon, 15 Oct 2007) | 1 line
* Speed up rake tasks and remove rails environment dependencies
------------------------------------------------------------------------
r43 | sbecker | 2007-07-02 15:30:29 -0400 (Mon, 02 Jul 2007) | 1 line
* Updated the docs regarding testing.
------------------------------------------------------------------------
r42 | sbecker | 2007-07-02 15:27:00 -0400 (Mon, 02 Jul 2007) | 1 line
* For production helper test, build packages once - on first setup.
------------------------------------------------------------------------
r41 | sbecker | 2007-07-02 15:14:13 -0400 (Mon, 02 Jul 2007) | 1 line
* Put build_all in test setup and delete_all in test teardown so all tests will pass the on first run of test suite.
------------------------------------------------------------------------
r40 | sbecker | 2007-07-02 14:55:28 -0400 (Mon, 02 Jul 2007) | 1 line
* Fix quotes, add contact info
------------------------------------------------------------------------
r39 | sbecker | 2007-07-02 14:53:52 -0400 (Mon, 02 Jul 2007) | 1 line
* Add note on how to run the tests for asset packager.
------------------------------------------------------------------------
r38 | sbecker | 2007-01-25 15:36:42 -0500 (Thu, 25 Jan 2007) | 1 line
added CHANGELOG w/ subversion log entries
------------------------------------------------------------------------
r37 | sbecker | 2007-01-25 15:34:39 -0500 (Thu, 25 Jan 2007) | 1 line
updated jsmin with new version from 2007-01-23
------------------------------------------------------------------------
r35 | sbecker | 2007-01-15 19:22:16 -0500 (Mon, 15 Jan 2007) | 1 line
require synthesis/asset_package in rake tasks, as Rails 1.2 seems to necessitate
------------------------------------------------------------------------
r34 | sbecker | 2007-01-05 12:22:09 -0500 (Fri, 05 Jan 2007) | 1 line
do a require before including in action view, because when running migrations, the plugin lib files don't automatically get required, causing the include to error out
------------------------------------------------------------------------
r33 | sbecker | 2006-12-23 02:03:41 -0500 (Sat, 23 Dec 2006) | 1 line
updating readme with various tweaks
------------------------------------------------------------------------
r32 | sbecker | 2006-12-23 02:03:12 -0500 (Sat, 23 Dec 2006) | 1 line
updating readme with various tweaks
------------------------------------------------------------------------
r31 | sbecker | 2006-12-23 01:52:25 -0500 (Sat, 23 Dec 2006) | 1 line
updated readme to show how to use different media for stylesheets
------------------------------------------------------------------------
r28 | sbecker | 2006-11-27 21:02:14 -0500 (Mon, 27 Nov 2006) | 1 line
updated compute_public_path, added check for images
------------------------------------------------------------------------
r27 | sbecker | 2006-11-10 18:28:29 -0500 (Fri, 10 Nov 2006) | 1 line
tolerate extra periods in source asset names. fixed subversion revision checking to be file specific, instead of repository specific.
------------------------------------------------------------------------
r26 | sbecker | 2006-06-24 17:04:27 -0400 (Sat, 24 Jun 2006) | 1 line
convert asset_packages_yml var to a class var
------------------------------------------------------------------------
r25 | sbecker | 2006-06-24 12:37:47 -0400 (Sat, 24 Jun 2006) | 1 line
Added ability to include assets by package name. In development, include all uncompressed asset files. In production, include the single compressed asset.
------------------------------------------------------------------------
r24 | sbecker | 2006-06-19 21:57:23 -0400 (Mon, 19 Jun 2006) | 1 line
Updates to README and about.yml
------------------------------------------------------------------------
r23 | sbecker | 2006-06-19 14:55:39 -0400 (Mon, 19 Jun 2006) | 2 lines
Modifying about.yml and README
------------------------------------------------------------------------
r21 | sbecker | 2006-06-19 12:18:32 -0400 (Mon, 19 Jun 2006) | 2 lines
added "formerly known as MergeJS"
------------------------------------------------------------------------
r20 | sbecker | 2006-06-19 12:14:46 -0400 (Mon, 19 Jun 2006) | 2 lines
Updating docs
------------------------------------------------------------------------
r19 | sbecker | 2006-06-19 11:26:08 -0400 (Mon, 19 Jun 2006) | 2 lines
removing compiled test assets from subversion
------------------------------------------------------------------------
r18 | sbecker | 2006-06-19 11:19:59 -0400 (Mon, 19 Jun 2006) | 2 lines
Initial import.
------------------------------------------------------------------------
r17 | sbecker | 2006-06-19 11:18:56 -0400 (Mon, 19 Jun 2006) | 2 lines
Creating directory.
------------------------------------------------------------------------

View file

@ -1,173 +0,0 @@
= AssetPackager
JavaScript and CSS Asset Compression for Production Rails Apps
== Description
When it comes time to deploy your new web application, instead of
sending down a dozen JavaScript and CSS files full of formatting
and comments, this Rails plugin makes it simple to merge and
compress JavaScript and CSS down into one or more files, increasing
speed and saving bandwidth.
When in development, it allows you to use your original versions
and retain formatting and comments for readability and debugging.
Because not all browsers will dependably cache JavaScript and CSS
files with query string parameters, AssetPackager writes a timestamp
or subversion revision stamp (if available) into the merged file names.
Therefore files are correctly cached by the browser AND your users
always get the latest version when you re-deploy.
This code is released under the MIT license (like Ruby). Youre free
to rip it up, enhance it, etc. And if you make any enhancements,
Id like to know so I can add them back in. Thanks!
* Formerly known as MergeJS.
== Credit
This Rails Plugin was inspired by Cal Henderson's article
"Serving JavaScript Fast" on Vitamin:
http://www.thinkvitamin.com/features/webapps/serving-javascript-fast
It also uses the Ruby JavaScript Minifier created by
Douglas Crockford.
http://www.crockford.com/javascript/jsmin.html
== Key Features
* Merges and compresses JavaScript and CSS when running in production.
* Uses uncompressed originals when running in development.
* Handles caching correctly. (No querystring parameters - filename timestamps)
* Versions each package individually. Updates to files in one won't re-trigger downloading the others.
* Uses subversion revision numbers instead of timestamps if within a subversion controlled directory.
* Guarantees new version will get downloaded the next time you deploy.
== Components
* Rake Task for merging and compressing JavaScript and CSS files.
* Helper functions for including these JavaScript and CSS files in your views.
* YAML configuration file for mapping JavaScript and CSS files to merged versions.
* Rake Task for auto-generating the YAML file from your existing JavaScript files.
== How to Use:
1. Download and install the plugin:
./script/plugin install http://sbecker.net/shared/plugins/asset_packager
2. Run the rake task "asset:packager:create_yml" to generate the /config/asset_packages.yml
file the first time. You will need to reorder files under 'base' so dependencies are loaded
in correct order. Feel free to rename or create new file packages.
IMPORTANT: JavaScript files can break once compressed if each statement doesn't end with a semi-colon.
The minifier puts multiple statements on one line, so if the semi-colon is missing, the statement may no
longer makes sense and cause a syntax error.
Example from a fresh rails app after running the rake task. (Stylesheets is blank because a
default rails app has no stylesheets yet.):
---
javascripts:
- base:
- prototype
- effects
- dragdrop
- controls
- application
stylesheets:
- base: []
Example with multiple merged files:
---
javascripts:
- base:
- prototype
- effects
- controls
- dragdrop
- application
- secondary:
- foo
- bar
stylesheets:
- base:
- screen
- header
- secondary:
- foo
- bar
3. Run the rake task "asset:packager:build_all" to generate the compressed, merged versions
for each package. Whenever you rearrange the yaml file, you'll need to run this task again.
Merging and compressing is expensive, so this is something we want to do once, not every time
your app starts. Thats why its a rake task.
4. Use the helper functions whenever including these files in your application. See below for examples.
5. Potential warning: css compressor function currently removes CSS comments. This might blow
away some CSS hackery. To disable comment removal, comment out /lib/synthesis/asset_package.rb line 176.
== JavaScript Examples
Example call:
<%= javascript_include_merged 'prototype', 'effects', 'controls', 'dragdrop', 'application', 'foo', 'bar' %>
In development, this generates:
<script type="text/javascript" src="/javascripts/prototype.js"></script>
<script type="text/javascript" src="/javascripts/effects.js"></script>
<script type="text/javascript" src="/javascripts/controls.js"></script>
<script type="text/javascript" src="/javascripts/dragdrop.js"></script>
<script type="text/javascript" src="/javascripts/application.js"></script>
<script type="text/javascript" src="/javascripts/foo.js"></script>
<script type="text/javascript" src="/javascripts/bar.js"></script>
In production, this generates:
<script type="text/javascript" src="/javascripts/base_1150571523.js"></script>
<script type="text/javascript" src="/javascripts/secondary_1150729166.js"></script>
Now supports symbols and :defaults as well:
<%= javascript_include_merged :defaults %>
<%= javascript_include_merged :foo, :bar %>
== Stylesheet Examples
Example call:
<%= stylesheet_link_merged 'screen', 'header' %>
In development, this generates:
<link href="/stylesheets/screen.css" media="screen" rel="Stylesheet" type="text/css" />
<link href="/stylesheets/header.css" media="screen" rel="Stylesheet" type="text/css" />
In production this generates:
<link href="/stylesheets/base_1150729166.css" media="screen" rel="Stylesheet" type="text/css" />
== Different CSS Media
All options for stylesheet_link_tag still work, so if you want to specify a different media type:
<%= stylesheet_link_merged :secondary, 'media' => 'print' %>
== Running the tests
So you want to run the tests eh? Ok, then listen:
This plugin has a full suite of tests. But since they
depend on rails, it has to be run in the context of a
rails app, in the vendor/plugins directory. Observe:
> rails newtestapp
> cd newtestapp
> ./script/plugin install http://sbecker.net/shared/plugins/asset_packager
> cd vendor/plugins/asset_packager/
> rake # all tests pass
== License
Copyright (c) 2006 Scott Becker - http://synthesis.sbecker.net
Contact Email: becker.scott@gmail.com
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.

View file

@ -1,22 +0,0 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
desc 'Default: run unit tests.'
task :default => :test
desc 'Test the asset_packager plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
desc 'Generate documentation for the asset_packager plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'AssetPackager'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end

View file

@ -1,8 +0,0 @@
author: Scott Becker
name: AssetPackager
summary: JavaScript and CSS Asset Compression for Production Rails Apps
homepage: http://synthesis.sbecker.net/pages/asset_packager
plugin: http://sbecker.net/shared/plugins/asset_packager
license: MIT
version: 0.2
rails_version: 1.1.2+

View file

@ -1,2 +0,0 @@
require 'synthesis/asset_package_helper'
ActionView::Base.send :include, Synthesis::AssetPackageHelper

View file

@ -1 +0,0 @@
# Install hook code here

View file

@ -1,205 +0,0 @@
#!/usr/bin/ruby
# jsmin.rb 2007-07-20
# Author: Uladzislau Latynski
# This work is a translation from C to Ruby of jsmin.c published by
# Douglas Crockford. Permission is hereby granted to use the Ruby
# version under the same conditions as the jsmin.c on which it is
# based.
#
# /* jsmin.c
# 2003-04-21
#
# Copyright (c) 2002 Douglas Crockford (www.crockford.com)
#
# 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 shall be used for Good, not Evil.
#
# 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.
EOF = -1
$theA = ""
$theB = ""
# isAlphanum -- return true if the character is a letter, digit, underscore,
# dollar sign, or non-ASCII character
def isAlphanum(c)
return false if !c || c == EOF
return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') || c == '_' || c == '$' ||
c == '\\' || c[0] > 126)
end
# get -- return the next character from stdin. Watch out for lookahead. If
# the character is a control character, translate it to a space or linefeed.
def get()
c = $stdin.getc
return EOF if(!c)
c = c.chr
return c if (c >= " " || c == "\n" || c.unpack("c") == EOF)
return "\n" if (c == "\r")
return " "
end
# Get the next character without getting it.
def peek()
lookaheadChar = $stdin.getc
$stdin.ungetc(lookaheadChar)
return lookaheadChar.chr
end
# mynext -- get the next character, excluding comments.
# peek() is used to see if a '/' is followed by a '/' or '*'.
def mynext()
c = get
if (c == "/")
if(peek == "/")
while(true)
c = get
if (c <= "\n")
return c
end
end
end
if(peek == "*")
get
while(true)
case get
when "*"
if (peek == "/")
get
return " "
end
when EOF
raise "Unterminated comment"
end
end
end
end
return c
end
# action -- do something! What you do is determined by the argument: 1
# Output A. Copy B to A. Get the next B. 2 Copy B to A. Get the next B.
# (Delete A). 3 Get the next B. (Delete B). action treats a string as a
# single character. Wow! action recognizes a regular expression if it is
# preceded by ( or , or =.
def action(a)
if(a==1)
$stdout.write $theA
end
if(a==1 || a==2)
$theA = $theB
if ($theA == "\'" || $theA == "\"")
while (true)
$stdout.write $theA
$theA = get
break if ($theA == $theB)
raise "Unterminated string literal" if ($theA <= "\n")
if ($theA == "\\")
$stdout.write $theA
$theA = get
end
end
end
end
if(a==1 || a==2 || a==3)
$theB = mynext
if ($theB == "/" && ($theA == "(" || $theA == "," || $theA == "=" ||
$theA == ":" || $theA == "[" || $theA == "!" ||
$theA == "&" || $theA == "|" || $theA == "?" ||
$theA == "{" || $theA == "}" || $theA == ";" ||
$theA == "\n"))
$stdout.write $theA
$stdout.write $theB
while (true)
$theA = get
if ($theA == "/")
break
elsif ($theA == "\\")
$stdout.write $theA
$theA = get
elsif ($theA <= "\n")
raise "Unterminated RegExp Literal"
end
$stdout.write $theA
end
$theB = mynext
end
end
end
# jsmin -- Copy the input to the output, deleting the characters which are
# insignificant to JavaScript. Comments will be removed. Tabs will be
# replaced with spaces. Carriage returns will be replaced with linefeeds.
# Most spaces and linefeeds will be removed.
def jsmin
$theA = "\n"
action(3)
while ($theA != EOF)
case $theA
when " "
if (isAlphanum($theB))
action(1)
else
action(2)
end
when "\n"
case ($theB)
when "{","[","(","+","-"
action(1)
when " "
action(3)
else
if (isAlphanum($theB))
action(1)
else
action(2)
end
end
else
case ($theB)
when " "
if (isAlphanum($theA))
action(1)
else
action(3)
end
when "\n"
case ($theA)
when "}","]",")","+","-","\"","\\", "'", '"'
action(1)
else
if (isAlphanum($theA))
action(1)
else
action(3)
end
end
else
action(1)
end
end
end
end
ARGV.each do |anArg|
$stdout.write "// #{anArg}\n"
end
jsmin

View file

@ -1,233 +0,0 @@
require 'yaml'
module Synthesis
class AssetPackage
# class variables
@@asset_packages_yml = $asset_packages_yml ||
(File.exists?("#{RAILS_ROOT}/config/asset_packages.yml") ? YAML.load_file("#{RAILS_ROOT}/config/asset_packages.yml") : nil)
# singleton methods
class << self
def merge_environments=(environments)
@@merge_environments = environments
end
def merge_environments
@@merge_environments ||= ["production"]
end
def parse_path(path)
/^(?:(.*)\/)?([^\/]+)$/.match(path).to_a
end
def find_by_type(asset_type)
@@asset_packages_yml[asset_type].map { |p| self.new(asset_type, p) }
end
def find_by_target(asset_type, target)
package_hash = @@asset_packages_yml[asset_type].find {|p| p.keys.first == target }
package_hash ? self.new(asset_type, package_hash) : nil
end
def find_by_source(asset_type, source)
path_parts = parse_path(source)
package_hash = @@asset_packages_yml[asset_type].find do |p|
key = p.keys.first
p[key].include?(path_parts[2]) && (parse_path(key)[1] == path_parts[1])
end
package_hash ? self.new(asset_type, package_hash) : nil
end
def targets_from_sources(asset_type, sources)
package_names = Array.new
sources.each do |source|
package = find_by_target(asset_type, source) || find_by_source(asset_type, source)
package_names << (package ? package.current_file : source)
end
package_names.uniq
end
def sources_from_targets(asset_type, targets)
source_names = Array.new
targets.each do |target|
package = find_by_target(asset_type, target)
source_names += (package ? package.sources.collect do |src|
package.target_dir.gsub(/^(.+)$/, '\1/') + src
end : target.to_a)
end
source_names.uniq
end
def build_all
@@asset_packages_yml.keys.each do |asset_type|
@@asset_packages_yml[asset_type].each { |p| self.new(asset_type, p).build }
end
end
def delete_all
@@asset_packages_yml.keys.each do |asset_type|
@@asset_packages_yml[asset_type].each { |p| self.new(asset_type, p).delete_all_builds }
end
end
def create_yml
unless File.exists?("#{RAILS_ROOT}/config/asset_packages.yml")
asset_yml = Hash.new
asset_yml['javascripts'] = [{"base" => build_file_list("#{RAILS_ROOT}/public/javascripts", "js")}]
asset_yml['stylesheets'] = [{"base" => build_file_list("#{RAILS_ROOT}/public/stylesheets", "css")}]
File.open("#{RAILS_ROOT}/config/asset_packages.yml", "w") do |out|
YAML.dump(asset_yml, out)
end
log "config/asset_packages.yml example file created!"
log "Please reorder files under 'base' so dependencies are loaded in correct order."
else
log "config/asset_packages.yml already exists. Aborting task..."
end
end
end
# instance methods
attr_accessor :asset_type, :target, :target_dir, :sources
def initialize(asset_type, package_hash)
target_parts = self.class.parse_path(package_hash.keys.first)
@target_dir = target_parts[1].to_s
@target = target_parts[2].to_s
@sources = package_hash[package_hash.keys.first]
@asset_type = asset_type
@asset_path = ($asset_base_path ? "#{$asset_base_path}/" : "#{RAILS_ROOT}/public/") +
"#{@asset_type}#{@target_dir.gsub(/^(.+)$/, '/\1')}"
@extension = get_extension
@match_regex = Regexp.new("\\A#{@target}_\\d+.#{@extension}\\z")
end
def current_file
@target_dir.gsub(/^(.+)$/, '\1/') +
Dir.new(@asset_path).entries.delete_if { |x| ! (x =~ @match_regex) }.sort.reverse[0].chomp(".#{@extension}")
end
def build
delete_old_builds
create_new_build
end
def delete_old_builds
Dir.new(@asset_path).entries.delete_if { |x| ! (x =~ @match_regex) }.each do |x|
File.delete("#{@asset_path}/#{x}") unless x.index(revision.to_s)
end
end
def delete_all_builds
Dir.new(@asset_path).entries.delete_if { |x| ! (x =~ @match_regex) }.each do |x|
File.delete("#{@asset_path}/#{x}")
end
end
private
def revision
unless @revision
revisions = [1]
@sources.each do |source|
revisions << get_file_revision("#{@asset_path}/#{source}.#{@extension}")
end
@revision = revisions.max
end
@revision
end
def get_file_revision(path)
if File.exists?(path)
begin
`svn info #{path}`[/Last Changed Rev: (.*?)\n/][/(\d+)/].to_i
rescue # use filename timestamp if not in subversion
File.mtime(path).to_i
end
else
0
end
end
def create_new_build
if File.exists?("#{@asset_path}/#{@target}_#{revision}.#{@extension}")
log "Latest version already exists: #{@asset_path}/#{@target}_#{revision}.#{@extension}"
else
File.open("#{@asset_path}/#{@target}_#{revision}.#{@extension}", "w") {|f| f.write(compressed_file) }
log "Created #{@asset_path}/#{@target}_#{revision}.#{@extension}"
end
end
def merged_file
merged_file = ""
@sources.each {|s|
File.open("#{@asset_path}/#{s}.#{@extension}", "r") { |f|
merged_file += f.read + "\n"
}
}
merged_file
end
def compressed_file
case @asset_type
when "javascripts" then compress_js(merged_file)
when "stylesheets" then compress_css(merged_file)
end
end
def compress_js(source)
jsmin_path = "#{RAILS_ROOT}/vendor/plugins/asset_packager/lib"
tmp_path = "#{RAILS_ROOT}/tmp/#{@target}_#{revision}"
# write out to a temp file
File.open("#{tmp_path}_uncompressed.js", "w") {|f| f.write(source) }
# compress file with JSMin library
`ruby #{jsmin_path}/jsmin.rb <#{tmp_path}_uncompressed.js >#{tmp_path}_compressed.js \n`
# read it back in and trim it
result = ""
File.open("#{tmp_path}_compressed.js", "r") { |f| result += f.read.strip }
# delete temp files if they exist
File.delete("#{tmp_path}_uncompressed.js") if File.exists?("#{tmp_path}_uncompressed.js")
File.delete("#{tmp_path}_compressed.js") if File.exists?("#{tmp_path}_compressed.js")
result
end
def compress_css(source)
source.gsub!(/\s+/, " ") # collapse space
source.gsub!(/\/\*(.*?)\*\/ /, "") # remove comments - caution, might want to remove this if using css hacks
source.gsub!(/\} /, "}\n") # add line breaks
source.gsub!(/\n$/, "") # remove last break
source.gsub!(/ \{ /, " {") # trim inside brackets
source.gsub!(/; \}/, "}") # trim inside brackets
source
end
def get_extension
case @asset_type
when "javascripts" then "js"
when "stylesheets" then "css"
end
end
def log(message)
puts message
end
def self.build_file_list(path, extension)
re = Regexp.new(".#{extension}\\z")
file_list = Dir.new(path).entries.delete_if { |x| ! (x =~ re) }.map {|x| x.chomp(".#{extension}")}
# reverse javascript entries so prototype comes first on a base rails app
file_list.reverse! if extension == "js"
file_list
end
end
end

Some files were not shown because too many files have changed in this diff Show more