Build your own WebMockup Tool using jQuery

This article is to show the capability of Javascript and jQuery Framework’s rather than as reference implementation to create a web mockup tool.  Infact this is not the best architecture to build a mockup tool in javascript.

Sample demo is hosted at  http://linkwithweb.appspot.com/DynamicForm.html   ( It only Works in Firefox and Chrome) 🙂

Concepts like  Drag/Drop , Resizable , Context Menu , Container’s, Grouping,

Cornering element view’s etc…. are being explained through this article.

I have used following javascript libraries to create this

  1. jquery
  2. jqueryui
  3. jquery.corner.js
  4. jquery.contextmenu.js

To create any Mockup tool we first to define few things that are required to build it

  1. Creation of Draggable Objects
  2. Creation of Droppable Area where object’s can be dropped.
  3. Layout of objects on droppable area(Dashboard)
  4. Deleting/Changing properties of objects/elements in the dashboard





Because i ‘m going to build a HTML mockup tool , i’ll define few html element’s as my draggable objects which i’m going to play with. Apart from this as i’m planning to make all objects/elements in my mockup tool as draggable one’s i’ll define a Dragholder which is used to hold my draggable objects. I have defined a class with name “drag” that defines all draggable objects

TextBox

<div id="drag1" class="drag">
     <input type="text" value="test" style="width: 80%; height: 80%;"></input>
</div>

Form

<form id="drag2" class="drag containerdiv">

	<div style=""
		class="context-menu context-menu-theme-vista context-menu-example-trigger menu-vista">
		<div class="context-menu-item">
			<div class="context-menu-item-inner">Form Container</div>
		</div>
	</div></form>

Button

<div id="drag3" class="drag">
	<input type="button" value="Drag Me" />
</div>

TextArea

<div id="drag4" class="drag">
	<textarea name="userInput" style="width: 80%; height: 80%;"></textarea>
</div>

SelectBox

<div id="drag4" class="drag">
	<select>
		<option>Small</option>
		<option selected="true">Medium</option>
		<option>Large</option>
		<option>X-Large</option>
	</select>
</div>

All the above elements have been defined with css class “drag” which we use to query latter.

Now lets define a container where all the above draggable elements will be dragged and played around

<div id="frame">
</div>

the above div element with id “frame” is used as container in our mockup tool.

Now as we have defined all elements in our HTML playground , let me explain the logic i’m going to use to create this.  Before going into that lemme explain that elements like form can have other elements so lets also make it droppable once it enters our playground

  1. Get all objects with class “drag”
  2. Add control key listener which add’s class “grouped” to elements if clicked by holding cotrol key ( Kind of selecting a element)
  3. Make all of then resizable.
  4. Add context menu for all elements
  5. Enable drag on them and assign them some id’s
  6. If element contain’s class “grouped” , moved all other elements with class “grouped” same width and height as our draggable element
  7. If elements also have class name “containerdiv” then they droppable apart from draggable, so enable droppable feature on them
Here is how sample menu is created
var menu = [
	{
		'Show an alert' : function() {
			alert('This is an alert!');
		}
	},
	{
		'Turn me red (try it)' : function() {
			this.style.backgroundColor = 'red';
		}
	},
	{
		'<b>HTML</b> <i>can be</i> <u>used in</u> <input type="text" value="test"/>' : function(
				menuItem, cmenu, e) {
			$t = $(e.target);
			return false;
		}
	},
	$.contextMenu.separator,
	{
		'Just above me was a separator!' : function() {
		}
	},
	{
		'Option with icon (for themes that support it)' : {
			onclick : function() {
			},
			icon : 'i_save.gif'
		}
	},
	{
		'Disabled option' : {
			onclick : function() {
			},
			disabled : true
		}
	},
	{
		'<div><div style="float:left;">Choose a color (don\'t close):</div><div class="swatch" style="background-color:red"></div><div class="swatch" style="background-color:green"></div><div class="swatch" style="background-color:blue"></div><div class="swatch" style="background-color:yellow"></div><div class="swatch" style="background-color:black"></div></div><br>' : function(
				menuItem, cmenu, e) {
			$t = $(e.target);
			if ($t.is('.swatch')) {
				this.style.backgroundColor = $t.css('backgroundColor');
				$t.parent().find('.swatch').removeClass('swatch-selected');
				$t.addClass('swatch-selected');
			}
			return false;
		}
	},
	{
		'Use fadeOut for hide transition' : function(menuItem, cmenu) {
			cmenu.hideTransition = 'fadeOut';
			cmenu.hideSpeed = 'fast';
		}
	},
	{
		'Use slideUp for hide transition (quirky in IE)' : function(
				menuItem, cmenu) {
			cmenu.hideTransition = 'slideUp';
		}
	},
	{
		'Use normal hide transition' : function(menuItem, cmenu) {
			cmenu.hideTransition = 'hide';
		}
	},
	{
		'Increments each time menu is displayed using beforeShow hook: #<span class="option-count">1</span>' : function() {
		}
	}, {
		'Custom menu item class and hover class for this item only' : {
			onclick : function() {
			},
			className : 'context-menu-item-custom',
			hoverClassName : 'context-menu-item-custom-hover'
		}
	} ];

