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!