<?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>Tech Blog</title>
	<atom:link href="http://assanka.net/content/tech/feed/" rel="self" type="application/rss+xml" />
	<link>http://assanka.net/content/tech</link>
	<description>Just another Arb-assk2009003.turmeric.assanka.com Blogs weblog</description>
	<lastBuildDate>Tue, 29 Jun 2010 09:52:15 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Behind the scenes of the new CIPR site</title>
		<link>http://assanka.net/content/tech/2010/06/06/behind-the-site-cipr/</link>
		<comments>http://assanka.net/content/tech/2010/06/06/behind-the-site-cipr/#comments</comments>
		<pubDate>Sun, 06 Jun 2010 11:09:13 +0000</pubDate>
		<dc:creator>Andrew Betts</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[cipr]]></category>
		<category><![CDATA[drupal]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[walkthough]]></category>

		<guid isPermaLink="false">http://assanka.net/content/tech/?p=139</guid>
		<description><![CDATA[We present a detailed overview of the way we built the CIPR's new website using Drupal.  Find out what modules and techniques were used to create all the main user facing features of the site, and the technical architecture that is serving it.]]></description>
			<content:encoded><![CDATA[<p>When the <a href="http://www.cipr.co.uk">CIPR </a>asked us to redevelop their website, we jumped at the chance to bring open source technology and web standards to an organisation that represents and promotes the marketing communications industry.  With 9,500 members &#8211; all professional communicators &#8211; it was a daunting task to produce something that the membership would approve of.</p>
<p>We chose to use Drupal &#8211; having successfully built Drupal sites for the likes of News International, we knew we could hit the ground running and end up with something that would be able to serve the CIPR for several years.</p>
<h2>The challenges</h2>
<p>The existing site had 30,000 static HTML files, with widely varying markup and no style consistency.  Because the site was really difficult to edit, many of the most active areas had been spun out into microsites with an asp.net based proprietary content management system.  These microsites were editable by their respective content owners, but with no oversight, and with complete content control available to the editors, so styles began to diverge even more.</p>
<p>A number of third party services and applications needed to be integrated into the new site. <a href="http://www.eventbrite.com">Eventbrite </a>for event ticketing, <a href="http://www.ezproxy.com">ezProxy </a>for access to academic journal services, and the CIPR&#8217;s own Ladder CPD system (another Assanka project).  For Ladder and ezProxy, we needed to be able to authenticate CIPR members outside of Drupal, so we would need to have some kind of single sign on system centered on the Drupal site.</p>
<p>Details about the members themselves are held in the CIPR&#8217;s membership system, a client-server application with a Windows front end and a Microsoft SQL Server database.  On the old site, synchronising this with the database the website was using for authentication required a long manual process and, for some reason, a number of Microsoft Word mail merges. The membership system is here to stay, so our solution would need to vastly improve on this process to get member profile updates into the website far faster than was possible before.</p>
<p>Finally, it&#8217;s worth mentioning the workflow challenges. Under the old site, the institute&#8217;s staff had to email their requested changes to the web team using a Microsoft Word based form. The web team would implement them &#8220;within 5 working days&#8221;, by editing static HTML files manually, and could not provide any content oversight, only the technical support to put the content online. For a new site to be successful, the management and oversight of the institute&#8217;s web output would need to change completely.</p>
<h2>Build process</h2>
<p>Our build process followed a fairly typical sequence:</p>
<ol>
<li><strong>Site map workshops:</strong> We gathered together and worked with the main content creators and heads of department. Over a number of sessions, and using a lot of Post-it notes and a very large wall, we produced a site map.  The site map was transferred to Mindmeister, where it continued to be collaboratively edited and refined.</li>
<li><strong>Wireframing:</strong> We produced wireframes of a new home page and typical article pages, to explore the organisation and prioritisation of content.  We needed a number of different, flexible content positions to accommodate a variety of content and media.</li>
<li><strong>Creative concepts: </strong>The wireframes were used as a basis for producing creative concepts to establish a new stylish look and feel for the site.  Three concepts were presented, from which the CIPR chose one.</li>
<li><strong>Standardisation:</strong> From the concept mock-ups, we derived a set of design standards. This is where we sought out any inconsistencies in the design and attacked them to produce a documented design standard that would be used as a rule book for developing the remaining layouts. The standards cover column layouts, margins, font and colour palettes, image aspect ratios and sizes, paragraph formats, heading levels, link and button styles and all interaction styling.  It&#8217;s easy to forget interaction styles &#8211; for every button, for example, you need a standard, hovered, pressed and disabled style.</li>
<li><strong>Layout development:</strong> Using the design standards, the concepts were expanded into the dozen or so different layouts that would be used in the final site.</li>
<li><strong>Rendering:</strong> With all layouts complete, HTML templates and an optimised stylesheet could be produced to serve all the layouts.</li>
<li><strong>Drupal build: </strong>Skinning Drupal with the new templates, installing and configuring all the content types and modules we would need, and writing our own bespoke modules for our special requirements.</li>
<li><strong>UI behaviours and trackers: </strong>JavaScript libraries and loaders for all the interface behaviours, with graceful degradation for users with JavaScript disabled.</li>
</ol>
<h2>Special features</h2>
<p>Much of the functionality of the new site came out of the box with Drupal, but we added some of our own modules to enable some particular functionality:</p>
<h3>Node image</h3>
<p>Our design called for image based teasers for content pages.  You can see examples of these on the homepage of the site, under &#8216;Features&#8217;.  The image used for the feature teaser cannot be required to be part of the content of the page itself, so it has to be separately mapped to the node.  Additionally, our design standards mandated a specific aspect ratio for all images on the site, and four specific valid sizes, so all images, whether used for feature teasers or within the content of the node, must be made to fit these design rules.</p>
<p>To achieve this, we wrote a new module, which we call <strong>Node Image</strong>, and a <a href="http://tinymce.moxiecode.com/">TinyMCE </a>plugin called Node Image Chooser, to replace the standard TinyMCE Image insert/edit dialog.  The Node Image module also alters the node edit form to add the ability to attach images outside the content body (to serve as feature teasers).</p>
<p>The form for uploading and editing node image nodes provides a cropper tool to allow the user to preview the original uploaded image (often a digicam photo at a far higher resolution than desired), and crop the four valid image sizes out of it, using a draggable frame with a constrained aspect ratio.  This allows editors to quickly create the four variants of each image, so for any image we can always offer the choice of all four possible sizes, without requiring editors to be trained in image manipulation software.</p>
<h3>Restricted content</h3>
<p>We needed to be able to protect some pages so that they were only accessible by CIPR members or even a smaller subset of the membership who were perhaps members of a particular regional or sectoral group.  However, access to this information is one of the most important and valuable benefits of membership, so we wanted to ensure that non-members could see that this content existed, even if they couldn&#8217;t access it themselves.</p>
<p>Many newspaper sites handle this situation with a concept called a content barrier.  The user is shown a teaser of the page content, and a message indicating that in order to read the remainder of the content, they must pay a fee or register.  We started with the existing <a href="http://drupal.org/project/restricted_content">Restricted Content</a> module, but completely rewrote it to support tokens and offer completely different views to anonymous users versus those who were logged in but simply did not have the required role.  We&#8217;ve released these changes back to the community &#8211; <a href="http://drupal.org/node/441404">our modified version is available on the Drupal project page</a>.</p>
<p>We married this module with a bespoke (and very simple) robots login module, which identifies legitimate search engine crawlers from Google, Yahoo and Microsoft, and allow them to view all protected content as if they were members, but with a NOARCHIVE restriction.  This further contributes towards our goal of making this member only content visible to non-members by allowing them to find it via a general web search, though they still need to join in order to view the full page.</p>
<h3>Single sign on</h3>
<p>We didn&#8217;t just need to authenticate users for the benefit of Drupal.  We also need them to be logged in for other sites as well, notably Ladder, the CIPR&#8217;s Continuing Professional Development system.  We wanted to ensure that users did not have to log in several times to access these services.  So we turned Drupal into a single sign on hub.</p>
<p>Our solution starts with the <a href="http://drupal.org/project/logincookie">Login Cookie</a> module, modified to support tokens and shared secret based signing.  When users log in, we use this module to set a cookie containing various particulars about their user account, and a signature that can be verified by co-operating apps on the same root domain.</p>
<p>The second part of the solution is the need to redirect users to a querystring-specified destination on login and logout.  This is to enable users initiating login directly from an external app to be seamlessly redirected back to it after their login has been processed.  We started with the Login Destination module, but found this to be over-engineered for our needs in many ways, while it also missed a key requirement &#8211; redirect on logOUT.  So we ended up writing a bespoke module for this.</p>
<p>Finally, the <a href="http://drupal.org/project/services">services </a>module gave external apps that read the SSO cookie the ability to query the user&#8217;s full Drupal user account to get the details that we did not include in the cookie.</p>
<h3><span style="font-weight: normal">Social sharing</span></h3>
<p><span style="font-weight: normal">Our requirements included the usual need to allow social sharing of content via Digg, Facebook, Twitter and so on, as well as the ability to send pages by email to friends, and bookmark pages in the browser.  All these were easily solved with <a href="http://www.addthis.com">AddThis</a> which also adds analytics into the bargain.</span></p>
<h3>Inline audio and video</h3>
<p>The CIPR needs to be able to include videos from <a href="http://www.youtube.com">Youtube </a>and <a href="http://www.vimeo.com">Vimeo</a>, and publishes its own video content via <a href="http://www.vzaar.com">Vzaar</a>, all of which have straightforward embed codes, but there&#8217;s no simple way to insert these into TinyMCE in a way that allows the designer (not editorial users) to control their dimensions and style.</p>
<p>We wrote a new TinyMCE plug-in to replace the media plug-in, which allows CIPR editors to find an online video and just paste the URL into the plug-in dialog.  The plug-in recognises the URL format, queries the API of the appropriate video service provider, and displays the video title to the user to confirm they have the right one.  The user can then simply choose which of our standard image sizes they wish to use for the video player &#8211; ensuring that video players are shown at exactly the same size as embedded images.</p>
<p>This continues a theme of replacing complex functionality with alternatives that separate content decisions from style at the admin level.  So if an editor wants to insert a video, they should not have the choice of what width and height to assign to the player.  That is a decision made by the designer, not user.  So our plug-ins don&#8217;t offer the choice.  As a result all the CIPR&#8217;s videos are presented in the same way.</p>
<p>Audio files are embedded using the same plug-in, recognising the file as an audio format, and embedding the excellent <a href="http://www.google.com/reader">Google Reader</a> inline mp3 player.</p>
<h3>Forms</h3>
<p>The CIPR needs loads of forms.  Membership applications, renewals, applications to join groups, entries for awards competitions, and so on.  Drupal&#8217;s capabilities to create arbitrary user defined forms and aggregate the results did not really do the job, so we turned to the service provided by <a href="http://www.formassembly.com">Formassembly</a>.  This third party service allows a non-specialist end user to create a wide variety of forms, and process results in many different ways, as well as providing useful connectors such as <a href="http://www.salesforce.com">Salesforce </a>integration and one-off <a href="http://www.paypal.com">Paypal </a>payments.</p>
<p>We created a TinyMCE plug-in to insert verified Formassembly form IDs into a specially designed machine tag in the content of any page.  We paired this with an output filter that converted the machine tag into the full HTML of the form, making a few changes to it on the way through &#8211; tweaking Formassembly&#8217;s HTML mark-up slightly allowed it to be styled by our CSS without having to include a second set of form styles.</p>
<h3>Search</h3>
<p>Drupal does offer user facing search via various means, but we felt that we needed something really awesome to make up for the years of having no search on the CIPR site at all.  We wanted something that offered a great relevance engine to surface the best matching content.  In short, we wanted something like Google on a small scale.</p>
<p>Fortunately, for a fee, that&#8217;s exactly what we got.  The search engine on the CIPR&#8217;s new site is powered by <a href="http://www.google.com/sitesearch">Google Site Search</a>, a paid-for API-driven service that builds and uses a dedicated index on Google&#8217;s servers, but is delivered and branded entirely within your own site.</p>
<h3>Course finder and member directory: searchable views</h3>
<p>The old site already had a directory of members, and we wanted to maintain that, and add directories of PR suppliers and courses as well.  The <a href="http://drupal.org/project/better_exposed_filters">Better Exposed Filters</a> module came in handy here, providing an excellent checkbox list interface to options such as level, cost and type of course.</p>
<h3>Events calendar and Eventbrite integration</h3>
<p>With the CIPR&#8217;s events all managed on Eventbrite, we needed some close integration between the Eventbrite accounts and the CIPR site.  Using Eventbrite&#8217;s XML feeds, we perform a regularly scheduled import of event data and use it to populate Event nodes within Drupal.  Eventbrite already provides a lot of meta data &#8211; and we add a machine tag in the Eventbrite body text to link the events into courses within the CIPR course finder.</p>
<p>This also enables features such as listing all available dates for a course on the course page, and allowing users to navigate to alternative dates from an event page.</p>
<h3>Meta data</h3>
<p>The existence of content types like courses and events made us think that it would be really useful to have the ability to assign arbitrary key-value data to any node, and display it in a table alongside the content, in a similar way to Wikipedia&#8217;s boxes.  We used CCK fields to collect the meta data and a custom block to display it.</p>
<h2>Content types</h2>
<p>Our full set of Drupal content types are:</p>
<ul>
<li><strong>Standard page: </strong>A flexible content page</li>
<li><strong>Blog post:</strong> Automatically bylined and aggregated under an author or topic.</li>
<li><strong>Course: </strong>Anything that can loosely be described as a training course is created using the Course node type, which has additional CCK fields for properties such as length, level, type, cost and accreditation.  This then powers the course finder.</li>
<li><strong>Course provider:</strong> Used to record details of an institution that provides training courses.  A course is linked relationally to a course provider which allows course provider pages to display a list of their available courses.</li>
<li><strong>Event: </strong>A single event, normally an instance of a training course, imported from Eventbrite, linked to a course</li>
<li><strong>Forum topic: </strong>Using the forums module, forums provide discussion areas for groups</li>
<li><strong>Node Image: </strong>Our bespoke node type for constrained-size images</li>
</ul>
<h2>Optimisation</h2>
<p>Finally, we thought we would share a few tips on optimising delivery and measurement.</p>
<ul>
<li><strong>Third party JavaScript loading:</strong> We use JavaScript from a number of third parties, such as AddThis and Google.  We developed a simple scheduled task to download these libraries and cache them on our servers so that we could deliver all the JavaScript to power the site&#8217;s UI behaviours in as few downloads as possible.</li>
<li><strong>JavaScript grouping: </strong>We group all our JavaScript files into two downloads &#8211; one in the &lt;head&gt;, and one just before &lt;/body&gt;.  A script in our theme concatenates the script files, and also returns 304 Not Modified responses whenever it can.</li>
<li><strong>Tracking 404s:</strong> It&#8217;s really easy when using Drupal to make the mistake of including your tracking JavaScript on your 404 error page.  Not only does this incorrectly inflate your traffic figures, but it&#8217;s a lost opportunity to create a useful broken links report.  We use Google&#8217;s recommended method for tracking 404s, and then we filter them out of the website profile we use for reporting traffic numbers.</li>
<li><strong>Caching of twitter / facebook feeds:</strong> Updates posted to the CIPR&#8217;s twitter and Facebook accounts are downloaded on a schedule and cached on our servers as fully rendered HTML to allow them to be included in our pages at the final stage of assembly.</li>
<li><strong>Feedburner:</strong> Using a simple user-agent and request-uri based redirect in an Apache rewrite rule, all end user requests for RSS feeds are diverted to <a href="http://www.feedburner.com">Feedburner</a>, which provides excellent analytics to let us know how many readers are consuming each of our RSS feeds, using which devices or clients.  It also reduces the load on our servers to have over 95% of our feed requests answered by Feedburner.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://assanka.net/content/tech/2010/06/06/behind-the-site-cipr/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Thoughts on CSS transitions in Firefox</title>
		<link>http://assanka.net/content/tech/2010/05/20/thoughts-on-css-transitions-in-firefox/</link>
		<comments>http://assanka.net/content/tech/2010/05/20/thoughts-on-css-transitions-in-firefox/#comments</comments>
		<pubDate>Thu, 20 May 2010 14:36:05 +0000</pubDate>
		<dc:creator>Andrew Betts</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Animation]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Future Tech]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://assanka.net/content/tech/?p=129</guid>
		<description><![CDATA[To produce the CSS3 ticker plugin for jQuery that I&#8217;ve been working on recently, I used CSS3 transitions.  These are the magic new properties in CSS that make a browser animate elements without using JavaScript.
The logic for implementing native animations in the browser is a no-brainer, particularly if you try and use a site [...]]]></description>
			<content:encoded><![CDATA[<p>To produce the <a href="/content/tech/2010/03/14/smooth-css3-ticker-jquery-plugin/">CSS3 ticker plugin for jQuery</a> that I&#8217;ve been working on recently, I used <a href="http://webkit.org/blog/138/css-animation/">CSS3 transitions</a>.  These are the magic new properties in CSS that make a browser animate elements without using JavaScript.</p>
<p>The logic for implementing native animations in the browser is a no-brainer, particularly if you try and use a site that implements scrolling tickers by incrementing an element&#8217;s <code>left</code> or <code>margin-left</code> properties every few milliseconds.   Watch your interactions with the rest of the page slow down to a sluggish crawl and realise that animating the positioning of DOM elements in JavaScript can sometimes be pretty slow.</p>
<p>With animation increasingly important to creating rich and usable user interfaces, the chance to start optimising the performance of animation in the browser via native implementation is a great step forward.  Unfortunately, this is currently only supported by Webkit based browsers.  In developing the ticker, I wanted to see if support in Firefox would be possible.</p>
<p>From version 1.9.3 of Gecko, the <code>-moz-transition</code> family of CSS properties have been added, so I tried grabbing the latest version of <a href='http://www.mozilla.org/projects/minefield/'>Minefield</a> (the pre-release codename for Firefox) and running the ticker plugin.</p>
<p>It doesn&#8217;t work.  Specifically, it doesn&#8217;t work for these reasons:</p>
<ol>
<li>It doesn&#8217;t seem to be possible to act on position properties.  <code>left</code>, <code>right</code>, <code>top</code> and <code>bottom</code> do not appear to animate.  It might be possible to work around this by switching to animating margins rather than absolute positioning, but hopefully it will be possible to animate these before the Firefox 3.7 release.</li>
<li>It doesn&#8217;t appear to fire the <code>mozTransitionEnd</code> event, which you&#8217;d expect from a full implementation of the standard.  As a result, my ticker can&#8217;t loop.</li>
<li>It doesn&#8217;t seem to be possible to change animation properties (such as the duration) from JavaScript.  I need this to disable animation while repositioning the ticker for the next loop.</li>
</ol>
<p>This is clearly an early pre-release version, and it&#8217;s encouraging to see support starting to emerge from the Firefox camp.  I think I might have retired by the time this kind of feature hits Internet Explorer!</p>
]]></content:encoded>
			<wfw:commentRss>http://assanka.net/content/tech/2010/05/20/thoughts-on-css-transitions-in-firefox/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Smooth CSS3 ticker jQuery plugin</title>
		<link>http://assanka.net/content/tech/2010/03/14/smooth-css3-ticker-jquery-plugin/</link>
		<comments>http://assanka.net/content/tech/2010/03/14/smooth-css3-ticker-jquery-plugin/#comments</comments>
		<pubDate>Sun, 14 Mar 2010 11:50:26 +0000</pubDate>
		<dc:creator>Andrew Betts</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[Tickers]]></category>
		<category><![CDATA[UI Design]]></category>

		<guid isPermaLink="false">http://assanka.net/content/tech/?p=105</guid>
		<description><![CDATA[I&#8217;ve recently been building a status display for the Assanka office, which shows us information like current Zabbix monitoring triggers, support requests from clients, Pingdom status alerts, and even naming and shaming Assankans who&#8217;ve failed to file their time-sheets on time.
In the process, I was pointed at the gorgeous Panic status board.  Our objectives [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve recently been building a status display for the Assanka office, which shows us information like current Zabbix monitoring triggers, support requests from clients, Pingdom status alerts, and even naming and shaming Assankans who&#8217;ve failed to file their time-sheets on time.</p>
<p>In the process, I was pointed at the gorgeous <a href="http://www.panic.com/blog/2010/03/the-panic-status-board/">Panic status board</a>.  Our objectives were similar and I was attracted to the idea of adding the scrolling tickers (at least, we assume they are scrolling from Panic&#8217;s photograph), which run along the bottom of their board.  I was reminded of the kind of tickers you get on 24 hour news channels (here&#8217;s one from BBC World):</p>
<p><img src="/content/tech/files/2010/03/bbcworldcap42.jpg" alt="BBC World screen capture showing scrolling ticker" /></p>
<p>Tickers in web browsers are difficult for a number of reasons.  First, continuously animating the <code>left</code> or <code>margin-left</code> properties of an element in JavaScript is computationally expensive, particularly if you want to reposition the element often enough to create a properly smooth animation.  Sites that use Javascript for tickers in this manner tend to hog your computer&#8217;s CPU and feel slow and laggy as a result.  Many sites give up on the traditional ticker and go for something that&#8217;s easier for the browser to animate, like a &#8216;type in&#8217; reveal (eg the BBC News homepage ticker), or a vertical &#8217;scroll up&#8217; ticker.</p>
<p><img src="/content/tech/files/2010/05/Capture.jpg" alt="BBC News online ticker" /></p>
<p>Another issue with continuous tickers is making them loop properly.  When your animation reaches the end of the text, you have to scroll it all the way off the left edge of the screen before it can be repositioned and re-enter the frame from the right.  This leaves an ugly gap in your ticker.</p>
<p>Finally, if your ticker is updating with new information in real time, making changes that are noticeable in the visible portion of the ticker is amateur hour stuff.  You rarely see TV tickers suddenly change mid-scroll, and yet stories are seamlessly added and removed as necessary, <em>outside of the visible frame</em>.</p>
<p>Since Panic didn&#8217;t release any code (boo), I set out to produce a jQuery plugin that would enable news tickers to be easily added to a page, using CSS3 animation effects.  If it&#8217;s working for you, you should see a couple of tickers scrolling away right here:</p>
<div class="iframe-wrapper">
  <iframe src="/content/tech/files/2010/05/webkitscroller.html" frameborder="0" style="height:85px;width:590px;">Please upgrade your browser</iframe>
</div>
<p>If it&#8217;s not working, you&#8217;re probably not using a browser that supports CSS3 transitions.  Try it in Chrome, or Safari.  Support should be added to Firefox pretty soon.  But the objective here is not to get universal compatibility, since I can easily dictate which browser our status board will use.</p>
<p>With CSS3 transitions, we substantially improve the performance issue of tickers.  And this will improve further with browsers implementing these effects at an increasingly lower level.  This leaves the looping and updating issues to resolve before we can claim a properly news-grade ticker.</p>
<h2>Looping</h2>
<p>There are two possible solutions to the looping problem.  The first is to target segments of the ticker just after they have left the frame on the left, and move them to the end of the ticker on the right.  The second is to make several copies of your ticker content, and when you get to the end of the second copy, reposition the entire ticker element so that the first copy lines up with where the second copy had got to, then scroll on into the second copy again and repeat.</p>
<p>I chose the second approach, because you need several copies of the ticker anyway if it&#8217;s too short to fill the width of the frame with enough left over to sort the looping out.</p>
<h2>Updates</h2>
<p>In order to enable the ticker to be updated dynamically, and yet not suffer any noticeable changes in the visible portion, I added some public methods &#8211; addMessage() and removeMessage(), which allow you to pass a list element and have it queued for addition or removal at an appropriate moment.</p>
<p>This does require breaking a rather holy tenet of jQuery, namely returning a reference to the ticker rather than returning the jQuery chain, so you have an object on which you can call the public methods.</p>
<p>Every time the ticker hits the end of the text, it is  repositioned with the penultimate copy in view, all copies of the ticker text are updated to match the copy to their right, and the final copy (the original set of elements) is updated with the queued additions and removals (out of view).   As a result, when you call the add or remove methods, you may have to wait some time before you see the effect, but the ticker will never judder or change while it is visible to the user.</p>
<h2>How to use</h2>
<p>Your ticker must be a block element containing a UL.  This is because the ticker needs a frame to mask the content as it slides through.</p>
<pre class="brush: xml;">
&lt;div id='ticker1'&gt;
  &lt;ul&gt;
    &lt;li&gt;Headline one&lt;/li&gt;
    &lt;li&gt;Headline two&lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
  var ticker = $('#ticker1').ticker();
  var newid = ticker.addMsg('Headline three');
  setTimeout(function() {
    ticker.removeMsg(newid);
  }, 60000);
&lt;/script&gt;
</pre>
<h2>Settings and methods</h2>
<p>The following settings are available, which can be passed to the <code>ticker()</code> function in a standard json object:</p>
<ul>
<li><strong>pxpersec</strong>: Speed of the ticker in pixels per second (optional; default is 30)</li>
</ul>
<p>Calls to the <code>ticker()</code> function return a reference to the ticker, not the jQuery chain.  The ticker reference exposes the following methods:</p>
<ul>
<li><strong>string addMsg(<em>msg</em>)</strong>: Add a new message to the ticker.  Can be a jQuery object or raw list item element (which will be moved from its current location in the DOM), or a plain text string.  Returns a string identifier for the message (to enable you to remove it later).  If you pass an element that already has an ID attribute, that ID is used (and returned).</li>
<li><strong>void removeMsg(<em>msg</em>)</strong>: Remove a message from the ticker.  Can be a jQuery object or raw list item element (which must be in the ticker already), or a string identifier returned from the addMsg call that added the message to the ticker. Returns nothing.</li>
</ul>
<h2>Get the code</h2>
<ul>
<li><a href="/content/tech/files/2010/05/jquery.ticker.js">Download development</a> (commented, 7KB)</li>
<li><a href="/content/tech/files/2010/05/jquery.ticker.min.js">Download production</a> (minified, 4KB)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://assanka.net/content/tech/2010/03/14/smooth-css3-ticker-jquery-plugin/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Blocking events with blocker lists</title>
		<link>http://assanka.net/content/tech/2009/12/07/blocking-events-with-blocker-lists/</link>
		<comments>http://assanka.net/content/tech/2009/12/07/blocking-events-with-blocker-lists/#comments</comments>
		<pubDate>Mon, 07 Dec 2009 14:42:35 +0000</pubDate>
		<dc:creator>Andrew Betts</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Scrolling]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[User experience]]></category>

		<guid isPermaLink="false">http://assanka.net/content/tech/?p=56</guid>
		<description><![CDATA[It&#8217;s often useful to be able to detect scroll events using the onscroll event handler in JavaScript.  For example, every time a user scrolls to nearly the bottom of the page, you load more content to create an &#8216;endless&#8217; page.  In my case, I have two DIVs set to overflow: auto, with chat [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s often useful to be able to detect scroll events using the onscroll event handler in JavaScript.  For example, every time a user scrolls to nearly the bottom of the page, you load more content to create an &#8216;endless&#8217; page.  In my case, I have two DIVs set to overflow: auto, with chat histories in each, where the chats have been taking place simultaneously.  I want to detect when the user scrolls one of the DIVs (either one) and then scroll the other one to keep the two in sync.  That is to say, we want messages that are currently vertically in the middle of DIV 1 to have been posted at around the same time as the messages at the same vertical viewport offset in DIV 2.</p>
<p>Try scrolling either of the panels in this example:</p>
<div class="iframe-wrapper">
  <iframe src="/content/tech/files/2009/12/testcase_blockerlist11.html" frameborder="0" style="height:220px;width:450px;">Please upgrade your browser</iframe>
</div>
<p>You should find it scrolls madly and unpredictably until you press stop.</p>
<p>The first problem is that onscroll is not simply fired when you finish scrolling.  It fires like a machine gun continuously while the mouse cursor is moving on the scroll handle.  The solution to this is a watchdog.  A watchdog is a timer that will execute action A if action B does not happen within X seconds.  So every time onscroll fires, we reset the timer, and if it gets to zero we know that the user has finished scrolling (or at least has paused for long enough for us to do something about it).</p>
<div class="iframe-wrapper">
  <iframe src="/content/tech/files/2009/12/testcase_blockerlist2.html" frameborder="0" style="height:220px;width:450px;">Please upgrade your browser</iframe>
</div>
<p>You&#8217;ll find that although it&#8217;s not quite as crazed, the panels do keep scrolling, one after the other.</p>
<p>The problem now is that when you scroll DIV 1, and this causes an automatic scroll of DIV 2, that automatic scroll ALSO triggers the onscroll event and so we then scroll DIV 1 again.  The solution is to have a variable that flags whether we are currently paying attention to scroll events, and turn it off when we don&#8217;t want to detect scrolling (ie just before the auto-scroll) and then on again after the scroll has completed (I&#8217;m using an animation library so the scroll takes around half a second to complete).  Try this (don&#8217;t click the button yet, just scroll the panels):</p>
<div class="iframe-wrapper">
  <iframe src="/content/tech/files/2009/12/testcase_blockerlist3.html" frameborder="0" style="height:220px;width:550px;">Please upgrade your browser</iframe>
</div>
<p>Success.  However, this approach is a bit short-sighted.</p>
<p>You also need to turn off scroll detection when adding or removing content from either DIV, because changing the height of the content within the element can also fire the onscroll event.  So if this is a chat window, and we&#8217;re adding and removing content in the DIVs, we have to disable scroll detection while we&#8217;re doing that and then turn it on again afterwards.</p>
<p>Sadly, our scroll detection flag is crude &#8211; it&#8217;s a sheer yes/no, and if it&#8217;s already set to no (say in order to execute a lengthy scrolling animation), and in the meantime we need to do something quick (say adding a line to one of the DIVs) then that quick operation is going to disable scroll detection (unnecessarily, as it&#8217;s already disabled) and then crucially enable it again before the scroll animation has finished.  Click the button in the example above to demonstrate this (and then scroll).  You should, intermittently, get the unwanted cascade effect you saw in example 2.</p>
<p>What we need is a list, not a flag.  Enter the blocker list &#8211; an object that collates tokens from each procedure in the script that is currently blocking scroll detection.  So if a procedure wants to disable scrolling for a period of time, it adds its token to the blocker list, and then removes its token when it&#8217;s done.  When a scroll event fires, we now only need to know whether there are any items in the blocker list, and then we can work out if it&#8217;s ok to process the event.</p>
<p>It&#8217;s also worth noting that there&#8217;s no need to actually count the number of items in the blocker list &#8211; it&#8217;s enough simply to know that there is at least one item in there.  And as a further optimisation, we don&#8217;t actually need to compute this when scroll events fire (frequently), because the value will only change when some function wants to add or remove its token from the list (less frequent).  So we can have enableScrollDetection and disableScrollDetection functions that deal with the blocker list, and which ultimately simply change the old scrolldetection flag to true or false.</p>
<div class="iframe-wrapper">
  <iframe src="/content/tech/files/2009/12/testcase_blockerlist4.html" frameborder="0" style="height:220px;width:550px;">Please upgrade your browser</iframe>
</div>
<p>There might be a neater way of achieving this, but this certainly works for me.  I&#8217;d welcome any comments or suggestions.  The sources for all these demos are available in the iframes above.</p>
]]></content:encoded>
			<wfw:commentRss>http://assanka.net/content/tech/2009/12/07/blocking-events-with-blocker-lists/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Disappearing text cursor in Firefox</title>
		<link>http://assanka.net/content/tech/2009/11/25/disappearing-text-cursor-in-firefox/</link>
		<comments>http://assanka.net/content/tech/2009/11/25/disappearing-text-cursor-in-firefox/#comments</comments>
		<pubDate>Wed, 25 Nov 2009 13:03:15 +0000</pubDate>
		<dc:creator>Andrew Betts</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[User experience]]></category>

		<guid isPermaLink="false">http://assanka.net/content/tech/?p=43</guid>
		<description><![CDATA[Do you ever find that sometimes when you try and type into a textbox, there is no cursor there, but it still accepts your input and the text appears as if there is a cursor?  I came across this problem in Firefox 3 and searching online revealed only solutions to an earlier problem that [...]]]></description>
			<content:encoded><![CDATA[<p>Do you ever find that sometimes when you try and type into a textbox, there is no cursor there, but it still accepts your input and the text appears as if there is a cursor?  I came across this problem in Firefox 3 and searching online revealed only <a href="http://www.nestedelements.com/2008/02/26/firefoxs-disappearing-cursor/">solutions</a> <a href="http://www.webdeveloper.com/forum/showthread.php?t=150640">to</a> <a href="http://blog.tremend.ro/2007/01/22/mouse-cursor-disappears-in-firefox/">an</a> <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=167801">earlier</a> <a href="http://www.fleegix.org/articles/2007-02-14-mystery-of-the-disappearing-cursor-caret">problem</a> that affected Firefox 2, in which the overflow configuration of the INPUT&#8217;s containing element would affect whether the cursor appeared in the input or not.</p>
<p>But this was clearly not my problem, and various sources suggested that this had in any case been fixed for Firefox 3.  I discovered that my problem was rather simpler.  If you disable an element that has focus, and then re-enable it, you don&#8217;t get the cursor back.</p>
<p>Solution: blur (remove focus from) the element before you disable it.  Those sources that do refer to this specific problem tend to suggest that you focus a different element before you disable the text box.  But this is not necessary &#8211; you can just blur the element that has focus and leave the page with nothing focused.</p>
<h2>Problem test case</h2>
<p>Try clicking and typing in the field below &#8211; if your browser exhibits this problem, the text cursor will not appear.  If it does, then your browser does not have this problem.</p>
<div class="iframe-wrapper">
  <iframe src="/content/tech/files/2009/12/testcase_ffcursor1.html" frameborder="0" style="height:35px;width:300px;">Please upgrade your browser</iframe>
</div>
<p>This demo has a single text field which, when you click into it, is briefly disabled, and then enabled again.  The disabling of the field while it has focus triggers this problem, so the text cursor does not appear (but you can still type into the field!).</p>
<h2>Solution test case</h2>
<p>Now try typing in this field &#8211; the cursor should appear consistently.</p>
<div class="iframe-wrapper">
  <iframe src="/content/tech/files/2009/12/testcase_ffcursor2.html" frameborder="0" style="height:35px;width:300px;">Please upgrade your browser</iframe>
</div>
<p>This demo still briefly disables and re-enables the text field when it gets focus, but this time it blurs it right before it&#8217;s disabled, then focuses it again after re-enabling it.  The result is that when you place your mouse cursor in the field to manually give it focus, the text cursor appears as normal.</p>
]]></content:encoded>
			<wfw:commentRss>http://assanka.net/content/tech/2009/11/25/disappearing-text-cursor-in-firefox/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Validating HTML input in PHP</title>
		<link>http://assanka.net/content/tech/2009/10/10/validating-html-input-in-php/</link>
		<comments>http://assanka.net/content/tech/2009/10/10/validating-html-input-in-php/#comments</comments>
		<pubDate>Sat, 10 Oct 2009 11:51:41 +0000</pubDate>
		<dc:creator>Andrew Betts</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[DTDs]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[User input]]></category>
		<category><![CDATA[Validation]]></category>
		<category><![CDATA[XHTML]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://assanka.net/content/tech/?p=38</guid>
		<description><![CDATA[It&#8217;s often the case that as web developers, we need to &#8216;clean&#8217; input from end users to ensure it does not contain any nasty formatting or script that we don&#8217;t want to allow on our sites.  Forums in particular often suffer from either security holes that allow cross site scripting attacks (XSS) or are [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s often the case that as web developers, we need to &#8216;clean&#8217; input from end users to ensure it does not contain any nasty formatting or script that we don&#8217;t want to allow on our sites.  Forums in particular often suffer from either security holes that allow cross site scripting attacks (XSS) or are so restrictive in what they allow to be input that it causes a nuisance to the user (for example, disallowing all HTML but allowing BBCode instead).</p>
<p>This problem is often solved with complex classes or functions in PHP that are designed to strip out the nasty stuff while allowing as much useful formatting as possible.  We realised that these functions are pretty much just reinventing the wheel, because there is already a pretty good mechanism for parsing and validating XML syntax: libxml, which has PHP bindings and can be accessed using SimpleXML.</p>
<p>What&#8217;s more, libxml can parse an XML document for conformance to a DTD, so if you include an XHTML Transitional DTD in your XML code string, you can check that the markup is valid XHTML.</p>
<p>Here&#8217;s the PHP to do this.  This is tested on PHP 5.3 with libxml2-2.6.26-2.1.2.8.</p>
<pre class="brush: php;">
function isXML($str) {
	libxml_use_internal_errors(true);
	libxml_clear_errors();
	$options = (strpos($str, '&amp;lt;!DOCTYPE') !== false) ? (LIBXML_DTDLOAD + LIBXML_DTDVALID) : 0;
	simplexml_load_string($str, 'SimpleXMLElement', $options);
	$errors = libxml_get_errors();
	return (empty($errors) or $errors[0]-&amp;gt;level == LIBXML_ERR_WARNING) ? true : false;
}
</pre>
<p>You could of course use the contents of <code>$errors</code> to feed back to the user, or potentially deal with a validation failure more intelligently, but for now true or false will do.</p>
<p>So the markup submitted by a user is valid.  Excellent.  But just because the markup is valid doesn&#8217;t mean it&#8217;s safe to output to the browser.  You&#8217;ll also want to ensure it contains no <code>&lt;script type="text/javascript"&gt;</code> sections or event handlers, and may want to restrict the set of elements available.  This is where you can start getting creative with your own DTD spec.  Just start with the standard you want to conform to for the whole page (say XHTML) and strip out anything you don&#8217;t like.</p>
<p>We&#8217;ll start by removing the HEAD tag and all its contents.  Our users will not be writing entire documents, just fragments of body markup, so we don&#8217;t want a HEAD, TITLE, or any META tags, etc.</p>
<p>You can continue, removing things like SCRIPT, OBJECT, forms, frames, and so on.  Be careful where elements are defined using presets, which often contain the nasties, for example the <code>%event</code> set of attributes grants an element the ability to fire event handlers.  Fortunately this is almost exclusively used as part of <code>%attrs</code>, so we can just remove it from that superset.</p>
<p>We&#8217;ll also define a new root element <code>fragment_under_test</code> to ensure that we don&#8217;t cause any confusion and lead anyone to believe that they&#8217;re writing a normal <code>&lt;html&gt;</code> or <code>&lt;body&gt;</code>.</p>
<p>Once we&#8217;re done, we can then wrap the isXML function in a convenience function that adds our new custom DTD.</p>
<pre class="brush: php;">
function isXHTMLFragment($str) {
	return isXML(&amp;quot;&amp;lt;!DOCTYPE fragment_under_test system \&amp;quot;http://www.example.com/dtds/xhtml-content-restrictive.dtd\&amp;quot;&amp;gt;&amp;lt;fragment_under_test&amp;gt;&amp;quot;.$str.&amp;quot;&amp;lt;/fragment_under_test&amp;gt;&amp;quot;);
}
</pre>
<p>If you want, feel free to <a href="/content/tech/files/2009/12/dtdfiles.zip">download the DTD I created for this article</a>.</p>
<p>Now you can use the fast libxml to validate user input in a fairly bulletproof way.</p>
<p>Finally, and very importantly, make sure you cache the schemas on your server in an XML catalog file.  If you don&#8217;t do this, libxml will make an external HTTP request for the DTD schema file every time you call the function.  In fact, since most web documents cite W3C DTDs, they are having enormous problems with software making repeated requests for the standard XHTML, HTML 4 etc DTDs which haven&#8217;t changed in years.  Be a good net citizen, and cache your schemas.  In this case we&#8217;re writing and hosting our own anyway, but if you&#8217;re using a public schema you may as well save yourself the pointless HTTP traffic, and it&#8217;ll speed up the validation as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://assanka.net/content/tech/2009/10/10/validating-html-input-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JSON2.js vs Prototype</title>
		<link>http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/</link>
		<comments>http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/#comments</comments>
		<pubDate>Wed, 02 Sep 2009 16:24:23 +0000</pubDate>
		<dc:creator>Andrew Betts</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Prototype]]></category>

		<guid isPermaLink="false">http://assanka.net/content/tech/?p=5</guid>
		<description><![CDATA[We use Douglas Crockford&#8217;s json2.js frequently in our web apps.  Its stringify method allows JavaScript data structures to be trivially serialised before submission via AJAX to a web service.   It works by descending through the structure, calling the toJSON() method on anything it finds.  It also creates toJSON methods for data [...]]]></description>
			<content:encoded><![CDATA[<p>We use <a title="Douglas Crockford" rel="homepage" href="http://crockford.com/">Douglas Crockford</a>&#8217;s json2.js frequently in our web apps.  Its stringify method allows JavaScript data structures to be trivially serialised before submission via AJAX to a web service.   It works by descending through the structure, calling the <code>toJSON()</code> method on anything it finds.  It also creates <code>toJSON</code> methods for data types that do not already have them, on the basis that future browsers will introduce <code>toJSON()</code> support &#8211; at which point the native implementation can be used because it&#8217;s likely to be a lot faster.</p>
<p>Recently I needed to use this method to serialise some data in a JavaScript library that might be used in &#8216;foreign&#8217; web pages.  My own library was nicely encapsulated, and didn&#8217;t interfere with any other JavaScript that might be running on the page, and it included Douglas Crockford&#8217;s JSON2.js implementation.</p>
<p>But on one of our clients&#8217; sites, it didn&#8217;t work.  I got this:</p>
<pre class="brush: jscript;">
{&quot;key&quot;:&quot;val&quot;,[\{\&quot;key\&quot;:\&quot;val\&quot;\},\{\&quot;key\&quot;:\&quot;val\&quot;\}]}
</pre>
<p>What&#8217;s happened here is that any arrays in my data structure have been stringified twice.  This didn&#8217;t happen in my dev environment.  I narrowed down the differences and realised what was causing this effect.  They&#8217;re using <a href="http://prototypejs.org">Prototype</a>.  We&#8217;re not.</p>
<p>Prototype modifies a number of JavaScript&#8217;s native objects, including the Array object, and&#8230; you guessed it, adds a <code>toJSON()</code> method to it. Unfortunately it does not return what Crockford&#8217;s <a title="JSON" rel="homepage" href="http://json.org/">JSON</a> implementation is expecting.  From the docs for json2:</p>
<blockquote><p>A toJSON method does not serialize: it returns the value represented by the name/value pair that should be serialized, or undefined if nothing should be serialized.</p></blockquote>
<p>Prototype&#8217;s <code>toJSON()</code> <em>is</em> serialising.  There don&#8217;t seem to be any sensible solutions to this online, but it&#8217;s actually relatively simple to solve using a replacer function, allowed for in the json2 API:</p>
<pre class="brush: jscript;">
var reqdata = JSON.stringify(req, function(key, value) {
	if (typeof this[key] == 'object' &amp;&amp; Object.prototype.toString.apply(this[key]) === '[object Array]') {
		return this[key];
	} else {
		return value;
	}
});
</pre>
<p>Essentially this says &#8216;for each key in the data structure, if the value is an array, use the raw value, otherwise use the value you gave me&#8217;.  This makes sense when you look at the sequence of steps that stringify() goes through for each key it encounters:</p>
<ol>
<li>If the value has a toJSON() method, call it.</li>
<li>If a replacer function has been given, call it.</li>
<li>If the remaining value is a scalar, return it.</li>
<li>If the remaining value is an object, stringify each member, then concatenate keys and values within braces {key:val,key:val}</li>
<li>If the remaining value is an array, stringify each element, then concatenate values within brackets [val,val,val]</li>
</ol>
<p>So, <code>stringify()</code> has already called Prototype&#8217;s <code>toJSON()</code> method by the time it executes the replacer function, but we can use the replacer function to restore the original value, allowing <code>stringify()</code> to then deal with the array by calling itself recursively.</p>
<p>The result is that we can ensure that even if a <code>toJSON()</code> method does exist on the Array object, its output is ignored, and we then get the JSON string that we wanted.</ol>
]]></content:encoded>
			<wfw:commentRss>http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Personal data in cookies</title>
		<link>http://assanka.net/content/tech/2009/09/01/personal-data-in-cookies/</link>
		<comments>http://assanka.net/content/tech/2009/09/01/personal-data-in-cookies/#comments</comments>
		<pubDate>Tue, 01 Sep 2009 16:12:40 +0000</pubDate>
		<dc:creator>Andrew Betts</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[cookies]]></category>
		<category><![CDATA[data protection]]></category>
		<category><![CDATA[privacy]]></category>

		<guid isPermaLink="false">http://assanka.net/content/tech/?p=9</guid>
		<description><![CDATA[Personal data in cookies is bad.  I accepted this for quite some time, until I actually stopped to think about it, and realised how much simpler our scaling would be if we could store a lot more state information in cookies.]]></description>
			<content:encoded><![CDATA[<p>Personal data in cookies is bad.  I accepted this for quite some time, until I actually stopped to think about it, and realised how much simpler our scaling would be if we could store a lot more state information in cookies.</p>
<p>So&#8230; conventional wisdom.  This is bad:</p>
<pre class="brush: plain;">
myCookie=Andrew,andrew@trib.tv,123456,TE51 7NG,4,2009-05-22 17:43:06,138cb381a04dba00340ab450deea934;
</pre>
<p>This imaginary cookie contains my display name, email address, user identifier, postcode, user level, cookie generation time, and security signature.</p>
<p>In my experience the first argument people have against the data I have in my cookie is that &#8216;the user could just change it!  You&#8217;ve got their permissions level in there and everything.  Are you mad!?&#8217;.</p>
<h2>The &#8216;user could just change it&#8217; argument</h2>
<p>Yes, they could.  So in order to solve this problem you only need to make it impossible for a user to write a legitimate cookie value, or edit a cookie such that it remains legitimate.  This is trivially simple to do and validate, much more so than implementing a scalable session store at the server end.</p>
<p>All you do is include, within your cookie, two additional items &#8211; the generation time of the cookie, and a signature string.  The signature string is the result of running the rest of the cookie (excluding the signature), AND a constant value NOT included in the cookie, through a hashing algorithm, such as md5, crc32 or sha1 (use sha1 for preference).</p>
<p>Here&#8217;s an example, using the cookie above.  The constant value not included in the cookie (normally a string of gibberish) will for our purposes be &#8216;password&#8217; (obviously you should use something a lot more random than that).  Sticking all the parameters of the cookie together, and adding the super-secure, super secret constant on the end, we get:</p>
<pre class="brush: plain;">
Andrewandrew@trib.tv123456TE51 7NG42009-05-22 17:43:06password
</pre>
<p>Then we run this through our hashing algorithm of choice, and end up with:</p>
<pre class="brush: plain;">
138cb381a04dba00340ab450deea934
</pre>
<p>Which we pop on the end of the cookie before it is sent to the user in a Set-Cookie header.  When that cookie is then received in a subsequent request (to a different server), the server only needs to know the super-secret constant and recalculate the hash to be able to verify that whoever wrote the cookie ALSO knows the super secret constant.  The user doesn&#8217;t know it, so the user can&#8217;t write a cookie that we&#8217;re going to believe.  No matter how much the user wants to get to user level 10, they&#8217;re just gonna have to earn it.</p>
<p>But, wail the doubters, you can see all the data on the wire in plain text!</p>
<h2>The &#8216;you can see it on the wire&#8217; argument</h2>
<p>When you put together the web page your user has requested &#8211; your shopping cart or social network or Tom Jones fan page or whatever &#8211; you are probably going to print a nice friendly message in the top right of the page that says &#8216;Hello Andrew!&#8217; with links to exciting places such as &#8216;My Account&#8217;, &#8216;Preferences&#8217; and &#8216;Log out&#8217;.  When the user goes to the my account page, you&#8217;re going to include loads more details as well, like their email address, phone number, postcode, and so on.  And of course because your app has a ton of JavaScript making life easy for the user, you&#8217;ve probably got their email address in a JavaScript variable somewhere so you can prepopulate &#8216;email a friend&#8217; dialogue boxes and overlays.</p>
<p>So all the content that you object to including in a cookie, you are including in the HTML responses that you send back to the user.  If it&#8217;s OK in the response, why not allow it in the request?</p>
<p>There is a  possible sub argument here: using DNS cache poisoning an attacker could intercept a request more easily than they could intercept a response.  But if your site is at risk from attempts at DNS poisoning (which is a lot more difficult than it used to be), then you probably have bigger worries than just leaking some personal data.</p>
<h2>Conclusion</h2>
<p>In many cases, storing personal data in cookies is a great idea, and is not a privacy issue.  If you&#8217;re storing something that you&#8217;re not intending to use in JavaScript, and want to make it less readable (though not actually secure) you could easily obfuscate it, or make it actually secure using a cheap-to-decrypt symmetric encryption with a key known to all your servers, which still avoids the need to store the data in a server-side session store.</p>
]]></content:encoded>
			<wfw:commentRss>http://assanka.net/content/tech/2009/09/01/personal-data-in-cookies/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Auto growing textareas</title>
		<link>http://assanka.net/content/tech/2009/05/04/auto-growing-textareas/</link>
		<comments>http://assanka.net/content/tech/2009/05/04/auto-growing-textareas/#comments</comments>
		<pubDate>Mon, 04 May 2009 16:24:52 +0000</pubDate>
		<dc:creator>Andrew Betts</dc:creator>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[User experience]]></category>
		<category><![CDATA[User input]]></category>

		<guid isPermaLink="false">http://assanka.net/content/tech/?p=3</guid>
		<description><![CDATA[This feels like a topic that&#8217;s been explored to death already, but I really don&#8217;t like most implementations of this technique, so here&#8217;s how we do it.
First, in case anyone has just arrived from Mars, or even more unlikely, isn&#8217;t familiar with Facebook, the auto-growing textarea is a text box that gets bigger as you [...]]]></description>
			<content:encoded><![CDATA[<p>This feels like a topic that&#8217;s been explored to death already, but I really don&#8217;t like most implementations of this technique, so here&#8217;s how we do it.</p>
<p>First, in case anyone has just arrived from Mars, or even more unlikely, isn&#8217;t familiar with Facebook, the auto-growing textarea is a text box that gets bigger as you type into it, so that you never see a scroll bar (unless you&#8217;re typing war and peace).</p>
<p>There are lots of ways of doing each part of this.  The parts I&#8217;ll look at are (a) what triggers a review of the text box&#8217;s size, (b) how to determine whether a resize is required, and (c) how to perform a resize.</p>
<h2>Triggering a review</h2>
<p>There are four ways this is typically done:</p>
<ol>
<li> On an interval, say every 50ms</li>
<li> On keypress</li>
<li> On change</li>
<li> On keyup</li>
</ol>
<p>I&#8217;m staggered by how many scripts insist on doing this kind of thing on an interval.  Setting up interval timers for no good reason is a shortcut to terrible performance and memory leaks galore.  So we won&#8217;t be doing that.  onkeypress and onchange events are triggered before the box is updated with the latest keypress, so we want to avoid those, as the latest keypress might have been the one to bring us onto a new line.  That leaves onkeyup, which is fired after the box is updated with the new character, and allows us to inspect it and decide whether to increase its size.</p>
<h2>To resize or not to resize</h2>
<p>How to determine whether to resize the box comes in three flavours:</p>
<ol>
<li> Count the number of newline characters in the textarea&#8217;s value, and see if that matches the number of rows the textarea has.</li>
<li> Create a &#8217;shadow&#8217; DIV which is off screen (eg. margin-left: -10000px) and has no height declared, but otherwise has the same style properties as the textarea, fill it with the textarea&#8217;s content, measure the subsequent height of the DIV, then see if that height matches the current height of the textarea.</li>
<li> Check whether scrollHeight (the height of the scollable content of the box) &gt; clientHeight (the height of the box itself).</li>
</ol>
<p>In this case I was suprised at the number of implementations that favoured counting newlines.  This only works if combined with counting the number of characters in the textarea&#8217;s value, AND a monospaced font, AND knowing the number of columns in the textarea.  In short, er, it&#8217;s mad.</p>
<p>Second option, and the one favoured by a lot of the framework plugins due to the ease with which you can create shadow elements in the likes of jQuery, is to create a shadow DIV. This has the advantage of telling you the actual pixel height of the text, even where it is LESS than the height of the textarea box.  Otherwise, you&#8217;re limited to measuring clientHeight and scrollHeight, which are exactly the same if the textarea isn&#8217;t scrolling, regardless of how much space you&#8217;ve got to spare at the bottom.  My issue with this method is that it basically requires use of a framework to not be painful, and even then it&#8217;s non-trivial amounts of code, and adds needless pollution to the DOM.</p>
<p>So that leaves relying on scrollHeight and clientHeight.  These are well supported and efficient to read, so provided that you work around the issue of scrollHeight always being at least equal to clientHeight, this offers a very lightweight solution.  The features that you can achieve with a shadow DIV that you can&#8217;t do by simply reading scrollHeight and clientHeight are (a) you can ensure there is always a blank line at the bottom of your textarea, and (b) you can shrink the textarea if the user deletes text from it.  I&#8217;m personally of the view that neither of these is actually particularly desirable.  There&#8217;s potentially an argument for leaving a blank line at the bottom, but equally the uer might feel like they&#8217;re just being pressured to write more.</p>
<h2>How to resize the box</h2>
<p>OK, so if you&#8217;ve concluded that the user is writing chapter and verse and the textarea is in need of a bit more space, how do we go about doing it?</p>
<ol>
<li> Animate the CSS height property</li>
<li> Add some height via the CSS height property</li>
<li> Add 1 to the rows attribute</li>
</ol>
<p>A fair few of the framework plugins use their framework&#8217;s animation capabilities to animate the grow effect.  I don&#8217;t like this.  Just because you have an animation effect available doesn&#8217;t mean it&#8217;s appropriate to use it, and there are many situations where the end user just doesn&#8217;t want to wait 300ms for the privilege of using a fresh line.</p>
<p>Simply adding height to the CSS property is a fair way of doing it, but unless you do some maths on the user&#8217;s line height (or hard code some magic numbers), you can&#8217;t necessarily guarantee that the resulting height will be a multiple of the textarea&#8217;s line height.</p>
<p>Easiest, most efficient solution: add one to the rows attribute of the textarea.  rows is part of XHTML as well as HTML 4.01, and has universal support going back yonks.</p>
<h2>Pasting</h2>
<p>Watch out for pastes.  If the user pastes in a large quantity of text, they will trigger only one keyup event, but will have added many lines to the textarea.  Make sure that if a resize is required, you trigger another review after the resize is complete.</p>
<h2>Max height: the war and peace scenario</h2>
<p>If the user really does seem to be writing a novel, we probably should call it a day on growing the textarea at some stage, and certainly <strong>before it gets to be taller than the viewport</strong>.  You can check the height against the viewport height, though I typically just restrict it to an arbitrary height, say 30 rows.</p>
<h2>The code</h2>
<p>You&#8217;ll need a textarea element with an ID of <code>mytextarea</code> to make this code sample work, and obviously you can easily modify it to use selectors from your favourite framework rather than the native <code>document.getElementById</code>.</p>
<pre class="brush: jscript;">
document.getElementById('mytextarea').onkeyup = function() {
	var ta = document.getElementById('mytextarea');
	var maxrows = 30;
	var lh = ta.clientHeight / ta.rows;
	while (ta.scrollHeight &amp;gt; ta.clientHeight &amp;amp;&amp;amp; !window.opera &amp;amp;&amp;amp; ta.rows &amp;lt; maxrows) {
		ta.style.overflow = 'hidden';
		ta.rows += 1;
	}
	if (ta.scrollHeight &amp;gt; ta.clientHeight) ta.style.overflow = 'auto';
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://assanka.net/content/tech/2009/05/04/auto-growing-textareas/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