Below is sample code on how we implement above functionality using jquery

/**
 * Called once document is loaded
 */
$(document).ready(function() {
	$('a[rel*=facebox]').facebox({
		div : '#frame',
		loading_image : 'loading.gif',
		close_image : 'closelabel.gif'
	});

	// Counter
	counter = 0;

	// Make element draggable
	// $(".dragged").resizable().draggable();
	enableDrag("frame", ".drag", "false");

	// $(".drag").corner();

	// Make element droppable
	$("#frame").droppable({
		drop : function(ev, ui) {
			if (ui.helper.attr('id').search(/drag[0-9]/) != -1) {
				counter++;
				var element = $(ui.draggable).clone();

				// alert(element);
				element.addClass("tempclass");

				$(this).append(element);

				$(".tempclass").attr("id", "clonediv" + counter);
				$("#clonediv" + counter).removeClass("tempclass");

				// Get the dynamically item id
				draggedNumber = ui.helper.attr('id').search(/drag([0-9])/)
				// itemDragged = "dragged" + RegExp.$1
				// //console.log(itemDragged)
				itemDragged = "dragged";

				$("#clonediv" + counter).addClass(itemDragged);

				enableDrag("parent", "#clonediv" + counter, "true");

			}
		}
	});
});

Code for function enableDrag which is main funciton in our project

/**
 * This function enables drag on a object and contains its drag in the passed
 * container
 *
 * @param dragContainment
 * @param draggableClass
 * @param resizable
 */
