<!-- -------------------------------------------------------------------------
# Copyright (c) 2007 Humanized, Inc. All rights reserved.
# ----------------------------------------------------------------------------
#
#   File: todo_sync.html
#   Author: Aza Raskin
#
# -------------------------------------------------------------------------- -->


<html>
<head>
  <title>Todo List</title>

  <style>
    body{ background: #cc3300; color: white; font-size: 20pt; margin-top:10px;}
    li{ list-style-type: none; }
    li a { font-size: 10pt; color: #ccc;}
    li img { cursor: move; padding-left: 3px; padding-right: 3px;}
    li input{ position: relative; top: -5px; }
    .box{ width: 300px; height: 30px; margin-left: 15px; }
    #undo{ color: #ccc;
           font-size: 15pt;
           margin-left: 30px;
           background-color: #dd4411; 
           padding: 5px; 
           display: none; 
           width: 140px;
            position: relative;
            top: 10px; }

    #done{ margin-left: 42px; }
  </style>
  
  <script src="jquery.js"></script>
  <script src="interface.js"></script>
  <script src="cookie.js"></script>
  
  <script>
  //-------------------------------------------------------------------------
  // GLOBAL VARIABLES
  // ------------------------------------------------------------------------
  
  // Holds the to-do items which have been deleted on the client side. Because
  // the user could undo the deletes, the fact of their deletion has not yet
  // been sent to the server.
  var EVENT_QUEUE = [];

  // How long we should wait after the animation for to-do item deletion
  // completes before we should show/hide the undo link.
  var UNDO_DISPLAY_WAIT = 300;

  // The name of the cookie that stores the event queue.
  var COOKIE_NAME = "event queue cookie";

  //-------------------------------------------------------------------------
  // GLOBAL EVENT BINDING
  // ------------------------------------------------------------------------

  $(document).ready(function(){
    doInit();
    $(window).unload( doUnload );
  });


  //-------------------------------------------------------------------------
  // HELPER FUNCTION
  // ------------------------------------------------------------------------

  // IE doesn't have a array.toSource function, so we have to implement one.
  // It does the inverse of eval. So eval(toSource([1,2,3])) = [1,2,3].
  function toSource( list ){
    return '["%s"]'.replace( "%s", list.join('","') );
  }

  //-------------------------------------------------------------------------
  // IMPLEMENTATION
  // ------------------------------------------------------------------------

  // Handles the logic for showing and hiding the undo link.
  function updateUndoLink(){
    // If there are no items that are undoable, hide the undo link.
    if( EVENT_QUEUE.length == 0 ){
      setTimeout( '$("#undo").fadeOut();', UNDO_DISPLAY_WAIT )
    }
    // If there are any items that are undoable, show the undo link.
    else if( EVENT_QUEUE.length == 1 ){
      $("#levels").text( "" );

      // Only do the fade in if the undo link is currently hidden.
      if( $("#undo").css("display") == "none" ){
        setTimeout( '$("#undo").fadeIn()', UNDO_DISPLAY_WAIT );
      }
    }
    // If there is more than one thing that can be undone, then let the user
    // know how many levels of undo there are.
    else if( EVENT_QUEUE.length > 1 ){
      $("#levels").text( "("+EVENT_QUEUE.length+")" );
    }

    saveEventQueueCookie();
  }

  // Saves the undo state to a cookie
  function saveEventQueueCookie(){
    $.cookie( COOKIE_NAME, toSource(EVENT_QUEUE) );
  }

  // Clears the undo state cookie
  function clearEventQueueCookie(){
    $.cookie( COOKIE_NAME, null );
  }

  // Loads the event queue from the cookie, dealing with
  // all edge cases.
  function getEventQueueFromCookie(){
    var eventQueue = eval( $.cookie(COOKIE_NAME) );

    if( eventQueue == null ){
      eventQueue = [];
    }

    return eventQueue;
  }

  // Syncs the event queue with the state from the event queue
  // cookie.
  function syncEventQueueWithCookie(){  
    EVENT_QUEUE = getEventQueueFromCookie();

    // Hide the uncommited but deleted items.
    for( var i in EVENT_QUEUE ){
      $("#"+EVENT_QUEUE[i]).hide();
    }

    // Show (without animation) the undo button.
    if( EVENT_QUEUE.length > 0 ){
      $("#undo").show();
      updateUndoLink();
    }    
  }

  // Sets up all event binding.
  function doInit(){
    // Setup the to-do items to be sortable. For more information, please see
    // http://interface.eyecon.ro/docs/sort
    $("#todo-list").Sortable({
      accept: "sortable",
      axis: "vertically",
      handle: "img"
    });

    // Handler for the delete link.
    $("#todo-list a").click( function(event){

      // Apparently, assigning a jQuery return value to a variable
      // does not work in IE6. Instead, we'll create a helper
      // function that does roughly the same thing.
      getParent = function(){ return $(event.target).parent(); }

      // Hide the newly deleted to-do item.
      getParent().slideUp();
      
      // Get the ID of the to-do item. If it doesn't have one, throw
      // an error.
      theId = getParent().attr("id");
      if( theId == null ){
        alert( "Error: To-do item needs an id." )
      }

      // Add the to-do item to the event queue.
      EVENT_QUEUE.push( getParent().attr("id") )

      updateUndoLink();
    });

    // Handler for clicking on the Undo link
    $("#undo").click( function(){
      // Get the last to-do item added to the event queue and un-hide it.
      $( "#" + EVENT_QUEUE.pop() ).slideDown();
      updateUndoLink();
    })

    syncEventQueueWithCookie();
  }

  // When the user navigates away from the current page, let the server know
  // which to-dos have been deleted.
  function doUnload(){
    otherEventQueue = getEventQueueFromCookie();

    // Comparing two arrays as equal always returns false, even if they have
    // the same content. So we convert to their string representation
    // to do the comparison.
    // 
    // If this page's event queue is not in sync with the latest saved-in-the-
    // cookie event queue, we know that this page is out-of-sync with
    // the last-modified page. So we sync the event queue to cookie event
    // queue
    if( EVENT_QUEUE.toString() != otherEventQueue.toString() ){
      EVENT_QUEUE = otherEventQueue;
    }

    // Otherwise, we commit the deletions.
    else {
      for( var i=0; i<EVENT_QUEUE.length; i++ ){
        // For this demo, we'll use an alert instead of an AJAX call.
        alert( "Performing an AJAX call to delete: " + $("#"+EVENT_QUEUE[i]).text() );
      }
      
      // We've commited everything to the server. We can safely kill the saved event queue. 
      clearEventQueueCookie();
    }

  }
  </script>
</head>
<body>

<div class="box">
  <a href="#" id="undo">Undo delete! <span id="levels"></span></a>
</div>

<ul id="todo-list">
  <li class="sortable" id="todo1"><img src="grip.gif"/><input type="checkbox">Call Home<a href="#">(delete)</a></li>
  <li class="sortable" id="todo2"><img src="grip.gif"/><input type="checkbox">Change car oil<a href="#">(delete)</a></li>
  <li class="sortable" id="todo3"><img src="grip.gif"/><input type="checkbox">Do laundry<a href="#">(delete)</a></li>
  <li class="sortable" id="todo4"><img src="grip.gif"/><input type="checkbox">Craft Halloween costume<a href="#">(delete)</a></li>
  <li class="sortable" id="todo5"><img src="grip.gif"/><input type="checkbox">Paint Warhammer figures<a href="#">(delete)</a></li>
  <li class="sortable" id="todo6"><img src="grip.gif"/><input type="checkbox">Make sure to never expect <a href="#">(delete)</a></li>
  <li class="sortable" id="todo7"><img src="grip.gif"/><input type="checkbox">The spanish inquisition <a href="#">(delete)</a></li>
  <li class="sortable" id="todo8"><img src="grip.gif"/><input type="checkbox">Write blog post <a href="#">(delete)</a></li>
  <li class="sortable" id="todo9"><img src="grip.gif"/><input type="checkbox">Destroy all humans <a href="#">(delete)</a></li>
  <li class="sortable" id="todo10"><img src="grip.gif"/><input type="checkbox">Allow for creation of to-dos <a href="#">(delete)</a></li>
</ul>

<form action="todo.html">
<input id="done" type="submit" value="Done"/>
</form>

</body>
</html>