jquery: igoogle box-set like script

hello world! (sounds familiar..)

lr1 parser is complete! woooah. it takes me 3 weeks of work and now i will have a printed permanent smile on my face for at least 1 month. well i will post source codes, but not for now :).

today i’d like to show you a nice work made with jquery, a powerful javascript library that i discovered two days ago.

This is a tutorial that will show how to make a simple but nice customizable blocks-made interface/homepage, with jquery. the script is based on this other tutorial.

Here it is the demo: http://www.valerioriva.it/jquery/lotti-block.html
how to use the demo: you will see 6 blocks. click on the wrench to enter customization mode, a dialog will appear. click on every boxes’ icon (click and drag to move blocks with 4 arrow cross) and customize your layout. save or restore states/order/everything if you don’t like the customization.

features:

  • movable blocks
  • closable blocks
  • minimizable blocks
  • save blocks’ state and order into cookies

let’s start with the html template.

build your html layout.. with divs! made 1 or 2 or 3 or how many you need div “columns”. i’ll choose 3. then, you have to put inside each columns some divs that will be acting as boxes with some simply css commands.

here it is my example [remember to include always jquery library, jquery cookie plugin, jquery ui theme script and of course, my script (lotti-block)]
there is also a css example for people that doesn’t know how to make divs act as blocks

download the demo files!