function enableDrag(dragContainment, draggableClass, resizable) {
	// Make element draggable and resizable
	$(draggableClass).click(function(event) {
		if (event.ctrlKey) {
			$(this).toggleClass('grouped');
		}
	});
	/**
	 * Add Context Menu
	 */
	$(draggableClass).find(".menu-vista").contextMenu(menu, {
		theme : 'vista',
		beforeShow : beforeShowFunc
	});
	$(draggableClass).contextMenu(draggableObjectMenu, {
		theme : 'vista'
	});

	var element;

	/**
	 * Make the element resizable
	 */
	if (resizable == "true") {
		element = $(draggableClass).corner().resizable();
	} else {
		element = $(draggableClass).corner();
	}

	/**
	 * If element is containerdiv then make it droppable
	 */
	if (element.hasClass("containerdiv")) {

		// Make element droppable
		element.droppable({
			greedy : true,
			out : function(event, ui) {

				var element = $(ui.draggable);
				if (!element.hasClass("containerdiv")) {
					$(this).parent().append(element);
				}
			},
			drop : function(ev, ui) {
				if (ui.helper.attr('id').search(/drag[0-9]/) != -1) {
					var element = $(ui.draggable);
					counter++;

					element = $(ui.draggable).clone();
					// alert(element);
					element.addClass("tempclass");

					$(this).append(element);

					$(".tempclass").attr("id", "clonediv" + counter);
					$("#clonediv" + counter).removeClass("tempclass");

					// Get the dynamically item id
					draggedNumber = ui.helper.attr('id').search(/drag([0-9])/)
					// itemDragged = "dragged" + RegExp.$1
					// //console.log(itemDragged)
					itemDragged = "dragged";

					$("#clonediv" + counter).addClass(itemDragged);

					enableDrag("frame", "#clonediv" + counter, "true");

				} else {
					var element = $(ui.draggable);
					$(this).append(element);
				}
			}
		});

	}

	/**
	 * Now make element draggable
	 */
	element.draggable({
		helper : 'clone',
		containment : dragContainment,

		start : function(event, ui) {
			posTopArray = [];
			posLeftArray = [];
			thisElem = $(this);

			if ($(this).hasClass("grouped")) { // Loop through each element and
				// store beginning start and
				// left positions

				$(".grouped").each(function(i) {
					thiscsstop = $(this).css('top');
					if (thiscsstop == 'auto')
						thiscsstop = 0; // For IE

					thiscssleft = $(this).css('left');
					if (thiscssleft == 'auto')
						thiscssleft = 0; // For IE

					posTopArray[i] = parseInt(thiscsstop);
					posLeftArray[i] = parseInt(thiscssleft);
				});
			}

			begintop = $(this).offset().top; // Dragged element top position
			beginleft = $(this).offset().left; // Dragged element left position
		},

		drag : function(event, ui) {
			var topdiff = $(this).offset().top - begintop; // Current distance
			// dragged element
			// has traveled
			// vertically
			var leftdiff = $(this).offset().left - beginleft; // Current
			// distance
			// dragged
			// element has
			// traveled
			// horizontally

			var draggingId = $(this).attr("id");
			if ($(this).hasClass("grouped")) {
				// //console.log("Initial pos "+begintop +"
				// Left----"+beginleft);
				// //console.log("Diff Value is "+topdiff +"
				// Left----"+leftdiff);
				$(".grouped").each(function(i) {
					if (draggingId === $(this).attr("id")) {
						topdiff = $(this).offset().top - begintop; // Current
						leftdiff = $(this).offset().left - beginleft; // Current
					}
				});

				$(".grouped").each(function(i) {
					// //console.log("Pres pos for "+$(this).attr('id')+" is
					// "+posTopArray[i] +" Left----"+posLeftArray[i]);
					if (draggingId !== $(this).attr("id")) {
						$(this).css('top', posTopArray[i] + topdiff); // Move
						$(this).css('left', posLeftArray[i] + leftdiff); // Move
					}
				});

			}
		},

		// When first dragged
		stop : function(ev, ui) {
			var pos = $(ui.helper).offset();
			objName = "#clonediv" + counter
			var draggingId = $(this).attr("id");

			var leftValue = pos.left;
			var topValue = pos.top;

			/**
			 * The below if loop is written to fix the relative and abosiulte
			 * positioning issue. When any draggable object is dropped into a
			 * droppable container which resides in another container then the
			 * droppable with more z-index becomes relative positining for its
			 * sub elements
			 */
			if ($(this).parent().hasClass("containerdiv")) {
				// console.log("Left Pos "+leftValue);
				// console.log("Top Pos "+topValue);
				// console.log("$(this).parent().attr('left')
				// "+$(this).parent().offset().left);
				// console.log("$(this).parent().offset().top
				// "+$(this).parent().offset().top);
				leftValue = pos.left - $(this).parent().offset().left;
				topValue = pos.top - $(this).parent().offset().top;
			}

			if ($(this).hasClass("drag")) {
				$(objName).css({
					"left" : leftValue,
					"top" : topValue
				});
				$(objName).removeClass("drag");

			} else {
				objName = draggingId;
				thisElem.css({
					"left" : leftValue,
					"top" : topValue
				});
			}

			// console.log("Left Pos "+leftValue);
			// console.log("Top Pos "+topValue);

			var topdiff = $(this).offset().top - begintop; // Current distance
			// dragged element
			// has traveled
			// vertically
			var leftdiff = $(this).offset().left - beginleft; // Current
			// distance
			// dragged
			// element has
			// traveled
			// horizontally

			// This logic for Group Elements

			if ($(this).hasClass("grouped")) {
				// //console.log("Initial pos "+begintop +"
				// Left----"+beginleft);
				// //console.log("Diff Value is "+topdiff +"
				// Left----"+leftdiff);
				$(".grouped").each(function(i) {
					if (draggingId === $(this).attr("id")) {
						topdiff = $(this).offset().top - begintop; // Current
						// distance
						// dragged
						// element
						// has
						// traveled
						// vertically
						leftdiff = $(this).offset().left - beginleft; // Current
						// distance
						// dragged
						// element
						// has
						// traveled
						// horizontally
					}
				});

				$(".grouped").each(function(i) {
					// //console.log("Pres pos for "+$(this).attr('id')+" is
					// "+posTopArray[i] +" Left----"+posLeftArray[i]);
					if (draggingId !== $(this).attr("id")) {
						$(this).css('top', posTopArray[i] + topdiff); // Move
						// element veritically - current css top + distance
						// dragged element has travelled vertically
						$(this).css('left', posLeftArray[i] + leftdiff); // Move
						// element horizontally-current css left + distance
						// dragged element has travelled horizontally
					}
				});

			}

		}
	});

}

