From 9263249e77e4de531d2e1939c22b75f01fbbcfdb Mon Sep 17 00:00:00 2001 From: lukemelia Date: Mon, 3 Jul 2006 00:22:28 +0000 Subject: [PATCH] This changeset adds the ability to drag and drop actions between contexts and ajaxomagically update the action's context in the backend. I've tested it in Firefox and Safari, but not on other browsers. When you start dragging an action, the other contexts collapse to provide easier targets for dropping. After the drop, the contexts return to their previously states of collapsed/expanded. git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@275 a4c988fc-2ded-0310-b66e-134b36920a42 --- tracks/app/controllers/todo_controller.rb | 19 ++- tracks/app/views/todo/update_action.rjs | 15 ++- tracks/public/javascripts/application.js | 6 +- tracks/public/javascripts/todo-items.js | 135 +++++++++++++++++++--- tracks/public/javascripts/toggle_notes.js | 11 +- tracks/public/stylesheets/standard.css | 5 +- 6 files changed, 163 insertions(+), 28 deletions(-) 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;