<html>
 <head><title>Demo</title>
 <link type="text/css" href="css/demo/jquery-ui-1.7.2.demo.css" rel="stylesheet" />
 <script type="text/javascript" src="js/jquery-1.3.2.min.js"></script>
 <script type="text/javascript" src="js/jquery.cookie.js"></script>
 <script type="text/javascript" src="js/jquery-ui-1.7.2.demo.min.js"></script>
 <script type="text/javascript" src="js/lotti-block.js"></script>
 <style>
 #left { width: 20%; float:left; }
 #center { width: 58%; float: left; margin-right:1%; margin-left:1%; }
 #right { width: 20%; float:right; }
 .box { background-color:#eef3f8; border:1px solid #d5dde5; border-bottom:4px solid #d5dde5; padding:10px; margin-bottom:15px; overflow:hidden; }
 .box h3{ background:#d5dde5; color:#1d3652 }
 .portlet-header .ui-icon { float: right; }
 .placeholder { border: 1px dotted black; visibility: visible !important; background-color: #0066CC; color: #FFFFFF; font-weight: bold; }
 .placeholder * { visibility: hidden; }
 .ui-sortable-placeholder { border: 1px dotted black; visibility: visible !important; height: 100px !important; }
 .ui-sortable-placeholder * { visibility: hidden; }
 </style>
 </head>
 <body>
 
<div id="customize-dialog" title="Reset Layout Page">
<p>Now you can customize your page.<br /> Move boxes from column to another, minimize/close them or get back to default settings by clicking on these buttons:</p>
</div>


<div id="left">
 <div align="center">PLACEHOLDER</div>
 <div id="b1"><h3>Block 1</h3>
 <div>Block 1</div>
 </div>
 <div id="b2"><h3>Block 2</h3>
 <div>Block 2</div>
 </div>
</div>
<div id="center" >
 <div align="center">PLACEHOLDER</div>
 <div id="b3"><h3>Block 3</h3>
 <div>Block 3</div>
 </div>
 <div id="b4"><h3>Block 4</h3>
 <div>Block 4</div>
 </div>
</div>
<div id="right" >
 <div align="center">PLACEHOLDER</div>
 <div id="b5"><h3>Block 5</h3>
 <div>Block 5</div>
 </div>
 <div id="b6"><h3>Block 6</h3>
 <div>Block 6</div>
 </div>
</div>


 </body>
</html>

ok. now let’s examine this page. every column (they have IDs: left, center and right) must have the class identifier “sortable” and each block must have a different ID (in this example from b1 to b6). then placeholder divs (class=”placeholder”) blocks are used only to facilitate boxes drag’n’drop inside each column.
Every box have some special class that are: customizer, movable, minimizable, closable. each of those words adds an ability to the block. There must be at least 1 customizer (and preferably it doesn’t have to be closeable). you can use every combinations of those class identifier.

Then, a box is defined, as you can see, by this code (for example, i took the last block)

<div id="b3"><h3>Block 3</h3>
 <div>Block 3</div>
 </div>

REMEMBER! there must be a tag with class identifier “portlet-header” (i used h3 but it could be a div too) and another tag (preferably a div) with class identifier “portlet-content”

there is a “dialog” div too (it will act as a dialog box) that will appear when the customizer icon is clicked. it contains buttons to save or restore the homepage configuration/style.

now let’s see something about the js code

//configurable variables
var cookiename='demo';
var cookie_options = { path: '/', expires: 10 };

function resetCookie() //delete cookie
{
jQuery.cookie(cookiename, null, cookie_options);
}

function resetCookieState() //delete only blocks' states from cookie
{
var cookie = jQuery.cookie(cookiename);
if (!cookie) return;

var cookie=cookie.split("|");

var savedBlocks = cookie[0];
var savedState = cookie[1];

jQuery.cookie(cookiename, savedBlocks+'|', cookie_options);
}

function resetCookieOrder() //delete only blocks' order from cookie
{
var cookie = jQuery.cookie(cookiename);
if (!cookie) return;

var cookie=cookie.split("|");

var savedBlocks = cookie[0];
var savedState = cookie[1];

jQuery.cookie(cookiename, '|'+savedState, cookie_options);
}

function getCookie() //read the cookie and restore blocks' order and states
{
var cookie = jQuery.cookie(cookiename);
if (!cookie) return;

var cookie=cookie.split("|");

var savedBlocks;
var savedStates;

if (cookie[0]) savedBlocks = cookie[0];
if (cookie[1]) savedStates = cookie[1];

//order
if (savedBlocks)
{

        var orders = savedBlocks.split(";");

//below you need to be replace with your columns id!!
        jQuery("#left").sortable('toArray');
        jQuery("#center").sortable('toArray');
        jQuery("#right").sortable('toArray');

//here too!!
        if(orders[0]) restoreOrder('#left',orders[0]);
        if(orders[1]) restoreOrder('#center',orders[1]);
        if(orders[2]) restoreOrder('#right',orders[2]);
  }

//states
if (savedStates)
{
        var states = savedStates.split(",");
        var blocks_id = new Array();
        var blocks_state = new Array();
        var i=0;
        for (i=0; i<states.length; i++)
        {
            var temp = states[i].split("=");
            blocks_id[i]=temp[0];
            blocks_state[i]=temp[1];
        }

        for (i=0; i<states.length; i++)
        {
        	 var item=blocks_id[i];
        	 var state=blocks_state[i];
           if (state==1)
             jQuery("#"+item).find(".portlet-content").hide();
           else if (state==2)
             jQuery("#"+item).hide();
        }
   }
}

function setCookie() //save the blocks' order and states inside a single cookie
{
  var s="";
  var i=0;
  var n=0;

 //order
//below you need to be replace with your columns id!!
        if (jQuery("#left").sortable('toArray')!="undefined")		s+=jQuery("#left").sortable('toArray')+";"
if (jQuery("#center").sortable('toArray')!="undefined")		s+=jQuery("#center").sortable('toArray')+";"
if (jQuery("#right").sortable('toArray')!="undefined")		s+=jQuery("#right").sortable('toArray')+";"

  s=s.substr(0,s.length-1);

  s+='|';

  //states
  			var blocks_minimized = new Array();
  			var blocks_closed = new Array();

  			n=jQuery('.portlet-content:hidden').size();
  			for (i=0; i<n; i++)
     			s+=jQuery('.portlet-content:hidden').eq(i).parent('.minimizable').attr('id')+"=1,";

  			n=jQuery('.closable:hidden').size();
  			for (i=0; i<n; i++)
    			s+=jQuery('.closable:hidden').eq(i).attr('id')+"=2,";

  if (n!=0) s=s.substr(0,s.length-1);

  if (s.length>0) jQuery.cookie(cookiename, s, cookie_options);
  else jQuery.cookie(cookiename, null);
}

function restoreOrder(list,order)  //restore blocks' order for each column
{
var list = jQuery(list);
if (list == null) return

// make array from saved order
var IDs = order.split(",");

for (var i = 0, n = IDs.length; i<n; i++)
        {
var item = IDs[i];

 	   // select the item from the proper column
var child = jQuery("div.ui-sortable").children("#" + item);

// make a copy of the item
var savedOrd = jQuery("div.ui-sortable").children("#" + item);

// remove the original item
child.remove();

           //insert the copy inside the ordered column
jQuery(list).append(savedOrd);
}
}

function removeButtons() //remove cutomization buttons
{
jQuery(".ui-icon-newwin").replaceWith('');
jQuery(".ui-icon-arrow-4").replaceWith('');
jQuery(".ui-icon-power").replaceWith('');
}

function addButtons() //adds customization buttons and actions
{
removeButtons();
jQuery(".minimizable").find(".portlet-header").append('<span class="ui-icon ui-icon-newwin"></span>')
jQuery(".movable").find(".portlet-header").append('<span class="ui-icon ui-icon-arrow-4"></span>')
jQuery(".closable").find(".portlet-header").append('<span class="ui-icon ui-icon-power"></span>')

jQuery(".minimizable").find(".portlet-header").find(".ui-icon-newwin").click(function() {
jQuery(this).parents(".minimizable").find(".portlet-content").toggle();
});

jQuery(".closable").find(".portlet-header").find(".ui-icon-power").click(function() {
                        jQuery(this).parents(".closable").hide();

});
}

function enableCustomization() //shows the dialog, the placeholders, makes columns item sortable and adds buttons
{
jQuery('#customize-dialog').dialog('open');
jQuery(".placeholder").show();
jQuery(".sortable").sortable({
    														handle: '.ui-icon-arrow-4',
    														tolerance: 'pointer',
items: '.movable',
    														connectWith: '.sortable',
    														update : function () { setCookie(); }
    										});
   											addButtons();
}

function disableCustomization() //remove buttons, set the cookie, hides the placeholders and block the columns
{
removeButtons();
   											setCookie();
jQuery(".placeholder").hide();
                				jQuery(".sortable").sortable('destroy');
}

jQuery(function() { //jQuery "Main"

jQuery('#customize-dialog').dialog({ //configure the dialog box
                         autoOpen: false, width: 400, buttons: {
                              "Everything": function() { //reset to normal page layout
                                jQuery(this).dialog("close"); //close the dialog
                                resetCookie(); //full reset
                                window.location.reload(); //refresh page to see changes
                              },
                              "States": function() { //reset only the states
                                jQuery(this).dialog("close"); //close the dialog
                                resetCookieState(); //partial reset, only states
                                window.location.reload(); //refresh page to see changes
                              },
                              "Order": function() { //reset only the order
                                jQuery(this).dialog("close"); //close the dialog
                                resetCookieOrder(); //partial reset, only order
                                window.location.reload(); //refresh page to see changes
                              },
                              "Save": function() { //save state and order
                                jQuery(this).dialog("close");  //close the dialog
                              }
                         },
   											 close: function(event, ui) { disableCustomization(); } //every time the dialog is closed.. disableCustomization is launched!
                });

    jQuery('.placeholder').hide(); //hides the placeholders

jQuery('.customizer').find('.portlet-header').append('<span class="ui-icon ui-icon-wrench"></span>') //adds the customization button on customizer boxes..

jQuery('.customizer').find('.portlet-header').find('.ui-icon-wrench').click(function() { //and add his action
enableCustomization();
});

jQuery('.sortable').sortable({ //instiate columns as sortable to fetch order from cookie
    				handle: '.ui-icon-arrow-4',
    				tolerance: 'pointer',
items: '.movable',
    				connectWith: '.sortable',
    });

 		getCookie(); //read the cookie

jQuery('.sortable').sortable( 'refresh' ) //refresh columns status
 		jQuery('.sortable').sortable('destroy'); //lock columns

});

well, i commented every function, i hope you will understand. if not, go to jQuery documentation or view the linked tutorials on top of this post.
however, inside the cookie the order is save with strings like this: b1,b2;b3,b4;b5,b6 where the ; stands for “end of column” so it is most important to put ids in the correct order inside getCookie e setCookie functions. the blocks’ state is saved like this: b1=1,b2=2,b4=1 where 1 stands for minimized and 2 for closed. no need to save “normal state” because we already have it by default (everytime you load the page, every is on default order/state). that’s all. hope you liked! feel free to ask questions!

2 thoughts on “jquery: igoogle box-set like script

  1. Will

    Hey there, I’m trying to make a custom version of this whereby the portlets “flow” in a certain order when one is moved or removed. Basically, I’d like portlets to only go to one of six locations on the page (as opposed to “stacking” on top of each other if I’ve added them all to one column).

    For example, if I move portlet 1 (P1 to P6), then the view should change from:

    P1 P2 P3
    P4 P5 P6

    to

    P2 P3 P4
    P5 P6 P1

    not

    P4 P2 P3
    P5 P5
    P1

    Any thoughts on how to do this?

  2. Lotti Post author

    well, i didn’t fully understand your question but i think that you need a different structure from mine.

    i designed 3 different column and made blocks moveable between them.
    from what you wrote you need a continuous row represented as two rows so you can move a block from head to tail and accordingly move the other to the head of one position.

    this “effect” can be done using an unordered list as block container (list =>

Leave a Reply

Your email address will not be published. Required fields are marked *