Below is code which generate HTML Code from the design we made

/**
 * On Spit html button clicked alert the HTML in the frame that gets generated
 */
function alertHTML() {
	var htmlClone = $("#frame").clone();
	alert("<html><head></head><body>" + htmlClone.html() + "</body></html>");
}

Code has been checked in to follow svn url https://linkwithweb.googlecode.com/svn/trunk/WebMockup

Njoy playing with javascript and jQuery

Advertisements

Javascript RSA Encryption and Java Decryption

Many of us have been working with Javascript since long time but when ever i ask people how to send encrypted data, the only answer is to use SSL . But this article shows how to send encrypted data even when we don’t have ssl enabled. This can come in to handy in many scenario’s

I used jCryption and Javascript Library to encrypt in Javascript and BouncyCastle Library on Javabackend to decypt,

Here is the flow in the example

  1. First Generate  RSA keys on server end ( Store in session).
  2. Send public key to client (javascript)
  3. Store keys in javascript variable
  4. In All subsequent requests use this key to encrypt data and send to server
  5. Use keys stored in session  to decrypt data and send response to server

Keys generation utility class in Java

package com.linkwithweb.encryption;

import java.io.IOException;
import java.security.KeyPair;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class EncryptionServlet
 */
public class EncryptionServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * Default constructor.
	 */
	public EncryptionServlet() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void service(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		if (request.getParameter("generateKeypair") != null) {

			JCryptionUtil jCryptionUtil = new JCryptionUtil();

			KeyPair keys = null;
			if (request.getSession().getAttribute("keys") == null) {
				keys = jCryptionUtil.generateKeypair(512);
				request.getSession().setAttribute("keys", keys);
			}

			StringBuffer output = new StringBuffer();

			String e = JCryptionUtil.getPublicKeyExponent(keys);
			String n = JCryptionUtil.getPublicKeyModulus(keys);
			String md = String.valueOf(JCryptionUtil.getMaxDigits(512));

			output.append("{\"e\":\"");
			output.append(e);
			output.append("\",\"n\":\"");
			output.append(n);
			output.append("\",\"maxdigits\":\"");
			output.append(md);
			output.append("\"}");

			output.toString();
			response.getOutputStream().print(
					output.toString().replaceAll("\r", "").replaceAll("\n", "")
							.trim());
		} else {
			response.getOutputStream().print(String.valueOf(false));
		}
	}

}

All client code is there in index.jsp and framework.js

Javascript Function that gets keys from server and stores in javascript variable

/**
 * Get Security keys from server so that we can encrypt request in future
 */
function getKeys() {
	$.jCryption.getKeys("EncryptionServlet?generateKeypair=true", function(
			receivedKeys) {
		keys = receivedKeys;
	});
}

On login button clicked here is how you encrypt and send request to server

/**
 * Called on Login Button clicked
 */
