<?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>Michael Schuerig &#187; JavaScript</title>
	<atom:link href="http://www.schuerig.de/michael/blog/index.php/category/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.schuerig.de/michael/blog</link>
	<description>Sentenced to making sense</description>
	<lastBuildDate>Tue, 06 Sep 2011 07:21:06 +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>Collecting DOM events</title>
		<link>http://www.schuerig.de/michael/blog/index.php/2008/01/08/event-collector/</link>
		<comments>http://www.schuerig.de/michael/blog/index.php/2008/01/08/event-collector/#comments</comments>
		<pubDate>Tue, 08 Jan 2008 20:24:12 +0000</pubDate>
		<dc:creator>michael</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Prototype.js]]></category>

		<guid isPermaLink="false">http://schuerig.de/michael/blog/index.php/2008/01/08/event-collector/</guid>
		<description><![CDATA[The DOM change notification I presented yesterday is one piece in the puzzle to rejuvenate my languishing JavaScript form validator. I&#8217;ve not yet made up my mind on a lot of issues, but there&#8217;s one thing that&#8217;s certain: the next version will have to be able to work with changing forms, that is, forms that [...]]]></description>
			<content:encoded><![CDATA[<p>The DOM change notification I presented <a href="http://schuerig.de/michael/blog/index.php/2008/01/07/dom-change-notification/">yesterday</a> is one piece in the puzzle to rejuvenate my languishing JavaScript <a href="http://schuerig.de/michael/blog/index.php/2006/12/15/rails-almost-automatic-client-side-validation/">form validator</a>. I&#8217;ve not yet made up my mind on a lot of issues, but there&#8217;s one thing that&#8217;s certain: the next version will have to be able to work with changing forms, that is, forms that show different input elements depending on already chosen values.</p>
<p>Since yesterday, I have a way to be notified when the DOM has changed. As it is, the <tt>dom:changed</tt> custom event is completely general and not quite what I need. Of course, I do need some kind of notification, but what I get when a function changes the DOM in several places, is a barrage of <tt>dom:changed</tt> events. I don&#8217;t want to do lots of stuff for each of these events, but I&#8217;ve got to do something about the events collectively. There is no natural bracketing for events, there&#8217;s nothing that says which events belong to the same high-level change. But that&#8217;s not really necessary, rather, it is good enough to collect all relevant events that occur in period of time.</p>
<p>Here&#8217;s a class that collects all events that it receives over a given <tt>interval</tt> (of seconds) and passes them on to a <tt>handler</tt> function.</p>
<p><code>
<pre>
Element.EventCollector = Class.create({
  initialize: function(handler, interval) {
    this.handler = handler;
    this.interval = (interval || 1) * 1000;
    this.reset();
    this.dischargeEvents = this.discharge.bind(this);
  },
  reset: function() {
    this.events = [];
    this.timer = null;
  },
  observer: function() {
    return this.onEvent.bindAsEventListener(this);
  },
  onEvent: function(event) {
    this.events.push(event);
    this.wait();
  },
  wait: function() {
    if (!this.timer) {
      this.timer = setTimeout(this.dischargeEvents, this.interval);
    }
  },
  discharge: function() {
    this.timer = null;
    var events = this.events;
    this.reset();
    this.handler(events);
  }
});
</pre>
<p></code></p>
<p>Add some syntactic sugar, so that we can do things like</p>
<p><code>
<pre>
document.body.collectEvents('dom:changed', function(events) { ... });
</pre>
<p></code></p>
<p><code>
<pre>
Element.addMethods({
  collectEvents: function(element, eventName, handler, interval) {
    var collector = new Element.EventCollector(handler, interval);
    $(element).observe(eventName, collector.observer());
  }
});
</pre>
<p></code></p>
<p>For my motivating case of the validator that&#8217;s still not exactly what I need. I can arrange to get a notification that a whole bunch of elements was changed, but I don&#8217;t want to deal with these elements individually. The validator I have (and the next version I envision) works it&#8217;s way down from a starting element and attaches its magic pixie dust to everything below. Thus, what I need is a common ancestor element for all the changed elements. The idea is easy: Go down from the root of the &#8220;family&#8221; (document) tree and follow it until the path to the elements branches.</p>
<p><code>
<pre>
Element.addMethods({
  commonAncestor: function() {
    var ancestorsLists = $A(arguments).map(function(el) {
      el = $(el);
      var ancestors = el.ancestors().reverse()
      ancestors.push(el);
      return ancestors;
    });
    var common = document;
    Enumerable.zip.apply(ancestorsLists.shift(), ancestorsLists).each(
      function(ancestorTuple) {
        var cand = ancestorTuple.shift();
        if (!ancestorTuple.all(function(el) { return el == cand; })) {
          throw $break;
        }
        common = cand;
      });
    return common;
  }
});
</pre>
<p></code></p>
<p>[See <a href="http://prototypejs.org/api/element/methods/ancestors"><tt>Element#ancestors</tt></a>, <a href="http://prototypejs.org/api/enumerable/zip"><tt>Enumerable#zip</tt></a>, <a href="http://prototypejs.org/api/enumerable/each"><tt>$break</tt></a>.]</p>
<p>Finally, here&#8217;s an example that puts the existing pieces together.</p>
<p><code>
<pre>
document.observe('dom:loaded', function() {
  document.body.collectEvents('dom:changed', function(events) {
    var affectedElements = events.invoke('element');
    var ancestor = Element.commonAncestor.apply(null, affectedElements);
    console.log('dom:changed below: ', ancestor);
  }, 10);
});
</pre>
<p></code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.schuerig.de/michael/blog/index.php/2008/01/08/event-collector/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>The DOMs they are a-changin&#8217;</title>
		<link>http://www.schuerig.de/michael/blog/index.php/2008/01/07/dom-change-notification/</link>
		<comments>http://www.schuerig.de/michael/blog/index.php/2008/01/07/dom-change-notification/#comments</comments>
		<pubDate>Mon, 07 Jan 2008 13:01:11 +0000</pubDate>
		<dc:creator>michael</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Prototype.js]]></category>

		<guid isPermaLink="false">http://schuerig.de/michael/blog/index.php/2008/01/07/dom-change-notification/</guid>
		<description><![CDATA[So we&#8217;ve put in the effort to make our JavaScript code nicely unobtrusive and all is well &#8212; until it isn&#8217;t. A pesky Ajax request has messed up the DOM and suddenly there are elements in there where our unobtrusive behavioral goodness has not been attached. Never give up, never surrender. You won&#8217;t even have [...]]]></description>
			<content:encoded><![CDATA[<p>So we&#8217;ve put in the effort to make our JavaScript code nicely unobtrusive and all is well &#8212; until it isn&#8217;t. A pesky Ajax request has messed up the DOM and suddenly there are elements in there where our unobtrusive behavioral goodness has not been attached. Never give up, never surrender. You won&#8217;t even have to get out Grabthar&#8217;s hammer, it&#8217;s only just a small matter of programming.</p>
<p>First, let&#8217;s shrink the problem a little. Let&#8217;s concentrate on changes effected through the abstraction layer provided by the <a href="http://www.prototypejs.org/">Prototype</a> library. Prototype implements several DOM-changing functions on the <tt>Element</tt> singleton and also mixes them into extended DOM elements. These functions are <tt>remove</tt>, <tt>update</tt>, <tt>replace</tt>, <tt>insert</tt>, <tt>wrap</tt>, and <tt>empty</tt>. Prototype also gives us the <tt>wrap</tt> function that wraps a function around another, somewhat akin to <a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming"><tt>AOP</tt></a>&#8216;s around advice, but without pointcuts.</p>
<p>Now the strategy is clear: Replace the original DOM-changing functions with versions that record a bit of information about these changes. A good mechanism to serve the actual notification is to use <a href="http://www.prototypejs.org/api/element/fire">Custom Events</a>. Then, anyone intersted in changes to a branch of the DOM can register as an observer for our new <tt>dom:changed</tt> event.</p>
<p>However, it is a good idea not to fire the event straightaway after the DOM has been changed. We might be in the middle of a batch of changes and the DOM in a correspondingly unorderly state. Besides, it is sometimes necessary to give the browser&#8217;s rendering engine a bit of breathing room after changes. For all this, at first the event data is appended to a list and a timer is set to dispatch the corresponding events some time later. This timer is reset for every change, so that for a batch of changes it only executes once and and then fires all events successively.</p>
<p>To understand the resulting behavior, it is important to know that JavaScript is single-threaded. As a result, when a timer times out, it does not interrupt the currently executing code. Rather, the function attached to the timer is scheduled to be executed as soon as there is nothing else to do. Therefore, the usual sequence is like this</p>
<ul>
<li>An event triggers a DOM-changing function
<ul>
<li>Change the DOM</li>
<li>Remember the change</li>
<li>Change the DOM</li>
<li>Remember the change</li>
<li>&#8230;</li>
</ul>
</li>
<li>&#8230;</li>
<li>Fire events for all remembered changes</li>
</ul>
<p>Finally, here&#8217;s the code.</p>
<p><code>
<pre>
(function() {
  var methods = ['remove', 'update', 'replace', 'insert', 'wrap', 'empty'];
  var changes = [];
  var timeout;

  function rememberChange(element, method) {
    changes.push({
      element: $(element),
      parent: element.parentNode,
      operation: method
    });
  }

  function scheduleEvent() {
    if (timeout) {
      clearTimeout(timeout);
    }
    timeout = setTimeout(fireEvent, 10);
  }

  function fireEvent() {
    changes.each(function(change) {
      var affectedNode = change.element;
      var operation = change.operation;
      if (!change.parent ||
          (change.parent &amp;&amp; !affectedNode.descendantOf(change.parent))) {
        affectedNode = change.parent || affectedNode.parentNode;
        operation += 'Child';
      }
      affectedNode.fire('dom:changed', {operation: operation});
    });
    changes = [];
  }

  methods.each(function(m) {
    Element.Methods[m] = Element.Methods[m].wrap(
      function(proceed, element) {
        rememberChange(element, m);
        scheduleEvent();
        return proceed.apply(null, $A(arguments).slice(1));
      });
  });
})();
</pre>
<p></code></p>
<p>When an element has been removed from the DOM, it is pointless to fire an event on it that bubbles upwards. There&#8217;s no surface to bubble up to anymore. Therefore, in this case, the <tt>dom:changed</tt> event is fired on the parent of the removed element and the operation is signified as <tt>removeChild</tt>.</p>
<p>To peek at the modification events, or do something useful with them, add code like this</p>
<p><code>
<pre>
document.observe('dom:loaded', function() {
  document.body.observe('dom:changed', function(event) {
    console.log('dom:changed: ', event.element(), event.memo.operation);
  });
});
</pre>
<p></code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.schuerig.de/michael/blog/index.php/2008/01/07/dom-change-notification/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>JavaScript FSM v0.2.2</title>
		<link>http://www.schuerig.de/michael/blog/index.php/2007/12/30/jsfsm-022/</link>
		<comments>http://www.schuerig.de/michael/blog/index.php/2007/12/30/jsfsm-022/#comments</comments>
		<pubDate>Sun, 30 Dec 2007 12:25:58 +0000</pubDate>
		<dc:creator>michael</dc:creator>
				<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://schuerig.de/michael/blog/index.php/2007/12/30/jsfsm-022/</guid>
		<description><![CDATA[I&#8217;ve released a new version, 0.2.2, of the JavaScript FSM builder. There are some enhancements, such as accessors for successorStates and expectedEvents that I found useful for implementing a first (almost) practical example involving drag &#38; drop. Speaking of which, this whole thing didn&#8217;t come into being because of a pressing need. Currently, it&#8217;s more [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve released a new version, 0.2.2, of the JavaScript FSM builder. There are some enhancements, such as accessors for <tt>successorStates</tt> and <tt>expectedEvents</tt> that I found useful for implementing a first (almost) practical <a href="http://www.schuerig.de/michael/javascript/jsfsm/examples/dragdrop.html">example involving drag &amp; drop</a>.</p>
<p>Speaking of which, this whole thing didn&#8217;t come into being because of a pressing need. Currently, it&#8217;s more of a solution looking for a problem. I know that state machines can be very helpful for UI construction, however, this particular incarnation still has to prove itself.</p>
<p>All the rest</p>
<ul>
<li><a href="http://www.schuerig.de/michael/javascript/jsfsm/fsm.js">Just the script</a></li>
<li><a href="http://www.schuerig.de/michael/javascript/jsfsm/spec/fsm.html">The specs</a> (Look at the source)</li>
<li><a href="http://www.schuerig.de/michael/javascript/jsfsm/jsfsm-latest.tar.gz">The complete package</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.schuerig.de/michael/blog/index.php/2007/12/30/jsfsm-022/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Finite State Machines in JavaScript</title>
		<link>http://www.schuerig.de/michael/blog/index.php/2007/12/22/javascript-fsm/</link>
		<comments>http://www.schuerig.de/michael/blog/index.php/2007/12/22/javascript-fsm/#comments</comments>
		<pubDate>Sat, 22 Dec 2007 18:26:46 +0000</pubDate>
		<dc:creator>michael</dc:creator>
				<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://schuerig.de/michael/blog/index.php/2007/12/22/javascript-fsm/</guid>
		<description><![CDATA[I like finite state machines and I wanted to try metaprogramming in JavaScript ever since I&#8217;d seen Adam McCrea presentation on the topic. The result is an FSM builder in JavaScript. Machine descriptions look like my test machine here: var Machine = FSM.build(function(fsm) { with (fsm) { onUnexpectedEvent(function() { ... }); state('start', 'initial') .event('go') .goesTo('middle') [...]]]></description>
			<content:encoded><![CDATA[<p>I like finite state machines and I wanted to try metaprogramming in JavaScript ever since I&#8217;d seen Adam McCrea <a href="http://www.adamlogic.com/2007/03/20/3_metaprogramming-javascript-presentation">presentation</a> on the topic.</p>
<p>The result is an FSM builder in JavaScript. Machine descriptions look like my test machine here:</p>
<p><code>
<pre>
var Machine = FSM.build(function(fsm) { with (fsm) {
  onUnexpectedEvent(function() { ... });

  state('start', 'initial')
    .event('go')
      .goesTo('middle')
      .doing(function() { ... })
      .doing('phew')
    .event('run')
      .goesTo('finish')
    .onExiting(function() { ... });

  state('middle')
    .onUnexpectedEvent(function() { ... })
    .onEntering(function() { ... })
    .event('back')
      .goesTo('start')
      .onlyIf(function() { return true_or_false })
    .event('go')
      .goesTo('finish');

  state('finish', 'final');
}});

function TestMachine() {}
// amend TestMachine.prototype here all the way you want
TestMachine.prototype.phew = function() { ... };
TestMachine.prototype = new Machine(TestMachine.prototype);
</pre>
<p></code></p>
<p>There&#8217;s no documentation yet, but thorough <del>unit tests</del><ins>specs</ins><br />
using NicolÃ¡s Sanguinetti&#8217;s very nice <a href="http://code.google.com/p/js-spec/">js-spec</a> framework.</p>
<p>Something to look at:</p>
<ul>
<li><a href="http://www.schuerig.de/michael/javascript/jsfsm/fsm.js">Just the script</a></li>
<li><a href="http://www.schuerig.de/michael/javascript/jsfsm/spec/fsm.html">The specs</a> (Look at the source)</li>
<li><a href="http://www.schuerig.de/michael/javascript/jsfsm/jsfsm-latest.tar.gz">The complete package</a></li>
</ul>
<p>The script is meant to be usable independently of and play nicely with any other libraries.</p>
<p><del>The manner in which an FSM is attached to classes/objects is still rather convoluted. The general idea is that it (a) should be possible to insert the FSM as the first link in the prototype chain and (b) should be possible to directly attach it to an existing object. I&#8217;d appreciate suggestions how to make this nice and shiny.</del></p>
]]></content:encoded>
			<wfw:commentRss>http://www.schuerig.de/michael/blog/index.php/2007/12/22/javascript-fsm/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Looking good at any (font and window) size</title>
		<link>http://www.schuerig.de/michael/blog/index.php/2007/02/22/size-dependent-layout/</link>
		<comments>http://www.schuerig.de/michael/blog/index.php/2007/02/22/size-dependent-layout/#comments</comments>
		<pubDate>Thu, 22 Feb 2007 22:13:32 +0000</pubDate>
		<dc:creator>michael</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://schuerig.de/michael/blog/index.php/2007/02/22/size-dependent-layout/</guid>
		<description><![CDATA[Make your layout look good a different screen and font sizes. What it looks like Example 1, Example 2. Here&#8217;s what the form example (example 1) is supposed to look like. At 1024 x 768 pixels, the navigation links and Save button are positioned fixed in a column to the left. At 800 x 600 [...]]]></description>
			<content:encoded><![CDATA[<p>Make your layout look good a different screen and font sizes.</p>
<h3>What it looks like</h3>
<p><a href="http://www.schuerig.de/michael/webdesign/demo/form.html">Example 1</a>,<br />
<a href="http://www.schuerig.de/michael/webdesign/demo/tagcloud.html">Example 2</a>.</p>
<p>Here&#8217;s what the form example (example 1) is <em>supposed</em> to look like.</p>
<p>At 1024 x 768 pixels, the navigation links and Save button are positioned fixed in a column to the left.</p>
<p><img alt="large layout" src="http://www.schuerig.de/michael/webdesign/demo/form1024x768.png" /></p>
<p>At 800 x 600 pixels, the navigation links are only shown on hover over the icon in the top-left corner; the Save button is tucked under the form itself.</p>
<p><img alt="large layout" src="http://www.schuerig.de/michael/webdesign/demo/form800x600-1.png" /><br /><img alt="large layout" src="http://www.schuerig.de/michael/webdesign/demo/form800x600-2.png" /></p>
<h3>What you need</h3>
<p>Grab <a href="http://www.schuerig.de/michael/webdesign/demo/javascripts/layout.js">layout.js</a>.</p>
<p>You also need <a href="http://prototypejs.org/assets/2007/1/18/prototype.js">Prototype.js</a>.</p>
<h3>What you need to do</h3>
<p>Include the scripts in your page.</p>
<p><code>
<pre>
&lt;script src="javascripts/prototype.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="javascripts/layout.js" type="text/javascript"&gt;&lt;/script&gt;
</pre>
<p></code></p>
<p>Activate layout switching in the head of your page</p>
<p><code>
<pre>
&lt;script type="text/javascript"&gt;
//&lt;![CDATA[
Event.observe(window, 'load', function() {
  Layout.initialize({observeFontSize: true});
});
//]]&gt;
&lt;/script&gt;
</pre>
<p></code></p>
<p>Most likely, the default size thresholds set in <code>layout.js</code> won&#8217;t fit your needs. See the file itself for how to set them to different values.</p>
<p>(Re-)Write your stylesheets so that they make use of the size information. As an example, here&#8217;s the CSS I use for styling the navigation list on the two example pages.</p>
<p><code>
<pre>
#navitrigger {
  display: none;
}

#navigation {
  position: fixed;
  top: 0em;
  left: 0em;
  margin: 1em;
  padding: 0;
  text-align: right;
  z-index: 10;
}

#navigation ul {
  width: 10em;
  margin: 0;
  padding: 0;
  list-style-type: none;
}

#navigation li {
  font-weight: bold;
}

#navigation .current {
  display: block;
  width: 100%;
  color: #bbb;
  background-color: #ffecac;
}

#navigation li.admin {
  border-top: 1px solid #bbb;
}

#navigation li a {
  display: block;
  width: 100%;
}

body.small #navigation {
  position: absolute;
  text-align: left;
  z-index: 30;
}

body.small #navigation ul {
  display: none;
  background: #fff;
  border: 1px solid #888;
}

body.small #navitrigger {
  display: block;
  font-size: 150%;
  font-weight: bold;
}

body.small #navigation:hover ul {
  display: block;
}
</pre>
<p></code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.schuerig.de/michael/blog/index.php/2007/02/22/size-dependent-layout/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Return of the Tag Cloud &#8212; ajaxing the tagged</title>
		<link>http://www.schuerig.de/michael/blog/index.php/2007/02/22/tag-cloud/</link>
		<comments>http://www.schuerig.de/michael/blog/index.php/2007/02/22/tag-cloud/#comments</comments>
		<pubDate>Thu, 22 Feb 2007 22:10:28 +0000</pubDate>
		<dc:creator>michael</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://schuerig.de/michael/blog/index.php/2007/02/22/tag-cloud/</guid>
		<description><![CDATA[On my continuing quest to become more Web-2.0-ish, I&#8217;ve tried my luck tilting at&#8230; well, tag clouds. To get an idea what I&#8217;m talking about, please have a look. These are the rudimentary basics to create a tag cloud like that. In the controller def tags @popular_tags = Tag.find_most_popular(:limit =&#62; 30) end In the view [...]]]></description>
			<content:encoded><![CDATA[<p>On my continuing quest to become more Web-2.0-ish, I&#8217;ve tried my luck tilting at&#8230; well, tag clouds. To get an idea what I&#8217;m talking about, please <a href="http://www.schuerig.de/michael/webdesign/demo/tagcloud.html">have a look</a>.</p>
<p>These are the rudimentary basics to create a tag cloud like that.</p>
<p>In the controller</p>
<p><code>
<pre>
def tags
  @popular_tags = Tag.find_most_popular(:limit =&gt; 30)
end
</pre>
<p></code></p>
<p>In the view</p>
<p><code>
<pre>
&lt;ul id="tags" class="tagcloud"&gt;
&lt;% @popular_tags.sort_by(&#038;:name).each do |t| -%&gt;
  &lt;%= weighted_tag t, tag_options %&gt;
&lt;% end -%&gt;
&lt;/ul&gt;
</pre>
<p></code></p>
<p>or, to get a somewhat nicer look, and to demonstrate some of the options</p>
<p><code>
<pre>
&lt;%
  tag_options = {
    :wrap =&gt; ['li', '&lt;span class="dlm"&gt;[&lt;/span&gt;', '&lt;span class="dlm"&gt;]&lt;/span&gt;'],
    :min_size =&gt; 100, :max_size =&gt; 500,
    :link_options =&gt; { :class =&gt; 'tag' })
  }
-%&gt;
&lt;h2&gt;Tags&lt;/h2&gt;
&lt;ul id="tags" class="tagcloud"&gt;
&lt;% @popular_tags.sort_by(&#038;:name).each do |t| -%&gt;
  &lt;%= weighted_tag t, tag_options %&gt;
&lt;% end -%&gt;
&lt;/ul&gt;
</pre>
<p></code></p>
<p><a href="http://schuerig.de/michael/blog/wp-content/uploads/2007/06/tag_cloud-0.0.2.tar.gz">Here you can download a do-it-yourself kit of all the pieces.</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.schuerig.de/michael/blog/index.php/2007/02/22/tag-cloud/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Auto-completion for tag lists</title>
		<link>http://www.schuerig.de/michael/blog/index.php/2007/01/31/auto-completion-for-tag-lists/</link>
		<comments>http://www.schuerig.de/michael/blog/index.php/2007/01/31/auto-completion-for-tag-lists/#comments</comments>
		<pubDate>Wed, 31 Jan 2007 22:08:08 +0000</pubDate>
		<dc:creator>michael</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://schuerig.de/michael/blog/?p=11</guid>
		<description><![CDATA[See the end of this post for an important update. Standard Rails auto-completion is geared toward completing a single value entered in a text field. For most cases this is exactly what&#8217;s needed, but there is one prominent case that is different: auto-completion of tags. If something can have a tag at all, it&#8217;s part [...]]]></description>
			<content:encoded><![CDATA[<p><em>See the end of this post for an important update.</em></p>
<p>Standard Rails auto-completion is geared toward completing a single value entered in a text field. For most cases this is exactly what&#8217;s needed, but there is one prominent case that is different: auto-completion of tags. If something can have a tag at all, it&#8217;s part and parcel of the deal that it can have multiple tags.</p>
<p>As far as I can tell, some configuration tweak is not enough to make standard auto-completion do our bidding. But not all is lost. It&#8217;s just a small matter of programming to get what we want.</p>
<p>I&#8217;m assuming that you&#8217;re using <a href="http://www.agilewebdevelopment.com/plugins/acts_as_taggable"><tt>Acts As Taggable</tt></a> or <a href="http://www.agilewebdevelopment.com/plugins/acts_as_taggable_on_steroids"><tt>Acts As Taggable On Steroids</tt></a> for the model-level tagging, or otherwise that you know what you&#8217;re doing.</p>
<p>First, we need client-side support for picking out only the tag we&#8217;re currently editing.</p>
<p><code>
<pre>
Ajax.TagAutocompleter = Class.create();
Object.extend(Object.extend(Ajax.TagAutocompleter.prototype, Ajax.Autocompleter.prototype), {
  getToken: function() {
    var range = this.rangeForCaret();
    var value = this.element.value.substring(range.start, range.end);
    return value;
  },
  updateElement: function(selectedElement) {
    var oldValue = this.element.value;
    var range = this.rangeForCaret();
    var prefix = oldValue.substring(0, range.start);
    var suffix = oldValue.substring(range.end);
    var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
    this.element.value = prefix + value + suffix;
    this.element.focus();
  },
  rangeForCaret: function() {
    var value = this.element.value;
    var start = 0, end = value.length;
    var pos;
    if (typeof this.element.selectionStart != 'undefined') {
      pos = this.element.selectionStart;
    } else if (document.selection) {
      var sel = document.selection.createRange();
      var selLength = sel.text.length;
      sel.moveStart('character', -(value.length));
      pos = sel.text.length - selLength;
    }
    if (pos != undefined) {
      end = pos;
      while (value.charAt(end) != ',' &amp;&amp; end &lt; value.length) {
        end++;
      }
      start = end - 1;
      while (value.charAt(start) != ',' &amp;&amp; start &gt; 0) {
        start--;
      }
      while (start &lt; end &amp;&amp; (value.charAt(start) == ' ' || value.charAt(start) == ',')) {
        start++;
      }
      while (start &lt; end &amp;&amp; value.charAt(end) == ' ') {
        end--;
      }
    }
    return {start: start, end: end};
  }
});
</pre>
<p></code></p>
<p>Yes, the code of <tt>rangeForCaret</tt> is a bit convoluted, let&#8217;s say traditional JavaScript-style. The general approach for getting the position of the text cursor (&#8220;caret&#8221;) is explained <a href="http://www.theblueform.com/Home/TheMakingOf.aspx">here</a>. The purpose of all the stuff is to support auto-completion in the middle of the text field, not just at its end.</p>
<p>On the server-side, in the relevant controller, there&#8217;s still some work to do as the standard <tt>auto_complete_for</tt> can&#8217;t handle tags. Put the following method in your controller base class, <tt>app/controllers/application.rb</tt>.</p>
<p><code>
<pre>
def self.tag_auto_complete_for(object)
  define_method("auto_complete_for_#{object}_tag_list") do
    @items = Tag.find(:all,
      :conditions => [ 'LOWER(name) LIKE ?', '%' + params[object][:tag_list].downcase + '%' ],
      :\order => 'name ASC',
      :limit => 10)
    render :inline => "&lt;%= auto_complete_result @items, :name %&gt;"
  end
end
</pre>
<p></code></p>
<p>Then, in an individual controller, create the tag auto-completion method like this</p>
<p><code>
<pre>
class PeopleController &lt; ApplicationController
  tag_auto_complete_for :person
end
</pre>
<p></code></p>
<p>Almost done!</p>
<p>What&#8217;s still missing is a way to activate auto-completion for the tag input field. It would surely be possible to copy &amp; change <tt>text_field_with_auto_complete</tt> or to monkey patch it appropriately. I prefer another way, in fact, I&#8217;m not using <tt>text_field_with_auto_complete</tt> at all, not even where it would work. Instead, I prefer more unobtrusive way.</p>
<p>The way starts with the class <tt>autocomplete</tt> to text fields that are amenable to auto-completion &#8212; including, of course, the tag list field.</p>
<p><code>
<pre>
  &lt;%= f.text_field :tag_list, :class => 'autocomplete' %&gt;
</pre>
<p></code></p>
<p>As this in itself is completely inert, we need to nudge it a bit in <tt>application.js</tt>. Here&#8217;s a function that looks for all text fields with class <tt>autocomplete</tt> and actually makes them do what they say.</p>
<p><code>
<pre>
function installAutocompletion() {
  $$('input.autocomplete[type=text]').each(function(element) {
    var fieldId = element.id;
    var completions = document.createElement('div');
    completions.id = fieldId + '_auto_complete';
    completions.className = 'auto_complete';
    completions.style.display = 'none';
    element.parentNode.insertBefore(completions, element.nextSibling);

    var url = 'auto_complete_for_' + fieldId;
    if (/_tag_list$/.test(fieldId)) {
      new Ajax.TagAutocompleter(element, completions, url);
    } else {
      new Ajax.Autocompleter(element, completions, url);
    }
  });
}
</pre>
<p></code></p>
<p>Be sure to call this method some time after the page is loaded. If you don&#8217;t have a function from where to call it, add this to <tt>application.js</tt></p>
<p><code>
<pre>
  Event.observe(window, 'load', installAutocompletion);
</pre>
<p></code></p>
<h3>Update, 2007-02-05</h3>
<p>I may not have seen the wood for the trees when looking at the original code of the Rails/Script.aculo.us autocompleter. Of course it is fully capable of auto-completing more than one token in the same text field &#8212; with a very little configuration tweak, no less.</p>
<p>What it can&#8217;t do is auto-completion in the middle of the line; it only works at the end. That&#8217;s an acceptable restriction, to my mind. So, forget about <code>Ajax.TagAutocompleter</code> from above and instead install your autocompleters like this</p>
<p><code>
<pre>
function installAutocompletion() {
  $$('input.autocomplete[type=text]').each(function(element) {
    var fieldId = element.id;
    var completions = document.createElement('div');
    completions.id = fieldId + '_auto_complete';
    completions.className = 'auto_complete';
    completions.style.display = 'none';
    element.parentNode.insertBefore(completions, element.nextSibling);

    var url = 'auto_complete_for_' + fieldId;
    var options = {};
    if (/_tag_list$/.test(fieldId)) {
      options.tokens = ',';
    }
    new Ajax.Autocompleter(element, completions, url, options);
  });
}
</pre>
<p></code></p>
]]></content:encoded>
			<wfw:commentRss>http://www.schuerig.de/michael/blog/index.php/2007/01/31/auto-completion-for-tag-lists/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Rails: Almost Automatic Client-Side Validation</title>
		<link>http://www.schuerig.de/michael/blog/index.php/2006/12/15/rails-almost-automatic-client-side-validation/</link>
		<comments>http://www.schuerig.de/michael/blog/index.php/2006/12/15/rails-almost-automatic-client-side-validation/#comments</comments>
		<pubDate>Fri, 15 Dec 2006 02:37:44 +0000</pubDate>
		<dc:creator>michael</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://schuerig.de/michael/blog/?p=8</guid>
		<description><![CDATA[Ruby on Rails already does a good job of specifying almost declaratively what conditions objects must meet to be considered valid&#8211;that is, how they are validated. Thus, in order to make sure that a Person&#8217;s last_name attribute does not exceed 100 characters, you would write something like this class Person &#60; ActiveRecord::Base validates_length_of :last_name, :maximum [...]]]></description>
			<content:encoded><![CDATA[<p>Ruby on Rails already does a good job of specifying <em>almost</em> declaratively what conditions objects must meet to be considered valid&#8211;that is, how they are validated.</p>
<p>Thus, in order to make sure that a Person&#8217;s <tt>last_name</tt> attribute does not exceed 100 characters, you would write something like this</p>
<p><code></p>
<pre>
class Person &lt; ActiveRecord::Base
  validates_length_of :last_name, :maximum =&gt; 100
end
</pre>
<p></code></p>
<p>Somewhere in a view you&#8217;ll probably refer to the <tt>last_name</tt> attribute like this</p>
<p><code></p>
<pre>
&lt;% form_for :person, @person, :url =&gt; { :action =&gt; "update" } do |f| -%&gt;
  Last name : &lt;%= f.text_field :last_name %&gt;
  &lt;%= submit_tag %&gt;
&lt;% end -%&gt;
</pre>
<p></code></p>
<p>There you get a nice form where users can enter last names as long as they may wish&#8211;only to be shown an error message complaining about the length in cases where they have unwittingly overshot the limit. How should they have known?</p>
<p>Well, as a nice and caring person you could add an annotation to each input field saying how long its contents are allowed to be. That would be slightly better, but still leaves more work for the users than necessary.</p>
<p>How about this: Users are immediately notified in an unobtrusive way that some of the content they have (or have not yet) filled into a form does not constitute valid data. Furthermore, users are kept from submitting a form that contains obviously invalid data.</p>
<p>Should be easy, shouldn&#8217;t it? Yes, it should and actually it is just two plugins away.</p>
<h3>Raising awareness of validations</h3>
<p>Out of the box, Rails handles validations in such a way that it adds callbacks for checking them to model classes, but otherwise immediately loses awareness of them. When later on in the lifecycle of a model instance validations are checked, Rails does so on autopilot. A model class just does it&#8217;s thing, it won&#8217;t tell you what it&#8217;s doing, but you can be sure it&#8217;ll complain when something is wrong. Let&#8217;s add some reflection to make model classes a bit more loquacious. The details are irrelevant, just install the <b>Validation Reflection</b> plugin into your Rails application</p>
<p><code></p>
<pre>
$ script/plugin install svn://rubyforge.org//var/svn/valirefl/validation_reflection/trunk
</pre>
<p></code></p>
<h3>Tell the browser</h3>
<p>So far, the newfound awareness of validations idles away on the server-side. Somehow it has to be transported to the user&#8217;s browser. That is one of the things the <b>Client-Side Validation</b> plugin does. Install with</p>
<p><code></p>
<pre>
$ script/plugin install svn://rubyforge.org//var/svn/clientsidevali/client_side_validation/trunk
</pre>
<p></code></p>
<p>The first thing this plugin does is that it enhances tags generated by Rails&#8217;s helper methods in such a way that they contain encoded information about the validation constraints that apply to their values.</p>
<h3>Engage the client</h3>
<p>Still, even though validation information has been inserted into the HTML sent to the browser, where it lays dormant. On the client-side, JavaScript is where the action is. Therefore the task of checking validations there is handled by a validator written in JavaScript included with the Client-Side Validation plugin.</p>
<p>The generic script for the validator is included in your views or layout as part of the default scripts, therefore it is probably just there already.</p>
<p><code></p>
<pre>
&lt;%= javascript_include_tag :defaults %&gt;
</pre>
<p></code></p>
<p>Here we come to a point where you, the programmer, have to make some decisions and do some work. First, you need to decide (or have your code decide) what locale your users are in. Remember, dates don&#8217;t look the same all over the world.</p>
<p><code></p>
<pre>
 &lt;%= javascript_include_tag 'validators-en' %&gt;
</pre>
<p></code></p>
<p>Currently English (<tt>en</tt>) and German (<tt>de</tt>) are supported.</p>
<p>Then, you have to tell the validator that there&#8217;s work for it to do. For this, all forms that ought to be validated have to be marked with the <em>class</em> <tt>validated</tt>. Thus, the above form becomes</p>
<p><code></p>
<pre>
&lt;% form_for :person, @person, :url =&gt; { :action =&gt; "update" }, <span style="color:#f00">:html =&gt; { :class =&gt; 'validated' }</span> do |f| -%&gt;
  Last name : &lt;%= f.text_field :last_name %&gt;
  &lt;%= submit_tag %&gt;
&lt;% end -%&gt;
</pre>
<p></code></p>
<p>Also, you need to nudge the validator to start looking at the forms by adding a line to <tt>public/javascripts/application.js</tt></p>
<p><code></p>
<pre>
Form.Validator.installForAllValidatedForms();
</pre>
<p></code></p>
<h3>You ain&#8217;t see nothing yet</h3>
<p>If you&#8217;ve followed all these steps, the validator is probably doing its work, but hardly see anything of it. You may notice that the submit button is enabled when the form is valid and disabled when it is invalid, but that is all there is.</p>
<p>A further, but still invisible, thing that the validator does is that it adds (removes) the class &#8220;<tt>invalid</tt>&#8221; to input elements when they are invalid. Making this change in attribute visible is a small matter of CSS</p>
<p><code></p>
<pre>
 .invalid {
   border: 1px solid #f00;
 }
</pre>
<p></code></p>
<p>The result is not pretty, but you can push your stylesheeting abilities to the limit to make them look however you like. Also, the validator offers hooks for customizing most aspects. But that is left for a future installment.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.schuerig.de/michael/blog/index.php/2006/12/15/rails-almost-automatic-client-side-validation/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
	</channel>
</rss>

