diff --git a/tracks/app/controllers/todo_controller.rb b/tracks/app/controllers/todo_controller.rb index 873501d2..17f9b6d9 100644 --- a/tracks/app/controllers/todo_controller.rb +++ b/tracks/app/controllers/todo_controller.rb @@ -189,6 +189,23 @@ class TodoController < ApplicationController @saved = @item.save end + + def update_context + self.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_action' + else + render :update do |page| + page.replace_html "info", 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.", "class" => "warning") + end + end + end def deferred_update_action #self.init @@ -289,7 +306,7 @@ class TodoController < ApplicationController end end end - + protected def check_user_return_item diff --git a/tracks/app/views/todo/update_action.rjs b/tracks/app/views/todo/update_action.rjs index c16c3ae2..952dae81 100644 --- a/tracks/app/views/todo/update_action.rjs +++ b/tracks/app/views/todo/update_action.rjs @@ -1,11 +1,20 @@ if @saved + item_container_id = "item-#{@item.id}-container" + logger.info("@item.context_id = #{@item.context_id}") + logger.info("@original_item_context.id = #{@original_item_context_id}") if @item.context_id == @original_item_context_id - page.replace_html "item-#{@item.id}-container", :partial => 'todo/item' - page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3 + page.replace_html item_container_id, :partial => 'todo/item' + page.visual_effect :highlight, item_container_id, :duration => 3 else page["item-#{@item.id}-container"].remove + page.call "todoItems.expandNextActionListingByContext", "c#{@item.context_id}items", true page.insert_html :bottom, "c#{@item.context_id}items", :partial => 'todo/item' - page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3 + page.delay(0.5) do + page.call "todoItems.ensureContainerHeight", "c#{@original_item_context_id}items" + page.call "todoItems.ensureContainerHeight", "c#{@item.context_id}items" + page.call "todoItems.makeItemDraggable", item_container_id + page.visual_effect :highlight, item_container_id, :duration => 3 + end end else page.hide "info" diff --git a/tracks/public/javascripts/application.js b/tracks/public/javascripts/application.js index 0904202d..ad53d973 100644 --- a/tracks/public/javascripts/application.js +++ b/tracks/public/javascripts/application.js @@ -6,7 +6,11 @@ Ajax.Responders.register({ onComplete: function() { if($('busy') && Ajax.activeRequestCount==0) Element.hide('busy'); - } + }, +// uncomment the next three lines for easier debugging with FireBug +// onException: function(source, exception) { +// console.error(exception); +// } }); /** diff --git a/tracks/public/javascripts/todo-items.js b/tracks/public/javascripts/todo-items.js index 006ec574..4ab26782 100644 --- a/tracks/public/javascripts/todo-items.js +++ b/tracks/public/javascripts/todo-items.js @@ -11,20 +11,42 @@ var todoItems = { addNextActionListingToggles: function() { - this.contextCollapseCookieManager = new CookieManager(); - var toggleElems = document.getElementsByClassName('container_toggle'); - toggleElems.each(function(toggleElem){ + $$('.container_toggle').each(function(toggleElem){ Event.observe(toggleElem, 'click', todoItems.toggleNextActionListing); toggleElem.onclick = function() {return false;}; //workaround for Event.stop problem with Safari 2.0.3. See http://particletree.com/notebook/eventstop/ + }); + todoItems.setNextActionListingTogglesToCookiedState(); + }, + setNextActionListingTogglesToCookiedState: function() + { + contextCollapseCookieManager = new CookieManager(); + $$('.container_toggle').each(function(toggleElem){ containerElem = todoItems.findNearestParentByClassName(toggleElem, "container"); collapsedCookie = contextCollapseCookieManager.getCookie(todoItems.buildCookieName(containerElem)); - if (collapsedCookie) + itemsElem = todoItems.findItemsElem(toggleElem); + isExpanded = Element.visible(itemsElem); + if (collapsedCookie && isExpanded) { - itemsElem = todoItems.findItemsElem(toggleElem); todoItems.collapseNextActionListing(toggleElem, itemsElem); + } + else if (!collapsedCookie && !isExpanded) + { + todoItems.expandNextActionListing(toggleElem, itemsElem); } }); }, + collapseAllNextActionListing: function(except) + { + $$('.container_toggle').each(function(toggleElem){ + if (toggleElem != except) + itemsElem = todoItems.findItemsElem(toggleElem); + isExpanded = Element.visible(itemsElem); + if (isExpanded) + { + todoItems.collapseNextActionListing(toggleElem, itemsElem); + } + }); + }, ensureVisibleWithEffectAppear: function(elemId) { @@ -58,31 +80,51 @@ var todoItems = { contextCollapseCookieManager.clearCookie(todoItems.buildCookieName(containerElem)) } }, - - expandNextActionListing: function(toggleElem, itemsElem) + findToggleElemForContext : function(contextElem) { - Effect.BlindDown(itemsElem, { duration: 0.4 }); + childElems = $A($(contextElem).getElementsByTagName('a')); + return childElems.detect(function(childElem) { return childElem.className == 'container_toggle' }); + }, + expandNextActionListing: function(toggleElem, itemsElem, skipAnimation) + { + itemsElem = $(itemsElem) + if (skipAnimation == true) { + itemsElem.style.display = 'block'; + } + else + { + Effect.BlindDown(itemsElem, { duration: 0.4 }); + } toggleElem.setAttribute('title', 'Collapse'); - imgElem = todoItems.findFirstImgElementWithSrcContaining(toggleElem, 'expand.png'); + imgElem = todoItems.findToggleImgElem(toggleElem); imgElem.src = imgElem.src.replace('expand','collapse'); imgElem.setAttribute('title','Collapse'); }, - + ensureContainerHeight: function(itemsElem) + { + itemsElem = $(itemsElem); + Element.setStyle(itemsElem, {height : ''}); + Element.setStyle(itemsElem, {overflow : ''}); + }, + expandNextActionListingByContext: function(itemsElem, skipAnimation) + { + contextElem = todoItems.findNearestParentByClassName($(itemsElem), "context"); + toggleElem = todoItems.findToggleElemForContext(contextElem); + todoItems.expandNextActionListing(toggleElem, itemsElem, skipAnimation); + }, collapseNextActionListing: function(toggleElem, itemsElem) { Effect.BlindUp(itemsElem, { duration: 0.4}); toggleElem.setAttribute('title', 'Expand'); - imgElem = todoItems.findFirstImgElementWithSrcContaining(toggleElem, 'collapse.png'); + imgElem = todoItems.findToggleImgElem(toggleElem); imgElem.src = imgElem.src.replace('collapse','expand'); imgElem.setAttribute('title','Expand'); }, - - findFirstImgElementWithSrcContaining: function(searchRootElem, srcString) + findToggleImgElem: function(toggleElem) { - childImgElems = $A(searchRootElem.getElementsByTagName('img')); - return childImgElems.detect(function(childImgElem) { return childImgElem.src.indexOf(srcString) != -1 }); + childImgElems = $A(toggleElem.getElementsByTagName('img')); + return childImgElems[0]; }, - buildCookieName: function(containerElem) { tracks_login = contextCollapseCookieManager.getCookie('tracks_login'); @@ -110,6 +152,67 @@ var todoItems = { return document.getElementsByClassName('toggle_target',containerElem)[0]; else return null; + }, + + addItemDragDrop: function() + { + $$('.item-container').each(function(containerElem){ + todoItems.makeItemDraggable(containerElem); + }); + $$('.context').each(function(contextElem){ + todoItems.makeContextDroppable(contextElem); + }); + }, + makeItemDraggable: function(itemContainerElem) + { + new Draggable($(itemContainerElem).id, + { + handle:'description', + starteffect:todoItems.startDraggingItem, + endeffect:todoItems.stopDraggingItem, + revert:true + }); + }, + makeContextDroppable: function(contextElem) + { + Droppables.add($(contextElem).id, + { + accept:'item-container', + hoverclass:'item-container-drop-target', + onDrop: todoItems.itemDrop, + zindex: 1000 + }); + }, + startDraggingItem:function(draggable) + { + parentContainer = todoItems.findNearestParentByClassName(draggable, 'container'); + draggable.parentContainer = parentContainer; + toggleElem = document.getElementsByClassName('container_toggle',parentContainer)[0]; + todoItems.collapseAllNextActionListing(toggleElem); + }, + stopDraggingItem:function(draggable) + { + todoItems.setNextActionListingTogglesToCookiedState(); + }, + + itemDrop:function(draggableElement, droppableElement) { + if (draggableElement.parentContainer == droppableElement) { + return; //same destination as original, nothing to be done + } + itemElementId = draggableElement.id + todoId = draggableElement.id.match(/\d+/)[0]; + contextId = droppableElement.id.match(/\d+/)[0]; + Draggables.drags.each(function(drag) { + if (drag.element == draggableElement) { + drag.destroy(); + } + }) + new Ajax.Request('/todo/update_context', { + asynchronous:true, + evalScripts:true, + parameters:"id=" + todoId + "&context_id=" + contextId + }) } } Event.observe(window, "load", todoItems.addNextActionListingToggles); +Event.observe(window, "load", todoItems.addItemDragDrop); diff --git a/tracks/public/javascripts/toggle_notes.js b/tracks/public/javascripts/toggle_notes.js index 1b86f1c4..5ad9f810 100644 --- a/tracks/public/javascripts/toggle_notes.js +++ b/tracks/public/javascripts/toggle_notes.js @@ -1,13 +1,12 @@ function toggleAll(className) { - var elems = document.getElementsByClassName(className); - for (var i = 0; i < elems.length; i++) { - if (elems[i].style.display == 'block') + document.getElementsByClassName(className).each(function(elem){ + if (elem.style.display == 'block') { - elems[i].style.display = 'none'; + elem.style.display = 'none'; } else { - elems[i].style.display = 'block'; + elem.style.display = 'block'; } - } + }); } \ No newline at end of file diff --git a/tracks/public/stylesheets/standard.css b/tracks/public/stylesheets/standard.css index 306e1855..15d29d52 100644 --- a/tracks/public/stylesheets/standard.css +++ b/tracks/public/stylesheets/standard.css @@ -129,6 +129,7 @@ a.show_notes:hover {background-image: url(../images/notes_on.png); background-re opacity: .75; color: #eee; width: 100%; + z-index:1100; } #date { @@ -202,7 +203,9 @@ h2 a:hover { padding:2px; clear: left; } - +.item-container-drop-target { + border:2px inset black; +} .container a.icon { float: left; vertical-align: middle;