function onLoginButtonClicked() {
	var user = $("#login_user").val();
	var password = $("#login_password").val();
	$.jCryption.encrypt(user, keys, function(encrypted) {
		encryptedUser = encrypted;
		$.jCryption.encrypt(password, keys, function(encryptedPasswd) {
			encryptedPassword = encryptedPasswd;
			/**
			 * As both userName and password are encrypted now Submit login
			 */
			submitLoginRequest();
		});
	});
}

/**
 * Submit Login request
 */
function submitLoginRequest() {
	sendAjaxRequest("LoginServlet", {
		username : encryptedUser,
		password : encryptedPassword
	}, function(data) {
		if (data.length > 0) {
			$("#login_status").empty();
			$("#login_status").append(data);
		}
	});
}

And below is svn URL to download sample source code  https://linkwithweb.googlecode.com/svn/trunk/Utilities/jCryptionTutorial

Next version of tutorial will be from flex to java. Njoy reading and playing with Encryption code

Rico Provides the All Power Full Ajax Table

Hello All,

I’m really impressed the way RICO has revolutionized the way we render table. Tables means Header + Data + Footer. But rico has changed the idea we define a table. It made it Header + Footer + Dynamic JavaScript or a DataSource. It also added a very nice functionality of progressive downloading. I.e Data can be downloaded while scrolling. It mixes the powerful feature of Ajax with object oriented javascript to provide this solution

Here is the example

Rico.loadModule('LiveGridAjax','LiveGridMenu','greenHdg.css');
Rico.loadModule('LiveGridMenu');

Rico.onLoad( function() {
var buffer = new Rico.Buffer.AjaxXML('test.xml');
var opts = {
useUnformattedColWidth: false,
defaultWidth : 90,
visibleRows : 'data',
frozenColumns: 1,
canPrint: false,
highltOnMouseOver: true,
rowHighlightColor: '#c4c4c5',
specFilterSort: {type:'raw', canSort:true, canFilter:true, quotes:"'"},
specFilter: {type:'raw', canSort:false, canFilter:true, quotes:"'"},
specSort: {type:'raw', canSort:true, canFilter:false, quotes:"'"},
columnSpecs : ['specSort','specFilterSort','specSort','specSort','specFilterSort']
};
var grid = new Rico.LiveGrid('data_grid', buffer, opts);
grid.menu=new Rico.GridMenu();

});

Now Sample Data can be some thing like this

5 Star Wars Action 9.0 135001 1977 6 The Lord of the Rings: The Two Towers Action 9.0 115175 2002 7 Star Wars: Episode V - The Empire Strikes Back Action 9.0 104167 1980 5 Star Wars Action 9.0 135001 1977 6 The Lord of the Rings: The Two Towers Action 9.0 115175 2002 7 Star Wars: Episode V - The Empire Strikes Back Action 9.0 104167 1980

Rico Has Lots of Functionalitites which add dynamic Sorting Searching features to you your grid without you explicitly handling those features in UI. Only thing we have toi write is code to render data and filter data. Rico takes care of rest

Enjoy RICO at http://openrico.org/
It has got a live grid which is of a lot of help in many projects.

Parametrize Strings In Java Script

We have lots of utilities in Many Languages which can spit parametrized text. But in Javascript we dont find any such utilities. Here is one of the useful function which can do this.

Lets say We want to say Welcome Message to User and Based on User Name we want to show a warning message that account expires. Then we create a placeholder in Welcome String for user name and For nUmber of days. here is how we do in Javascript

var welcomeString = “Welcome $1. We would like to inform you that your account exipers in $2 days”;

We now use it like this
Final String would be

jstrprintf(welcomeString,”Ashwin”,1000);
This will print
Welcome Ashwin. We would like to inform you that your account exipers in 1000 days


function jstrprintf() {
len = arguments.length;
if (len == 0) { return; }
if (len == 1) { return arguments[0]; }

var result;
var regexstr;
var replstr;
var formatstr;
var re;

result = "";
regexstr = "";
replstr = "";
formatstr = arguments[0];

for (var i=1; i<arguments.length; i++) {
replstr += String(i+100) + arguments[i] + String(i + 100);
regexstr += String(i+100) + "(.*)" + String(i+100);
}
re = new RegExp(regexstr);
var result;
result = replstr.replace(re, formatstr);
return result;
}