<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>richardbondi.net</title>
	<atom:link href="http://richardbondi.net/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://richardbondi.net/blog</link>
	<description></description>
	<lastBuildDate>Thu, 21 Jul 2011 00:12:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Replace Carriage Return in OGNL</title>
		<link>http://richardbondi.net/blog/replace-carriage-return-in-ognl/</link>
		<comments>http://richardbondi.net/blog/replace-carriage-return-in-ognl/#comments</comments>
		<pubDate>Thu, 21 Jul 2011 00:12:02 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=185</guid>
		<description><![CDATA[I want to share this little secret, like many times, I need to do something and search but come up empty on how.  I tried to search for &#8220;replace carriage return with br tag&#8221; and many similar searches but found nothing. A little imagination is needed at times to find a workable solution. The idea [...]]]></description>
			<content:encoded><![CDATA[<p>I want to share this little secret, like many times, I need to do something and search but come up empty on how.  I tried to search for &#8220;replace carriage return with br tag&#8221; and many similar searches but found nothing.  A little imagination is needed at times to find a workable solution.</p>
<p>The idea is to display data previously submitted from a text area in an html element and preserve the line feeds.</p>
<p>The problem is that using a normal processing of a string to try and replace will result in the &lt; and &gt; being replaced with &amp;lt; and &amp;gt;</p>
<p>ex.</p>
<pre>
&lt;!-- this will not work as stated above --&gt;
&lt;div class=&quot;message&quot;&gt;
  &lt;s:property value=&quot;%{comment.replaceAll(&#039;\n&#039;,&#039;&lt;br/&gt;&#039;)}&quot;/&gt;
&lt;/div&gt;
</pre>
<p>The same holds true in the back end, example java code:</p>
<pre>
// will not work either, the &lt; will get replaced with &amp;lt; etc when setting &lt;s:property value=&quot;%{comment}&quot;/&gt;
comment = comment.replaceAll(&quot;\n&quot;, &quot;&lt;br/&gt;&quot;);
</pre>
<p>The solution:</p>
<pre>
&lt;div class=&quot;message&quot;&gt;
  &lt;s:set value=&quot;%{comment.split(&#039;\n&#039;)}&quot; name=&quot;message&quot;/&gt;
  &lt;s:iterator value=&quot;#message&quot;&gt;
     &lt;s:property value=&quot;%{[0].top}&quot;/&gt;&lt;br/&gt;
  &lt;/s:iterator&gt;
&lt;/div&gt;
</pre>
<p>The above uses the <a href="http://struts.apache.org/2.0.14/docs/ognl-basics.html">OGNL stack</a> <code>[0].top</code> to retrieve only the textual information for display.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Freplace-carriage-return-in-ognl%2F&amp;linkname=Replace%20Carriage%20Return%20in%20OGNL"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/replace-carriage-return-in-ognl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Toshiba Satellite Wireless Problem/Solution when Running on Battery</title>
		<link>http://richardbondi.net/blog/toshiba-satellite-wireless-problemsolution-when-running-on-battery/</link>
		<comments>http://richardbondi.net/blog/toshiba-satellite-wireless-problemsolution-when-running-on-battery/#comments</comments>
		<pubDate>Thu, 13 Jan 2011 20:44:07 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Computer]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=178</guid>
		<description><![CDATA[I had just struggled for several days and far too many website visits trying to solve this problem and wanted to share the solution. I had just bought a new Toshiba laptop and I love it except for the problem I encountered with the wireless connection. As long as I was plugged in to power, [...]]]></description>
			<content:encoded><![CDATA[<p>I had just struggled for several days and far too many website visits trying to solve this problem and wanted to share the solution.  I had just bought a new Toshiba laptop and I love it except for the problem I encountered with the wireless connection.  As long as I was plugged in to power, everything was fine.  When I was not plugged in, the wireless connection was unusable.  I had already spent a considerable of time, installing all the software I need and transferring all of my data, so returning it was not the option I was willing to accept.</p>
<p>One of the obvious things to do is check the driver and see if it is up to date.  What is not obvious is that just because Windows says that you have the most current version, that does not mean you have the most current version.  So if you have a Toshiba Satellite, first check what wireless adapter it has.</p>
<ol>
<li>Open the Device Manager(Click the start button and type in &#8220;Device Manager&#8221; then select)</li>
<li>Look for &#8220;Network Adapters&#8221;</li>
<li>If your wireless adapter shows &#8220;Realtek&#8221; go <a href="http://www.realtek.com/downloads/downloadsView.aspx?Langid=1&amp;PNid=21&amp;PFid=48&amp;Level=5&amp;Conn=4&amp;DownTypeID=3&amp;GetDown=false">here </a>to get the latest driver that Windows does not know about</li>
<li>Select the driver for the model that matches the model in &#8220;Device Manager&#8221; in step 1</li>
<li>Download/Install the new driver and reboot</li>
</ol>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Ftoshiba-satellite-wireless-problemsolution-when-running-on-battery%2F&amp;linkname=Toshiba%20Satellite%20Wireless%20Problem%2FSolution%20when%20Running%20on%20Battery"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/toshiba-satellite-wireless-problemsolution-when-running-on-battery/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Javascript Diff Combines Scripts to Fill the Gap</title>
		<link>http://richardbondi.net/blog/javascript-diff-combines-scripts-to-fill-the-gap/</link>
		<comments>http://richardbondi.net/blog/javascript-diff-combines-scripts-to-fill-the-gap/#comments</comments>
		<pubDate>Fri, 07 Jan 2011 05:31:16 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=166</guid>
		<description><![CDATA[There is a wealth of great code available for anyone to use to cover nearly every possible scenario. Sometimes a little creativity and minor effort is needed when what you need is not readily available. I was in need of a client side script for displaying diffs. I found this which is nice. It is [...]]]></description>
			<content:encoded><![CDATA[<p>There is a wealth of great code available for anyone to use to cover nearly every possible scenario.  Sometimes a little creativity and minor effort is needed when what you need is not readily available.</p>
<p>I was in need of a client side script for displaying diffs.  I found <a href="http://snowtide.com/jsdifflib">this</a> which is nice.  It is limited however to a line by line diff and is lacking in a word by word diff.</p>
<p>I also found <a href="http://ejohn.org/projects/javascript-diff-algorithm/">this</a> which is does word diff but not line by line.  Looking at the site, it seems that it does a nice job of formatting inline diffs.  Looking a little further into the code, it has an undocumented function for displaying the diffs as two separate strings.</p>
<p>So, all I had to do is get the &#8220;Line diff&#8221; script to call the &#8220;Word diff&#8221; script in the right place to get the desired result, a side by side diff view with word diff highlighting.</p>
<p>You can experiment with the <a href="../static/diff">demo</a>.</p>
<p><a href="../static/diff/diff.zip">Download</a> the demo code.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Fjavascript-diff-combines-scripts-to-fill-the-gap%2F&amp;linkname=Javascript%20Diff%20Combines%20Scripts%20to%20Fill%20the%20Gap"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/javascript-diff-combines-scripts-to-fill-the-gap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sanitizing HTML Injection Using DOM</title>
		<link>http://richardbondi.net/blog/sanitizing-html-injection-using-dom/</link>
		<comments>http://richardbondi.net/blog/sanitizing-html-injection-using-dom/#comments</comments>
		<pubDate>Tue, 06 Jul 2010 21:14:36 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=127</guid>
		<description><![CDATA[This article explains how to use the DOM to sanitize user input against HTML injection. In many cases, something as simple as replacing the < and > with &#38;lt; and &#38;gt;. In other cases, you may want to display user entry and preserve HTML entities such as PRE tags. You still will need to eliminate [...]]]></description>
			<content:encoded><![CDATA[<p>This article explains how to use the DOM to sanitize user input against HTML injection.  In many cases, something as simple as replacing the <code>< and ></code> with <code>&amp;lt; and &amp;gt;</code>.  In other cases, you may want to display user entry and preserve HTML entities such as PRE tags.  You still will need to eliminate the injection attacks.  I searched the web and did not find what I needed so I created this method.</p>
<p>See <a href="../static/sanitize/injection.html" target=_blank>example</a>:</p>
<p><span id="more-127"></span></p>
<p>The process is pretty straight forward.  </p>
<ol>
<li>Create ad dummy div element</li>
<li>Set the content of the element to the text you wish to sanitize</li>
<li>Loop through all of the element&#8217;s tags</li>
<li>Remove undesirable elements</li>
<li>Remove unwanted attributes</li>
<li>Loop through and remove all events</li>
</ol>
<pre>
function sanitize(text){

    // create a dummy element
    var div = document.createElement('div');
    div.innerHTML = text;
    var alltags = div.getElementsByTagName('*');

    // loop through any html tags that may have been entered(backwards because we delete as we go)
    for (var i = alltags.length-1; i > -1; i--) {
        var el = alltags.item(i);
		var elname = el.tagName.toLowerCase();

		// remove undesirable elements
		if(elname=='object' || elname=='iframe' || elname=='embed'){
            var p = el.parentNode;
			p.removeChild(el);
			continue;
		}

		// remove styling, this could be used to block entire page
		// you probably would do this selectively keeping whitelist items only
        el.removeAttribute('style');
        el.removeAttribute('class');

        // loop through any attributes(backwards because we delete as we go)
        var attrs = el.attributes;
        var l = attrs.length;

        for (var a = l - 1; a > -1; a--) {
            var attr = attrs[a].name.toLowerCase();
			var val = attrs[a].value.toLowerCase();

            if(attr=='href' &#038;&#038; val.indexOf('javascript') > -1){
                el.removeAttribute(attr);
            }

            // remove onAnything
            if (attr.indexOf('on') == 0) {
                el.removeAttribute(attr);
            }
        }
    }

    var r = div.innerHTML;
    div = null;
    return r;
}
</pre>
<p>I hope you find this approach useful.  This is a generic example and may or may not be adaptable to your application.  Please give the code a try or play with the <a href="../static/sanitize/injection.html" target=_blank>example</a> and leave a comment with your findings.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Fsanitizing-html-injection-using-dom%2F&amp;linkname=Sanitizing%20HTML%20Injection%20Using%20DOM"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/sanitizing-html-injection-using-dom/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dynamic Loading and Executing of Javascript in AJAX</title>
		<link>http://richardbondi.net/blog/dynamic-loading-and-executing-of-javascript-in-ajax/</link>
		<comments>http://richardbondi.net/blog/dynamic-loading-and-executing-of-javascript-in-ajax/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 17:04:15 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=98</guid>
		<description><![CDATA[You may find there are times when you use certain scripts on an AJAX site, but not on all pages. It would be very inefficient to load them all immediately in the HEAD of the HTML document. Also if you are developing a plugin architecture, it would be preferable to load them at runtime. Today [...]]]></description>
			<content:encoded><![CDATA[<p>You may find there are times when you use certain scripts on an AJAX site, but not on all pages.  It would be very inefficient to load them all immediately in the HEAD of the HTML document.  Also if you are developing a plugin architecture, it would be preferable to load them at runtime.  Today I will share with you how to load and execute a script dynamically.  This is a multi step process involving the tracking of what scripts have been called to load, what has been loaded and what has been executed.</p>
<p><span id="more-98"></span></p>
<p>First we need some variables to store what scripts have been called to load, have been fully loaded and have been executed.  I will use the term plugins in the examples.</p>
<pre>
var plugins = [];  // all plugins used in a page
var plugin_urls = [];  // hold loaded scripts here to not duplicate
var loaded_plugins = [];   // script is fully loaded
var unrendered = [];  // holder for multiple use of same plugin on page
</pre>
<p>Now that the variables are in place, we need to have a function to load the plugin scripts and check to make sure we do not load duplicates.  We will use an event handler for when the script has finished loading.  You may need more than the url if you have multiple instances of the same script with different parameters.  The whole example makes that assumption.  There are many ways you could handle this.  We will look at just the url for simplicity.</p>
<pre>
function loadPlugin(url){
	// check to see if plugin has been called to load, if so no need to continue
	if (ArrayContains(plugin_urls,url))
		return;

	plugin_urls.push(url); // track loading calls

	// add plugin script
    var script = document.createElement("script");
	script.src = url;
	script.type="text/javascript";

	// once fully loaded, update instances
    if (script.readyState){  //IE
        script.onreadystatechange = function(){
            if (script.readyState == "loaded" ||
                    script.readyState == "complete"){
                script.onreadystatechange = null;
                updateplugins(url);
            }
        };
    } else {  //Others
        script.onload = function(){
			updateplugins(url);
        };
    }

	document.getElementsByTagName("head")[0].appendChild(script);
}
</pre>
<p>And the event handler.  You may need more than the url if you have multiple instances of the same script with different parameters.</p>
<pre>
function updateplugins(url){

    // this plugin has finished loading
    loaded_plugins.push(url);	

	// render all instances of the plugin?
	for(var p=unrendered.length-1;p>-1;p--){
		var src = unrendered[p]; // url of script

		if (!ArrayContains(loaded_plugins, src)) // different plugin finished loading
			continue;

		renderplugin(src);
	}
}
</pre>
<p>A function to render the plugin</p>
<pre>
function renderplugin(url){
       /*
            your calls to the newly loaded script
            You will probably store other parameters
            beside the url
       */

       ArrayRemove(unrendered,url); // we will not need to look any more in the event handler
}
</pre>
<p>Utility array functions</p>
<pre>
function ArrayContains(ar, value) {
	for (var i = 0;i < ar.length; i++) {
		if (ar[i] == value) {
			return true;
		}
	}
	return false;
}

function ArrayRemove(ar, value) {
	for (var i = 0;i < ar.length; i++) {
		if (ar[i] == value) {
			ar.splice(i,1);
		}
	}
}
</pre>
<p>We are talking about AJAX here so you would be calling the plugins based on a call you made to the server for example in the onreadystatechange function of an XMLHttpRequest.  The below loop will queue all plugins from the request one at a time and they will be removed from the queue once rendered.  The script may have been loaded on a previous call so we check to see if it is fully loaded and if so render it, if not continue to check other scripts.  The queued scripts not handled here will be handled in the event handler described above.  All will get fired and the handler will check what needs to be executed.</p>
<pre>
	unrendered = []; // queue for plugins loading
	for (var p in plugins) {
		var src = plugins[p];

		unrendered.push(src); // automatically removed when rendered
		loadPlugin(src);  

		// not finished loading yet, remains queued
		if (!ArrayContains(loaded_plugins, src)) {
			continue;
		}

		renderplugin(src); // render specific instance of plugin
	}
</pre>
<p>This technique is used in the <a href="../../static/wikihelp/">Wiki Web Help</a> project</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Fdynamic-loading-and-executing-of-javascript-in-ajax%2F&amp;linkname=Dynamic%20Loading%20and%20Executing%20of%20Javascript%20in%20AJAX"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/dynamic-loading-and-executing-of-javascript-in-ajax/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Javascript Tab Control</title>
		<link>http://richardbondi.net/blog/javascript-tab-control/</link>
		<comments>http://richardbondi.net/blog/javascript-tab-control/#comments</comments>
		<pubDate>Sat, 12 Dec 2009 14:32:53 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=86</guid>
		<description><![CDATA[Greetings. Today&#8217;s tip is a minimal javascript tab control. See Example I prefer to use a hybrid approach where the layout and style is done in the html and css and the functionality is done in java script. First we will look at the html layout. &#60;div id=&#34;tabdiv2&#34; class=&#34;pane&#34;&#62; &#60;div id=&#34;ctab21&#34; class=&#34;tabcontent&#34;&#62;Second control tab 1&#60;/div&#62; [...]]]></description>
			<content:encoded><![CDATA[<p>Greetings. Today&#8217;s tip is a minimal javascript tab control.</p>
<p><img src="../static/tabs/tabs.png" alt="Image of tab control" /></p>
<p>See <a href="../static/tabs">Example</a></p>
<p><span id="more-86"></span></p>
<p>I prefer to use a hybrid approach where the layout and style is done in the html and css and the functionality is done in java script. First we will look at the html layout.</p>
<pre>
&lt;div id=&quot;tabdiv2&quot;  class=&quot;pane&quot;&gt;
     &lt;div id=&quot;ctab21&quot; class=&quot;tabcontent&quot;&gt;Second control tab 1&lt;/div&gt;
        &lt;div id=&quot;ctab22&quot; class=&quot;tabcontent&quot;&gt;content of tab 2 Control 2&lt;/div&gt;
        &lt;div id=&quot;ctab23&quot; class=&quot;tabcontent&quot;&gt;&lt;div style=&quot;background:green;width:100%;height:100%;&quot;&gt;Green&lt;/div&gt;&lt;/div&gt;
        &lt;div id=&quot;ctab24&quot; class=&quot;tabcontent&quot;&gt;
            &lt;div id=&quot;tabdiv&quot; class=&quot;pane&quot;&gt;
                &lt;div id=&quot;ctab1&quot; class=&quot;tabcontent&quot;&gt;This is the content of tab 1&lt;/div&gt;
                &lt;div id=&quot;ctab2&quot; class=&quot;tabcontent&quot;&gt;content of tab 2 here&lt;/div&gt;
                &lt;div id=&quot;ctab3&quot; class=&quot;tabcontent&quot;&gt;&lt;div style=&quot;background:red;width:100%;height:100%;&quot;&gt;RED&lt;/div&gt;&lt;/div&gt;
           &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
</pre>
<p>The <strong>pane </strong>class is the holder for the entire tab control and the <strong>tabcontent </strong>class is for the individual tabs of the control. Here is the rest of the css.</p>
<pre>
div.taboff{
    border:1px solid #888888;
    background:#eeeedc;
    color:#888888;
    float:left;
    margin-right:5px;
    padding:0 8px 0 8px;
    line-height:20px;
    font-weight:bold;
}

div.tabon{
    border:1px solid #888888;
    border-bottom:1px solid #ffffff;
    background:#ffffdd;
    color:#0000aa;
    float:left;
    margin-right:5px;
    padding:0 8px 0 8px;
    line-height:20px;
    font-weight:bold;
}

div.pane{
    position:relative;
    vertical-align:text-top;
    display:inline-block;
    margin:auto;
    width:340px;
    z-index:0;
}

div.tabcontent{
    border:1px solid #888888;
    position:absolute;
    text-align:left;
    padding:5px;
    background:#ffffdd;
    display:block;
    z-index:-1;
}
</pre>
<p><strong>tabon</strong> and <strong>taboff</strong> control the tab appearance. <strong>pane</strong> is the tabcontrol. <strong>tabcontent</strong> is for whatever you want to display inside the individual tabs. Pay close attention to the z-index of pane and tabcontent so that the tabs appear correctly when selected.</p>
<p>And finally the javascript. Here is the class:</p>
<pre>
var tabs = function(){

  return{
    info : null,
    target : null,
    name : null,
    height : null,
    width : null,

    create : function(obj){

        this.target = document.getElementById(obj.target);
        this.info = obj.info;
        this.name = obj.name;
        this.height = obj.height;
        this.width = obj.width;

        this.target.style.height = this.height;

        for(var i=0;i&lt;this.info.length;i++){
            var tab = document.createElement(&quot;div&quot;);
            tab.setAttribute(&#039;id&#039;,this.name+&#039;tab&#039;+i);
            tab.className = &#039;taboff&#039;;
            var html = this.info[i].label;
            if(this.info[i].icon != null)
              html += &quot; &lt;div style=&#039;;margin:auto;padding-left:5px;display:inline;&#039;&gt;&quot;
                      +    &quot;&lt;img  style=&#039;vertical-align:middle&#039; src=&#039;&quot;+this.info[i].icon+&quot;&#039; /&gt;&lt;/div&gt;&quot;
            tab.innerHTML = html;

            var self = this;
            tab.onclick = function(){
                for (var i = 0; i &lt; self.info.length; i++) {
                    var tab = document.getElementById(self.name+&#039;tab&#039;+i);
                    var content = document.getElementById(self.info[i].content);
                    if(tab==this){
                        tab.className = &quot;tabon&quot;;
                        content.style.display = &#039;block&#039;;
                    }else{
                        content.style.display = &#039;none&#039;;
                        tab.className = &quot;taboff&quot;;
                    }
                }
            };

            tab.style.height = &quot;22px&quot;;
            this.target.appendChild(tab);

            var cont = document.getElementById(this.info[i].content);
            cont.style.left = 0;
            cont.style.display = &#039;none&#039;;
            cont.style.width = this.width;
            cont.style.top = (tab.offsetHeight - 1) + &#039;px&#039;;
            cont.style.height = parseInt(this.height) - (tab.offsetHeight - 1) +&quot;px&quot;;
        }

        document.getElementById(this.name+&#039;tab0&#039;).className = &quot;tabon&quot;;
        document.getElementById(this.info[0].content).style.display=&#039;block&#039;;

    }    

  };
}
</pre>
<p>To use the class simply call it when the page loads with the desired parameters:</p>
<pre>
function showtabs(){

    var tab1 = tabs();
    tab1.create(
        {
            name : "demo1",
            target:"tabdiv",
            width : "328px",
            height : "115px",
            info:[
                {label:"Uno" , content: "ctab1"},
                {label:"Dos" , content: "ctab2"},
                {label:"Red" , content: "ctab3"}
            ]
        }
    );

    var tab2 = tabs();
    tab2.create(
        {
            name : "demo2",
            target:"tabdiv2",
            width : "340px",
            height : "150px",
            info:[
                {label:"First" , content: "ctab21", icon: "images/go-home.png"},
                {label:"2nd" , content: "ctab22", icon: "images/utilities-notes.png"},
                {label:"Green" , content: "ctab23", icon: "images/preferences-users.png"},
                {label:"Tabs in Tabs" , content: "ctab24", icon: "images/office-calendar.png"}
            ]
        }
    );
}
</pre>
<p>The tabs are created with the create function and a single JSON object with all the information. The <strong>name</strong> attribute must be unique for multiple tabs as in the example. The <strong>target</strong> attribute is the id of the placeholder in the html. The <strong>info</strong> array defines the individual tabs. <strong>label</strong> is the text of each tab. <strong>content</strong> is the id of the placeholder for that tab and the <strong>icon</strong> is optional.</p>
<p><a href="./static/tabs/tabs.zip">Download</a></p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Fjavascript-tab-control%2F&amp;linkname=Javascript%20Tab%20Control"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/javascript-tab-control/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Keep Your Visitors Oriented &#8211; Ajax Popup Form</title>
		<link>http://richardbondi.net/blog/keep-your-visitors-oriented-ajax-popup-form/</link>
		<comments>http://richardbondi.net/blog/keep-your-visitors-oriented-ajax-popup-form/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 16:09:09 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=81</guid>
		<description><![CDATA[In a previous article I showed how to add a simple popup form to a site. This was done inline for illustration. Today I will present the advanced version using ajax to both retrieve and submit the form content. By having the form content on the server, you have a much easier to maintain approach, [...]]]></description>
			<content:encoded><![CDATA[<p>In a previous article I showed how to add a simple popup form to a site. This was done inline for illustration. Today I will present the advanced version using ajax to both retrieve and submit the form content. By having the form content on the server, you have a much easier to maintain approach, especially with larger forms.</p>
<p>This script will create the form by fetching the form content from the server. It will create the form with all the interaction needed to cleanly submit the form without leaving the page.</p>
<p>See <a href="../static/ajaxform">example</a>.</p>
<p><span id="more-81"></span><br />
The script is called something like this:</p>
<pre>
  &lt;div style=&quot;position:relative;&quot; id=&#039;register&#039;&gt;
  &lt;/div&gt;

   &lt;p&gt;
       &lt;a href=&quot;javascript:popup.show(&#039;register.html&#039;, &#039;register&#039;, &#039;register.php&#039;, 260, &#039;validateRegister()&#039;)&quot;&gt;
            Register Here
        &lt;/a&gt;
   ...
  &lt;/p&gt;
</pre>
<p>The first parameter is the html of the for we want to present.<br />
The
<div> tag is our placeholder. Its id is passed as the second parameter to the script.<br />
The 3rd paramenter handles the form submission.<br />
Parameter 4 is the width.<br />
Optionally we can pass a validation function in parameter 5.</p>
<p>Here is the script:</p>
<pre>
var popup = {

    postmode: null,

    show : function(form, id, handler, w, validator) {
        var xmlHttp=popup.getRequestObject();
        if (xmlHttp==null) {
          alert (&quot;Your browser does not support AJAX!&quot;);
          return;
        } 

        var elid = id;
        var element = document.getElementById(id);
        var action = handler;
        var width = w;
        var val = validator==null?&quot;&quot;:&quot;,\&quot;&quot;+validator+&quot;\&quot;&quot;;
        element.style.display = &#039;block&#039;;
        xmlHttp.onreadystatechange=function (){
            if (xmlHttp.readyState == 4 &amp;&amp; xmlHttp.status == 200) {
                var response = xmlHttp.responseText;
                element.innerHTML = &quot;&lt;form onsubmit=&#039;popup.submitform(this,\&quot;&quot;
                                    +handler+&quot;\&quot;,\&quot;&quot;
                                    +elid+&quot;\&quot;&quot;
                                    +val+&quot;);return false;&#039;&gt;&lt;div style=&#039;width:&quot;+width
                                    +&quot;;position:absolute;&#039;&gt;&lt;div class=&#039;popup&#039;&gt;&lt;div style=&#039;float:right;&#039;&gt;&quot;
                                    +&quot;&lt;a href=&#039;javascript:popup.closeform(\&quot;&quot;
                                    +elid+&quot;\&quot;);&#039; class=&#039;close&#039;&gt;&quot;
                                    +&quot;x&lt;/a&gt;&lt;/div&gt;&lt;div style=&#039;clear:both;&#039;&gt;&lt;/div&gt;&quot;
                                    +response
                                    +&quot;&lt;/div&gt;&lt;/div&gt;&lt;/form&gt;&quot;;
            }
        };

        xmlHttp.open(&quot;GET&quot;,form,this.postmode);
        xmlHttp.send(null);
    },

    closeform : function(id){
        document.getElementById(id).style.display=&#039;none&#039;;
    },

    submitform : function(frm,action,id,val){
        if (val != null) {
            if(!eval(val))
                return false;

        }

        var url = action;
        var params = &#039;&#039;;
        var element = document.getElementById(id);
        for(i=0; i&lt;frm.elements.length; i++){
            if(frm.elements[i].name!=&#039;&#039;){
                params+= + i==0?&#039;&#039;:&#039;&amp;&#039;;
                params+=frm.elements[i].name + &quot;=&quot; + escape(frm.elements[i].value);
            }
        }        

        var xmlHttp=popup.getRequestObject();

        xmlHttp.onreadystatechange=function (){
            if (xmlHttp.readyState == 4 &amp;&amp; xmlHttp.status == 200) {
                var response = xmlHttp.responseText.replace(/^\s+|\s+$/g, &#039;&#039;); // trim

                if (response == &#039;ok&#039;) {
                    alert(&#039;Success!&#039;);
                    element.style.display = &#039;none&#039;;
                }
                else
                    alert(&#039;Submission failure&#039;);
            }
        };

        xmlHttp.open(&quot;POST&quot;,url,this.postmode);
        xmlHttp.setRequestHeader(&quot;Content-type&quot;, &quot;application/x-www-form-urlencoded&quot;);
        xmlHttp.setRequestHeader(&quot;Content-length&quot;, params.length);
        xmlHttp.setRequestHeader(&quot;Connection&quot;, &quot;close&quot;);
        xmlHttp.send(params);
    },

    getRequestObject : function() {
        var xmlHttp=null;
        try{
            this.postmode = true;
            xmlHttp=new XMLHttpRequest();
        }catch (e){
          // Internet Explorer
          try{
            this.postmode = false;
            xmlHttp=new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);
          } catch (e){
                xmlHttp=new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);
            }
          }
        return xmlHttp;
    }
}
</pre>
<p>The show() function is called as described above. It will create the<br />
<form> element and what is necessary to display our close button, call the validation if needed, submit the data and prompt with success or failure.</p>
<p>The submitform() function is created by the show() function and is called via the onsubmit which is also created by show().</p>
<p>The basic form appearance is set int the popup element of the style sheet and the close button appearance is set in the close style sheet element. Add your own elements to the style sheet as needed.</p>
<pre>
  a.close{
        background:#aaaaaa;
        color:white;
        border:2px solid #ccccff;
        font-size:16px;
        font-weight:bold;
        line-height:28px;
        vertical-align:middle;
        padding:0 5px 3px 5px;
        text-decoration:none;
   }

   form{
        margin:0;
        padding:0;
   }

   div.popup{
        position:absolute;
        top:3px;
        left:3px;
        border:3px solid #cccccc;
        background:#fcfcfc;
        padding:3px 8px 15px 8px;
        font-family: Arial, Helvetica, sans-serif;
        font-size:12px;
   }
</pre>
<p><a href="..//static/ajaxform/ajaxform.zip">Download</a></p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Fkeep-your-visitors-oriented-ajax-popup-form%2F&amp;linkname=Keep%20Your%20Visitors%20Oriented%20%26%238211%3B%20Ajax%20Popup%20Form"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/keep-your-visitors-oriented-ajax-popup-form/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java Swing Image Map</title>
		<link>http://richardbondi.net/blog/java-swing-image-map/</link>
		<comments>http://richardbondi.net/blog/java-swing-image-map/#comments</comments>
		<pubDate>Thu, 03 Dec 2009 22:59:17 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=69</guid>
		<description><![CDATA[Here I will present a simple Image Map class written in java using swing. This will allow you to create an interactive image and take action base on where the image was clicked. This could be used for a map of a campground, townhouses, subdivision, storage facility, city zoning or building floor plan among others. [...]]]></description>
			<content:encoded><![CDATA[<p>Here I will present a simple Image Map class written in java using swing. This will allow you to create an interactive image and take action base on where the image was clicked. This could be used for a map of a campground, townhouses, subdivision, storage facility, city zoning or building floor plan among others. Each active section of the map will use the java <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Polygon.html" target=_blank>Polygon </a>class to outline the section.</p>
<p><img class="alignnone" title="Image map image" src="../static/imagemap/screen.png" alt="Image map image" width="400" height="180" /></p>
<p>The example from this image is a section of a street map of individual lots. It will highlight the lot when clicked and display information about the lot in the other panel.</p>
<p><span id="more-69"></span><br />
We do several simple tasks with our ImagePanel class. First we listen for the mouse events and fire a PropertyChange for the the main application. Below we loop through the Vector of Polygons to see if the mouse point is contained in any of the Polygons. If so, the index is sent to the main application. The index is used by the main application to retrieve additional data.</p>
<pre>
    public void mouseReleased(MouseEvent e) {

        for(int i=0; i&lt;polys.size();i++){
            if(polys.get(i).contains(e.getX(), e.getY())){
                this.firePropertyChange(&quot;CurrentMap&quot;, currentMap, i);
                currentMap = i;
                repaint();
                break; // no need to keep checking
            }
        }
    }
</pre>
<p>Also we override the paint() function to highlight the selected area.</p>
<pre>
    public void paint(java.awt.Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.drawImage(this.img, 0, 0, this);

        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, this.alpha));
        g2.setColor(this.fill);
        if(this.currentMap > -1)
            g2.fillPolygon(this.polys.get(this.currentMap));
    }
</pre>
<p>And we respond to the property change in the main application. The index tells us which data to present.</p>
<pre>
    public void propertyChange(PropertyChangeEvent arg0) {
        if(arg0.getSource().equals(imagepanel)){
            int index = (Integer)arg0.getNewValue();
            if(index> -1){
                lot.setText(lots.get(index));
                rent.setText(prices.get(index));
                status.setText(statuses.get(index));
            }
        }
    }
</pre>
<p>The Polygons are passed to the ImageMap class through its member function setPolygons(Vector<Polygon> polygons) which takes a Vector of Polygons. Also the fill color is set with setFill(Color color) or to change from the default opacity with setFill(Color color, float alpha).</p>
<p>Here is the entire class:</p>
<pre>
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Polygon;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

import javax.swing.JPanel;

public class ImagePanel extends JPanel implements MouseListener{
    private Image img;
    private Vector&lt;Polygon&gt; polys;
    private int currentMap;
    private Color fill;
    private float alpha;

    public ImagePanel(Image image){
        this.img = image;
        currentMap = -1;
        addMouseListener(this);
        alpha = 0.1f;
        fill = Color.BLUE;
    }

    public void setPolygons(Vector&lt;Polygon&gt; polygons){
        this.polys = polygons;
    }

    public void setFill(Color color){
        this.fill = color;
    }

    public void setFill(Color color, float alpha){
        this.fill = color;
        this.alpha = alpha;
    }

    public void paint(java.awt.Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.drawImage(this.img, 0, 0, this);

        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, this.alpha));
        g2.setColor(this.fill);
        if(this.currentMap &gt; -1)
            g2.fillPolygon(this.polys.get(this.currentMap));
    }

    public void mouseReleased(MouseEvent e) {

        for(int i=0; i&lt;polys.size();i++){
            if(polys.get(i).contains(e.getX(), e.getY())){
                this.firePropertyChange(&quot;CurrentMap&quot;, currentMap, i);
                currentMap = i;
                repaint();
                break; // no need to keep checking
            }
        }
    }

    // unused mouse
    public void mouseClicked(MouseEvent arg0) {}

    public void mouseEntered(MouseEvent arg0) {}

    public void mouseExited(MouseEvent arg0) {}

    public void mousePressed(MouseEvent arg0) {}

}
</pre>
<p><a href="../static/imagemap/ImageMap.zip">Download </a>source.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Fjava-swing-image-map%2F&amp;linkname=Java%20Swing%20Image%20Map"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/java-swing-image-map/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Save Time With jEdit and Regular Expressions</title>
		<link>http://richardbondi.net/blog/save-time-with-jedit-and-regular-expressions/</link>
		<comments>http://richardbondi.net/blog/save-time-with-jedit-and-regular-expressions/#comments</comments>
		<pubDate>Tue, 01 Dec 2009 19:03:55 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=50</guid>
		<description><![CDATA[Today&#8217;s trick is a time saver that I have been using for some time. It involves using regular expression searches with the jEdit editor. This handy little tool is a free open source editor written in Java. Let&#8217;s look at an example. Take the simple form below for illustration. &#60;head&#62;&#60;title&#62;Regular Expression Example Page&#60;/title&#62;&#60;/head&#62; &#60;body&#62; &#60;form action="nowhere.php" [...]]]></description>
			<content:encoded><![CDATA[<p>Today&#8217;s trick is a time saver that I have been using for some time. It involves using regular expression searches with the <a href="http://jedit.org/" target="_blank">jEdit </a>editor. This handy little tool is a free open source editor written in Java.</p>
<p>Let&#8217;s look at an example. Take the simple form below for illustration.</p>
<pre>
 &lt;head&gt;&lt;title&gt;Regular Expression Example Page&lt;/title&gt;&lt;/head&gt;
 &lt;body&gt;
  &lt;form action="nowhere.php" method="post"&gt;
   Last &lt;input type="text" name="Last"&gt;&lt;br /&gt;
   First &lt;input type="text" name="first"&gt;&lt;br /&gt;
   Address &lt;input type="text" name="address"&gt;&lt;br /&gt;
   Address 2 &lt;input type="text" name="address2"&gt;&lt;br /&gt;
   City &lt;input type="text" name="city"&gt;&lt;br /&gt;
   State &lt;input type="text" name="state"&gt;&lt;br /&gt;
   Zip &lt;input type="text" name="zip"&gt;&lt;br /&gt;
   &lt;input type="submit" /&gt;
  &lt;/form&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Now let&#8217;s say we want to validate the form. To do this we need the id attribute for each input tag. In jEdit you can select the lines for the inputs in question. Pull up the search dialog by hitting ctl-f or from the menu. Since you selected only the lines you want, the dialog will appear with the option to search the selection.</p>
<p><span id="more-50"></span><br />
<img src="../static/regex/searchdialog.png" alt="image" /></p>
<p>Be sure to have the Regular Expressions box checked. We can add the id to our form with the following:</p>
<p>Search:</p>
<p><code>name="([0-9A-Za-z_]*)"</code></p>
<p>Replace:</p>
<p><code>name="$1" id="$1"</code></p>
<p>Now we should have the following:</p>
<pre>
 &lt;head&gt;&lt;title&gt;Regular Expression Example Page&lt;/title&gt;&lt;/head&gt;
 &lt;body&gt;
  &lt;form action=&quot;nowhere.php&quot; method=&quot;post&quot;&gt;
   Last &lt;input type=&quot;text&quot; name=&quot;Last&quot; id=&quot;Last&quot;&gt;&lt;br /&gt;
   First &lt;input type=&quot;text&quot; name=&quot;first&quot; id=&quot;first&quot;&gt;&lt;br /&gt;
   Address &lt;input type=&quot;text&quot; name=&quot;address&quot; id=&quot;address&quot;&gt;&lt;br /&gt;
   Address 2 &lt;input type=&quot;text&quot; name=&quot;address2&quot; id=&quot;address2&quot;&gt;&lt;br /&gt;
   City &lt;input type=&quot;text&quot; name=&quot;city&quot; id=&quot;city&quot;&gt;&lt;br /&gt;
   State &lt;input type=&quot;text&quot; name=&quot;state&quot; id=&quot;state&quot;&gt;&lt;br /&gt;
   Zip &lt;input type=&quot;text&quot; name=&quot;zip&quot; id=&quot;zip&quot;&gt;&lt;br /&gt;
   &lt;input type=&quot;submit&quot; /&gt;
  &lt;/form&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Now we get to the good stuff. jEdit has a console plugin that is a must have. From the console plugin you can execute commands from the system shell without leaving the editor. You can output the results to a new buffer in the editor by pressing the following from the console window:</p>
<p><img src="../static/regex/runbuffer.png" alt="image" /></p>
<p>Save your file at this point. From the command window you can change to the directory of the current buffer by typing cd $d. Once in the directory run grep name= index.html(assuming) on unix systems or findstr name= index.htm on Windows and press the button to run to a new buffer. This will give you:</p>
<pre>
   Last &lt;input type=&quot;text&quot; name=&quot;Last&quot; id=&quot;Last&quot;&gt;&lt;br /&gt;
   First &lt;input type=&quot;text&quot; name=&quot;first&quot; id=&quot;first&quot;&gt;&lt;br /&gt;
   Address &lt;input type=&quot;text&quot; name=&quot;address&quot; id=&quot;address&quot;&gt;&lt;br /&gt;
   Address 2 &lt;input type=&quot;text&quot; name=&quot;address2&quot; id=&quot;address2&quot;&gt;&lt;br /&gt;
   City &lt;input type=&quot;text&quot; name=&quot;city&quot; id=&quot;city&quot;&gt;&lt;br /&gt;
   State &lt;input type=&quot;text&quot; name=&quot;state&quot; id=&quot;state&quot;&gt;&lt;br /&gt;
   Zip &lt;input type=&quot;text&quot; name=&quot;zip&quot; id=&quot;zip&quot;&gt;&lt;br /&gt;
</pre>
<p>in a new buffer. Now we can search and replace. Note: if this seems intimidating, you could just copy and paste the lines you want into a new buffer. However the console method is preferred because typically in the real world it is not as simple as the example. Not all inputs will be one line after another.</p>
<p>Search for:</p>
<p><code>^.*name="([0-9A-Za-z_]*)".*\n</code></p>
<p>Replace with:</p>
<pre>
element=document.getElementById('$1');
if(element.value==''){
  alert('Please complete all required fields');
  element.focus();
  element.select();
  return false;
}
</pre>
<p>Note: to enter the above use ctl-Enter at the end of each line. This will allow you to replace with multiple lines. Also if you are using findstr in Windows you need to replace the \n with \r\n in the search.</p>
<p>The result form validation script body:</p>
<pre>
element=document.getElementById('Last');
if(element.value==''){
  alert('Please complete all required fields');
  element.focus();
  element.select();
  return false;
}

element=document.getElementById('first');
if(element.value==''){
  alert('Please complete all required fields');
  element.focus();
  element.select();
  return false;
}

element=document.getElementById('address');
if(element.value==''){
  alert('Please complete all required fields');
  element.focus();
  element.select();
  return false;
}

element=document.getElementById('address_2');
if(element.value==''){
  alert('Please complete all required fields');
  element.focus();
  element.select();
  return false;
}

element=document.getElementById('city');
if(element.value==''){
  alert('Please complete all required fields');
  element.focus();
  element.select();
  return false;
}

element=document.getElementById('state');
if(element.value==''){
  alert('Please complete all required fields');
  element.focus();
  element.select();
  return false;
}

element=document.getElementById('zip');
if(element.value==''){
  alert('Please complete all required fields');
  element.focus();
  element.select();
  return false;
}
</pre>
<p>For the php server end:</p>
<p>Search:</p>
<p><code>^.*name="([0-9A-Za-z_]*)".*\n</code></p>
<p>Replace:</p>
<p><code>\$$1 = \$_POST["$1"];\n</code></p>
<p>Result:</p>
<pre>
$Last = $_POST["Last"];
$first = $_POST["first"];
$address = $_POST["address"];
$address_2 = $_POST["address_2"];
$city = $_POST["city"];
$state = $_POST["state"];
$zip = $_POST["zip"];
</pre>
<p>I hope you have found this useful or at least it has stimulated you with ideas of other ways you can save time and carpal tunnel with this technique.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Fsave-time-with-jedit-and-regular-expressions%2F&amp;linkname=Save%20Time%20With%20jEdit%20and%20Regular%20Expressions"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/save-time-with-jedit-and-regular-expressions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java Ring Chart</title>
		<link>http://richardbondi.net/blog/java-ring-chart/</link>
		<comments>http://richardbondi.net/blog/java-ring-chart/#comments</comments>
		<pubDate>Mon, 30 Nov 2009 22:29:05 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://richardbondi.net/blog/?p=19</guid>
		<description><![CDATA[This week I am going to talk about Java graphics and how they can be used to create a custom Ring Chart control also commonly referred to as a doughnut chart. This is the type of chart typically used for disk allocation in programs similar to Overdisk. They however are not limited to this application. [...]]]></description>
			<content:encoded><![CDATA[<p>This week I am going to talk about Java graphics and how they can be used to create a custom Ring Chart control also commonly referred to as a doughnut chart.</p>
<p>This is the type of chart typically used for disk allocation in programs similar to Overdisk. They however are not limited to this application.  This class will dynamically size to the maximum amount of its container.  Also, the thickness is dynamic and will set itself based on the number of rings added.  The rings can start and end at any angle.  The segments of each ring are divided proportionally based on the segments value and drawn to the proper arc length based on the start and end angle.</p>
<p> <img src="/blog/static/ringchart/chart.png" alt="Ring Chart" /></p>
<p><span id="more-19"></span></p>
<p>The ArcSegment Class.</p>
<p>First we need to be able to draw the individual segments.  We will create our own custom shape.  To accomplish this we will use Java&#8217;s GeneralPath class which uses the Shape interface.  The GeneralPath will create our ArcSegment by combining arcs from the Arc2D.Double class and connecting them with the GeneralPath&#8217;s lineTo() member function. </p>
<p>By creating our own Shape in this way, we can treat it as any other Shape in Java.  This is useful for displaying tooltips as we do in this example.  We do this by using the contains() method of the Shape interface.</p>
<pre>

public class ArcSegment {
    public ArcSegment(){

    }

    public Shape Create(
            double centerx,
            double centery,
            double radius,
            double width,
            double start,
            double extent
        )
    {
        GeneralPath gp = new GeneralPath();
        GeneralPath dummy = new GeneralPath(); // used to find arc endpoints

        double left, top;
        left = centerx - radius;
        top = centery - radius;

        Shape outer = new Arc2D.Double(left, top, 2 * radius, 2 * radius, start, extent, Arc2D.OPEN);
        Shape inner = new Arc2D.Double(left + width, top + width, 2 * radius - 2 * width
                        , 2 * radius - 2 * width, start+extent, -extent, Arc2D.OPEN);
        gp.append(outer, false);

        dummy.append(new Arc2D.Double(left + width, top + width, 2 * radius - 2 * width
                , 2 * radius - 2 * width, start, extent, Arc2D.OPEN),false);

        Point2D point = dummy.getCurrentPoint();

        if(point!=null)gp.lineTo(point.getX(), point.getY());
        gp.append(inner, false);

        dummy.append(new Arc2D.Double(left, top, 2 * radius, 2 * radius, start+extent, -extent, Arc2D.OPEN),false);

        point = dummy.getCurrentPoint();
        gp.lineTo(point.getX(), point.getY());
        return gp;
    }
}
</pre>
<p>The dummy GeneralPath above is used to find the endpoint of the next arc.  We first draw the outer arc clockwise and end by drawing the inner arc counterclockwise.  The dummy GeneralPath is not rendered but it is a handy way to find the endpoint of the other arc.  So we draw the outer arc clockwise, the use the dummy arc also clockwise for the inner arc.  This will end the stroke at the point we need to connect from the outer arc and also the starting point of rendering the counterclockwise inner arc.  Now we have the building blocks in place for drawing the rings.</p>
<p>The Ring Class</p>
<p>This class will create a ring based on the start and end points.  It will take a value, a label and a color for each segment of the ring and divide them accordingly based on the values.  The Value, Label and Color Vectors are set in the Ring class and the ring is created by creating the segments.</p>
<pre>

public class Ring {
    private Vector&lt;String&gt; Labels;
    private Vector&lt;Double&gt; Values;
    private Vector&lt;Color&gt; Colors;
    private double x;
    private double y;
    private double radius;
    private double ringwidth;
    private double start;
    private double end;
    private Shape[] segments;

    public Ring(){
        Labels = new Vector&lt;String&gt;();
        Values = new Vector&lt;Double&gt;();
        Colors = new Vector&lt;Color&gt;();
        this.start = 0;
        this.end = 360;
        segments = null;
    }

    public void setStart(double start){
        this.start = start;
    }

    public void setEnd(double end){
        this.end = end;
    }

    public double getValue(int i){
    if(this.segments==null){
        this.createSegments();
    }
    return Values.get(i);
    }

    public int count(){
        if(this.segments==null){
            this.createSegments();
        }
        return Values.size();
    }

    public String getLabel(int index){
        return Labels.get(index);
    }

    public Color getColor(int index){
        return Colors.get(index);
    }

    public void setCenter(double x, double y){
        this.x = x;
        this.y = y;
    }

    public void setRadius(double radius){
        this.radius = radius;
    }

    public void setRingWidth(double width){
        this.ringwidth = width;
    }

    public void addItem(String label, double val, Color color){
        Labels.add(label);
        Values.add(val);
        Colors.add(color);
    }

    public Shape getSegment(int index){
        if(this.segments==null){
            this.createSegments();
        }

        return segments[index];
    }

    public void createSegments(){
        Shape[] shapes = new Shape[Values.size()];
        double sum = 0;
        double span = this.end - this.start;
        for(int i=0; i&lt;Values.size(); i++){
            sum += Values.get(i);
        }

        double strt = this.start;
        for(int i=0; i&lt;Values.size(); i++){
            double extent = (Values.get(i) / sum) *span;
            ArcSegment arc = new ArcSegment();
            shapes[i] = arc.Create(this.x,this.y,this.radius,this.ringwidth,strt,extent);
            strt += extent;
        }

        this.segments = shapes;

    }
}
</pre>
<p>Now we put it all together in the RingChart class.</p>
<p>The RingChart Class</p>
<p>This is the class that will be called from the application.  It takes a Vector of type Ring and does all the rendering from the information provided by the Vector.  Each Ring will contain the start angle, end angle and the values.  The work is done in the paintComponent() method.  In this method we loop through each Ring and for each Ring we loop through drawing each segment.  Tooltip functionality is accomplished by looping through the Rings/Segments and checking to see if the mouse position is contained in the segment.</p>
<pre>

public class RingChart extends JPanel implements ComponentListener{
    Vector&lt;Ring&gt; rings;

    public RingChart() {
        setOpaque(false);
        addComponentListener(this);
        rings = new Vector&lt;Ring&gt;();

        ToolTipManager.sharedInstance().registerComponent(this);
    }

    protected void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
        Stroke s = new BasicStroke(1.25f);
        g2.setColor(Color.WHITE);
        g2.fillRect(0, 0, getWidth(), getHeight());
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setStroke(s);

        Insets insets = this.getInsets();
        int h = this.getHeight() - insets.top - insets.bottom;
        int w = this.getWidth() - insets.left - insets.right;
        int m = Math.min(h, w);

        for(int r = 0; r &lt; rings.size(); r++){
            Ring ring = rings.get(r);
            double rw = ((double)m / ((double)rings.size()+1))/2;
            ring.setRingWidth(rw);
            ring.setRadius(2*rw +rw*(double)r);
            ring.setCenter((double)this.getWidth()/2.0, (double)this.getHeight()/2.0);
            ring.createSegments();            

            for (int index = 0; index &lt; ring.count(); index++) {
                g2.setColor(ring.getColor(index));
                g2.fill(ring.getSegment(index));
                g2.setColor(Color.BLACK);
                g2.draw(ring.getSegment(index));
            }
        }

    }

    public JToolTip createToolTip(){
        JToolTip tooltip = super.createToolTip();
        tooltip.setBackground(new Color(255,255,140,128));
        this.repaint(); // TODO: will this bog us down?
        return tooltip;
    }

    public String getToolTipText(java.awt.event.MouseEvent e) {

        for(int r = 0; r &lt; rings.size(); r++){
            Ring ring = rings.get(r);
            for (int index = 0; index &lt; ring.count(); index++) {
                if (ring.getSegment(index).contains(e.getPoint())) {
                    return ring.getLabel(index)+&quot;: &quot;+ring.getValue(index);
                }
            }
        }
        return super.getToolTipText(e);
    }

    public void setRings(Vector&lt;Ring&gt; rings){
        this.rings = rings;
    }

    @Override
    public void componentHidden(ComponentEvent arg0) {    }

    @Override
    public void componentMoved(ComponentEvent arg0) {    }

    @Override
    public void componentResized(ComponentEvent arg0) {
        repaint();
    }

    @Override
    public void componentShown(ComponentEvent arg0) {    }

}
</pre>
<p>Download source code and example <a href="static/ringchart/RingChart.zip">here.</a>.</p>
<p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Frichardbondi.net%2Fblog%2Fjava-ring-chart%2F&amp;linkname=Java%20Ring%20Chart"><img src="http://s7.addthis.com/static/btn/sm-share-en.gif" alt="Share/Bookmark"/></a> </p>]]></content:encoded>
			<wfw:commentRss>http://richardbondi.net/blog/java-ring-chart/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

