- <%= remote_star_icon %>
- <%= remote_toggle_checkbox unless source_view_is :deferred %>
- <%= remote_edit_button unless suppress_edit_button %>
+ <%= remote_star_icon(todo) %>
+ <%= remote_toggle_checkbox(todo) unless source_view_is :deferred %>
+ <%= remote_edit_button(todo) unless suppress_edit_button %>
- <%= grip_span %>
- <%= date_span -%>
+ <%= grip_span(todo) %>
+ <%= date_span(todo) -%>
<%= h todo.description %>
- <%= image_tag_for_recurring_todo(todo) if @todo.from_recurring_todo? %>
- <%= tag_list %>
- <%= deferred_due_date %>
- <%= project_and_context_links( parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %>
+ <%= image_tag_for_recurring_todo(todo) if todo.from_recurring_todo? %>
+ <%= tag_list(todo) %>
+ <%= deferred_due_date(todo) %>
+ <%= project_and_context_links( todo, parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %>
<%= collapsed_notes_image(todo) if todo.notes? %>
<%= collapsed_successors_image(todo) unless todo.pending_successors.empty? %>
diff --git a/app/views/todos/add_predecessor.js.erb b/app/views/todos/add_predecessor.js.erb
index aaa5f45a..25517fbc 100644
--- a/app/views/todos/add_predecessor.js.erb
+++ b/app/views/todos/add_predecessor.js.erb
@@ -19,7 +19,7 @@ function replace_updated_predecessor() {
function show_in_tickler_box() {
$("#tickler-empty-nd").hide();
- $('#tickler').html( html_for_deferred_todos() );
+ $('#tickler').append( html_for_deferred_todo() );
}
function regenerate_predecessor_family() {
@@ -37,12 +37,8 @@ function html_for_predecessor() {
return "<%= escape_javascript(render(:partial => @predecessor, :locals => { :parent_container_type => parent_container_type })) %>";
}
-function html_for_deferred_todos() {
- return "<%= escape_javascript(render(:partial => 'todos/deferred', :locals => { :deferred => @todo.project.deferred_todos,
- :collapsible => false,
- :append_descriptor => t('todos.append_in_this_project'),
- :parent_container_type => 'project',
- :pending => @todo.project.pending_todos })) %>";
+function html_for_deferred_todo() {
+ return "<%= escape_javascript(render(:partial => @todo, :locals => { :parent_container_type => parent_container_type })) %>";
}
<% end # if !@saved
diff --git a/app/views/todos/remove_predecessor.js.erb b/app/views/todos/remove_predecessor.js.erb
index 5b6ff3bb..85ea8fa5 100644
--- a/app/views/todos/remove_predecessor.js.erb
+++ b/app/views/todos/remove_predecessor.js.erb
@@ -1,35 +1,63 @@
-if @removed
- status_message = t('todos.removed_predecessor', :successor => @successor.description, :predecessor => @predecessor.description)
- page.notify :notice, status_message, 5.0
+<% # TODO: lots of overlap with add_predecessor --> helpers?
+ if @removed -%>
+ TracksPages.page_notify('notice', "<%= t('todos.removed_predecessor', :successor => @successor.description, :predecessor => @predecessor.description) %>", 8);
- # replace old predecessor with one without the successor
- page.replace dom_id(@predecessor), :partial => 'todos/todo', :locals => {
- :todo => @predecessor, :parent_container_type => parent_container_type }
+ replace_updated_predecessor();
+ regenerate_predecessor_family();
+ update_successor();
+<% else -%>
+ TracksPages.page_notify('error', "<%=t('todos.error_removing_dependency')%>", 8);
+<% end -%>
- # regenerate predecessor family
+function replace_updated_predecessor() {
+ $('#<%= dom_id(@predecessor) %>').html( html_for_predecessor() );
+}
+
+function regenerate_predecessor_family() {
+<%
parents = @predecessor.predecessors
until parents.empty?
parent = parents.pop
- parents += parent.predecessors
- page[parent].replace_html :partial => 'todos/todo', :locals => { :todo => parent, :parent_container_type => parent_container_type }
+ parents += parent.predecessors -%>
+ $('#<%= dom_id(parent) %>').html("<%= escape_javascript(render(:partial => parent, :locals => { :parent_container_type => parent_container_type })) %>");
+<%end -%>
+}
+
+function update_successor() {
+ <%
+ if @successor.active? -%>
+ <%= "remove_successor();" unless source_view_is_one_of(:todo, :context) %>
+ <%= "hide_empty_message();" unless empty_container_msg_div_id.nil? %>
+ <%= "show_empty_deferred_message(); " if @remaining_deferred_or_pending_count == 0 %>
+ <% if source_view_is_one_of(:todo, :deferred, :tag) -%>
+ $('#c<%= @successor.context_id %>').fadeIn(500, function() {});
+ $('#no_todos_in_tag_view').slideUp(100);
+ <% end -%>
+ $('#<%=item_container_id(@successor)%>').append(html_for_new_successor());
+ $('#<%= dom_id(@successor, 'line')%>').effect('highlight', {}, 2000 ); <%
+ elsif @successor.deferred? -%>
+ $('#<%= dom_id(@successor)%>').html(html_for_new_successor()); <%
end
-
- # update display if pending->active
- if @successor.active?
- page[@successor].remove unless source_view_is_one_of(:todo, :context)
- page[empty_container_msg_div_id].hide unless empty_container_msg_div_id.nil?
- page.call "todoItems.ensureVisibleWithEffectAppear", "c#{@successor.context_id}"
- page.insert_html :bottom, item_container_id(@successor), :partial => 'todos/todo', :locals => {
- :todo => @successor, :parent_container_type => parent_container_type }
- page.visual_effect :highlight, dom_id(@successor, 'line'), {'startcolor' => "'#99ff99'"}
- end
-
- # update display if pending->deferred
- if @successor.deferred?
- page.replace dom_id(@successor), :partial => 'todos/todo', :locals => {
- :todo => @successor, :parent_container_type => parent_container_type }
- end
- page << "enable_rich_interaction();"
-else
- page.notify :error, t('todos.error_removing_dependency'), 8.0
-end
+ %>
+}
+
+function hide_empty_message() {
+ $('#<%=empty_container_msg_div_id%>').hide();
+}
+
+function show_empty_deferred_message() {
+ $('#tickler-empty-nd').slideDown(100);
+}
+function remove_successor() {
+ <% # TODO: last todo in context --> remove context??
+ -%>
+ $('#<%=dom_id(@successor)%>').remove();
+}
+
+function html_for_predecessor() {
+ return "<%= @removed ? escape_javascript(render(:partial => @predecessor, :locals => { :parent_container_type => parent_container_type })) : "" %>";
+}
+
+function html_for_new_successor() {
+ return "<%= @removed ? escape_javascript(render(:partial => @successor, :locals => { :parent_container_type => parent_container_type })) : "" %>";
+}
\ No newline at end of file
diff --git a/app/views/todos/update.js.erb b/app/views/todos/update.js.erb
index 2c638cef..f7813960 100644
--- a/app/views/todos/update.js.erb
+++ b/app/views/todos/update.js.erb
@@ -16,6 +16,7 @@
animation << "hide_context" if update_needs_to_hide_context
animation << "highlight_updated_todo"
animation << "update_empty_tag_container" if source_view_is(:tag)
+ animation << "update_predecessors"
%>
<%= render_animation(animation) %>
@@ -35,7 +36,7 @@ function add_to_existing_container(next_steps) {
$('#<%= item_container_id(@todo) %>').append(html_for_todo());
<% if source_view_is_one_of(:project,:calendar) -%>
next_steps.go();
- <% if (@target_context_count==1) || (@todo.deferred? && @remaining_deferred_or_pending_count == 1) -%>
+ <% if (@target_context_count==1) || ( (@todo.deferred? || @todo.pending?) && @remaining_deferred_or_pending_count == 1) -%>
$("#<%= empty_container_msg_div_id %>").slideUp(100);
<% end -%>
<% else -%>
@@ -109,4 +110,9 @@ function update_predecessors() {
$('#<%=dom_id(p)%>').html('<%=escape_javascript(render(:partial => p, :locals => { :parent_container_type => parent_container_type }))%>');
}
<% end -%>
+ <% @removed_predecessors.each do |p| -%>
+ if ($('#<%=item_container_id(p)%>')) {
+ $('#<%=dom_id(p)%>').html('<%=escape_javascript(render(:partial => p, :locals => { :parent_container_type => parent_container_type }))%>');
+ }
+ <% end -%>
}
\ No newline at end of file
diff --git a/app/views/users/destroy.js.rjs b/app/views/users/destroy.js.erb
similarity index 100%
rename from app/views/users/destroy.js.rjs
rename to app/views/users/destroy.js.erb
diff --git a/features/dependencies.feature b/features/dependencies.feature
index 78ce5696..1cef6d98 100644
--- a/features/dependencies.feature
+++ b/features/dependencies.feature
@@ -9,21 +9,21 @@ Feature: dependencies
| testuser | secret | false |
And I have logged in as "testuser" with password "secret"
- @selenium @wip
- Scenario: Adding dependency to dependency
+ @selenium
+ Scenario: Adding dependency to dependency by drag and drop
Given I have a project "dependencies" with 3 todos
And "Todo 2" depends on "Todo 1"
When I visit the "dependencies" project
And I drag "Todo 3" to "Todo 2"
- Then the dependencies of "Todo 2" should include "Todo 1"
- And the dependencies of "Todo 3" should include "Todo 2"
+ Then the successors of "Todo 1" should include "Todo 2"
+ And the successors of "Todo 2" should include "Todo 3"
When I expand the dependencies of "Todo 1"
Then I should see "Todo 2" within the dependencies of "Todo 1"
And I should see "Todo 3" within the dependencies of "Todo 1"
When I expand the dependencies of "Todo 2"
Then I should see "Todo 3" within the dependencies of "Todo 2"
- @selenium @wip
+ @selenium
Scenario: Adding dependency with comma to todo # for #975
Given I have a context called "@pc"
And I have a project "dependencies" that has the following todos
@@ -32,9 +32,49 @@ Feature: dependencies
| test me | @pc |
When I visit the "dependencies" project
And I drag "test me" to "test,1, 2,3"
- Then the dependencies of "test me" should include "test,1, 2,3"
- When I edit the dependency of "test me" to '"test,1, 2,3" <"@pc"; "dependencies">,"test,1, 2,3" <"@pc"; "dependencies">'
+ Then the successors of "test,1, 2,3" should include "test me"
+ When I edit the dependency of "test me" to "'test,1, 2,3' <'@pc'; 'dependencies'>,'test,1, 2,3' <'@pc'; 'dependencies'>"
Then there should not be an error
Scenario: Deleting a predecessor will activate successors
Given this is a pending scenario
+
+ @selenium
+ Scenario: I can edit a todo to add the todo as a dependency to another
+ Given I have a context called "@pc"
+ And I have a project "dependencies" that has the following todos
+ | description | context |
+ | test 1 | @pc |
+ | test 2 | @pc |
+ | test 3 | @pc |
+ When I visit the "dependencies" project
+ When I edit the dependency of "test 1" to "'test 2' <'@pc'; 'dependencies'>"
+ Then I should see "test 1" within the dependencies of "test 2"
+ And I should see "test 1" in the deferred container
+ When I edit the dependency of "test 1" to "'test 2' <'@pc'; 'dependencies'>, 'test 3' <'@pc'; 'dependencies'>"
+ Then I should see "test 1" within the dependencies of "test 2"
+ Then I should see "test 1" within the dependencies of "test 3"
+ When I edit the dependency of "test 1" to "'test 2' <'@pc'; 'dependencies'>"
+ And I edit the dependency of "test 2" to "'test 3' <'@pc'; 'dependencies'>"
+ Then I should see "test 1" within the dependencies of "test 3"
+ Then I should see "test 2" within the dependencies of "test 3"
+
+ @selenium
+ Scenario: I can remove a dependency by editing the todo
+ Given I have a context called "@pc"
+ And I have a project "dependencies" that has the following todos
+ | description | context |
+ | test 1 | @pc |
+ | test 2 | @pc |
+ And "test 1" depends on "test 2"
+ When I visit the "dependencies" project
+ Then I should see "test 1" in the deferred container
+ When I edit the dependency of "test 1" to ""
+ Then I should not see "test 1" within the dependencies of "test 2"
+ And I should not see "test 1" in the deferred container
+
+ Scenario: Deleting a predecessor will activate successors
+ Given this is a pending scenario
+
+ Scenario: Deleting a successor will update predecessor
+ Given this is a pending scenario
\ No newline at end of file
diff --git a/features/step_definitions/todo_steps.rb b/features/step_definitions/todo_steps.rb
index 557da9b3..04e39fc2 100644
--- a/features/step_definitions/todo_steps.rb
+++ b/features/step_definitions/todo_steps.rb
@@ -162,14 +162,15 @@ When /^I submit the new multiple actions form with$/ do |multi_line_descriptions
submit_multiple_next_action_form
end
-When /^I edit the dependency of "([^"]*)" to '([^'']*)'$/ do |todo_name, deps|
+When /^I edit the dependency of "([^"]*)" to "([^"]*)"$/ do |todo_name, deps|
todo = @dep_todo = @current_user.todos.find_by_description(todo_name)
todo.should_not be_nil
# click edit
selenium.click("//div[@id='line_todo_#{todo.id}']//img[@id='edit_icon_todo_#{todo.id}']", :wait_for => :ajax, :javascript_framework => :jquery)
fill_in "predecessor_list_todo_#{todo.id}", :with => deps
- # submit form
- selenium.click("//div[@id='edit_todo_#{todo.id}']//button[@id='submit_todo_#{todo.id}']", :wait_for => :ajax, :javascript_framework => :jquery)
+
+ submit_edit_todo_form(todo)
+ sleep(1) # TODO: replace with some wait_for
end
Then /^I should see ([0-9]+) todos$/ do |count|
@@ -179,11 +180,12 @@ Then /^I should see ([0-9]+) todos$/ do |count|
end
Then /^there should not be an error$/ do
- # form should be gone and thus not errors visible
+ sleep(5)
+ # form should be gone and thus no errors visible
selenium.is_visible("edit_todo_#{@dep_todo.id}").should == false
end
-Then /^the dependencies of "(.*)" should include "(.*)"$/ do |child_name, parent_name|
+Then /^the successors of "(.*)" should include "(.*)"$/ do |parent_name, child_name|
parent = @current_user.todos.find_by_description(parent_name)
parent.should_not be_nil
@@ -206,6 +208,17 @@ Then /^I should see "([^\"]*)" within the dependencies of "([^\"]*)"$/ do |succe
selenium.wait_for_element(xpath, :timeout_in_seconds => 5)
end
+Then /^I should not see "([^"]*)" within the dependencies of "([^"]*)"$/ do |successor_description, todo_description|
+ todo = @current_user.todos.find_by_description(todo_description)
+ todo.should_not be_nil
+ successor = @current_user.todos.find_by_description(successor_description)
+ successor.should_not be_nil
+ # let selenium look for the presence of the successor
+ xpath = "xpath=//div[@id='line_todo_#{todo.id}']//div[@id='successor_line_todo_#{successor.id}']//span"
+ selenium.is_element_present(xpath).should be_false
+end
+
+
Then /^I should see the todo "([^\"]*)"$/ do |todo_description|
selenium.is_element_present("//span[.=\"#{todo_description}\"]").should be_true
end
@@ -236,4 +249,22 @@ end
Then /^a confirmation for adding a new context "([^"]*)" should be asked$/ do |context_name|
selenium.get_confirmation.should == "New context \"#{context_name}\" will be also created. Are you sure?"
-end
\ No newline at end of file
+end
+
+Then /^I should see "([^"]*)" in the deferred container$/ do |todo_description|
+ todo = @current_user.todos.find_by_description(todo_description)
+ todo.should_not be_nil
+
+ xpath = "//div[@id='tickler']//div[@id='line_todo_#{todo.id}']"
+
+ selenium.is_element_present(xpath).should be_true
+end
+
+Then /^I should not see "([^"]*)" in the deferred container$/ do |todo_description|
+ todo = @current_user.todos.find_by_description(todo_description)
+ todo.should_not be_nil
+
+ xpath = "//div[@id='tickler']//div[@id='line_todo_#{todo.id}']"
+
+ selenium.is_element_present(xpath).should be_false
+end
diff --git a/features/support/world.rb b/features/support/world.rb
index f3da0aa1..1ecc5a26 100644
--- a/features/support/world.rb
+++ b/features/support/world.rb
@@ -15,6 +15,10 @@ module TracksStepHelper
selenium.click("xpath=//form[@id='project_form']//button[@id='project_new_project_submit']", :wait_for => :ajax, :javascript_framework => :jquery)
end
+ def submit_edit_todo_form (todo)
+ selenium.click("//div[@id='edit_todo_#{todo.id}']//button[@id='submit_todo_#{todo.id}']", :wait_for => :ajax, :javascript_framework => :jquery)
+ end
+
def format_date(date)
# copy-and-past from ApplicationController::format_date
return date ? date.in_time_zone(@current_user.prefs.time_zone).strftime("#{@current_user.prefs.date_format}") : ''
diff --git a/public/javascripts/application.js b/public/javascripts/application.js
index 4ab33189..76ce56be 100644
--- a/public/javascripts/application.js
+++ b/public/javascripts/application.js
@@ -338,7 +338,7 @@ var TodoItems = {
/* set behavior for toggle checkboxes for Recurring Todos */
$(".item-container input.item-checkbox").live('click', function(ev){
- put_with_ajax_and_block_element(this.value, $(this));
+ put_with_ajax_and_block_element(this.value, $(this).parents(".item-container"));
return false;
});
@@ -362,14 +362,23 @@ var TodoItems = {
return false;
});
+ // defer a todo
$(".item-container a.icon_defer_item").live('click', function(ev){
if ($(this).attr("x_defer_alert") == "true")
alert ($(this).attr("x_defer_date_after_due_date"));
else
- put_with_ajax_and_block_element(this.href, $(this));
+ put_with_ajax_and_block_element(this.href, $(this).parents(".item-container"));
return false;
});
+ /* delete button to delete a project from the list */
+ $('.item-container a.delete_dependency_button').live('click', function(evt){
+ predecessor_id=$(this).attr("x_predecessors_id");
+ ajax_options = default_ajax_options_for_scripts('DELETE', this.href, $(this).parents('.item-container'));
+ ajax_options.data += "&predecessor="+predecessor_id
+ $.ajax(ajax_options);
+ return false;
+ });
}
}
@@ -941,8 +950,37 @@ function enable_rich_interaction(){
/* multiple: true,
multipleSeparator:',' */
- $('input[name=predecessor_list]:not(.ac_input)').autocomplete({
- source: relative_to_root('auto_complete_for_predecessor')
+ $('input[name=predecessor_list]:not(.ac_input)')
+ .bind( "keydown", function( event ) { // don't navigate away from the field on tab when selecting an item
+ if ( event.keyCode === $.ui.keyCode.TAB &&
+ $( this ).data( "autocomplete" ).menu.active ) {
+ event.preventDefault();
+ }
+ })
+ .autocomplete({
+ minLength: 0,
+ source: function( request, response ) {
+ last_term = extractLast( request.term );
+ if (last_term != "" && last_term != " ")
+ $.getJSON( relative_to_root('auto_complete_for_predecessor'), {
+ term: last_term
+ }, response );
+ },
+ focus: function() {
+ // prevent value inserted on focus
+ return false;
+ },
+ select: function( event, ui ) {
+ var terms = split( this.value );
+ // remove the current input
+ terms.pop();
+ // add the selected item
+ terms.push( ui.item.value );
+ // add placeholder to get the comma-and-space at the end
+ //terms.push( "" );
+ this.value = terms.join( ", " );
+ return false;
+ }
});
/* have to bind on keypress because of limitations of live() */