mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-03 22:41:48 +01:00
* Replaced select widgets with AutoCompleters for project & context in new item and edit forms. You can now create a new project and/or context on the fly as you're adding an item.
* Rolled Deferred controller, views & helper into Todo. * Made the show_from field always available on the add new item and edit forms. * Added validation to Todo to make sure a context is specified. * Set initial state of Todo to :deferred when show_from is specified (I still need to make handle setting show_from during an edit). * Created a generic rescue_action in ApplicationController and removed boilerplate rescue code from a few actions in TodoController. * Made all Todo creation requests use the TodoController, removing duplicate logic from ContextController and ContextController * Removed unused lightbox-style add item form * Remove unused update_context and update_project actions * Updated rails javascripts to the same as our vendor/rails git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@352 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
parent
5d51ca84ba
commit
bb1cfe613a
36 changed files with 627 additions and 720 deletions
|
|
@ -54,6 +54,18 @@ class ApplicationController < ActionController::Base
|
|||
render :text => message, :status => status
|
||||
end
|
||||
|
||||
def rescue_action(exception)
|
||||
log_error(exception) if logger
|
||||
respond_to do |wants|
|
||||
wants.html do
|
||||
notify :warning, "An error occurred on the server."
|
||||
render :action => "index"
|
||||
end
|
||||
wants.js { render :action => 'error' }
|
||||
wants.xml { render :text => 'An error occurred on the server.' + $! }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_current_user
|
||||
|
|
|
|||
|
|
@ -61,46 +61,6 @@ class ContextController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
# Called by a form button
|
||||
# Parameters from form fields are passed to create new action
|
||||
# in the selected context.
|
||||
def add_item
|
||||
self.init
|
||||
@item = @user.todos.build
|
||||
@item.attributes = params["todo"]
|
||||
|
||||
if @item.due?
|
||||
@date = parse_date_per_user_prefs(params["todo"]["due"])
|
||||
@item.due = @date.to_s(:db)
|
||||
else
|
||||
@item.due = ""
|
||||
end
|
||||
|
||||
@saved = @item.save
|
||||
if @saved
|
||||
@up_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.state = ? and todos.context_id = ?", @user.id, 'active', @item.context_id]).size.to_s
|
||||
end
|
||||
|
||||
return if request.xhr?
|
||||
|
||||
# fallback for standard requests
|
||||
if @saved
|
||||
notify :notice, 'Added new next action.'
|
||||
redirect_to :controller => 'todo', :action => 'list'
|
||||
else
|
||||
notify :warning, 'The next action was not added. Please try again.'
|
||||
redirect_to :controller => 'todo', :action => 'list'
|
||||
end
|
||||
|
||||
rescue
|
||||
if request.xhr? # be sure to include an error.rjs
|
||||
render :action => 'error'
|
||||
else
|
||||
notify :warning, 'An error occurred on the server.'
|
||||
redirect_to :controller => 'todo', :action => 'list'
|
||||
end
|
||||
end
|
||||
|
||||
# Edit the details of the context
|
||||
#
|
||||
def update
|
||||
|
|
|
|||
|
|
@ -1,131 +0,0 @@
|
|||
class DeferredController < ApplicationController
|
||||
|
||||
model :user
|
||||
model :project
|
||||
model :context
|
||||
|
||||
helper :todo
|
||||
|
||||
prepend_before_filter :login_required
|
||||
layout "standard"
|
||||
|
||||
|
||||
def index
|
||||
@source_view = 'deferred'
|
||||
init_data_for_sidebar
|
||||
@page_title = "TRACKS::Tickler"
|
||||
@tickles = @user.todos.find_in_state(:all, :deferred, :order => "show_from ASC")
|
||||
@count = @tickles.size
|
||||
end
|
||||
|
||||
def create
|
||||
@source_view = 'deferred'
|
||||
@item = Todo.new_deferred
|
||||
@item.attributes = params["todo"]
|
||||
if params["todo"]["show_from"]
|
||||
@item.show_from = parse_date_per_user_prefs(params["todo"]["show_from"])
|
||||
end
|
||||
@item.user_id = @user.id
|
||||
|
||||
if @item.due?
|
||||
@item.due = parse_date_per_user_prefs(params["todo"]["due"])
|
||||
else
|
||||
@item.due = ""
|
||||
end
|
||||
|
||||
@saved = @item.save
|
||||
|
||||
if @saved
|
||||
@up_count = @user.todos.count_in_state(:deferred)
|
||||
end
|
||||
|
||||
respond_to do |wants|
|
||||
wants.html { redirect_to :action => "index" }
|
||||
wants.js
|
||||
wants.xml { render :xml => @item.to_xml( :root => 'todo', :except => :user_id ) }
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@source_view = 'deferred'
|
||||
init_projects_and_contexts
|
||||
@item = check_user_return_item
|
||||
render :template => 'todo/edit.rjs'
|
||||
end
|
||||
|
||||
def update
|
||||
@source_view = 'deferred'
|
||||
@item = check_user_return_item
|
||||
@original_item_context_id = @item.context_id
|
||||
@item.attributes = params["item"]
|
||||
|
||||
if params["item"]["show_from"]
|
||||
@item.show_from = parse_date_per_user_prefs(params["item"]["show_from"])
|
||||
end
|
||||
|
||||
if @item.due?
|
||||
@item.due = parse_date_per_user_prefs(params["item"]["due"])
|
||||
else
|
||||
@item.due = ""
|
||||
end
|
||||
|
||||
@saved = @item.save
|
||||
end
|
||||
|
||||
def destroy
|
||||
@item = check_user_return_item
|
||||
context_id = @item.context_id
|
||||
@saved = @item.destroy
|
||||
|
||||
respond_to do |wants|
|
||||
wants.html do
|
||||
notify :notice, 'Successfully deleted next action' if @saved
|
||||
redirect_to :action => "index"
|
||||
end
|
||||
wants.js do
|
||||
@down_count = @user.todos.count_in_state(:deferred) if @saved
|
||||
render
|
||||
end
|
||||
wants.xml { render :xml => @item.to_xml( :root => 'todo', :except => :user_id ) }
|
||||
end
|
||||
|
||||
rescue
|
||||
respond_to do |wants|
|
||||
wants.html do
|
||||
notify :warning, 'An error occurred on the server.'
|
||||
redirect_to :action => "index"
|
||||
end
|
||||
wants.js { render :action => 'error' }
|
||||
wants.xml { render :text => "500 Server Error: There was an error deleting the action.", :status => 500 }
|
||||
end
|
||||
end
|
||||
|
||||
# Check for any due tickler items, activate them
|
||||
# Called by periodically_call_remote
|
||||
def check_tickler
|
||||
now = Date.today()
|
||||
@due_tickles = @user.todos.find_in_state(:all, :deferred, :conditions => ['show_from < ? OR show_from = ?', now, now ], :order => "show_from ASC")
|
||||
# Change the due tickles to active
|
||||
@due_tickles.each do |t|
|
||||
t.activate!
|
||||
t.save
|
||||
end
|
||||
respond_to do |wants|
|
||||
wants.html { redirect_to :controller => 'todo', :action => 'index' }
|
||||
wants.js
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_user_return_item
|
||||
item = Todo.find( params['id'] )
|
||||
if @user == item.user
|
||||
return item
|
||||
else
|
||||
notify :warning, "Item and session user mis-match: #{item.user.name} and #{@user.name}!"
|
||||
render_text ""
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -86,46 +86,6 @@ class ProjectController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
# Called by a form button
|
||||
# Parameters from form fields are passed to create new action
|
||||
# in the selected context.
|
||||
def add_item
|
||||
self.init
|
||||
@item = @user.todos.build
|
||||
@item.attributes = params["todo"]
|
||||
|
||||
if @item.due?
|
||||
@date = parse_date_per_user_prefs(params["todo"]["due"])
|
||||
@item.due = @date.to_s(:db)
|
||||
else
|
||||
@item.due = ""
|
||||
end
|
||||
|
||||
@saved = @item.save
|
||||
if @saved
|
||||
@up_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.state = ? and todos.project_id = ?", @user.id, 'active', @item.project_id]).size.to_s
|
||||
end
|
||||
|
||||
return if request.xhr?
|
||||
|
||||
# fallback for standard requests
|
||||
if @saved
|
||||
notify :notice, 'Added new next action.'
|
||||
redirect_to :controller => 'todo', :action => 'index'
|
||||
else
|
||||
notify :warning, 'The next action was not added. Please try again.'
|
||||
redirect_to :controller => 'todo', :action => 'index'
|
||||
end
|
||||
|
||||
rescue
|
||||
if request.xhr? # be sure to include an error.rjs
|
||||
render :action => 'error'
|
||||
else
|
||||
notify :warning, 'An error occurred on the server.'
|
||||
redirect_to :controller => 'todo', :action => 'index'
|
||||
end
|
||||
end
|
||||
|
||||
# Edit the details of the project
|
||||
#
|
||||
def update
|
||||
|
|
|
|||
|
|
@ -62,15 +62,41 @@ class TodoController < ApplicationController
|
|||
def create
|
||||
init
|
||||
@item = @user.todos.build
|
||||
p = params['todo'] || params['request']['todo']
|
||||
@item.attributes = p
|
||||
p = params['request'] || params
|
||||
@item.attributes = p['todo']
|
||||
|
||||
if p['todo']['project_id'].blank? && !p['project_name'].blank? && p['project_name'] != 'None'
|
||||
project = @user.projects.find_by_name(p['project_name'].strip)
|
||||
unless project
|
||||
project = @user.projects.build
|
||||
project.name = p['project_name'].strip
|
||||
project.save
|
||||
@new_project_created = true
|
||||
end
|
||||
@item.project_id = project.id
|
||||
end
|
||||
|
||||
if p['todo']['context_id'].blank? && !p['context_name'].blank?
|
||||
context = @user.contexts.find_by_name(p['context_name'].strip)
|
||||
unless context
|
||||
context = @user.contexts.build
|
||||
context.name = p['context_name'].strip
|
||||
context.save
|
||||
@new_context_created = true
|
||||
end
|
||||
@item.context_id = context.id
|
||||
end
|
||||
|
||||
if @item.due?
|
||||
@date = parse_date_per_user_prefs(p["due"])
|
||||
@date = parse_date_per_user_prefs(p['todo']['due'])
|
||||
@item.due = @date.to_s(:db)
|
||||
else
|
||||
@item.due = ""
|
||||
end
|
||||
|
||||
if p['todo']['show_from']
|
||||
@item.show_from = parse_date_per_user_prefs(p['todo']['show_from'])
|
||||
end
|
||||
|
||||
@saved = @item.save
|
||||
|
||||
|
|
@ -78,28 +104,12 @@ class TodoController < ApplicationController
|
|||
wants.html { redirect_to :action => "index" }
|
||||
wants.js do
|
||||
if @saved
|
||||
init_todos
|
||||
@up_count = @todos.reject { |x| !x.active? or x.context.hide? }.size.to_s
|
||||
determine_down_count
|
||||
end
|
||||
render :action => 'create'
|
||||
end
|
||||
wants.xml { render :xml => @item.to_xml( :root => 'todo', :except => :user_id ) }
|
||||
end
|
||||
|
||||
# if you're seeing the message 'An error occurred on the server.' and you want to debug, comment out the rescue section and check the Ajax response for an exception message
|
||||
rescue
|
||||
respond_to do |wants|
|
||||
wants.html do
|
||||
notify :warning, "An error occurred on the server."
|
||||
render :action => "index"
|
||||
end
|
||||
wants.js { render :action => 'error' }
|
||||
wants.xml { render :text => 'An error occurred on the server.' + $! }
|
||||
end
|
||||
end
|
||||
|
||||
def add_item
|
||||
create
|
||||
end
|
||||
|
||||
def edit
|
||||
|
|
@ -119,7 +129,6 @@ class TodoController < ApplicationController
|
|||
#
|
||||
def toggle_check
|
||||
init
|
||||
logger.info "source view is " + @source_view
|
||||
@item = check_user_return_item
|
||||
@item.toggle_completion()
|
||||
@saved = @item.save
|
||||
|
|
@ -144,7 +153,28 @@ class TodoController < ApplicationController
|
|||
@item = check_user_return_item
|
||||
@original_item_context_id = @item.context_id
|
||||
@original_item_project_id = @item.project_id
|
||||
@item.attributes = params["item"]
|
||||
|
||||
if params['item']['project_id'].blank? && !params['project_name'].blank? && params['project_name'] != 'None'
|
||||
project = @user.projects.find_by_name(params['project_name'].strip)
|
||||
unless project
|
||||
project = @user.projects.build
|
||||
project.name = params['project_name'].strip
|
||||
project.save
|
||||
@new_project_created = true
|
||||
end
|
||||
params["item"]["project_id"] = project.id
|
||||
end
|
||||
|
||||
if params['item']['context_id'].blank? && !params['context_name'].blank?
|
||||
context = @user.contexts.find_by_name(params['context_name'].strip)
|
||||
unless context
|
||||
context = @user.contexts.build
|
||||
context.name = params['context_name'].strip
|
||||
context.save
|
||||
@new_context_created = true
|
||||
end
|
||||
params["item"]["context_id"] = context.id
|
||||
end
|
||||
if params["item"].has_key?("due")
|
||||
params["item"]["due"] = parse_date_per_user_prefs(params["item"]["due"])
|
||||
else
|
||||
|
|
@ -155,42 +185,9 @@ class TodoController < ApplicationController
|
|||
if @context_changed then @remaining_undone_in_context = @user.contexts.find(@original_item_context_id).not_done_todos.length; end
|
||||
@project_changed = @original_item_project_id != @item.project_id
|
||||
if (@project_changed && !@original_item_project_id.nil?) then @remaining_undone_in_project = @user.projects.find(@original_item_project_id).not_done_todos.length; end
|
||||
determine_down_count
|
||||
end
|
||||
|
||||
def update_context
|
||||
init
|
||||
@item = check_user_return_item
|
||||
context = Context.find(params['context_id']);
|
||||
if @user == context.user
|
||||
@original_item_context_id = @item.context_id
|
||||
@item.context_id = context.id
|
||||
@item.context = context
|
||||
@saved = @item.save
|
||||
render :action => 'update'
|
||||
else
|
||||
render :update do |page|
|
||||
page.notify :warning, content_tag("div", "Error updating the context of the dragged item. Item and context user mis-match: #{@item.user.name} and #{@context.user.name}! - refresh the page to see them."), 8.0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_project
|
||||
init
|
||||
@item = check_user_return_item
|
||||
project = Project.find(params['project_id']);
|
||||
if @user == project.user
|
||||
@original_item_context_id = @item.context_id
|
||||
@item.project_id = project.id
|
||||
@item.project = project
|
||||
@saved = @item.save
|
||||
render :action => 'update'
|
||||
else
|
||||
render :update do |page|
|
||||
page.notify :warning, content_tag("div", "Error updating the project of the dragged item. Item and project user mis-match: #{@item.user.name} and #{@project.user.name}! - refresh the page to see them."), 8.0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def destroy
|
||||
@item = check_user_return_item
|
||||
@context_id = @item.context_id
|
||||
|
|
@ -224,16 +221,6 @@ class TodoController < ApplicationController
|
|||
wants.xml { render :text => '200 OK. Action deleted.', :status => 200 }
|
||||
|
||||
end
|
||||
|
||||
rescue
|
||||
respond_to do |wants|
|
||||
wants.html do
|
||||
notify :error, 'An error occurred on the server.', 8.0
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
wants.js { render :action => 'error' }
|
||||
wants.xml { render :text => 'An error occurred on the server.' + $! }
|
||||
end
|
||||
end
|
||||
|
||||
def completed
|
||||
|
|
@ -250,6 +237,31 @@ class TodoController < ApplicationController
|
|||
@done_archive = @done.completed_more_than 28.day.ago
|
||||
end
|
||||
|
||||
def tickler
|
||||
init
|
||||
@source_view = 'deferred'
|
||||
@page_title = "TRACKS::Tickler"
|
||||
@tickles = @user.todos.find_in_state(:all, :deferred, :order => "show_from ASC")
|
||||
@count = @tickles.size
|
||||
end
|
||||
|
||||
# Check for any due tickler items, activate them
|
||||
# Called by periodically_call_remote
|
||||
def check_tickler
|
||||
now = Date.today()
|
||||
@due_tickles = @user.todos.find_in_state(:all, :deferred, :conditions => ['show_from < ? OR show_from = ?', now, now ], :order => "show_from ASC")
|
||||
# Change the due tickles to active
|
||||
@due_tickles.each do |t|
|
||||
t.activate!
|
||||
t.save
|
||||
end
|
||||
respond_to do |wants|
|
||||
wants.html { redirect_to :controller => 'todo', :action => 'index' }
|
||||
wants.js
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def check_user_return_item
|
||||
|
|
@ -296,6 +308,9 @@ class TodoController < ApplicationController
|
|||
@down_count = @user.projects.find(@item.project_id).todos.count_in_state(:active)
|
||||
end
|
||||
end
|
||||
from.deferred do
|
||||
@down_count = @user.todos.count_in_state(:deferred)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
module DeferredHelper
|
||||
end
|
||||
|
|
@ -7,16 +7,14 @@ module TodoHelper
|
|||
count = Todo.find_all("done=0 AND context_id=#{context.id}").length
|
||||
end
|
||||
|
||||
def form_remote_tag_edit_todo( item, type )
|
||||
(type == "deferred") ? controller_name = 'deferred' : controller_name = 'todo'
|
||||
form_remote_tag( :url => { :controller => controller_name, :action => 'update', :id => item.id },
|
||||
def form_remote_tag_edit_todo( item )
|
||||
form_remote_tag( :url => { :controller => 'todo', :action => 'update', :id => item.id },
|
||||
:html => { :id => "form-action-#{item.id}", :class => "inline-form" }
|
||||
)
|
||||
end
|
||||
|
||||
def link_to_remote_todo( item, options = {})
|
||||
(options[:type] == "deferred") ? controller_name = 'deferred' : controller_name = 'todo'
|
||||
url_options = { :controller => controller_name, :action => 'destroy', :id => item.id, :_source_view => @source_view }
|
||||
def link_to_remote_todo(item)
|
||||
url_options = { :controller => 'todo', :action => 'destroy', :id => item.id, :_source_view => @source_view }
|
||||
|
||||
str = link_to_remote( image_tag_for_delete,
|
||||
{ :url => url_options, :confirm => "Are you sure that you want to delete the action, \'#{item.description}\'?" },
|
||||
|
|
@ -107,6 +105,33 @@ module TodoHelper
|
|||
javascript_tag str
|
||||
end
|
||||
|
||||
def item_container_id
|
||||
return "tickler-items" if source_view_is :deferred
|
||||
return "p#{@item.project_id}" if source_view_is :project
|
||||
return "c#{@item.context_id}"
|
||||
end
|
||||
|
||||
def parent_container_type
|
||||
return 'tickler' if source_view_is :deferred
|
||||
return 'project' if source_view_is :project
|
||||
return 'context'
|
||||
end
|
||||
|
||||
def empty_container_msg_div_id
|
||||
return "p#{@item.project_id}empty-nd" if source_view_is :project
|
||||
return "c#{@item.context_id}empty-nd" if source_view_is :context
|
||||
return "tickler-empty-nd" if source_view_is :deferred
|
||||
nil
|
||||
end
|
||||
|
||||
def project_names_for_autocomplete
|
||||
array_or_string_for_javascript( ['None'] + @projects.collect{|p| p.name } )
|
||||
end
|
||||
|
||||
def context_names_for_autocomplete
|
||||
array_or_string_for_javascript( @contexts.collect{|c| c.name } )
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def image_tag_for_delete
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ class Todo < ActiveRecord::Base
|
|||
validates_length_of :notes, :maximum => 60000, :allow_nil => true
|
||||
# validates_chronic_date :due, :allow_nil => true
|
||||
validates_presence_of :show_from, :if => :deferred?
|
||||
validates_presence_of :context
|
||||
|
||||
def validate
|
||||
if deferred? && show_from != nil && show_from < Date.today()
|
||||
|
|
@ -63,12 +64,14 @@ class Todo < ActiveRecord::Base
|
|||
original_project.nil? ? Project.null_object : original_project
|
||||
end
|
||||
|
||||
def self.new_deferred
|
||||
todo = self.new
|
||||
def todo.set_initial_state
|
||||
self.state = 'deferred'
|
||||
alias_method :original_set_initial_state, :set_initial_state
|
||||
|
||||
def set_initial_state
|
||||
if show_from && (show_from > Date.today())
|
||||
write_attribute self.class.state_column, 'deferred'
|
||||
else
|
||||
original_set_initial_state
|
||||
end
|
||||
todo
|
||||
end
|
||||
|
||||
def self.not_done( id=id )
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@
|
|||
</div><!-- [end:display_box] -->
|
||||
|
||||
<div id="input_box">
|
||||
<%= render :partial => "shared/add_new_item_form", :locals => {:hide_link => false, :msg => ""} %>
|
||||
<%= render :partial => "shared/new_action_form", :locals => {:hide_link => false, :msg => ""} %>
|
||||
<%= render :partial => "shared/new_action_form" %>
|
||||
<%= render :partial => "shared/add_new_item_form" %>
|
||||
<%= render "shared/sidebar" %>
|
||||
</div><!-- End of input box -->
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<div id="item-<%= item.id %>-container" class="item-container">
|
||||
<div id="item-<%= item.id %>">
|
||||
<%= link_to_remote_todo item, { :type => 'deferred' } %>
|
||||
<div class="description">
|
||||
<%= show_date( item.show_from ) -%>
|
||||
|
||||
<%= sanitize(item.description) %>
|
||||
<% if item.due -%>
|
||||
(action due on <%= item.due.to_s %>)
|
||||
<% end -%>
|
||||
<%= link_to( "[C]", { :controller => "context", :action => "show", :name => urlize(item.context.name) }, :title => "View context: #{item.context.name}" ) %>
|
||||
<% if item.project_id -%>
|
||||
<%= link_to( "[P]", { :controller => "project", :action => "show", :name => urlize(item.project.name) }, :title => "View project: #{item.project.name}" ) %>
|
||||
<% end -%>
|
||||
<% if item.notes? -%>
|
||||
<%= toggle_show_notes( item ) %>
|
||||
<% end -%>
|
||||
</div>
|
||||
</div><!-- [end:item-<%= item.id %>] -->
|
||||
<div id="action-<%= item.id %>-edit-form" class="edit-form" style="display:none;">
|
||||
<%= form_remote_tag_edit_todo( item, "deferred" ) -%>
|
||||
<% #note: edit form will load here remotely -%>
|
||||
<%= end_form_tag -%>
|
||||
</div><!-- [end:action-<%= item.id %>-edit-form] -->
|
||||
</div><!-- [end:item-<%= item.id %>-container] -->
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<div id="tickler" class="container project">
|
||||
<h2>Deferred actions</h2>
|
||||
|
||||
<div class="items toggle_target">
|
||||
<div id="tickler-empty-nd" style="display:<%= @tickles.empty? ? 'block' : 'none'%>;">
|
||||
<div class="message"><p>Currently there are no deferred actions</p></div>
|
||||
</div>
|
||||
|
||||
<%= render :partial => "item", :collection => @tickles %>
|
||||
|
||||
</div><!-- [end:items] -->
|
||||
</div><!-- [end:tickler] -->
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
page.hide "status"
|
||||
if @saved
|
||||
page.notify :notice, "Added new next action", 5.0
|
||||
page.replace_html "badge_count", @up_count
|
||||
page.send :record, "Form.reset('todo-form-new-action');Form.focusFirstElement('todo-form-new-action')"
|
||||
page.insert_html :bottom, "tickler", :partial => 'deferred/item', :object => @item
|
||||
page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3
|
||||
page["tickler-empty-nd"].hide
|
||||
else
|
||||
page.show 'status'
|
||||
page.replace_html 'status', "#{error_messages_for('item')}"
|
||||
end
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
if @saved
|
||||
page["item-#{@item.id}-container"].remove
|
||||
page['badge_count'].replace_html @down_count
|
||||
if @down_count == 0
|
||||
page.visual_effect :appear, "tickler-empty-nd", :duration => 0.4
|
||||
end
|
||||
else
|
||||
page.notify :error, "There was an error deleting the item #{@item.description}", 8.0
|
||||
end
|
||||
|
|
@ -1 +0,0 @@
|
|||
page.notify :error, @error_message || "An error occurred on the server.", 8.0
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<div id="display_box">
|
||||
|
||||
<%= render :partial => "items" %>
|
||||
|
||||
</div><!-- End of display_box -->
|
||||
|
||||
<div id="input_box">
|
||||
<%= render :partial => "shared/add_new_item_form", :locals => {:hide_link => false, :msg => ""} %>
|
||||
<%= render "shared/sidebar" %>
|
||||
</div><!-- End of input box -->
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
page.hide "info"
|
||||
if @saved
|
||||
if @item.context_id == @original_item_context_id
|
||||
page.replace "item-#{@item.id}-container", :partial => 'deferred/item'
|
||||
page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3
|
||||
else
|
||||
page["item-#{@item.id}-container"].remove
|
||||
page.insert_html :bottom, "c#{@item.context_id}items", :partial => 'deferred/item'
|
||||
page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3
|
||||
end
|
||||
else
|
||||
page.show 'status'
|
||||
page.replace_html 'status', "#{error_messages_for('item')}"
|
||||
end
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
<li><%= navigation_link("Home", {:controller => "todo", :action => "index"}, {:accesskey => "t", :title => "Home"} ) %></li>
|
||||
<li><%= navigation_link( "Contexts", {:controller => "context", :action => "list"}, {:accesskey=>"c", :title=>"Contexts"} ) %></li>
|
||||
<li><%= navigation_link( "Projects", {:controller => "project", :action => "list"}, {:accesskey=>"p", :title=>"Projects"} ) %></li>
|
||||
<li><%= navigation_link( "Tickler", {:controller => "deferred", :action => "index"}, :title => "Ticker" ) %></li>
|
||||
<li><%= navigation_link( "Tickler", {:controller => "todo", :action => "tickler"}, :title => "Tickler" ) %></li>
|
||||
<li><%= navigation_link( "Done", {:controller => "todo", :action => "completed"}, {:accesskey=>"d", :title=>"Completed"} ) %></li>
|
||||
<li><%= navigation_link( "Notes", {:controller => "note", :action => "index"}, {:accesskey => "o", :title => "Show all notes"} ) %></li>
|
||||
<li><%= navigation_link( "Preferences", {:controller => "user", :action => "preferences"}, {:accesskey => "u", :title => "Show my preferences"} ) %></li>
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
<%= periodically_call_remote( :url => {:controller => "login", :action => "check_expiry"},
|
||||
:frequency => (5*60)) %>
|
||||
<% end -%>
|
||||
<%= periodically_call_remote( :url => {:controller => "deferred", :action => "check_tickler"},
|
||||
<%= periodically_call_remote( :url => {:controller => "todo", :action => "check_tickler"},
|
||||
:frequency => (10*60)) %>
|
||||
<%= yield %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -46,8 +46,6 @@
|
|||
</div><!-- [end:display_box] -->
|
||||
|
||||
<div id="input_box">
|
||||
|
||||
<%= render :partial => "shared/add_new_item_form", :locals => {:hide_link => false, :msg => ""} %>
|
||||
<%= render :partial => "shared/new_action_form", :locals => {:hide_link => false, :msg => ""} %>
|
||||
<%= render "shared/sidebar" %>
|
||||
<%= render :partial => "shared/add_new_item_form" %>
|
||||
<%= render "shared/sidebar" %>
|
||||
</div><!-- End of input box -->
|
||||
|
|
|
|||
|
|
@ -1,44 +1,19 @@
|
|||
<%
|
||||
case controller.controller_name
|
||||
when "context"
|
||||
add_string = "Add a next action in this context »"
|
||||
@selected_context = @context.id
|
||||
@selected_project = nil
|
||||
when "project"
|
||||
add_string = "Add a next action in this project »"
|
||||
@selected_context = @contexts[0].id unless @contexts.empty?
|
||||
@selected_project = @project.id
|
||||
when "deferred"
|
||||
add_string = "Add a deferred action »"
|
||||
@selected_context = @contexts[0].id unless @contexts.empty?
|
||||
@selected_project = nil
|
||||
else
|
||||
add_string = "Add a next action »"
|
||||
@selected_context = @contexts[0].id unless @contexts.empty?
|
||||
@selected_project = nil
|
||||
end
|
||||
@initial_context_name = @context.name unless @context.nil?
|
||||
@initial_context_name ||= @contexts[0].name unless @contexts[0].nil?
|
||||
@initial_project_name = @project.name unless @project.nil?
|
||||
%>
|
||||
|
||||
<% hide_link ||= false %>
|
||||
<% unless hide_link -%>
|
||||
<%= link_to_function(
|
||||
add_string,
|
||||
"Element.toggle('todo_new_action');Form.focusFirstElement('todo-form-new-action');",
|
||||
{:title => "Add the next action", :accesskey => "n"}) %>
|
||||
<% end -%>
|
||||
<%= link_to_function("Add a next action »",
|
||||
"Element.toggle('todo_new_action');Form.focusFirstElement('todo-form-new-action');",
|
||||
{:title => "Add a next action", :accesskey => "n"}) %>
|
||||
|
||||
<div id="todo_new_action" class="context_new" style="display:<%= hide_link ? 'block' : 'none'%>">
|
||||
<div id="todo_new_action" class="context_new" style="display:block">
|
||||
|
||||
<!--[form:todo]-->
|
||||
<% if controller.controller_name == "deferred" -%>
|
||||
<%= form_remote_tag(
|
||||
:url => { :controller => "deferred", :action => "create" },
|
||||
:html=> { :id=>'todo-form-new-action', :name=>'todo', :class => 'inline-form' }) %>
|
||||
<% else -%>
|
||||
<%= form_remote_tag(
|
||||
:url => { :controller => controller.controller_name, :action => "add_item" },
|
||||
:url => { :controller => "todo", :action => "create" },
|
||||
:html=> { :id=>'todo-form-new-action', :name=>'todo', :class => 'inline-form' }) %>
|
||||
<% end -%>
|
||||
|
||||
<div id="status"><%= error_messages_for("item") %></div>
|
||||
|
||||
|
|
@ -46,28 +21,40 @@
|
|||
<%= text_field( "todo", "description", "size" => 25, "tabindex" => 1) %><br />
|
||||
|
||||
<label for="todo_notes">Notes</label><br />
|
||||
<%= text_area( "todo", "notes", "cols" => 25, "rows" => 10, "tabindex" => 2) %><br />
|
||||
<%= text_area( "todo", "notes", "cols" => 25, "rows" => 6, "tabindex" => 2) %><br />
|
||||
|
||||
<label for="todo_context_id">Context</label><br />
|
||||
<%= select("todo", "context_id", @contexts.collect {|c| [c.name, c.id] }, { :selected => @selected_context }, {"tabindex" => 3}) %>
|
||||
<label for="todo_context_name">Context</label><br />
|
||||
<input id="todo_context_name" name="context_name" autocomplete="off" tabindex="3" size="25" type="text" value="<%= @initial_context_name %>" />
|
||||
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
contextAutoCompleter = new Autocompleter.Local('todo_context_name', 'context_list', <%= context_names_for_autocomplete %>, {choices:100,autoSelect:true});
|
||||
Event.observe($('todo_context_name'), "focus", contextAutoCompleter.activate.bind(contextAutoCompleter));
|
||||
Event.observe($('todo_context_name'), "click", contextAutoCompleter.activate.bind(contextAutoCompleter));
|
||||
</script>
|
||||
<br />
|
||||
|
||||
<label for="todo_project_id">Project</label><br />
|
||||
<%= select( "todo", "project_id", @projects.select{|x| x.active? }.collect {|p| [p.name, p.id] },
|
||||
{ :selected => @selected_project, :include_blank => true }, {"tabindex" => 4}) %>
|
||||
<label for="todo_project_name">Project</label><br />
|
||||
<input id="todo_project_name" name="project_name" autocomplete="off" tabindex="4" size="25" type="text" value="<%= @initial_project_name %>" />
|
||||
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
projectAutoCompleter = new Autocompleter.Local('todo_project_name', 'project_list', <%= project_names_for_autocomplete %>, {choices:100,autoSelect:true});
|
||||
Event.observe($('todo_project_name'), "focus", projectAutoCompleter.activate.bind(projectAutoCompleter));
|
||||
Event.observe($('todo_project_name'), "click", projectAutoCompleter.activate.bind(projectAutoCompleter));
|
||||
</script>
|
||||
<br />
|
||||
|
||||
<label for="todo_due">Due</label><br />
|
||||
<%= text_field("todo", "due", "size" => 10, "class" => "Date", "onFocus" => "Calendar.setup", "tabindex" => 5, "autocomplete" => "off") %>
|
||||
|
||||
<% if controller.controller_name == "deferred" -%>
|
||||
<br /> <label for="todo_show_from">Show from</label><br />
|
||||
<%= text_field("todo", "show_from", "size" => 10, "class" => "Date", "onFocus" => "Calendar.setup", "tabindex" => 5, "autocomplete" => "off") %><br />
|
||||
<% end -%>
|
||||
<%= text_field("todo", "show_from", "size" => 10, "class" => "Date", "onFocus" => "Calendar.setup", "tabindex" => 6, "autocomplete" => "off") %><br />
|
||||
|
||||
<div id="date-preview" style="display: none;"></div>
|
||||
|
||||
<br /><br />
|
||||
<input type="submit" value="Add item" tabindex="6">
|
||||
<%= source_view_tag( @source_view ) %>
|
||||
<input type="submit" value="Add item" tabindex="7">
|
||||
<%= end_form_tag %><!--[eoform:todo]-->
|
||||
|
||||
<%= observe_field "todo_due",
|
||||
|
|
@ -78,7 +65,5 @@
|
|||
:url => { :controller => "todo", :action => "date_preview" } %>
|
||||
|
||||
<%= calendar_setup( "todo_due" ) %>
|
||||
<% if controller.controller_name == "deferred" -%>
|
||||
<%= calendar_setup( "todo_show_from" ) %>
|
||||
<% end -%>
|
||||
</div><!-- [end:todo-new-action] -->
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
<%
|
||||
@selected_context = @contexts[0].id unless @contexts.empty?
|
||||
@selected_project = nil
|
||||
case controller.controller_name
|
||||
when "context"
|
||||
add_string = "Add a next action in this context »"
|
||||
@selected_context = @context.id
|
||||
when "project"
|
||||
add_string = "Add a next action in this project »"
|
||||
@selected_project = @project.id
|
||||
when "deferred"
|
||||
add_string = "Add a next action »"
|
||||
else
|
||||
add_string = "Add a deferred action »"
|
||||
end
|
||||
%>
|
||||
<% hide_link ||= false %>
|
||||
<% unless hide_link -%>
|
||||
<%= link_to_function(
|
||||
" ",
|
||||
"onclick=new Lightbox.base('mybox', { closeOnOverlayClick : true });Form.focusFirstElement('todo-form-new-action-lightbox'); return false;",
|
||||
{:title => "Add the next action", :accesskey => "q"}) %>
|
||||
<% end -%>
|
||||
|
||||
<div id="mybox" style="display:none">
|
||||
<div id="todo_new_action-lightbox" class="context_new">
|
||||
<%= form_remote_tag(
|
||||
:url => { :controller => controller.controller_name, :action => "add_item" },
|
||||
:html=> { :id=>'todo-form-new-action-lightbox', :name=>'todo', :class => 'inline-form' }) %>
|
||||
|
||||
<h2>Add a new next action:</h2>
|
||||
<div id="status">
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td><label for="todo_description">Description</label></td>
|
||||
<td><%= text_field( "todo", "description", "size" => 30, "tabindex" => 1) %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="todo_notes">Notes</label></td>
|
||||
<td><%= text_area( "todo", "notes", "cols" => 25, "rows" => 10, "tabindex" => 2) %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="todo_context_id">Context</label></td>
|
||||
<td><%= select("todo", "context_id", @contexts.collect {|c| [c.name, c.id] }, { :selected => @selected_context }, {"tabindex" => 3}) %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="todo_project_id">Project</label></td>
|
||||
<td><%= select( "todo", "project_id", @projects.select{|x| x.active? }.collect {|p| [p.name, p.id] }, { :selected => @selected_project, :include_blank => true }, {"tabindex" => 4}) %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="todo_due">Due</label></td>
|
||||
<td><%= text_field("todo", "due", "size" => 10, "class" => "Date", "onFocus" => "Calendar.setup", "tabindex" => 5, "autocomplete" => "off") %></td>
|
||||
</tr>
|
||||
|
||||
<% if controller.controller_name == "tickler" -%>
|
||||
<tr>
|
||||
<td><label for="todo_show_from">Show from</label></td>
|
||||
<td><%= date_select( "todo", "show_from", :start_year => Date.today.year, :order => [:year, :month, :day] ) %></td>
|
||||
</tr>
|
||||
<% end -%>
|
||||
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><input type="submit" value="Add item" tabindex="6"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<%= end_form_tag %><!--[eoform:todo]-->
|
||||
<%= calendar_setup( "todo_due" ) %>
|
||||
</div><!-- [end:todo-new-action] -->
|
||||
</div>
|
||||
|
|
@ -13,34 +13,26 @@
|
|||
<td><%= text_area( "item", "notes", "cols" => 20, "rows" => 5, "tabindex" => 2) %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label"><label for="item_context_id">Context</label></td>
|
||||
<td><select name="item[context_id]" id="item_context_id" tabindex="3">
|
||||
<% for @place in @contexts %>
|
||||
<% if @item %>
|
||||
<% if @place.id == @item.context_id %>
|
||||
<option value="<%= @place.id %>" selected="selected"><%= @place.name %></option>
|
||||
<% else %>
|
||||
<option value="<%= @place.id %>"><%= @place.name %></option>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<option value="<%= @place.id %>"><%= @place.name %></option>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</select></td>
|
||||
<td class="label"><label for="item_context_name">Context</label></td>
|
||||
<td><input id="item_context_name" name="context_name" autocomplete="off" tabindex="3" size="25" type="text" value="<%= @item.context.name %>" />
|
||||
<div class="page_name_auto_complete" id="edit_form_context_list" style="display:none"></div></td>
|
||||
<script type="text/javascript">
|
||||
editFormContextAutoCompleter = new Autocompleter.Local('item_context_name', 'edit_form_context_list', <%= context_names_for_autocomplete %>, {choices:100,autoSelect:true});
|
||||
Event.observe($('item_context_name'), "focus", editFormContextAutoCompleter.activate.bind(editFormContextAutoCompleter));
|
||||
Event.observe($('item_context_name'), "click", editFormContextAutoCompleter.activate.bind(editFormContextAutoCompleter));
|
||||
</script>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label"><label for="item_project_id">Project</label></td>
|
||||
<td class="label"><label for="item_project_name">Project</label></td>
|
||||
<td>
|
||||
<select name="item[project_id]" id="item_project_id" tabindex="4">
|
||||
<% if !@item.project_id? %>
|
||||
<option selected="selected"></option>
|
||||
<%= options_from_collection_for_select(@projects, "id", "name") %>
|
||||
<% else %>
|
||||
<option></option>
|
||||
<%= options_from_collection_for_select(@projects, "id", "name", @item.project_id) %>
|
||||
<% end %>
|
||||
</select>
|
||||
<input id="item_project_name" name="project_name" autocomplete="off" tabindex="4" size="25" type="text" value="<%= @item.project.nil? ? 'None' : @item.project.name %>" />
|
||||
<div class="page_name_auto_complete" id="edit_form_project_list" style="display:none"></div>
|
||||
</td>
|
||||
<script type="text/javascript">
|
||||
editFormProjectAutoCompleter = new Autocompleter.Local('item_project_name', 'edit_form_project_list', <%= project_names_for_autocomplete %>, {choices:100,autoSelect:true});
|
||||
Event.observe($('item_project_name'), "focus", editFormProjectAutoCompleter.activate.bind(editFormProjectAutoCompleter));
|
||||
Event.observe($('item_project_name'), "click", editFormProjectAutoCompleter.activate.bind(editFormProjectAutoCompleter));
|
||||
</script>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label"><label for="item_due">Due</td>
|
||||
|
|
@ -52,7 +44,7 @@
|
|||
<td><input name="item[show_from]" id="show_from_<%= @item.id %>" type="text" value="<%= format_date(@item.show_from) %>" tabindex="5" size="10" onFocus="Calendar.setup" /></td>
|
||||
</tr>
|
||||
<% end -%>
|
||||
<% if controller.controller_name == "project" || @item.current_state == :deferred -%>
|
||||
<% if controller.controller_name == "project" || @item.deferred? -%>
|
||||
<input type="hidden" name="on_project_page" value="true" />
|
||||
<% end -%>
|
||||
<tr>
|
||||
|
|
@ -62,6 +54,6 @@
|
|||
</table>
|
||||
|
||||
<%= calendar_setup( "due_#{@item.id}" ) %>
|
||||
<% if @item.current_state == :deferred -%>
|
||||
<% if @item.deferred? -%>
|
||||
<%= calendar_setup( "show_from_#{@item.id}" ) %>
|
||||
<% end -%>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,47 @@
|
|||
<div id="item-<%= item.id %>-container" class="item-container">
|
||||
|
||||
<div id="item-<%= item.id %>">
|
||||
<%= link_to_remote_todo item %>
|
||||
|
||||
<% unless source_view_is :deferred -%>
|
||||
<input type="checkbox" class="item-checkbox"
|
||||
onclick="new Ajax.Request('<%= url_for :controller => 'todo', :action => 'toggle_check', :id => item.id, :_source_view => @source_view %>', {asynchronous:true, evalScripts:true});"
|
||||
name="item_id" value="<%= item.id %>"<% if item.completed? %> checked="checked" <% end %> />
|
||||
<div class="description<%= staleness_class( item ) %>"><% # start of div which has a class 'description', and possibly 'stale_11', 'stale_12', 'stale_13' etc %>
|
||||
<% if item.completed? -%>
|
||||
<span class="grey"><%= format_date( item.completed_at ) %></span>
|
||||
<% else -%>
|
||||
<%= due_date( item.due ) -%>
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
|
||||
<%= sanitize(item.description) %>
|
||||
<% if item.completed? -%>
|
||||
(<%= item.context.name %><%= ", " + item.project.name if item.project_id %>)
|
||||
<% else -%>
|
||||
<% if parent_container_type == "project" -%>
|
||||
<div class="description<%= staleness_class( item ) %>"><% # start of div which has a class 'description', and possibly 'stale_11', 'stale_12', 'stale_13' etc %>
|
||||
<% if item.completed? -%>
|
||||
<span class="grey"><%= format_date( item.completed_at ) %></span>
|
||||
<% elsif item.deferred? -%>
|
||||
<%= show_date( item.show_from ) -%>
|
||||
<% else -%>
|
||||
<%= due_date( item.due ) -%>
|
||||
<% end -%>
|
||||
|
||||
<%= sanitize(item.description) %>
|
||||
|
||||
<% if item.deferred? && item.due -%>
|
||||
(action due on <%= item.due.to_s %>)
|
||||
<% end -%>
|
||||
|
||||
<% if item.completed? -%>
|
||||
(<%= item.context.name %><%= ", " + item.project.name if item.project_id %>)
|
||||
<% else -%>
|
||||
<% if (parent_container_type == "project" || parent_container_type == "tickler") -%>
|
||||
<%= item_link_to_context( item ) %>
|
||||
<% elsif item.project_id -%>
|
||||
<% end -%>
|
||||
<% if (parent_container_type == "context" || parent_container_type == "tickler") && item.project_id -%>
|
||||
<%= item_link_to_project( item ) %>
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
<% if item.notes? -%>
|
||||
<%= toggle_show_notes( item ) %>
|
||||
<% end -%>
|
||||
</div>
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
|
||||
<% if item.notes? -%>
|
||||
<%= toggle_show_notes( item ) %>
|
||||
<% end -%>
|
||||
</div>
|
||||
</div><!-- [end:item-item.id] -->
|
||||
<div id="action-<%= item.id %>-edit-form" class="edit-form" style="display:none;">
|
||||
<%= form_remote_tag_edit_todo( item, "immediate" ) -%>
|
||||
<%= form_remote_tag_edit_todo( item ) -%>
|
||||
<% #note: edit form will load here remotely -%>
|
||||
<%= end_form_tag -%>
|
||||
</div><!-- [end:action-item.id-edit-form] -->
|
||||
</div><!-- [end:item-item.id-container] -->
|
||||
</div><!-- [end:item-item.id-container] -->
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
if @saved
|
||||
page.notify :notice, "Added new next action", 5.0
|
||||
page['badge_count'].replace_html @up_count
|
||||
status_message = 'Added new next action'
|
||||
status_message += ' to tickler' if @item.deferred?
|
||||
status_message = 'Added new project / ' + status_message if @new_project_created
|
||||
status_message = 'Added new context / ' + status_message if @new_context_created
|
||||
page.notify :notice, status_message, 5.0
|
||||
page['badge_count'].replace_html @down_count
|
||||
page.send :record, "Form.reset('todo-form-new-action');Form.focusFirstElement('todo-form-new-action')"
|
||||
page.send :record, "Form.reset('todo-form-new-action-lightbox');Form.focusFirstElement('todo-form-new-action-lightbox')"
|
||||
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@item.context_id}"
|
||||
page.insert_html :bottom, "c#{@item.context_id}items", :partial => 'todo/item', :locals => { :parent_container_type => "context" }
|
||||
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@item.context_id}" if source_view_is(:todo)
|
||||
page << "contextAutoCompleter.options.array = #{context_names_for_autocomplete}" if @new_context_created
|
||||
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}" if @new_project_created
|
||||
page.insert_html :bottom, item_container_id, :partial => 'todo/item', :locals => { :parent_container_type => parent_container_type, :source_view => @source_view }
|
||||
page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3
|
||||
page["c#{@item.context_id}empty-nd"].hide
|
||||
page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
|
||||
else
|
||||
page.show 'status'
|
||||
page.replace_html 'status', "#{error_messages_for('item')}"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
if @saved
|
||||
page["item-#{@item.id}-container"].remove
|
||||
page['badge_count'].replace_html @down_count
|
||||
source_view do |from|
|
||||
from.todo { page.visual_effect :fade, "c#{@item.context_id}", :duration => 0.4 if @remaining_undone_in_context == 0 }
|
||||
from.context { page["c#{@item.context_id}empty-nd"].show if @down_count == 0 }
|
||||
from.project { page["p#{@item.project_id}empty-nd"].show if @down_count == 0 }
|
||||
end
|
||||
page.visual_effect :fade, "c#{@item.context_id}", :duration => 0.4 if source_view_is :todo && @remaining_undone_in_context == 0
|
||||
page[empty_container_msg_div_id].show if !empty_container_msg_div_id.nil? && @down_count == 0
|
||||
else
|
||||
page.notify :error, "There was an error deleting the item #{@item.description}", 8.0
|
||||
end
|
||||
|
|
@ -9,8 +9,6 @@
|
|||
</div><!-- End of display_box -->
|
||||
|
||||
<div id="input_box">
|
||||
<%= render :partial => "shared/add_new_item_form", :locals => {:hide_link => false, :msg => ""} %>
|
||||
<%= render :partial => "shared/new_action_form", :locals => {:hide_link => false, :msg => ""} %>
|
||||
<%= render :partial => "shared/new_action_form" %>
|
||||
<%= render :partial => "shared/add_new_item_form" %>
|
||||
<%= render "shared/sidebar" %>
|
||||
</div><!-- End of input box -->
|
||||
|
|
@ -1,10 +1,21 @@
|
|||
<div id="display_box">
|
||||
|
||||
<%= render :partial => "tickler_items" %>
|
||||
<div id="tickler" class="container project">
|
||||
<h2>Deferred actions</h2>
|
||||
|
||||
<div id="tickler-items" class="items toggle_target">
|
||||
<div id="tickler-empty-nd" style="display:<%= @tickles.empty? ? 'block' : 'none'%>;">
|
||||
<div class="message"><p>Currently there are no deferred actions</p></div>
|
||||
</div>
|
||||
|
||||
<%= render :partial => "todo/item", :collection => @tickles, :locals => { :parent_container_type => 'tickler' } %>
|
||||
|
||||
</div><!-- [end:items] -->
|
||||
</div><!-- [end:tickler] -->
|
||||
|
||||
</div><!-- End of display_box -->
|
||||
|
||||
<div id="input_box">
|
||||
<%= render :partial => "shared/add_new_item_form", :locals => {:hide_link => false, :msg => ""} %>
|
||||
<%= render :partial => "shared/add_new_item_form" %>
|
||||
<%= render "shared/sidebar" %>
|
||||
</div><!-- End of input box -->
|
||||
|
|
@ -1,15 +1,4 @@
|
|||
if @saved
|
||||
|
||||
item_container_id = "c#{@item.context_id}"
|
||||
parent_container_type = "context"
|
||||
if source_view_is :context
|
||||
empty_container_msg_div_id = "c#{@item.context_id}empty-nd"
|
||||
elsif source_view_is :project
|
||||
item_container_id = "p#{@item.project_id}"
|
||||
empty_container_msg_div_id = "p#{@item.project_id}empty-nd"
|
||||
parent_container_type = "project"
|
||||
end
|
||||
|
||||
if @saved
|
||||
page.remove "item-#{@item.id}-container"
|
||||
if @item.completed?
|
||||
# Don't try to insert contents into a non-existent container!
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
if @saved
|
||||
item_container_id = "item-#{@item.id}-container"
|
||||
status_message = 'Action saved'
|
||||
status_message += ' to tickler' if @item.deferred?
|
||||
status_message = 'Added new project / ' + status_message if @new_project_created
|
||||
status_message = 'Added new context / ' + status_message if @new_context_created
|
||||
page.notify :notice, status_message, 5.0
|
||||
page << "contextAutoCompleter.options.array = #{context_names_for_autocomplete}; contextAutoCompleter.changed = true" if @new_context_created
|
||||
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}; projectAutoCompleter.changed = true" if @new_project_created
|
||||
if source_view_is_one_of [:todo, :context]
|
||||
if @context_changed
|
||||
page[item_container_id].remove
|
||||
|
|
@ -12,7 +19,7 @@ if @saved
|
|||
if source_view_is :todo
|
||||
page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@item.context_id}"
|
||||
page.call "todoItems.expandNextActionListingByContext", "c#{@item.context_id}items", true
|
||||
page.insert_html :bottom, "c#{@item.context_id}items", :partial => 'todo/item', :locals => { :parent_container_type => "context" }
|
||||
page.insert_html :bottom, "c#{@item.context_id}items", :partial => 'todo/item', :locals => { :parent_container_type => parent_container_type }
|
||||
end
|
||||
page.replace_html("badge_count", @remaining_undone_in_context) if source_view_is :context
|
||||
page.delay(0.5) do
|
||||
|
|
@ -23,7 +30,7 @@ if @saved
|
|||
end
|
||||
end
|
||||
else
|
||||
page.replace item_container_id, :partial => 'todo/item', :locals => { :parent_container_type => "context" }
|
||||
page.replace item_container_id, :partial => 'todo/item', :locals => { :parent_container_type => parent_container_type }
|
||||
page.visual_effect :highlight, item_container_id, :duration => 3
|
||||
end
|
||||
elsif source_view_is :project
|
||||
|
|
@ -32,9 +39,18 @@ if @saved
|
|||
page.show("p#{@original_item_project_id}empty-nd") if (@remaining_undone_in_project == 0)
|
||||
page.replace_html "badge_count", @remaining_undone_in_project
|
||||
else
|
||||
page.replace item_container_id, :partial => 'todo/item', :locals => { :parent_container_type => "project" }
|
||||
page.replace item_container_id, :partial => 'todo/item', :locals => { :parent_container_type => parent_container_type }
|
||||
page.visual_effect :highlight, item_container_id, :duration => 3
|
||||
end
|
||||
elsif source_view_is :deferred
|
||||
if @item.deferred?
|
||||
page.replace item_container_id, :partial => 'todo/item', :locals => { :parent_container_type => parent_container_type }
|
||||
page.visual_effect :highlight, item_container_id, :duration => 3
|
||||
else
|
||||
page[item_container_id].remove
|
||||
page.show(empty_container_msg_div_id) if (@down_count == 0)
|
||||
page.replace_html "badge_count", @down_count
|
||||
end
|
||||
else
|
||||
logger.error "unexpected source_view '#{params[:_source_view]}'"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,14 +28,10 @@ ActionController::Routing::Routes.draw do |map|
|
|||
|
||||
# ToDo Routes
|
||||
map.connect 'done', :controller => 'todo', :action => 'completed'
|
||||
|
||||
# Deferred (Tickler) Routes
|
||||
map.connect 'tickler', :controller => 'deferred', :action => 'index'
|
||||
map.connect 'tickler/:action/:id', :controller => 'deferred'
|
||||
map.connect 'tickler', :controller => 'todo', :action => 'tickler'
|
||||
|
||||
# Context Routes
|
||||
map.connect 'context/create', :controller => 'context', :action => 'create'
|
||||
map.connect 'context/add_item', :controller => 'context', :action => 'add_item'
|
||||
map.connect 'context/order', :controller => 'context', :action => 'order'
|
||||
map.connect 'context/:id', :controller=> 'context', :action => 'show', :requirements => {:id => /\d+/}
|
||||
map.connect 'context/:context/feed/:action/:name/:token', :controller => 'feed'
|
||||
|
|
@ -46,7 +42,6 @@ ActionController::Routing::Routes.draw do |map|
|
|||
|
||||
# Projects Routes
|
||||
map.connect 'project/create', :controller => 'project', :action => 'create'
|
||||
map.connect 'project/add_item/:id', :controller => 'project', :action => 'add_item'
|
||||
map.connect 'project/toggle_check/:id', :controller => 'project', :action => 'toggle_check'
|
||||
map.connect 'project/order', :controller => 'project', :action => 'order'
|
||||
map.connect 'project/:project/feed/:action/:name/:token', :controller => 'feed'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ module Tracks
|
|||
end
|
||||
|
||||
def source_view_is( s )
|
||||
s == params[:_source_view].to_sym
|
||||
s == (params[:_source_view] || @source_view).to_sym
|
||||
end
|
||||
|
||||
def source_view_is_one_of( s=[] )
|
||||
|
|
|
|||
317
tracks/public/javascripts/dragdrop.js
vendored
317
tracks/public/javascripts/dragdrop.js
vendored
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
|
||||
//
|
||||
// See scriptaculous.js for full license.
|
||||
|
||||
|
|
@ -15,7 +16,8 @@ var Droppables = {
|
|||
element = $(element);
|
||||
var options = Object.extend({
|
||||
greedy: true,
|
||||
hoverclass: null
|
||||
hoverclass: null,
|
||||
tree: false
|
||||
}, arguments[1] || {});
|
||||
|
||||
// cache containers
|
||||
|
|
@ -37,12 +39,27 @@ var Droppables = {
|
|||
|
||||
this.drops.push(options);
|
||||
},
|
||||
|
||||
isContained: function(element, drop) {
|
||||
var parentNode = element.parentNode;
|
||||
return drop._containers.detect(function(c) { return parentNode == c });
|
||||
|
||||
findDeepestChild: function(drops) {
|
||||
deepest = drops[0];
|
||||
|
||||
for (i = 1; i < drops.length; ++i)
|
||||
if (Element.isParent(drops[i].element, deepest.element))
|
||||
deepest = drops[i];
|
||||
|
||||
return deepest;
|
||||
},
|
||||
|
||||
isContained: function(element, drop) {
|
||||
var containmentNode;
|
||||
if(drop.tree) {
|
||||
containmentNode = element.treeNode;
|
||||
} else {
|
||||
containmentNode = element.parentNode;
|
||||
}
|
||||
return drop._containers.detect(function(c) { return containmentNode == c });
|
||||
},
|
||||
|
||||
isAffected: function(point, element, drop) {
|
||||
return (
|
||||
(drop.element!=element) &&
|
||||
|
|
@ -68,18 +85,22 @@ var Droppables = {
|
|||
|
||||
show: function(point, element) {
|
||||
if(!this.drops.length) return;
|
||||
var affected = [];
|
||||
|
||||
if(this.last_active) this.deactivate(this.last_active);
|
||||
this.drops.each( function(drop) {
|
||||
if(Droppables.isAffected(point, element, drop)) {
|
||||
if(drop.onHover)
|
||||
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
||||
if(drop.greedy) {
|
||||
Droppables.activate(drop);
|
||||
throw $break;
|
||||
}
|
||||
}
|
||||
if(Droppables.isAffected(point, element, drop))
|
||||
affected.push(drop);
|
||||
});
|
||||
|
||||
if(affected.length>0) {
|
||||
drop = Droppables.findDeepestChild(affected);
|
||||
Position.within(drop.element, point[0], point[1]);
|
||||
if(drop.onHover)
|
||||
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
||||
|
||||
Droppables.activate(drop);
|
||||
}
|
||||
},
|
||||
|
||||
fire: function(event, element) {
|
||||
|
|
@ -207,8 +228,10 @@ Draggable.prototype = {
|
|||
|
||||
this.element = $(element);
|
||||
|
||||
if(options.handle && (typeof options.handle == 'string'))
|
||||
this.handle = Element.childrenWithClassName(this.element, options.handle, true)[0];
|
||||
if(options.handle && (typeof options.handle == 'string')) {
|
||||
var h = Element.childrenWithClassName(this.element, options.handle, true);
|
||||
if(h.length>0) this.handle = h[0];
|
||||
}
|
||||
if(!this.handle) this.handle = $(options.handle);
|
||||
if(!this.handle) this.handle = this.element;
|
||||
|
||||
|
|
@ -412,6 +435,7 @@ Draggable.prototype = {
|
|||
if(this.scrollInterval) {
|
||||
clearInterval(this.scrollInterval);
|
||||
this.scrollInterval = null;
|
||||
Draggables._lastScrollPointer = null;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -440,7 +464,14 @@ Draggable.prototype = {
|
|||
Position.prepare();
|
||||
Droppables.show(Draggables._lastPointer, this.element);
|
||||
Draggables.notify('onDrag', this);
|
||||
this.draw(Draggables._lastPointer);
|
||||
Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
|
||||
Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
|
||||
Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
|
||||
if (Draggables._lastScrollPointer[0] < 0)
|
||||
Draggables._lastScrollPointer[0] = 0;
|
||||
if (Draggables._lastScrollPointer[1] < 0)
|
||||
Draggables._lastScrollPointer[1] = 0;
|
||||
this.draw(Draggables._lastScrollPointer);
|
||||
|
||||
if(this.options.change) this.options.change(this);
|
||||
},
|
||||
|
|
@ -492,30 +523,41 @@ SortableObserver.prototype = {
|
|||
}
|
||||
|
||||
var Sortable = {
|
||||
sortables: new Array(),
|
||||
sortables: {},
|
||||
|
||||
options: function(element){
|
||||
element = $(element);
|
||||
return this.sortables.detect(function(s) { return s.element == element });
|
||||
_findRootElement: function(element) {
|
||||
while (element.tagName != "BODY") {
|
||||
if(element.id && Sortable.sortables[element.id]) return element;
|
||||
element = element.parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
options: function(element) {
|
||||
element = Sortable._findRootElement($(element));
|
||||
if(!element) return;
|
||||
return Sortable.sortables[element.id];
|
||||
},
|
||||
|
||||
destroy: function(element){
|
||||
element = $(element);
|
||||
this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
|
||||
var s = Sortable.options(element);
|
||||
|
||||
if(s) {
|
||||
Draggables.removeObserver(s.element);
|
||||
s.droppables.each(function(d){ Droppables.remove(d) });
|
||||
s.draggables.invoke('destroy');
|
||||
});
|
||||
this.sortables = this.sortables.reject(function(s) { return s.element == element });
|
||||
|
||||
delete Sortable.sortables[s.element.id];
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
create: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend({
|
||||
element: element,
|
||||
tag: 'li', // assumes li children, override with tag: 'tagname'
|
||||
dropOnEmpty: false,
|
||||
tree: false, // fixme: unimplemented
|
||||
tree: false,
|
||||
treeTag: 'ul',
|
||||
overlap: 'vertical', // one of 'vertical', 'horizontal'
|
||||
constraint: 'vertical', // one of 'vertical', 'horizontal', false
|
||||
containment: element, // also takes array of elements (or id's); or false
|
||||
|
|
@ -565,9 +607,17 @@ var Sortable = {
|
|||
var options_for_droppable = {
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
tree: options.tree,
|
||||
hoverclass: options.hoverclass,
|
||||
onHover: Sortable.onHover,
|
||||
greedy: !options.dropOnEmpty
|
||||
onHover: Sortable.onHover
|
||||
//greedy: !options.dropOnEmpty
|
||||
}
|
||||
|
||||
var options_for_tree = {
|
||||
onHover: Sortable.onEmptyHover,
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
hoverclass: options.hoverclass
|
||||
}
|
||||
|
||||
// fix for gecko engine
|
||||
|
|
@ -576,12 +626,9 @@ var Sortable = {
|
|||
options.draggables = [];
|
||||
options.droppables = [];
|
||||
|
||||
// make it so
|
||||
|
||||
// drop on empty handling
|
||||
if(options.dropOnEmpty) {
|
||||
Droppables.add(element,
|
||||
{containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
|
||||
if(options.dropOnEmpty || options.tree) {
|
||||
Droppables.add(element, options_for_tree);
|
||||
options.droppables.push(element);
|
||||
}
|
||||
|
||||
|
|
@ -592,11 +639,20 @@ var Sortable = {
|
|||
options.draggables.push(
|
||||
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
|
||||
Droppables.add(e, options_for_droppable);
|
||||
if(options.tree) e.treeNode = element;
|
||||
options.droppables.push(e);
|
||||
});
|
||||
|
||||
if(options.tree) {
|
||||
(Sortable.findTreeElements(element, options) || []).each( function(e) {
|
||||
Droppables.add(e, options_for_tree);
|
||||
e.treeNode = element;
|
||||
options.droppables.push(e);
|
||||
});
|
||||
}
|
||||
|
||||
// keep reference
|
||||
this.sortables.push(options);
|
||||
this.sortables[element.id] = options;
|
||||
|
||||
// for onupdate
|
||||
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
|
||||
|
|
@ -605,24 +661,21 @@ var Sortable = {
|
|||
|
||||
// return all suitable-for-sortable elements in a guaranteed order
|
||||
findElements: function(element, options) {
|
||||
if(!element.hasChildNodes()) return null;
|
||||
var elements = [];
|
||||
var only = options.only ? [options.only].flatten() : null;
|
||||
$A(element.childNodes).each( function(e) {
|
||||
if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
|
||||
(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
|
||||
elements.push(e);
|
||||
if(options.tree) {
|
||||
var grandchildren = this.findElements(e, options);
|
||||
if(grandchildren) elements.push(grandchildren);
|
||||
}
|
||||
});
|
||||
|
||||
return (elements.length>0 ? elements.flatten() : null);
|
||||
return Element.findChildren(
|
||||
element, options.only, options.tree ? true : false, options.tag);
|
||||
},
|
||||
|
||||
findTreeElements: function(element, options) {
|
||||
return Element.findChildren(
|
||||
element, options.only, options.tree ? true : false, options.treeTag);
|
||||
},
|
||||
|
||||
onHover: function(element, dropon, overlap) {
|
||||
if(overlap>0.5) {
|
||||
if(Element.isParent(dropon, element)) return;
|
||||
|
||||
if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
|
||||
return;
|
||||
} else if(overlap>0.5) {
|
||||
Sortable.mark(dropon, 'before');
|
||||
if(dropon.previousSibling != element) {
|
||||
var oldParentNode = element.parentNode;
|
||||
|
|
@ -645,13 +698,37 @@ var Sortable = {
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
onEmptyHover: function(element, dropon) {
|
||||
if(element.parentNode!=dropon) {
|
||||
var oldParentNode = element.parentNode;
|
||||
dropon.appendChild(element);
|
||||
|
||||
onEmptyHover: function(element, dropon, overlap) {
|
||||
var oldParentNode = element.parentNode;
|
||||
var droponOptions = Sortable.options(dropon);
|
||||
|
||||
if(!Element.isParent(dropon, element)) {
|
||||
var index;
|
||||
|
||||
var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
|
||||
var child = null;
|
||||
|
||||
if(children) {
|
||||
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
|
||||
|
||||
for (index = 0; index < children.length; index += 1) {
|
||||
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
|
||||
offset -= Element.offsetSize (children[index], droponOptions.overlap);
|
||||
} else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
|
||||
child = index + 1 < children.length ? children[index + 1] : null;
|
||||
break;
|
||||
} else {
|
||||
child = children[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropon.insertBefore(element, child);
|
||||
|
||||
Sortable.options(oldParentNode).onChange(element);
|
||||
Sortable.options(dropon).onChange(element);
|
||||
droponOptions.onChange(element);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -683,6 +760,75 @@ var Sortable = {
|
|||
|
||||
Element.show(Sortable._marker);
|
||||
},
|
||||
|
||||
_tree: function(element, options, parent) {
|
||||
var children = Sortable.findElements(element, options) || [];
|
||||
|
||||
for (var i = 0; i < children.length; ++i) {
|
||||
var match = children[i].id.match(options.format);
|
||||
|
||||
if (!match) continue;
|
||||
|
||||
var child = {
|
||||
id: encodeURIComponent(match ? match[1] : null),
|
||||
element: element,
|
||||
parent: parent,
|
||||
children: new Array,
|
||||
position: parent.children.length,
|
||||
container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
|
||||
}
|
||||
|
||||
/* Get the element containing the children and recurse over it */
|
||||
if (child.container)
|
||||
this._tree(child.container, options, child)
|
||||
|
||||
parent.children.push (child);
|
||||
}
|
||||
|
||||
return parent;
|
||||
},
|
||||
|
||||
/* Finds the first element of the given tag type within a parent element.
|
||||
Used for finding the first LI[ST] within a L[IST]I[TEM].*/
|
||||
_findChildrenElement: function (element, containerTag) {
|
||||
if (element && element.hasChildNodes)
|
||||
for (var i = 0; i < element.childNodes.length; ++i)
|
||||
if (element.childNodes[i].tagName == containerTag)
|
||||
return element.childNodes[i];
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
tree: function(element) {
|
||||
element = $(element);
|
||||
var sortableOptions = this.options(element);
|
||||
var options = Object.extend({
|
||||
tag: sortableOptions.tag,
|
||||
treeTag: sortableOptions.treeTag,
|
||||
only: sortableOptions.only,
|
||||
name: element.id,
|
||||
format: sortableOptions.format
|
||||
}, arguments[1] || {});
|
||||
|
||||
var root = {
|
||||
id: null,
|
||||
parent: null,
|
||||
children: new Array,
|
||||
container: element,
|
||||
position: 0
|
||||
}
|
||||
|
||||
return Sortable._tree (element, options, root);
|
||||
},
|
||||
|
||||
/* Construct a [i] index for a particular node */
|
||||
_constructIndex: function(node) {
|
||||
var index = '';
|
||||
do {
|
||||
if (node.id) index = '[' + node.position + ']' + index;
|
||||
} while ((node = node.parent) != null);
|
||||
return index;
|
||||
},
|
||||
|
||||
sequence: function(element) {
|
||||
element = $(element);
|
||||
|
|
@ -705,20 +851,63 @@ var Sortable = {
|
|||
});
|
||||
|
||||
new_sequence.each(function(ident) {
|
||||
var n = nodeMap[ident];
|
||||
if (n) {
|
||||
n[1].appendChild(n[0]);
|
||||
delete nodeMap[ident];
|
||||
}
|
||||
var n = nodeMap[ident];
|
||||
if (n) {
|
||||
n[1].appendChild(n[0]);
|
||||
delete nodeMap[ident];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
serialize: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend(Sortable.options(element), arguments[1] || {});
|
||||
var name = encodeURIComponent(
|
||||
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
|
||||
return Sortable.sequence(element, arguments[1]).map( function(item) {
|
||||
return name + "[]=" + encodeURIComponent(item);
|
||||
}).join('&');
|
||||
|
||||
if (options.tree) {
|
||||
return Sortable.tree(element, arguments[1]).children.map( function (item) {
|
||||
return [name + Sortable._constructIndex(item) + "=" +
|
||||
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
|
||||
}).flatten().join('&');
|
||||
} else {
|
||||
return Sortable.sequence(element, arguments[1]).map( function(item) {
|
||||
return name + "[]=" + encodeURIComponent(item);
|
||||
}).join('&');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if child is contained within element */
|
||||
Element.isParent = function(child, element) {
|
||||
if (!child.parentNode || child == element) return false;
|
||||
|
||||
if (child.parentNode == element) return true;
|
||||
|
||||
return Element.isParent(child.parentNode, element);
|
||||
}
|
||||
|
||||
Element.findChildren = function(element, only, recursive, tagName) {
|
||||
if(!element.hasChildNodes()) return null;
|
||||
tagName = tagName.toUpperCase();
|
||||
if(only) only = [only].flatten();
|
||||
var elements = [];
|
||||
$A(element.childNodes).each( function(e) {
|
||||
if(e.tagName && e.tagName.toUpperCase()==tagName &&
|
||||
(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
|
||||
elements.push(e);
|
||||
if(recursive) {
|
||||
var grandchildren = Element.findChildren(e, only, recursive, tagName);
|
||||
if(grandchildren) elements.push(grandchildren);
|
||||
}
|
||||
});
|
||||
|
||||
return (elements.length>0 ? elements.flatten() : []);
|
||||
}
|
||||
|
||||
Element.offsetSize = function (element, type) {
|
||||
if (type == 'vertical' || type == 'height')
|
||||
return element.offsetHeight;
|
||||
else
|
||||
return element.offsetWidth;
|
||||
}
|
||||
25
tracks/public/javascripts/effects.js
vendored
25
tracks/public/javascripts/effects.js
vendored
|
|
@ -77,9 +77,12 @@ Element.getInlineOpacity = function(element){
|
|||
}
|
||||
|
||||
Element.childrenWithClassName = function(element, className, findFirst) {
|
||||
return [$A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) {
|
||||
return c.className ? Element.hasClassName(c, className) : false;
|
||||
})].flatten();
|
||||
var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
|
||||
var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) {
|
||||
return (c.className && c.className.match(classNameRegExp));
|
||||
});
|
||||
if(!results) results = [];
|
||||
return results;
|
||||
}
|
||||
|
||||
Element.forceRerendering = function(element) {
|
||||
|
|
@ -91,11 +94,6 @@ Element.forceRerendering = function(element) {
|
|||
} catch(e) { }
|
||||
};
|
||||
|
||||
['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
|
||||
'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(
|
||||
function(f) { Element.Methods[f] = Element[f]; }
|
||||
);
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Array.prototype.call = function() {
|
||||
|
|
@ -943,11 +941,18 @@ Effect.Fold = function(element) {
|
|||
effect.element.setStyle(oldStyle);
|
||||
} });
|
||||
}}, arguments[1] || {}));
|
||||
}
|
||||
};
|
||||
|
||||
['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
|
||||
'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(
|
||||
function(f) { Element.Methods[f] = Element[f]; }
|
||||
);
|
||||
|
||||
Element.Methods.visualEffect = function(element, effect, options) {
|
||||
s = effect.gsub(/_/, '-').camelize();
|
||||
effect_class = s.charAt(0).toUpperCase() + s.substring(1);
|
||||
new Effect[effect_class](element, options);
|
||||
return $(element);
|
||||
};
|
||||
};
|
||||
|
||||
Element.addMethods();
|
||||
97
tracks/public/javascripts/prototype.js
vendored
97
tracks/public/javascripts/prototype.js
vendored
|
|
@ -1,4 +1,4 @@
|
|||
/* Prototype JavaScript framework, version 1.5.0_pre1
|
||||
/* Prototype JavaScript framework, version 1.5.0_rc0
|
||||
* (c) 2005 Sam Stephenson <sam@conio.net>
|
||||
*
|
||||
* Prototype is freely distributable under the terms of an MIT-style license.
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
var Prototype = {
|
||||
Version: '1.5.0_pre1',
|
||||
Version: '1.5.0_rc0',
|
||||
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
|
||||
|
||||
emptyFunction: function() {},
|
||||
|
|
@ -25,7 +25,7 @@ var Class = {
|
|||
var Abstract = new Object();
|
||||
|
||||
Object.extend = function(destination, source) {
|
||||
for (property in source) {
|
||||
for (var property in source) {
|
||||
destination[property] = source[property];
|
||||
}
|
||||
return destination;
|
||||
|
|
@ -176,7 +176,7 @@ Object.extend(String.prototype, {
|
|||
},
|
||||
|
||||
evalScripts: function() {
|
||||
return this.extractScripts().map(eval);
|
||||
return this.extractScripts().map(function(script) { return eval(script) });
|
||||
},
|
||||
|
||||
escapeHTML: function() {
|
||||
|
|
@ -355,7 +355,7 @@ var Enumerable = {
|
|||
var result;
|
||||
this.each(function(value, index) {
|
||||
value = (iterator || Prototype.K)(value, index);
|
||||
if (value >= (result || value))
|
||||
if (result == undefined || value >= result)
|
||||
result = value;
|
||||
});
|
||||
return result;
|
||||
|
|
@ -365,7 +365,7 @@ var Enumerable = {
|
|||
var result;
|
||||
this.each(function(value, index) {
|
||||
value = (iterator || Prototype.K)(value, index);
|
||||
if (value <= (result || value))
|
||||
if (result == undefined || value < result)
|
||||
result = value;
|
||||
});
|
||||
return result;
|
||||
|
|
@ -447,7 +447,8 @@ var $A = Array.from = function(iterable) {
|
|||
|
||||
Object.extend(Array.prototype, Enumerable);
|
||||
|
||||
Array.prototype._reverse = Array.prototype.reverse;
|
||||
if (!Array.prototype._reverse)
|
||||
Array.prototype._reverse = Array.prototype.reverse;
|
||||
|
||||
Object.extend(Array.prototype, {
|
||||
_each: function(iterator) {
|
||||
|
|
@ -476,7 +477,7 @@ Object.extend(Array.prototype, {
|
|||
|
||||
flatten: function() {
|
||||
return this.inject([], function(array, value) {
|
||||
return array.concat(value.constructor == Array ?
|
||||
return array.concat(value && value.constructor == Array ?
|
||||
value.flatten() : [value]);
|
||||
});
|
||||
},
|
||||
|
|
@ -498,21 +499,13 @@ Object.extend(Array.prototype, {
|
|||
return (inline !== false ? this : this.toArray())._reverse();
|
||||
},
|
||||
|
||||
shift: function() {
|
||||
var result = this[0];
|
||||
for (var i = 0; i < this.length - 1; i++)
|
||||
this[i] = this[i + 1];
|
||||
this.length--;
|
||||
return result;
|
||||
},
|
||||
|
||||
inspect: function() {
|
||||
return '[' + this.map(Object.inspect).join(', ') + ']';
|
||||
}
|
||||
});
|
||||
var Hash = {
|
||||
_each: function(iterator) {
|
||||
for (key in this) {
|
||||
for (var key in this) {
|
||||
var value = this[key];
|
||||
if (typeof value == 'function') continue;
|
||||
|
||||
|
|
@ -590,9 +583,9 @@ var $R = function(start, end, exclusive) {
|
|||
var Ajax = {
|
||||
getTransport: function() {
|
||||
return Try.these(
|
||||
function() {return new XMLHttpRequest()},
|
||||
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
|
||||
function() {return new ActiveXObject('Microsoft.XMLHTTP')},
|
||||
function() {return new XMLHttpRequest()}
|
||||
function() {return new ActiveXObject('Microsoft.XMLHTTP')}
|
||||
) || false;
|
||||
},
|
||||
|
||||
|
|
@ -644,6 +637,7 @@ Ajax.Base.prototype = {
|
|||
this.options = {
|
||||
method: 'post',
|
||||
asynchronous: true,
|
||||
contentType: 'application/x-www-form-urlencoded',
|
||||
parameters: ''
|
||||
}
|
||||
Object.extend(this.options, options || {});
|
||||
|
|
@ -707,8 +701,7 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
|
|||
'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
|
||||
|
||||
if (this.options.method == 'post') {
|
||||
requestHeaders.push('Content-type',
|
||||
'application/x-www-form-urlencoded');
|
||||
requestHeaders.push('Content-type', this.options.contentType);
|
||||
|
||||
/* Force "Connection: close" for Mozilla browsers to work around
|
||||
* a bug where XMLHttpReqeuest sends an incorrect Content-length
|
||||
|
|
@ -739,7 +732,7 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
|
|||
|
||||
evalJSON: function() {
|
||||
try {
|
||||
return eval(this.header('X-JSON'));
|
||||
return eval('(' + this.header('X-JSON') + ')');
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
|
|
@ -900,13 +893,14 @@ if (!window.Element)
|
|||
|
||||
Element.extend = function(element) {
|
||||
if (!element) return;
|
||||
if (_nativeExtensions) return element;
|
||||
|
||||
if (!element._extended && element.tagName && element != window) {
|
||||
var methods = Element.Methods;
|
||||
var methods = Element.Methods, cache = Element.extend.cache;
|
||||
for (property in methods) {
|
||||
var value = methods[property];
|
||||
if (typeof value == 'function')
|
||||
element[property] = value.bind(null, element);
|
||||
element[property] = cache.findOrStore(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -914,6 +908,14 @@ Element.extend = function(element) {
|
|||
return element;
|
||||
}
|
||||
|
||||
Element.extend.cache = {
|
||||
findOrStore: function(value) {
|
||||
return this[value] = this[value] || function() {
|
||||
return value.apply(null, [this].concat($A(arguments)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Element.Methods = {
|
||||
visible: function(element) {
|
||||
return $(element).style.display != 'none';
|
||||
|
|
@ -1035,7 +1037,7 @@ Element.Methods = {
|
|||
|
||||
setStyle: function(element, style) {
|
||||
element = $(element);
|
||||
for (name in style)
|
||||
for (var name in style)
|
||||
element.style[name.camelize()] = style[name];
|
||||
},
|
||||
|
||||
|
|
@ -1105,6 +1107,29 @@ Element.Methods = {
|
|||
|
||||
Object.extend(Element, Element.Methods);
|
||||
|
||||
var _nativeExtensions = false;
|
||||
|
||||
if(!HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
|
||||
var HTMLElement = {}
|
||||
HTMLElement.prototype = document.createElement('div').__proto__;
|
||||
}
|
||||
|
||||
Element.addMethods = function(methods) {
|
||||
Object.extend(Element.Methods, methods || {});
|
||||
|
||||
if(typeof HTMLElement != 'undefined') {
|
||||
var methods = Element.Methods, cache = Element.extend.cache;
|
||||
for (property in methods) {
|
||||
var value = methods[property];
|
||||
if (typeof value == 'function')
|
||||
HTMLElement.prototype[property] = cache.findOrStore(value);
|
||||
}
|
||||
_nativeExtensions = true;
|
||||
}
|
||||
}
|
||||
|
||||
Element.addMethods();
|
||||
|
||||
var Toggle = new Object();
|
||||
Toggle.display = Element.toggle;
|
||||
|
||||
|
|
@ -1123,7 +1148,8 @@ Abstract.Insertion.prototype = {
|
|||
try {
|
||||
this.element.insertAdjacentHTML(this.adjacency, this.content);
|
||||
} catch (e) {
|
||||
if (this.element.tagName.toLowerCase() == 'tbody') {
|
||||
var tagName = this.element.tagName.toLowerCase();
|
||||
if (tagName == 'tbody' || tagName == 'tr') {
|
||||
this.insertContent(this.contentFromAnonymousTable());
|
||||
} else {
|
||||
throw e;
|
||||
|
|
@ -1396,7 +1422,7 @@ var Form = {
|
|||
form = $(form);
|
||||
var elements = new Array();
|
||||
|
||||
for (tagName in Form.Element.Serializers) {
|
||||
for (var tagName in Form.Element.Serializers) {
|
||||
var tagElements = form.getElementsByTagName(tagName);
|
||||
for (var j = 0; j < tagElements.length; j++)
|
||||
elements.push(tagElements[j]);
|
||||
|
|
@ -1518,23 +1544,17 @@ Form.Element.Serializers = {
|
|||
var value = '', opt, index = element.selectedIndex;
|
||||
if (index >= 0) {
|
||||
opt = element.options[index];
|
||||
value = opt.value;
|
||||
if (!value && !('value' in opt))
|
||||
value = opt.text;
|
||||
value = opt.value || opt.text;
|
||||
}
|
||||
return [element.name, value];
|
||||
},
|
||||
|
||||
selectMany: function(element) {
|
||||
var value = new Array();
|
||||
var value = [];
|
||||
for (var i = 0; i < element.length; i++) {
|
||||
var opt = element.options[i];
|
||||
if (opt.selected) {
|
||||
var optValue = opt.value;
|
||||
if (!optValue && !('value' in opt))
|
||||
optValue = opt.text;
|
||||
value.push(optValue);
|
||||
}
|
||||
if (opt.selected)
|
||||
value.push(opt.value || opt.text);
|
||||
}
|
||||
return [element.name, value];
|
||||
}
|
||||
|
|
@ -1751,7 +1771,8 @@ Object.extend(Event, {
|
|||
});
|
||||
|
||||
/* prevent memory leaks in IE */
|
||||
Event.observe(window, 'unload', Event.unloadCache, false);
|
||||
if (navigator.appVersion.match(/\bMSIE\b/))
|
||||
Event.observe(window, 'unload', Event.unloadCache, false);
|
||||
var Position = {
|
||||
// set to true if needed, warning: firefox performance problems
|
||||
// NOT neeeded for page scrolling, only if draggable contained in
|
||||
|
|
|
|||
|
|
@ -698,4 +698,34 @@ input.open_id {
|
|||
color: #000;
|
||||
padding-left: 18px;
|
||||
width:182px;
|
||||
}
|
||||
div.page_name_auto_complete {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.page_name_auto_complete ul {
|
||||
border: 1px solid #888;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
div.page_name_auto_complete ul li {
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
font-weight:bold;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
div.page_name_auto_complete ul li.selected {
|
||||
background-color: #ffb;
|
||||
}
|
||||
|
||||
div.page_name_auto_complete ul strong.highlight {
|
||||
color: #800;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'deferred_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class DeferredController; def rescue_action(e) raise e end; end
|
||||
|
||||
class DeferredControllerTest < Test::Unit::TestCase
|
||||
fixtures :users, :preferences, :projects, :contexts, :todos
|
||||
|
||||
def setup
|
||||
@controller = DeferredController.new
|
||||
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_create
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
xhr :post, :create, :todo => {:description => 'deferred controller test create', :notes => 'notes', :context_id => 1, :project_id => 1, :due => '', :show_from => '11/30/2030'}
|
||||
t = Todo.find_by_description('deferred controller test create')
|
||||
assert_equal :deferred, t.current_state.to_sym
|
||||
end
|
||||
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue