<?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/"
xmlns:series="http://organizeseries.com/"
> <channel><title>SitePoint &#187; JavaScript</title> <atom:link href="http://www.sitepoint.com/category/javascript/feed/" rel="self" type="application/rss+xml" /><link>http://www.sitepoint.com</link> <description>Learn CSS &#124; HTML5 &#124; JavaScript &#124; Wordpress &#124; Tutorials-Web Development &#124; Reference &#124; Books and More</description> <lastBuildDate>Mon, 13 May 2013 13:12:07 +0000</lastBuildDate> <language>en-US</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.5.1</generator> <item><title>5 Ways to Support High-Density Retina Displays</title><link>http://www.sitepoint.com/support-retina-displays/</link> <comments>http://www.sitepoint.com/support-retina-displays/#comments</comments> <pubDate>Mon, 06 May 2013 17:22:46 +0000</pubDate> <dc:creator>Craig Buckler</dc:creator> <category><![CDATA[Apple]]></category> <category><![CDATA[Browsers]]></category> <category><![CDATA[CSS]]></category> <category><![CDATA[CSS3]]></category> <category><![CDATA[HTML]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Responsive Web Design]]></category> <category><![CDATA[UX]]></category> <category><![CDATA[display]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <category><![CDATA[images]]></category> <category><![CDATA[javascript]]></category> <category><![CDATA[Retina]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65653</guid> <description><![CDATA[Are the images on your website looking a little ugly on your new iPad or MacBook Pro? Craig discusses Retina high-density pixel displays and offers a number of pragmatic solutions.]]></description> <content:encoded><![CDATA[<p></p><p>An interesting point was raised by Brendan Davis in my recent post <a
href="/rwd-scrollbars-is-chrome-better/">&#8220;Responsive Web Design and Scrollbars: Is Chrome’s Implementation Better?&#8221;</a>: <em>are RWD breakpoints affected by high pixel-density screens?</em></p><p>The short answer is: no &#8212; but we need to delve a little deeper and look at the problems they can cause.</p><h2>What is Retina?</h2><p>&#8220;Retina&#8221; is Apple&#8217;s brand name for double-density screens but other manufacturers are creating similar displays. The technology is used in recent iPhones, iPads, MacBook Pros and other high-end devices.</p><p>For example, the MacBook Pro 15&#8243; has a resolution of 2,880&#215;1,800 or 220 pixels per inch. At this scale, most people are unable to notice individual pixels at typical viewing distances &#8212; applications and websites would be too small to use.</p><p>Therefore, the device reverts to a standard resolution of 1,440&#215;900 but the additional pixels can be used to make fonts and graphics appear smoother.</p><h2>What&#8217;s the Problem?</h2><p>Standard-resolution bitmap images can look blocky on a Retina display. A 400 x 300 photograph is scaled to 800 x 600 pixels but there&#8217;s no additional detail. This can be noticeable when compared to smooth fonts and other high-resolution images.<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><h2>Real-World Usage</h2><p>If you look around the web, you&#8217;d be forgiven for thinking everyone has a Retina display. Currently, it&#8217;s only available in high-end devices, but these are coveted by developers so it leads to a disproportionate volume of online discussion. In the real world, the percentage of people using similar displays is in low single figures.</p><p>Let&#8217;s put it into context: if you&#8217;re not developing for the 1% of IE6/7 users, you probably shouldn&#8217;t be too concerned about people using Rentina &#8212; especially since they can still view your website.</p><p>That said, Retina-like screens will eventually migrate to all devices. There&#8217;s little reason to fret now, but there&#8217;s no harm in some forward planning. Let&#8217;s look at the options in order of recommendation&hellip;</p><h2>1. Use SVGs and CSS3 Effects</h2><p>The clue is in the name but Scalable Vector Graphics are &hellip; <em>scalable!</em> It doesn&#8217;t matter how big an SVG becomes &#8212; it will always be smooth because it&#8217;s defined using vectors (lines and shapes) rather than individual pixels.</p><p>SVG is not practical for photographs but is ideal for logos, diagrams and charts. The primary drawback is a lack of support in IE8 and below but you could always provide a PNG fallback or use a shim such as <a
href="http://raphaeljs.com/">Rapha&euml;l</a> or <a
href="http://code.google.com/p/svgweb/">svgweb</a>. See also: <a
href="http://www.sitepoint.com/add-svg-to-web-page/">How to Add Scalable Vector Graphics to Your Web Page</a>.</p><p>You may also be able to replace some images entirely. For example, titles, gradients, corners or shadows defined as graphics can be reproduced using CSS3 alone. They will render at a better quality, result in fewer HTTP requests and use less bandwidth.</p><h2>2. Use Webfonts Icons</h2><p>The more I use <a
href="http://www.sitepoint.com/webfont-icons/">webfonts icons</a>, the more I love them. Like SVGs, fonts are vectors so they&#8217;re scalable so you can use font sets which contain icons. They&#8217;re ideal for small, frequently used shapes such as email envelopes, telephones, widget controls and social media logos. They also work in every browser including IE6+.</p><p>There are plenty of commercial and free icon font sets available:</p><ul><li><a
href="http://typicons.com/">Typicons</a></li><li><a
href="http://fortawesome.github.io/Font-Awesome/">Font Awesome</a></li><li><a
href="http://somerandomdude.com/work/iconic/">Iconic</a></li><li><a
href="http://www.zurb.com/playground/foundation-icons">Foundation</a></li></ul><p>Or you can use a hosted font service such as <a
href="http://weloveiconfonts.com/">We Love Icon Fonts</a>.</p><p>I recommend creating your own small set of custom icons using online tools such as <a
href="http://fontello.com/">Fontello</a> or <a
href="http://www.sitepoint.com/icomoon-webfont-icon-packs/">IcoMoon</a>.</p><h2>3. Use High-Resolution Images When Practical</h2><p>Retina has four times more pixels than standard screens. If you have a 400 x 300 image (120,000 pixels), you&#8217;d need to use an 800 x 600 alternative (480,000 pixels) to render it well on a high-density display.</p><p>However, the high-resolution file size may not necessarily be four times larger. Every image is different but if it contains solid blocks of color or details which can be omitted, it may be practical to use a 800 x 600 image and scale it in the browser.</p><p>Be pragmatic: if the standard image is 200Kb and the high-resolution version is 250Kb, there is negligible benefit using image replacement techniques. Use the better version throughout.</p><h2>4. Use CSS Image Replacement</h2><p>There will be times when high-resolution versions of your image are four times larger &#8212; or more. In those circumstances you may want to consider image replacement techniques, i.e. the standard image is replaced by larger alternative on Retina displays. The following media query code could be used:</p><pre><code>#myimage {
	width: 400px;
	height: 300px;
	background: url(lo-res.jpg) 0 0 no-repeat;
}
@media
screen and (-webkit-min-device-pixel-ratio: 1.5),
screen and (-moz-min-device-pixel-ratio: 1.5),
screen and (min-device-pixel-ratio: 1.5) {
	#myimage {
		background-image: url(hi-res.jpg);
	}
}
</code></pre><p>The drawbacks:</p><ol><li>You will need to create and maintain two sets of images.</li><li>Some browsers will download both images.</li></ol><p>Remember that many of these users will be using smartphones or tablets on slower mobile networks. Detecting the connection speed would be more beneficial than determining the pixel density.</p><h2>5. Use JavaScript Image Replacement</h2><p>Retina display detection can be implemented using the following code:</p><pre><code>var isRetina = (
	window.devicePixelRatio &gt; 1 ||
	(window.matchMedia &amp;&amp; window.matchMedia(&quot;(-webkit-min-device-pixel-ratio: 1.5),(-moz-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5)&quot;).matches)
);
</code></pre><p>Once a Retina display is determined, you could:</p><ol><li>Loop through all page images and extract the URL.</li><li>Append &#8216;@2x&#8217; to the file name and attempt to load the resulting image URL using Ajax.</li><li>If found, replace the current image with the high-resolution alternative.</li></ol><p>Fortunately, the hard work&#8217;s been done for you at <a
href="http://retinajs.com/">retinajs.com</a>. While it only adds 4Kb weight, high-density display devices will download images twice &#8212; although the second time will occur as a background process after the page has loaded.</p><p>My advice: be practical and keep it simple. Don&#8217;t spend inordinate amounts of time attempting to solve minor rendering problems on devices with proportionally few users. Of course, none of that matters when your boss receives his new iPad and starts to complain about image quality&hellip;</p><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/support-retina-displays/feed/</wfw:commentRss> <slash:comments>6</slash:comments> </item> <item><title>Designing Experiences for Responsive Web Sites</title><link>http://www.sitepoint.com/designing-experiences-for-responsive-web-sites/</link> <comments>http://www.sitepoint.com/designing-experiences-for-responsive-web-sites/#comments</comments> <pubDate>Mon, 06 May 2013 09:13:17 +0000</pubDate> <dc:creator>Rahul Lalmalani</dc:creator> <category><![CDATA[CSS3]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Responsive Web Design]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=66021</guid> <description><![CDATA[When considering how to implement responsive web design techniques, Rahul Lalmalani suggests you take into account how a device might change user expectations of a site’s functionality.]]></description> <content:encoded><![CDATA[<p></p><h2>Before You Get Started</h2><p>Responsive Web design is intended to ensure that a site’s layout and content scale fluidly to the available screen real estate. This is a great approach for focusing your investments on improving site content and user functionality while ensuring that users have a good experience regardless of what device and screen size they use to visit your site. If you didn’t read the first article in this series, “<a
href="http://www.sitepoint.com/why-the-web-is-ready-for-responsive-web-design/" target="_blank">Why the Web Is Ready for Responsive Web Design</a>,” be sure to read it first.</p><p>It’s worth taking a step back, however, to think through your site’s experience and understand whether the device with which a user accesses your site <em>changes the user’s expectations of the site’s functionality</em><i>.</i> Is the user checking your site for quick updates with her cellphone while she’s on the go? Is he sitting down, 10 feet away from a large TV screen, looking to immerse himself in a relatively passive consumption experience of rich content, videos and games? Are other users sitting down at their PCs, looking to get the most from your site content? Most of all, how do these expectations affect the site layout and functionality that you provide at those corresponding screen sizes?<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><h2>What Kind of Site Is This?</h2><p>Planning the content hierarchy for your site across different form factors is definitely the first step to having a great responsive-site experience. Consider the following examples, which evaluate and compare the top experiences that customers want to have when they access your site from a 4-inch phone while they walk or take public transportation, when they’re sitting at their computer desk, and when they’re lounging on their couches in their living rooms.</p><h3>News Site (Content Consumption)</h3><p>People visit ContosoNews.com primarily to do one thing—catch up on the day’s current affairs. When you see how this site is presented on a PC screen, it’s designed to have a layout like a newspaper. More important, the single home page is expected to attract and retain different kinds of readers, with interests in current affairs, business, sports, entertainment and other topics, and show them that ContosoNews has content that will interest them. The home page has a rich layout with slide shows, cycling of recommended articles, various categories of news available below the fold if you scroll down, recommended editorials and even the weather. <b>Figure 1</b> shows a schematic illustration of the site at different resolutions.<br
/> <img
class="alignnone size-full wp-image-66029" alt="Comparing Layouts for ContosoNews.com" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/05/figure13.png" width="720" height="480" /></p><p><b>Figure 1. Comparing Layouts for ContosoNews.com</b></p><p>If you visit this site on your mobile phone browser, you see a subset of the content, with menu and link navigation to the remaining content. The content that was available on the PC has been prioritized, and the top headline has been given focus above the fold. The slide show of recommended articles is replaced by a series of blurbs with links. The top articles from the Other Categories section are gone, replaced with a single category picker that navigates away from the home page.</p><p>In this way, users visiting the site on a phone can, in a cursory glance, become aware of the content available for consumption and dig deeper at their convenience.</p><h3>Local Attraction (Hyper-Local Site)</h3><p>Contoso Station is a hip new restaurant in Seattle. When people visit the restaurant’s site on their PC or TV screen, the restaurant proudly shows its latest Yelp reviews, news articles and tweets from users who add the hashtag #i&amp;lt3contoso.</p><p>However, when you visit the site on a smartphone, the company makes a fair assumption that you’re visiting its site on the go with hopes to find its location, hours of operation and phone number. The phone might even request your location and show you a map with the quickest route to the restaurant. Some of the remaining content can be presented with much less detail—for example, the Yelp reviews are boiled down to one-line snippets—and the rest of the content (the Twitter feed, for example) can be hidden altogether for users visiting the site on their phones. <b>Figure 2</b> shows an example of this scenario.</p><p><img
class="alignnone size-full wp-image-66030" alt="Comparing Layouts for Contoso Station" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/05/figure21.png" width="720" height="480" /><br
/> <b>Figure 2. Comparing Layouts for Contoso Station</b></p><p>As seen in <b>Figure 2,</b> local businesses should prioritize and show an entirely different set of content to phone users and make their mobile experiences more sensitive to location.</p><h3>Media Site (Rich Audiovisual Content)</h3><p>ContosoTube is a popular Internet service where people share all kinds of videos. Users can see the latest top-rated and most frequently watched content. As they sign-in and explore the site, they can create and edit playlists of videos, get personalized recommendations, subscribe to other users’ playlists and even send each other messages.</p><p>The experience of ContosoTube on a phone is geared toward showing videos that a user has opened from other apps (instant messages, email, Twitter and so on), searching to view a video, and letting logged-in users access their existing subscriptions and playlists. Their experience is very limited for content curation.</p><p>What’s interesting about ContosoTube is that the Xbox site experience is similar to the phone experience from a user-functionality perspective, although the Xbox site is laid out differently based on screen real estate because even when ContosoTube users visit the site on their large screens, they are probably accessing it from their living room and doing so with controls less precise than a mouse. While the screen size of the TV might tempt developers to provide a more PC-like experience in terms of available functionality, it would be highly likely that users accessing ContosoTube on their TVs would focus primarily on watching content and not on creating it, managing it and messaging with others. <b>Figure 3</b> compares site layouts for ContosoTube.</p><p><img
class="alignnone size-full wp-image-66033" alt="ContosoTube on a PC, TV and Smartphone." src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/05/figure31.png" width="720" height="297" /><br
/> <b>Figure 3. ContosoTube on a PC, TV and Smartphone.</b></p><h3>HTML5 Games</h3><p>On <a
href="http://buildnewgames.com/a-study-in-adaptive-game-design/" target="_blank">Build New Games</a>, a website that explores HTML technologies for creating immersive gaming experiences on the browser, Jack Lawson provides a great discussion about what a gaming experience might be like for a responsively designed Web site.</p><p>A game is a great example of site design where users expect entirely different experiences based on the context in which they visit the site. For example, if a user visits the site WorldOfContosoCraft.com from his PC, he probably expects a full-fledged gaming experience—he can play the game himself, interact and communicate socially with other players through the in-game chat feature, make customizations and settings to his avatar and even participate in the in-game marketplace to buy upgrades, armor and other goodies.</p><p>On the console, this user might expect a similarly feature-rich experience, but he would also have expectations about being able to use his controller to drive the experience instead of the mouse and keyboard. (There are currently libraries for Chrome and Firefox—although with limited cross-browser support, as Nikhil Suresh points out in his discussion of <a
href="http://buildnewgames.com/console-experience-on-the-web/">controller support in JavaScript libraries</a>.)</p><p>On the phone itself, the user might be looking to perform simpler actions, such as checking up on his inventory and gamer stats, performing some customizations on the avatar and maybe buying some add-ons from the in-game marketplace. Game developers, who can provide such a contextually relevant experience to users who visit their site from their cellphone for a few minutes, can keep their users engaged in the overall experience even when they can’t play the game.</p><h2>Considerations for UI Design (aka Fat Fingers)</h2><p>In addition to information design, you need to think about modes of user input. Today, first and foremost, this means that your site UI be touch-friendly. Visitors are not using touch for your Web site only on phones and tablets; they also use touch-screen-based PCs. Moreover, when you think about users on the Xbox, they’re interacting with the UI elements of your Web page by using a joystick, which is not as precise as a mouse.</p><p>Ideally, you <em>do not</em> want to design and code your user interface elements (buttons, links, form controls and so on) differently for touch (tablets and phones) than for PCs with traditional mouse-keyboard elements. In fact, Windows 8 makes this distinction nonexistent, with users able to run Microsoft Surface with a USB mouse as well as desktops with touch-screens. Moving forward, it’s reasonable to assume that more traditional PCs will be equipped with touch-screen functionality.</p><p>That’s why the best approach is to design a one-size-fits-all interface for user inputs that is comfortable for touch users to access. Mouse and keyboard users can still interact with these pages just fine.</p><p>To highlight some paradigm shifts in this approach, let’s take the example of one of the most common forms of navigation, the drop-down menu, on my favorite local radio station, Contoso Music. (See <b>Figure 4.</b>) This is just one example of a solution to links and navigation menus for touch, but it illustrates the most important considerations we need to take.</p><p><img
class="alignnone size-full wp-image-66034" alt="The Drop-Down Navigation Menu for Contoso Music" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/05/figure4.png" width="720" height="305" /></p><p><b>Figure 4. The Drop-Down Navigation Menu for Contoso Music</b></p><p>This navigation menu has a couple of issues that go beyond responsive layout, but they are still an integral part of building a unified site experience that scales across multiple devices.</p><ol
start="1"><li>First, a lot of sites use navigation menus on which links are revealed when a user mouses over the menu titles. This is absolutely unacceptable because a mouse-over does not translate well to touch browsers. In fact, touch-input aside, you shouldn’t rely on a mouse-over to reveal any useful information at all because it is not keyboard accessible and goes against <a
href="http://www.w3.org/TR/WCAG/">W3C accessibility guidelines</a>.</li><li>Second, look at the relative sizes of the links Playlists and DJs. These two pieces of information are supposed to be at the same level in the hierarchy. However, the size of the link is determined by the size of the text. This makes the DJs link less prominent, and also harder to precisely tap on a touch-screen. The DJs link could be as small as 20 px by 40 px, which is not accessible.</li><li>Another subtle problem, which you can see by glancing at the menu list items, is that only the text items themselves are hyperlinks. Here again, the touch user would be better served if the target for the link Foo was the entire width of the flyout menu instead of just the text width.</li></ol><p>Moreover, users on all-in-one devices might utilize the same machine in different device configurations, in which case they might access your site with a mouse at one point and then revisit it later by using touch. It’s beneficial to provide the user with touch-friendly, well-spaced hyperlinks and navigation.</p><p>A common example of touch-friendly navigation that lots of sites use for their menus, especially on mobile apps or in a sidebar for tablets, is shown in <b>Figure 5.</b></p><p><img
class="alignnone size-full wp-image-66035" alt="A Touch-Friendly Redesign of the Contoso Music Navigation Menu" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/05/figure5.png" width="400" height="376" /></p><p><b>Figure 5. A Touch-Friendly Redesign of the Contoso Music Navigation Menu </b></p><p>The navigation menu utilizes touch, mouse or keyboard to expand and collapse the accordion-style submenus. All the links are the same width (even the submenu items), and for each link, the entire rectangle is clickable, not just the text.</p><p>A good example of a site that has made this transformation is MSN.com. The old MSN.com (shown in <b>Figure 6</b>) sports a significantly higher content density, with lots of text links (with smaller clickable areas) that are tightly packed (creating room for error when using touch and gaming joysticks), as well as a mouse-over to reveal the subcategories of news (see the menu under Entertainment).</p><p><img
class="alignnone size-full wp-image-66036" alt="The Old MSN.com" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/05/figure6.png" width="720" height="516" /></p><p><b>Figure 6. The Old MSN.com</b></p><p><b>Figure 7</b> shows the new touch-friendly version of MSN.com. While currently offered only on Windows 8, the touch-friendly UI will be rolled out across the board for all browsers after testing. Notice the more spacious layout and larger hit targets.</p><p><img
class="alignnone size-full wp-image-66037" alt="The New Look for MSN.com" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/05/figure7.png" width="720" height="517" /></p><p><b>Figure 7. The New Look for MSN.com</b></p><h2>One <em>Site</em> Fits All</h2><p>Responsive Web design should not only be about resizing the same content gracefully based on user screen sizes. To best connect with your users across multiple screens, your site should not only be aware of the device’s physical characteristics (such as screen size) but also infer the user’s physical circumstances, modes of input and the kind of information she is seeking.</p><p>In the next article in this series, I’ll cover some implementation techniques for responsive design.</p><div><p><em>This article is part of the HTML5 tech series from the Internet Explorer team. Try-out the concepts in this article with three months of free BrowserStack cross-browser testing @ <a
href="http://modern.ie" target="_blank">http://modern.IE</a>.</em></p></div><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/designing-experiences-for-responsive-web-sites/feed/</wfw:commentRss> <slash:comments>5</slash:comments> <series:name><![CDATA[Responsive Web Design]]></series:name> </item> <item><title>Unheap.com: a Better jQuery Plugin Repository?</title><link>http://www.sitepoint.com/unheap-jquery-plugin-repository/</link> <comments>http://www.sitepoint.com/unheap-jquery-plugin-repository/#comments</comments> <pubDate>Fri, 03 May 2013 16:36:48 +0000</pubDate> <dc:creator>Craig Buckler</dc:creator> <category><![CDATA[JavaScript]]></category> <category><![CDATA[jQuery]]></category> <category><![CDATA[Open source]]></category> <category><![CDATA[Tools and Libraries]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <category><![CDATA[javascript]]></category> <category><![CDATA[plugins]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65656</guid> <description><![CDATA[Do you need a jQuery plugin? Are you finding it difficult to locate one on the official repository? Craig suggests Unheap.com may be a better alternative...]]></description> <content:encoded><![CDATA[<p></p><p>I&#8217;ve never been <a
href="http://www.sitepoint.com/jquery-plugins-registry/">particularly complimentary</a> about the <a
href="http://plugins.jquery.com/">official jQuery Plugin Registry</a>. The team are working on it, but perhaps the kindest observation is that it&#8217;s <em>&#8220;ctional&#8221;</em> &#8212; functional with all the &#8220;fun&#8221; removed. The system lacks an easy interface, good search facilities, online demonstrations, developer ratings and reviews.</p><p>As an alternative, why not try <a
href="http://www.unheap.com/"><strong>unheap.com</strong></a> &#8212; a new independent jQuery plugin repository&hellip;</p><p><a
href="http://www.unheap.com/"><img
src="http://blogs.sitepointstatic.com/images/tech/821-unheap-jquery-plugins.jpg" alt="Unheap.com" class="center" /></a></p><p>The benefits of <a
href="http://www.unheap.com/"><strong>unheap.com</strong></a> become apparent the moment you use it:</p><ul><li>The site has a gorgeous dynamic and responsive interface &#8212; it&#8217;s a pleasure to use</li><li>Plugins are categorized in sections and sub-sections for interface, inputs, media, navigation and miscellaneous code.</li><li>The search facility works well.</li><li>Plugin lists have direct links to demonstration pages and videos.</li><li>Related plugins are listed.</li><li>User views, votes and bug reports are recorded.</li><li>Social media sharing facilities are provided.</li><li>An RSS feed of new and updated plugins is available for you to check in <a
href="http://www.sitepoint.com/goodbye-google-reader/"><s>Google Reader</s></a>, erm, a compatible RSS Reader.</li><li>The <a
href="http://www.unheap.com/submit">plugin submission process</a> is easy and straight-forward.</li><li>Almost 700 plugins are available at the time of writing.</li></ul><p>I&#8217;m impressed. Unless the jQuery team can produce a system as nice as <a
href="http://www.unheap.com/">unheap.com</a>, perhaps they should give up now and adopt it as the &#8220;official&#8221; repository?</p><p>Unless you&#8217;ve found something better?&hellip;</p><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/unheap-jquery-plugin-repository/feed/</wfw:commentRss> <slash:comments>6</slash:comments> </item> <item><title>Why the Web Is Ready for Responsive Web Design</title><link>http://www.sitepoint.com/why-the-web-is-ready-for-responsive-web-design/</link> <comments>http://www.sitepoint.com/why-the-web-is-ready-for-responsive-web-design/#comments</comments> <pubDate>Thu, 02 May 2013 00:28:02 +0000</pubDate> <dc:creator>Rahul Lalmalani</dc:creator> <category><![CDATA[CSS3]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Responsive Web Design]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65981</guid> <description><![CDATA[Former Microsoft engineer Rahul Lalmalani puts the case for implementing responsive web design now, dismissing a few myths and misapprehensions along the way.]]></description> <content:encoded><![CDATA[<p></p><h2>The Mobile Playing Field</h2><p>Today, a large portion of site traffic comes from mobile devices—namely smart phones and tablets—in addition to traditional PCs. Across the globe, mobile devices now account for <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl01" href="http://www.businessinsider.com/state-of-internet-slides-2012-10?op=1">12 percent of Internet traffic</a>, and this is scaling up faster than desktop Internet traffic. The fraction of mobile Web traffic is sufficiently higher in nations with high smartphone penetration (for example, <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl02" href="http://www.phonearena.com/news/Mobile-Browsing-accounts-for-20-of-North-American-web-traffic-according-to-new-report_id30602">20 percent of US-based Web traffic</a> is via mobile browsing). What’s more, this figure is expected to grow significantly over the next 10 years, as smartphones evolve and mature in terms of hardware and software and adoption picks up in South America, Asia and Africa.</p><p>Site owners have begun to take advantage of this trend over the past several years and have primarily relied on native mobile apps for top sites such as <em>Facebook, </em><em>Hulu</em> and t<em>he New York Times.</em> Moreover, up-and-coming Web services such as <em>Pulse, </em><em>Flipboard and others</em> have even taken to a mobile-first approach, by building apps for iOS and other ecosystems before building a Web site experience. Native apps allow developers to create unique phone-first, touch-optimized experiences for users to interact with their content to take advantage of features like camera integration, geo-location and offline data storage.<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><p>Targeting users on mobile natively makes good sense, especially within the US, where more than <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl03" href="http://www.engadget.com/2012/05/07/nielsen-smartphone-share-march-2012/">50 percent of mobile users own a smartphone</a>. While mobile apps offer site owners a way to connect with users on new form factors, new ways to monetize across platforms and new mobile-scenario-centric experiences to empower and delight their users, they offer an incomplete opportunity for developers compared to the ubiquity and reach of the Web. There are a couple of problems that affect a native mobile-only approach.</p><h2>Problem 1: Cost of Supporting Multiple Platforms</h2><p>Creating similar content and experiences across multiple platforms is costly and requires site owners to choose platforms for which to optimize. Additionally, this translates to a limited Web site experience for users who seek out your content from other platforms, especially when you need to prioritize your development investments.</p><p>Adopting a <em>responsively designed Web site</em> can help address the investment costs and ensure that your users across all the latest mobile operating systems are enjoying a consistently useful experience.</p><p>Scott Scazafavo, former vice president of product management at <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl04" href="http://allrecipes.com/">Allrecipes.com</a>, whose responsibilities included mobile product development, puts it this way:</p><blockquote><p><em>&#8220;To do a decent job developing a native mobile application that can compete with “best in class” offerings that are powered by live data or content (like we have at MSN and also at my previous employer, Allrecipes.com), it typically takes a minimum initial investment of about $250k to define, design and engineer that native application, and then an annual maintenance investment for that native app of $75K to $100K per platform to keep it evolving, feature wise, to keep consumers interested and adoption numbers healthy. That is over and above any internal work needed for design or engineering to create and maintain the services (APIs) that power those products.</em></p><p><em>The approach we have taken here at MSN for our TMX product (the new touch-first version of MSN.com currently available on IE10) with HTML5, as well as thin-shell apps to help deliver that product into app marketplaces, in addition to mobile browsers, comes only with a small incremental initial investment to what we do with internal resources to create that app product. [That figure is] probably a $25K to $50K initial investment per platform for each app, and a negligible maintenance cost thereafter to maintain those apps.&#8221;</em></p></blockquote><p>Similarly, by using responsive Web design techniques, <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl05" href="http://clipboard.com/">Clipboard.com</a> was able to target many mobile, small device browsers like Internet Explorer 10 on Windows 8 and Safari on iPhone/iPad at <em>half</em> its expected costs to develop when they began the project.</p><h2>Problem 2: Fragmented Ecosystems</h2><p>Even within a given platform, a plethora of device geometries and sizes—as well as supported platform versions—exist. This requires site owners to not only design for near-similar display sizes and resolutions, but also to submit to multiple app stores (Kindle store, Google Play and Nook store, all on the Android platform). Managing multiple assets within the same platform adds complexity to the support matrix. Fix a layout bug in your native app for the Nexus 7, and you might have to fix it again for the Kindle Fire app. This means all your users are not on the same app version, with the same feature set and the same bug fixes.</p><p>Similarly (even within the iOS app ecosystem), top apps like ESPN, Spotify, Angry Birds Space and the App Store itself <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl06" href="http://www.maclife.com/article/gallery/7_big_apps_still_need_iphone_5_optimization#slide-3">did not correctly occupy the full screen</a> when the iPhone 5 was released, instead just showing users a black bar at the top and bottom of the app. The addition of iPhone 5 required developers to ship app updates to address this simple layout bug.</p><p>We’re also still at a stage where vendors are <em>experimenting with new form factors</em>, such as the big screen. For example, more than 25 million Xbox Live users now have access to Internet Explorer 10 on their living room TV screens and are interacting with it not just through a pointer but also through more human-centric mechanisms such as Kinect and Xbox SmartGlass. Today’s technical decision makers are facing a fragmented and very volatile landscape of devices that their users have integrated into their daily routines.</p><h2>A Unifying Approach: Responsive Web Design</h2><p>Responsive Web design aims to provide an optimal viewing/consumption experience—<em>easy reading and navigation with minimum resizing, panning, and scrolling</em> —across the gamut of devices that exist in the market, <em>as well as to future-proof your site for those that are yet to come</em>. There already exist different Web tutorials regarding individual techniques that help a site become more responsive. This series aims not only to provide a unified approach to responsive Web design, it aims to impress upon decision makers and developers the immediate need for adopting responsiveness as part of their reach strategy. According to a crawl of the top 5,000 Web sites by <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl07" href="http://modern.ie/">modern.IE</a>, only about 14 percent of sites have some form of responsive design. It’s not difficult to see why developers think it’s a daunting task to consider.</p><p>Take a look at <strong>Figure 1.</strong> You can see the relative screen resolutions for the Web browsers on popular smartphones and tablets (the devices are identified in <strong>Table 1</strong>). The device resolutions, as well as ratio of CSS pixels to hardware pixels (a concept we’ll explain in part 3), are taken from <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl08" href="http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density">Wikipedia</a>. (Each square corresponds to 100 x 100 px of Web content, laid out at 1x optical zoom.)</p><p><img
class="alignnone size-full wp-image-65982" alt="Sampling of Resolutions for Several Current Devices" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/05/figure11.png" width="700" height="270" /><br
/> <strong>Figure 1. Sampling of Resolutions for Several Current Devices</strong></p><p><strong>Table 1. Key to Figure 1</strong></p><table><tbody><tr><td>A</td><td>iPhone 4</td></tr><tr><td>B</td><td>iPhone 5</td></tr><tr><td>C</td><td>Samsung Galaxy S3</td></tr><tr><td>D</td><td>Nokia Lumia 920</td></tr><tr><td>E</td><td>HTC 8X</td></tr><tr><td>1</td><td>Kindle Fire, Nook Color</td></tr><tr><td>2</td><td>Kindle Fire HD</td></tr><tr><td>3</td><td>LG Nexus 7</td></tr><tr><td>4</td><td>Kindle Fire HD 8.9</td></tr><tr><td>5</td><td>iPad and iPad Mini (different hardware resolutions but same number of CSS pixels, more on this in Part 2)</td></tr><tr><td>6</td><td>Microsoft Surface</td></tr></tbody></table><p>&nbsp;<br
/> &nbsp;<br
/> So is cross-browser, cross-device code the solution?</p><p>Traditionally, OS-specific apps have been able to provide more sophisticated user engagement because they have access to valuable user information, such as geo-location, offline storage and even custom font support for customized interfaces.</p><p>However, modern browsers such as Internet Explorer 10, Google Chrome (version 22), Safari 6 and Firefox (version 17) now offer the lion’s share of these experiences as part of their support for HTML5 and CSS3. HTML5 is not your grandpa’s HTML, which was originally designed to let people encode and deliver pieces of textual information across the Internet. HTML5 is intended for developers to write rich Web-based apps for the twenty-first century. Between HTML5 and CSS3, you get access to once-native features such as <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl10" href="http://mediaqueri.es/">media queries</a>, <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl11" href="http://html5demos.com/geo">geo-location</a>, <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl12" href="http://ie.microsoft.com/testdrive/Graphics/OpenType/">custom font support</a>, <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl13" href="http://ietestdrive2.com/OfflineSocialAlbums/Default.html">offline storage</a> and even <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl14" href="http://www.html5rocks.com/en/mobile/touch/">touch events</a>! This way, your sites can have a different look and layout on hardware of different dimensions, provide users with location-aware services and even provide a valuable experience when the user is disconnected from the Internet.</p><h2>HTML5 Myths</h2><p>There are some common HTML5 myths out there. These include:</p><p><strong>I can’t monetize HTML5.</strong></p><p>HTML5 sites have arguably more monetization opportunities than their app equivalents. App monetization today includes app purchases (although most apps in the iOS apps store are in the free to $0.99 range). This is probably the only way in which HTML5 site experiences can’t be directly monetized. Otherwise, developers have a lot of control over advertising and in-app or in-site purchases. More importantly, a lot of apps tend to limit the amount of navigation a user can do. For example, most reader and newspaper/magazine apps offer textual content and don’t provide the “linky-ness” of the Web, which allows users to navigate to related content while consuming the current Web page.</p><p>The Web site experience, when responsively implemented, retains the “linky” nature of the Web and can lead to a higher number of user impressions.</p><p><strong>HTML5 cannot be offline.</strong></p><p>HTML5 has a couple of different solutions for ensuring that users have a great offline experience. First and foremost, Web pages can specify which of their assets should be made available to users when they are disconnected (using <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl15" href="http://www.html5rocks.com/en/tutorials/appcache/beginner/">App Cache</a>). This way, the user can still interact with the page even while offline. Additionally, HTML5 can locally store user information and input using Local Storage, as well as IndexedDB. This data persists even if the user closes the browser and can be synced back to the server at a later point in time when the user relaunches the Web page.</p><p>Check out the demo for this <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl16" href="http://ie.microsoft.com/testdrive/html5/OfflineCalculator/">offline calculator</a>. A user needs to be connected to the Web only the first time he visits it. Subsequently, he can access it offline. Moreover, the user’s calculation and results are stored via Local Storage so he can come back at a later time and continue a calculation.</p><p>The <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl17" href="https://hacks.mozilla.org/2012/11/html5-mythbusting/">Mozilla hacks blog</a> is a great start at busting some common myths about HTML5. It’s important to note here that native apps use APIs that are optimized for device-specific performance. However, HTML5 and CSS3 provide developers with tools to build engaging experiences across a variety of form factors and ensure that you are not missing out on users visiting from other platforms.</p><p><a
id="ctl00_MTContentSelector1_mainContentContainer_ctl18" href="http://caniuse.com/">CanIUse.com</a> is a great resource for understanding the available browser support for specific HTML5 and CSS features.</p><h2>Media Queries and Responsive Design</h2><p>One of the new tools in CSS3 to aid in responsive Web design is called <em>media queries</em>. Media queries allow you to offer your users the same HTML content but enable the browser to detect the size constraints of the device (in pixels) and layout the same content in a different, relevant manner. You can grow or shrink the width of your text and image content, increase or decrease the number of columns in your newspaper-style layout or even hide pieces of information entirely, depending on what you think the right consumption experience is for your user on a given device.</p><p>With a combination of media queries to dictate the layout of your content, as well as browser detection to identify additional constraints of the user experience (for example, if the user is interacting with a site via Xbox 360 on a large TV screen), you can identify your users’ needs and deliver the right experience for the current context in which a user has accessed your content—whether it be to consume it richly on a desktop, interact with it via touch on a slate or quickly skim through it on the go on a phone—and do so gracefully with Web technologies.</p><p>What’s best, most modern mobile devices support HTML5 and CSS3! This way, you can create near-native experiences directly within the browser. Short of DRM support or access to certain device-unique hardware, there’s no limit to the kinds of experiences you can offer through HTML5, CSS3 and JavaScript. Check out <a
id="ctl00_MTContentSelector1_mainContentContainer_ctl19" href="http://atari.com/arcade#%21/arcade/atari-promo" class="broken_link">retro Atari video games</a> to get an idea of the kind of cool experiences you can build purely with standards-compliant Web technologies.</p><p>It should be noted that using media queries alone to build three different fixed-width layouts for your Web site can definitely help you target common screen dimensions today (for example, desktop, tablet and phone). However, <em>this is not truly responsive Web design</em>. It does not provide an optimal experience for users visiting your site with a device that has an intermediate width, nor does it prepare you for the next wave of “it” devices with new geometries and dimensions.</p><h2>Build Once! Deploy Once!</h2><p>If you choose to invest in your site experience, you can design a single HTML5, CSS3 and JavaScript experience that can scale across form factors, from a small smartphone touchscreen to a large cinema display or TV set. We’ll go into the implementation details later in the series, but what’s great to note here is that you never have to choose which of your users you want to delight with that cool new feature, or protect with that high-security patch.</p><p>In addition to simplifying your code base and support matrix, this has the following advantages.</p><h3>Benefit 1: Leave No Users Behind</h3><p>Betting on powerful native apps for the top one or two mobile platforms can mean that some of your users migrate to competitors if they offer a useful Web experience, with more reach, on all platforms.</p><h3>Benefit 2: Unified Ad Story</h3><p>Often, when sites rely on advertising for revenue, they engage with their business partners and sell them advertising piecemeal, based on whether the users are experiencing the full-blown Web version or a limited app version. Also, click-through rates for ads on mobile devices are less than those on desktop PCs, in which case the extra cost of engaging with partners, creating advertising assets for native apps and selling app-specific ad real estate does not justify the additional gains. For example, MSN.com (which has now begun to roll out a unified, media-query-based HTML5 Web site across its international markets) can now unify its ad partnership story across all device types.</p><p>With a single HTML5 experience that responsively scales to different form factors, you can cater to a single ad customer with the same set of ad assets across a variety of devices—in the living room, on the work desk and on the go.</p><h3>Benefit 3: Upgrade Your Site Experience Directly into Your App Experience</h3><p>Occasionally, you might still hit a roadblock where you want to deliver a great mobile experience to your users that takes advantage of their unique hardware—for example, you want users to get new content from your site by shaking their phone. In this case, you need to access the device accelerometer.</p><p>Well, the great news is that you can create a native app by applying a wrapper around your site content and only write the necessary native app code to interact with the additional hardware on the phone. For example, you can host (the responsively scaled down view of) your site content within a WebViewController on an iPhone and just listen for the accelerometer event in your objective-C native code.</p><p>This means that for any fixes/features that you build within the Web layer, you don’t need to go through the trouble of shipping app upgrades!</p><h2>“So, how do I start?”</h2><p>At this point, we have yet to talk about the “how-tos” of responsive Web design. I’ll get to that in the next part of the series, but I hope you’ve had a chance to consider the long-term benefits of a solution for delivering your content to your users that consists of a single code base, written in familiar Web technologies, with ever-growing support of open JavaScript libraries, rich HTML5 device integration and high-quality CSS3 layout and graphic support. If not, you can always look back at the quickly increasing list of devices in <strong>Figure 1</strong>.</p><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/why-the-web-is-ready-for-responsive-web-design/feed/</wfw:commentRss> <slash:comments>8</slash:comments> <series:name><![CDATA[Responsive Web Design]]></series:name> </item> <item><title>Accessible Audio Descriptions for HTML5 Video</title><link>http://www.sitepoint.com/accessible-audio-descriptions-for-html5-video/</link> <comments>http://www.sitepoint.com/accessible-audio-descriptions-for-html5-video/#comments</comments> <pubDate>Mon, 29 Apr 2013 21:32:51 +0000</pubDate> <dc:creator>James Edwards</dc:creator> <category><![CDATA[Accessibility]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[Intermediate]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <category><![CDATA[HTML5 Tutorials & Articles]]></category> <category><![CDATA[video]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65695</guid> <description><![CDATA[James describes a simple but surprisingly effective technique for synchronising multiple media sources in order to add accessible audio descriptions to an existing video.]]></description> <content:encoded><![CDATA[<p></p><p>A client recently asked me to produce an accessible video player, and one of the features she was very keen to have is <strong>audio descriptions</strong>. Audio descriptions are intended for people who are blind or have impaired vision, providing additional spoken information to describe important visual details.</p><p>Traditionally, audio-described videos have to be made specially, with the audio encoded in a separate track of the single video file. It takes pretty specialised video-editing equipment to encode these audio tracks, and that raises the bar for most content producers beyond a practical level.</p><p>All the audio-described content I&#8217;ve seen on the web is like this. For example, <a
href="http://www.bbc.co.uk/iplayer/">BBC iPlayer</a> has a <a
title="Audio Described programs on BBC iPlayer (bbc.co.uk)" href="http://www.bbc.co.uk/iplayer/tv/categories/audiodescribed">selection of such content</a>, but the video player doesn&#8217;t give you control over the relative volumes, and you can&#8217;t turn the audio-descriptions off — you can only watch separate described or non-described versions of the program.</p><h2>Enter HTML5</h2><p>The <a
title="The video element (whatwg.org)" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html">HTML5 video specification</a> does provide an <code><a
title="Media resources with multiple media tracks (whatwg.org)" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#media-resources-with-multiple-media-tracks">audioTracks</a></code> object, which would make it possible to implement an on/off button, and to control the audio and video volumes separately. But its browser support is virtually non-existent — at the time of writing, only <abbr
title="Internet Explorer 10">IE10</abbr> supports this feature.<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><p>In any case, what my client wanted was <strong>audio descriptions in a separate file</strong>, which could be added to a video without needing to create a separate version, and which would be easy to make without specialised software. And of course, it had to work in a decent range of browsers.</p><p>So my next thought was to use a <a
title="Synchronising multiple media elements (whatwg.org)" href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#synchronising-multiple-media-elements">MediaController</a>, which is a feature of <abbr
title="HyperText Markup Language Version 5">HTML5</abbr> audio and video that allows you to synchronise multiple sources. However browser support for this is equally scant — at the time of writing, only Chrome supports this feature.</p><p>But you know — even without that support, it&#8217;s clearly not a problem to <strong>start</strong> two media files at the same time, it&#8217;s just a case of <strong>keeping them in sync</strong>. So can we use existing, widely-implemented features to make that work?</p><h2>Video Events</h2><p>The video <abbr
title="Application Programming Interface">API</abbr> provides a number of events we can hook into, that should make it possible to synchronise audio playback with events from the video:</p><ul><li>The <code>"play"</code> event (which fires when the video is played).</li><li>The <code>"pause"</code> event (which fires when the video is paused).</li><li>The <code>"ended"</code> event (which fires when the video ends).</li><li>The <code>"timeupdate"</code> event (which fires continually while the video is playing).</li></ul><p>It&#8217;s the <code>"timeupdate"</code> event that&#8217;s really crucial. The <em>frequency</em> at which it fires is not specified, and practise it varies considerably — but as a rough, overall average, it amounts to 3–5 times per second, which is enough for our purposes.</p><p>I&#8217;ve seen a similar approach being tried to <a
title="Two videos in sync (html5demos.com)" href="http://html5demos.com/two-videos">synchronise two video files</a>, but it isn&#8217;t particularly successful, because even tiny discrepancies are very obvious. But audio descriptions generally don&#8217;t need to be so precisely in sync — a delay of <code>100ms</code> either way would be acceptable — and playing audio files is far less work for the browser anyway.</p><p>So all we need to do is use the video events we have, to lock the audio and video playback together:</p><ul><li>When the video is played, play the audio.</li><li>When the video is paused, pause the audio.</li><li>When the video ends, pause the video and audio together.</li><li>When the time updates, set the audio time to match the video time, if they&#8217;re different.</li></ul><p>After some experimentation, I discovered that the best results are achieved by comparing the time in whole seconds, like this:</p><pre class="brush: jscript; title: ; notranslate">
if(Math.ceil(audio.currentTime) != Math.ceil(video.currentTime))
{
  audio.currentTime = video.currentTime;
}
</pre><p>This seems counter-intuitive, and initially I had assumed we&#8217;d need as much precision as the data provides, but that doesn&#8217;t seem to be the case. By testing it using a literal audio copy of the video&#8217;s soundtrack (i.e. so the audio and video both produce identical sound), it&#8217;s easy to hear when the synchronisation is good or bad. Experimenting on that basis, I got much better synchronisation when rounding the figures, than not.</p><p>So here&#8217;s the final script. If the browser supports <code>MediaController</code> then we just use that, otherwise we implement manual synchronisation, as described:</p><pre class="brush: jscript; title: ; notranslate">
var video = document.getElementById('video');
var audio = document.getElementById('audio');
if(typeof(window.MediaController) === 'function')
{
  var controller = new MediaController();
  video.controller = controller;
  audio.controller = controller;
}
else
{
  controller = null;
}
video.volume = 0.8;
audio.volume = 1;
video.addEventListener('play', function()
{
  if(!controller &amp;&amp; audio.paused)
  {
    audio.play();
  }
}, false);
video.addEventListener('pause', function()
{
  if(!controller &amp;&amp; !audio.paused)
  {
    audio.pause();
  }
}, false);
video.addEventListener('ended', function()
{
  if(controller)
  {
    controller.pause();
  }
  else
  {
    video.pause();
    audio.pause();
  }
}, false);
video.addEventListener('timeupdate', function()
{
  if(!controller &amp;&amp; audio.readyState &gt;= 4)
  {
    if(Math.ceil(audio.currentTime) != Math.ceil(video.currentTime))
    {
      audio.currentTime = video.currentTime;
    }
  }
}, false);
</pre><p>Note that the <code>MediaController</code> itself is defined only through scripting, whereas it is possible to define a controller using the static <code>"mediagroup"</code> attribute:</p><pre class="brush: xml; title: ; notranslate">
&lt;video mediagroup=&quot;foo&quot;&gt; ... &lt;/video&gt;
&lt;audio mediagroup=&quot;foo&quot;&gt; ... &lt;/audio&gt;
</pre><p>If we did that, then it would work without JavaScript in Chrome. It would sync the media sources, but the user would have <strong>no control over the audio</strong> (including not being able to turn it off), because the browser <em>wouldn&#8217;t know what the audio represents</em>. This is the case in which it would be better to have the audio encoded into the video, because then it could appear in the <code>audioTracks</code> object, and the browser could recognise that and be able to provide native controls.</p><p>But since we have no <code>audioTracks</code> data, that&#8217;s rather a moot point! So if scripting is not available, the audio simply won&#8217;t play.</p><p><strong>Here&#8217;s the final demo</strong>, which will work in any recent version of Opera, Firefox, Chrome, Safari, or <abbr
title="Internet Explorer 9">IE9</abbr> or later:</p><ul><li><strong><a
href="http://jspro.brothercake.com/audio-descriptions/">Audio Descriptions Demo</a></strong></li></ul><p>This is just a simple proof-of-concept demo, of course — there&#8217;s no initial feature detection, and it only has the basic controls provided by the native <code>"controls"</code> attribute. For a proper implementation it would need custom controls, to provide (among other things) a button to switch the audio on and off, and separate volume sliders. The interface should also be accessible to the keyboard, which is not the case in some browsers&#8217; native controls. And it would need to handle buffering properly — as it is, if you seek past the point where the video has preloaded, the audio will continue to play freely until the video has loaded enough to bring it back into sync.</p><p>I might also mention that the descriptions themselves are hardly up to professional standards! That&#8217;s my voice you can hear, recorded and converted using <a
title="Audacity is a free audio editor and recorded" href="http://audacity.sourceforge.net/">Audacity</a>. But such as it is, I think it makes an effective demonstration, of how low the technical barrier-to-entry is with this approach. I didn&#8217;t have to edit the video, and I made the audio in an hour with free software.</p><p>As a proof of concept, I&#8217;d say it was pretty successful — and I&#8217;m sure my client will be very pleased!</p><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/accessible-audio-descriptions-for-html5-video/feed/</wfw:commentRss> <slash:comments>7</slash:comments> </item> <item><title>How to Build an Instagram-like Photo Sharing App with HTML5: Part 2</title><link>http://www.sitepoint.com/how-to-build-an-instagram-like-photo-sharing-app-with-html5-part-2/</link> <comments>http://www.sitepoint.com/how-to-build-an-instagram-like-photo-sharing-app-with-html5-part-2/#comments</comments> <pubDate>Fri, 26 Apr 2013 15:55:52 +0000</pubDate> <dc:creator>Rajasekharan Vengalil</dc:creator> <category><![CDATA[CSS]]></category> <category><![CDATA[CSS3]]></category> <category><![CDATA[HTML]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[jQuery]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65642</guid> <description><![CDATA[In Part 1, Rajasekharan Vengalil laid out the implementation details of his InstaFuzz app built with HTML5, CSS and JS. Here, he shows how Drag/Drop, File API, Canvas and Web Workers can be used.]]></description> <content:encoded><![CDATA[<p></p><p>In <a
href="http://www.sitepoint.com/how-to-build-an-instagram-like-photo-sharing-app-with-html5/">part 1</a> we took a look at some of the UI layout implementation details of the <a
href="http://blogorama.nerdworks.in/arbit/InstaFuzz/" target="_blank">InstaFuzz</a> app.  You can get the source code for the app from <a
href="http://sdrv.ms/14B672O" target="_blank">here</a> if you wish to run it locally.  In this installment we’ll take a look at some of the other bits such as how drag/drop, File API, Canvas and Web Workers are used.</p><h2>Drag/Drop</h2><p>One of the things that <i>InstaFuzz</i> supports is the ability to drag and drop image files directly on to the big blackish/blue box.  Support for this is enabled by handling the “drop” event on the CANVAS element.  When a file is dropped onto an HTML element the browser fires the “drop” event on that element and passes in a <a
href="http://aka.ms/dataTransfer"><i>dataTransfer</i></a> object which contains a <a
href="http://aka.ms/filesproperty"><i>files</i> property</a> that contains a reference to the list of files that were dropped.  Here’s how this is handled in the app (“picture” is the ID of the CANVAS element on the page):<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><pre>var pic = $("#picture");
pic.bind("drop", function (e) {
    suppressEvent(e);
    var files = e.originalEvent.dataTransfer.files;
    // more code here to open the file
});
&nbsp;
pic.bind("dragover", suppressEvent).bind("dragenter", suppressEvent);
&nbsp;
function suppressEvent(e) {
    e.stopPropagation();
    e.preventDefault();
}</pre><p>The <i>files</i> property is a collection of <a
href="http://aka.ms/FileObject"><i>File</i> objects</a> that can then subsequently be used with the File API to access the file contents (covered in the next section).  We also handle the <i>dragover</i> and <i>dragenter</i> events and basically prevent those events from propagating to the browser thereby preventing the browser from handling the file drop.  IE for instance might unload the current page and attempt to open the file directly otherwise.</p><h2>File API</h2><p>Once the file has been dropped, the app attempts to open the image and render it in the canvas.  It does this by using the <a
href="http://aka.ms/FileAPI">File API</a>.  The File API is a W3C specification that allows web apps to programmatically access files from the local file system in a secure fashion.  In <i>InstaFuzz</i> we use the <a
href="http://aka.ms/FileReaderObject"><i>FileReader</i> object</a> to read the file contents as a <a
href="http://aka.ms/dataProtocol">data URL</a> string like so using the <a
href="http://aka.ms/readAsDataURLmethod"><i>readAsDataURL</i></a> method:</p><pre>var reader = new FileReader();
reader.onloadend = function (e2) {
    drawImageToCanvas(e2.target.result);
};
reader.readAsDataURL(files[0]);</pre><p>Here, <i>files</i> is the collection of <i>File</i> objects retrieved from the function handling the “drop” event on the CANVAS element. Since we are interested only in a single file we simply pick the first file from the collection and ignore the rest if there are any.  The actual file contents are loaded asynchronously and once the load completes, the <a
href="http://aka.ms/onloadend"><i>onloadend</i></a> event is fired where we get the file contents as a data URL which we then subsequently draw on to the canvas.</p><h2>Rendering the filters</h2><p>Now the core functionality here is of course the application of the filters.  In order to be able to apply the filter to the image we need a way to access the individual pixels from the image.  And before we can access the pixels we need to have actually rendered the image on to our canvas.  So let’s first take a look at the code that renders the image that the user picked on to the canvas element.</p><h3>Rendering images on to the canvas</h3><p>The canvas element supports the rendering of <i>Image</i> objects via the <a
href="http://aka.ms/drawImage"><i>drawImage</i></a> method.  To load up the image file in an <i>Image</i> instance, <i>InstaFuzz</i> uses the following utility routine:</p><pre>App.Namespace.define("InstaFuzz.Utils", {
    loadImage: function (url, complete) {
        var img = new Image();
        img.src = url;
        img.onload = function () {
            complete(img);
        };
    }
});</pre><p>This allows the app to load up image objects from a URL using code such as the following:</p><pre>function drawImageToCanvas(url) {
    InstaFuzz.Utils.loadImage(url, function (img) {
        // save reference to source image
        sourceImage = img;
&nbsp;
        mainRenderer.clearCanvas();
        mainRenderer.renderImage(img);
&nbsp;
        // load image filter previews
        loadPreviews(img);
    });
}</pre><p>Here, <i>mainRenderer</i> is an instance created from the <i>FilterRenderer</i> constructor function defined in <i>filter-renderer.js</i>.  The app uses <i>FilterRenderer</i> objects to manage canvas elements – both in the preview pane as well as the main canvas element on the right.  The <i>renderImage</i> method on the <i>FilterRenderer</i> has been defined like so:</p><pre>FilterRenderer.prototype.renderImage = function (img) {
    var imageWidth = img.width;
    var imageHeight = img.height;
    var canvasWidth = this.size.width;
    var canvasHeight = this.size.height;
    var width, height;
&nbsp;
    if ((imageWidth / imageHeight) &gt;= (canvasWidth / canvasHeight)) {
        width = canvasWidth;
        height = (imageHeight * canvasWidth / imageWidth);
    } else {
        width = (imageWidth * canvasHeight / imageHeight);
        height = canvasHeight;
    }
&nbsp;
    var x = (canvasWidth - width) / 2;
    var y = (canvasHeight - height) / 2;
    this.context.drawImage(img, x, y, width, height);
};</pre><p>That might seem like a lot of code but all it does ultimately is to figure out the best way to render the image in the available screen area considering the aspect ratio of the image.  The key piece of code that actually renders the image on the canvas occurs on the last line of the method.  The <i>context</i> member refers to the 2D context acquired from the canvas object by calling its<a
href="http://aka.ms/getContext"> <i>getContext</i></a> method.</p><h3>Fetching pixels from the canvas</h3><p>Now that the image has been rendered we will need access to the individual pixels in order to apply all the different filters that are available.  This is easily acquired by calling <a
href="http://aka.ms/getImageData"><i>getImageData</i></a> on the canvas’s context object.  Here’s how <i>InstaFuzz</i> calls this from <i>instafuzz.js</i>.</p><pre>var imageData = renderer.context.getImageData(
    0, 0,
    renderer.size.width,
    renderer.size.height);</pre><p>The object returned by <i>getImageData</i> provides access to the individual pixels via its <i>data</i> property which in turn is an array like object that contains a collection of byte values where each value represents the color rendered for a single channel of a single pixel.  Each pixel is represented using 4 bytes that specify values for the red, green, blue and alpha channels.  It also has a <a
href="http://aka.ms/lengthProperty"><i>length</i></a><i> </i>property that returns the length of the buffer.  If you have a 2D co-ordinate you can easily transform that into an index into this array using code such as the following.  The color intensity values of each channel ranges from 0 through 255.  Here’s the utility function from <i>filters.js</i> that accepts as input an image data object along with 2D coordinates for the pixel the caller is interested in and returns an object containing the color values:</p><pre>function getPixel(imageData, x, y) {
    var data = imageData.data, index = 0;
&nbsp;
    // normalize x and y and compute index
    x = (x &lt; 0) ? (imageData.width + x) : x;
    y = (y &lt; 0) ? (imageData.height + y) : y;
    index = (x + y * imageData.width) * 4;
&nbsp;
    return {
        r: data[index],
        g: data[index + 1],
        b: data[index + 2]
    };
}</pre><h3>Applying the filters</h3><p>Now that we have access to the individual pixels, applying the filter is fairly straightforward.  Here, for instance is the function that applies a weighted grayscale filter on the image.  It simply picks intensities from the red, green and blue channels and sums them up after applying a multiplication factor on each channel and then assigns the result for all 3 channels.</p><pre>// "Weighted Grayscale" filter
Filters.addFilter({
    name: "Weighted Grayscale",
    apply: function (imageData) {
        var w = imageData.width, h = imageData.height;
        var data = imageData.data;
        var index;
        for (var y = 0; y &lt; h; ++y) {
            for (var x = 0; x &lt; w; ++x) {
                index = (x + y * imageData.width) * 4;
                var luminance = parseInt((data[index + 0] * 0.3) +
                                         (data[index + 1] + 0.59) +
                                         (data[index + 2] * 0.11));
                data[index + 0] = data[index + 1] =
                    data[index + 2] = luminance;
            }
&nbsp;
            Filters.notifyProgress(imageData, x, y, this);
        }
&nbsp;
        Filters.notifyProgress(imageData, w, h, this);
    }
});</pre><p>Once the filter has been applied we can have that reflected on the canvas by calling the <a
href="http://aka.ms/putImageData"><i>putImageData</i></a> method passing in the modified image data object.  While the weighted grayscale filter is fairly simple most of the other filters use an image processing technique known as <i>convolution</i>.  The code for all the filters is available in <i>filters.js</i> and the convolution filters were ported from the C code available <a
href="http://lodev.org/cgtutor/filtering.html">here</a>.</p><h2>Web Workers</h2><p>As you might imagine doing all this number crunching to apply the filters can potentially take a long time to complete.  The <i>motion blur</i> filter for instance uses a 9&#215;9 filter matrix for computing the new value for every single pixel and is in fact the most CPU intensive filter among them all.  If we were to do all this computation on the UI thread of the browser then the app would essentially freeze every time a filter was being applied.  To provide a responsive user experience the app delegates the core image processing tasks to a background script using the support for W3C <a
href="http://aka.ms/WebWorkers">Web Workers</a> in modern browsers.</p><p>Web workers allow web applications to have scripts run in a background task that executes in parallel along with the UI thread.  Communication between the worker and the UI thread is accomplished by passing messages using the <a
href="http://aka.ms/postMessage"><i>postMessage</i></a> API.  On both ends (i.e. the UI thread and the worker) this manifests as an event notification that you can handle.  You can only pass “data” between workers and the UI thread, i.e., you cannot pass anything that has to do with the user interface  – you cannot for instance, pass DOM elements to the worker from the UI thread.</p><p>In <i>InstaFuzz</i> the worker is implemented in the file <i>filter-worker.js</i>.  All it does in the worker is handle the <a
href="http://aka.ms/onmessage"><i>onmessage</i></a> event and apply a filter and then pass the results back via <i>postMessage</i>.  As it turns out, even though we cannot pass DOM elements (which means we cannot just hand a CANVAS element to the worker to have the filter applied) we can in fact pass the image data object as returned by the <i>getImageData</i> method that we discussed earlier. Here’s the filter processing code from <i>filter-worker.js</i>:</p><pre>importScripts("ns.js", "filters.js");
&nbsp;
var tag = null;
onmessage = function (e) {
    var opt = e.data;
    var imageData = opt.imageData;
    var filter;
&nbsp;
    tag = opt.tag;
    filter = InstaFuzz.Filters.getFilter(opt.filterKey);
&nbsp;
    var start = Date.now();
    filter.apply(imageData);
    var end = Date.now();
&nbsp;
    postMessage({
        type: "image",
        imageData: imageData,
        filterId: filter.id,
        tag: tag,
        timeTaken: end - start
    });
}</pre><p>The first line pulls in some script files that the worker depends on by calling <a
href="http://aka.ms/importScripts"><i>importScripts</i></a>.  This is similar to including a JavaScript file in a HTML document using the SCRIPT tag.  Then we set up a handler for the <i>onmessage</i> event in response to which we simply apply the filter in question and pass the result back to the UI thread by calling <i>postMessage</i>.  Simple enough!</p><p>The code that initializes the worker is in <i>instafuzz.js</i> and looks like this:</p><pre>var worker = new Worker("js/filter-worker.js");</pre><p>Not much is it?  When a message is sent by the worker to the UI thread we handle it by specifying a handler for the <i>onmessage</i> event on the worker object.  Here’s how this is done in <i>InstaFuzz</i>:</p><pre>worker.onmessage = function (e) {
    var isPreview = e.data.tag;
    switch (e.data.type) {
        case "image":
            if (isPreview) {
                previewRenderers[e.data.filterId].
                    context.putImageData(
                        e.data.imageData, 0, 0);
            } else {
                mainRenderer.context.putImageData(
                    e.data.imageData, 0, 0);
            }
&nbsp;
            break;
        // more code here
    }
};</pre><p>The code should be fairly self-explanatory.  It simply picks the image data object sent by the worker and applies it to the relevant canvas’s context object causing the modified image to be rendered on screen.  Scheduling a filter for conversion with the worker is equally simple.  Here’s the routine that performs this function in <i>InstaFuzz</i>:</p><pre>function scheduleFilter(filterId,
                             renderer,
                             img, isPreview,
                             resetRender) {
    if (resetRender) {
        renderer.clearCanvas();
        renderer.renderImage(img);
    }
&nbsp;
    var imageData = renderer.context.getImageData(
        0, 0,
        renderer.size.width,
        renderer.size.height);
&nbsp;
    worker.postMessage({
        imageData: imageData,
        width: imageData.width,
        height: imageData.height,
        filterKey: filterId,
        tag: isPreview
});
}</pre><h2>Wrapping it up</h2><p>The source for <i>InstaFuzz</i> is available for download <a
href="http://sdrv.ms/11110mf">here</a>.  We saw that fairly intricate user experiences are possible today with HTML5 technologies such as Canvas, Drag/Drop, File API and Web Workers.  Support for all of these technologies is quite good in pretty much all modern browsers.  One thing that we did not address here is the question of making the app compatible with older browsers.  That, truth be told, is a non-trivial but necessary task that I will hopefully be able to talk about in a future article.</p><div><p><em>This article is part of the HTML5 tech series from the Internet Explorer team. <a
href="http://www.microsoft.com/click/services/Redirect2.ashx?CR_CC=200210648">Try-out</a> the concepts in this article with three months of free BrowserStack cross-browser testing @ <a
href="http://modern.ie/">http://modern.IE</a></em></div><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/how-to-build-an-instagram-like-photo-sharing-app-with-html5-part-2/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>How to Build an Instagram-like Photo Sharing App with HTML5</title><link>http://www.sitepoint.com/how-to-build-an-instagram-like-photo-sharing-app-with-html5/</link> <comments>http://www.sitepoint.com/how-to-build-an-instagram-like-photo-sharing-app-with-html5/#comments</comments> <pubDate>Thu, 25 Apr 2013 14:16:53 +0000</pubDate> <dc:creator>Rajasekharan Vengalil</dc:creator> <category><![CDATA[CSS]]></category> <category><![CDATA[CSS3]]></category> <category><![CDATA[HTML]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[jQuery]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65596</guid> <description><![CDATA[Rajaskharan Vengalil set out to see if an app like Instagram could be built using only HTML, CSS and JavaScript. Turns out it is (sorry, belated spoiler alert!).]]></description> <content:encoded><![CDATA[<p></p><p>When I started out on this app I was only really just interested in seeing if the web platform had really evolved to a point where an app like the hugely popular <a
href="http://aka.ms/Instragram">Instagram</a> app could be built using just HTML, JavaScript and CSS.</p><p>As it turns out we can in fact do exactly that.  This article walks you through the technologies that make this possible and shows how it is entirely feasible today to build interoperable web applications that provide a great user experience no matter what brand of browser the user is running.</p><p>If you happen to be one of the two or so people who have not heard about <i>Instagram</i> then you might be pleased to hear that it is a hugely popular photo sharing and social networking service that allows you to take pictures, apply interesting digital filters on them and share them with the world.  The service became so popular that it was <a
href="http://finance.fortune.cnn.com/2012/04/09/breaking-facebook-buying-instagram-for-1-billion/">acquired by Facebook</a> for a bag full of cash and stock in April of 2012.</p><p><a
href="http://aka.ms/InstaFuzz">InstaFuzz</a> is the name of the app I put together and while I don’t expect to be acquired by Facebook or anybody else for a billion green it does, however, make the case that an app such as this one can be built using only standards compliant web technologies such as Canvas, File API, Drag/Drop, Web Workers, ES5 and CSS3 and still manage to run well on modern browsers such as <a
href="http://www.microsoft.com/click/services/Redirect2.ashx?CR_CC=200210648">Internet Explorer 10</a>, Google Chrome and Firefox.  And you could easily use the code to <a
href="http://www.microsoft.com/click/services/Redirect2.ashx?CR_CC=200185226">build a Windows Store app</a> too.<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><h2>About the app</h2><p>If you’d like to take a look at the app, then here’s where it is hosted:</p><p><a
href="http://aka.ms/InstaFuzz">http://blogorama.nerdworks.in/arbit/InstaFuzz/</a></p><p>As soon as you load it up, you’re presented with a screen that looks like this:</p><p><img
class="alignnone size-full wp-image-65597" alt="Instafuzz default" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/04/figure19.png" width="463" height="384" /></p><p>The idea is that you can load up a photograph into the app either by clicking on the big red “Add” button on the bottom left hand corner or drag and drop an image file into the blackish/blue area on the right. Once you do that you get something that looks like this:</p><p><img
class="alignnone size-full wp-image-65598" alt="Add an image" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/04/figure24.png" width="463" height="385" /></p><p>You’ll note that a list of digital filters listed on the left of the screen shows a preview of what the image would look like if you were to apply the said filter.  Applying a filter is a simple matter of clicking on one of the filter previews on the left.  Here’s what it looks like after applying the “Weighted Grayscale” filter followed by a “Motion Blur”.  As you can tell filters are <i>additive</i> – as you keep clicking on filters, they are applied on top of what was applied earlier:</p><p><img
class="alignnone size-full wp-image-65599" alt="filter applied" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/04/figure34.png" width="462" height="385" /></p><p>Let’s next take a look at how the UI layout has been put together.</p><h2>UI Layout</h2><p>The HTML markup is so little that I can actually reproduce the contents of the BODY tag in its entirety here (excluding the SCRIPT includes):</p><pre>&lt;header&gt;
    &lt;div id="title"&gt;InstaFuzz&lt;/div&gt;
&lt;/header&gt;
&lt;section id="container"&gt;
    &lt;canvas id="picture" width="650" height="565"&gt;&lt;/canvas&gt;
    &lt;div id="controls"&gt;
        &lt;div id="filters-list"&gt;&lt;/div&gt;
        &lt;button id="loadImage"&gt;Add&lt;/button&gt;
        &lt;input type="file" id="fileUpload"
           style="display: none;"
           accept="image/gif, image/jpeg, image/png" /&gt;
    &lt;/div&gt;
&lt;/section&gt;
&amp;nbsp;
&lt;!-- Handlebar template for a filter UI button --&gt;
&amp;nbsp;
&lt;script id="filter-template" type="text/x-handlebars-template"&gt;
    &lt;div data-filter-id="<b>{{</b>filterId<b>}}</b>"&gt;
        &lt;div&gt;<b>{{</b>filterName<b>}}</b>&lt;/div&gt;
        &lt;canvas width="128" height="128"&gt;&lt;/canvas&gt;
    &lt;/div&gt;
&lt;/script&gt;</pre><p>There’s nothing much going on here.  Pretty much everything should be standard fare.  I will however draw attention to the fact that I am using the <a
href="http://handlebarsjs.com/">Handlebars</a> JavaScript templating system here for rendering the markup for the list of filters on the left of the screen.  The template markup is declared in the HTML file (the SCRIPT tag in the snippet shown above) and then used from JavaScript.  The template markup is then bound to a JavaScript object that supplies the values for handlebars expressions such as <i>{{filterId}}</i> and <i>{{filterName}}.</i>  Here’s the relevant piece of JS from the app with a bit of DOM manipulation help from <a
href="http://jquery.com/">jQuery</a>:</p><pre>var templHtml = $("#filter-template").html(),
    template = Handlebars.compile(templHtml),
    filtersList = $("#filters-list");
&amp;nbsp;
var context = {
    filterName: filter.name,
    filterId: index
};
&amp;nbsp;
filtersList.append(template(context));</pre><p>As you can tell from the HTML markup, all the filter preview boxes feature a CANVAS tag, as does the big box on the right where the final output is rendered.  We’ll go into a bit more detail later on in the article as to how canvas technology is used to achieve these effects.</p><p>The app also uses<a
href="http://aka.ms/fontface"> CSS3 @font-face</a> fonts to render the text in the header and the “Add” button.  The fonts have been taken from the excellent <a
href="http://aka.ms/FontSquirrel">Font Squirrel</a> site and here’s what the declaration looks like:</p><pre>@font-face {
    font-family: 'TizaRegular';
    src: url('fonts/tiza/tiza-webfont.eot');
    src: url('fonts/tiza/tiza-webfont.eot?#iefix')
           format('embedded-opentype'),
         url('fonts/tiza/tiza-webfont.woff') format('woff'),
         url('fonts/tiza/tiza-webfont.ttf') format('truetype'),
         url('fonts/tiza/tiza-webfont.svg#TizaRegular') format('svg');
    font-weight: normal;
    font-style: normal;
}</pre><p>This directive causes the user agent to embed the font in the page and make it available under the name assigned to the <i>font-family</i> rule which in this case is “TizaRegular”.  After this we can assign this font to any CSS <i>font-family</i> rule like how we normally do.  In <i>InstaFuzz</i> I use the following rule to assign the font to the header element:</p><pre>font-family: TizaRegular, Cambria, Cochin, Georgia, Times,
    "Times New Roman", serif;</pre><p>You might also have noticed that there is a subtle shadow being dropped on the page by the container element.</p><p>This is made possible using the <a
href="http://aka.ms/boxshadowproperty">CSS3 box-shadow</a> rule and here’s how it’s used in <i>InstaFuzz</i>.</p><pre>-moz-box-shadow: 1px 0px 4px #000000, -1px -1px 4px #000000;
-webkit-box-shadow: 1px 0px 4px #000000, -1px -1px 4px #000000;
box-shadow: 1px 0px 4px #000000, -1px -1px 4px #000000;</pre><p>This causes the browser to render a shadow around the relevant element.  Each comma separated section in the value specifies the following attributes of the shadow:</p><ol><li>Horizontal offset</li><li>Vertical offset</li><li>Spread distance – positive values have the effect of softening the shadow</li><li>Shadow color</li></ol><p>One can specify multiple shadow values separated by comma as in fact has been done above.  Note that I’ve also specified the shadow using vendor prefix syntax for Firefox and Chrome/Safari using the <i>moz</i> and <i>webkit</i> prefixes.  This causes the shadow to continue to work in versions of those browsers where support for this capability was provided using the vendor prefixed version of the rule.  Note that the W3C version of the rule – <i>box-shadow</i> – is specified last.  This is done deliberately to ensure that in case the browser supports both the forms then only the W3C behavior is actually applied to the page.</p><p>One often finds that web developers either fail to include vendor prefixed version of a given CSS3 rule for all the browsers that support that rule and/or fail to include the W3C version as well.  Often developers just put the <i>webkit</i> version of the rule ignoring other browsers and the W3C standard version.  This causes two problems – [1] poor user experience for users who are using non-webkit browsers and [2] it ends up resulting in webkit becoming a de-facto standard for the web.  Ideally we want W3C to be driving the future of the web and not one specific browser implementation.  So here are some things to remember when playing with experimental implementations of CSS features:</p><ol><li>Use vendor prefixed versions of CSS rules by all means but remember to specify the rule for all supported browsers and not just the one that you happen to be testing the page in (if you’re using <a
href="http://www.microsoft.com/click/services/Redirect2.ashx?CR_CC=200117040" class="broken_link">Visual Studio</a> to edit your CSS then you might be interested in the supremely excellent extension for Visual Studio called <a
href="http://aka.ms/WebEssentials"><i>Web Essentials</i></a> that makes the job of managing vendor prefixes about as simple as it can possibly get).</li><li>Remember to specify the W3C version of the rule as well.</li><li>Remember to order the occurrence of the rules so that the W3C version shows up last.  This is to allow clients that support both the vendor prefixed version and the W3C version to use the W3C specified semantics for the rule.</li></ol><p>That’s all for now.  In the next and final article in this series, we’ll take a look at how the app supports drag/drop of files, the use of File API, how the filters themselves work and how we prevent the UI thread from freezing by delegating the core number crunching work to web workers.</p><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/how-to-build-an-instagram-like-photo-sharing-app-with-html5/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>HTML5, Older Browsers and the Shiv</title><link>http://www.sitepoint.com/html5-older-browsers-and-the-shiv/</link> <comments>http://www.sitepoint.com/html5-older-browsers-and-the-shiv/#comments</comments> <pubDate>Tue, 23 Apr 2013 04:22:37 +0000</pubDate> <dc:creator>Dmitri Lau</dc:creator> <category><![CDATA[Browsers]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65558</guid> <description><![CDATA[Dmitri Lau looks at how to use the HTML5Shiv tool to address the inability of certain older browsers to correctly interpret some HTML5 elements.]]></description> <content:encoded><![CDATA[<p></p><p>HTML5 introduced a few semantic elements that are not supported in older browsers. Some of these new elements are no different than generic block elements so they don&#8217;t pose any compatibility problems. All you need to ensure compatibility is to add a CSS rule to your website that causes the relevant elements to behave like block elements.</p><p>But Internet Explorer versions 8 and under pose a challenge. Any element not in the official roster of elements cannot be styled with CSS. That means we cannot make then behave like block elements or give them any formatting.</p><p>For example, the following code will not work.</p><pre class="brush: xml; title: ; notranslate">
&lt;style&gt;
section {display: block}
&lt;/style&gt;
&lt;section&gt;This is on its own line.&lt;/section&gt;
&lt;section&gt;This should appear on a separate line.&lt;/section&gt;
</pre><p>But that&#8217;s not all. These new elements behave as if they don&#8217;t exist. For example, the following CSS won&#8217;t work, since the <code>section</code> element won&#8217;t match the universal selector.</p><pre class="brush: xml; title: ; notranslate">
&lt;style&gt;
body * span {color: red}
&lt;/style&gt;
&lt;body&gt;
  &lt;section&gt;
    &lt;span&gt;This should be red, but won't be red in IE 8.&lt;/span&gt;
  &lt;/section&gt;
&lt;/body&gt;
</pre><p>Fortunately for us, a workaround exists that allows Internet Explorer (IE) to recognize these new elements allowing them to be styled, and thus giving us full use of these new semantic tags. It&#8217;s a tool called <a
href="http://code.google.com/p/html5shiv/">HTML5Shiv</a>.<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><p><em>As noted on the linked Google page, &#8220;shiv&#8221; and &#8220;shim&#8221; are interchangeable terms in this context.</em></p><p>But how did we go from IE not even acknowledging the existence of this element, to now being able to use it?</p><p>The trick is that calling <code>document.createElement("section")</code> will suddenly cause IE to recognize the <code>section</code> element. No one knows why, but it works and you don&#8217;t even need to use the node returned by that function.</p><p>But you need to make sure to call it early on in your website before any of those elements are used, otherwise it won&#8217;t work.</p><p>You will need to call it for each and every new HTML5 elements like so:</p><pre class="brush: jscript; title: ; notranslate">
&quot;abbr article aside audio bdi canvas data datalist details figcaption figure &quot;+
  &quot;footer header hgroup main mark meter nav output progress section &quot; +
  &quot;summary template time video&quot;
  .replace(/\w+/g, function(a){ document.createElement(a) });
</pre><p>Notice we&#8217;re using the <code>replace</code> method of the <code>string</code> object to succinctly iterate over each contiguous length of characters matched by the regular expression and executing the callback function for each character block which in turn calls <code>createElement</code>.</p><p>Here on in, we&#8217;ll call this method, &#8220;shivving the document&#8221;, so that the document can render the new HTML5 elements.</p><p>Now our previous two HTML examples work. But that&#8217;s not all there is to it.</p><h2>Pitfall 1: HTML5 and innerHTML</h2><p>If HTML is being generated using <code>innerHTML</code> and it is called on a node not currently attached to a document (AKA an orphaned node), then it&#8217;s deja vu all over again. The following two examples will not render the <code>section</code> element, even though it&#8217;s run on a document already shivved.</p><pre class="brush: jscript; title: ; notranslate">
var n1 = document.getElementById(&quot;n1&quot;);
n1.parentNode.removeChild(n1);
n1.innerHTML = &quot;&lt;section&gt;Sect 1&lt;\/section&gt;&quot;;  //won't work
</pre><pre class="brush: jscript; title: ; notranslate">
var n2 = document.createElement(&quot;div&quot;);
n2.innerHTML = &quot;&lt;section&gt;Sect 2&lt;\/section&gt;&quot;;  //won't work
</pre><p>In the second example above, if we append the node to the document first before calling <code>innerHTML</code>, then it will work:</p><pre class="brush: jscript; title: ; notranslate">
var n2 = document.createElement(&quot;div&quot;);
document.body.appendChild(n2);
n2.innerHTML = &quot;&lt;section&gt;Sect 2&lt;\/section&gt;&quot;;  //works
</pre><p>We can conclude that although we shivved the document earlier on, orphaned elements do not benefit from the shiv when calling <code>innerHTML</code>.</p><p>What can we do? For starters, whenever we need to set <code>innerHTML</code> we should append it to the document first. An alternative is to first shiv the <code>document</code> property of the orphan before working with the orphan.</p><p>First let&#8217;s put our shiv in its own function.</p><pre class="brush: jscript; title: ; notranslate">
function iehtml5shiv(doc) {
  &quot;abbr article aside audio bdi canvas data datalist details &quot; +
    &quot;figcaption figure footer header hgroup main mark meter nav &quot; +
    &quot;output progress section summary template time video&quot;
    .replace(/\w+/g, function(a){ doc.createElement(a) });
}
</pre><p>The next time we have an orphan element, we can do this:</p><pre class="brush: jscript; title: ; notranslate">
var n1 = document.createElement(&quot;div&quot;);
iehtml5shiv(n1.document);
n1.innerHTML = &quot;&lt;section&gt;Sect 1&lt;\/section&gt;&quot;;  //works
</pre><p>Notice how it&#8217;s just like shivving the document, but on the <code>document</code> property of the element. And notice we&#8217;re accessing <code>document</code> instead of <code>ownerDocument</code>. Both are different things as shown here:</p><pre class="brush: jscript; title: ; notranslate">
alert(n1.document == document);  //false
alert(n1.ownerDocument == document);  //true
</pre><p>Now we have two methods to make sure our call to <code>innerHTML</code> works when handling HTML5 elements.</p><h2>Pitfall 2: cloneNode</h2><p>It appears our cousin <code>cloneNode</code> is also susceptible to losing its shiv. Any HTML5 elements which are cloned, or have had their parents cloned, will lose their identity.</p><p>Notice how the below element has colons in its <code>nodeName</code>, meaning it&#8217;s being confused for an element from another namespace.</p><pre class="brush: jscript; title: ; notranslate">
var n2 = n1.cloneNode(true);
alert(n2.innerHTML);  //outputs: &lt;:section&gt;Sect 1&lt;/:section&gt;
</pre><p>This happens even if the node was already attached to the document.</p><p>There isn&#8217;t much we can do here except roll out your own implementation of <code>cloneNode</code>, which is trivial enough.</p><h2>Pitfall 3: Printing</h2><p>Whenever you print a webpage, IE appears to generate a new document before printing which means all the shiv workarounds are not preserved.</p><p>There isn&#8217;t much you can do to mitigate this. The HTML5Shiv tool works around this by listening for the <code>onbeforeprint</code> event and replacing all the HTML5 elements on the page with normal elements and then doing the reverse on the <code>onafterprint</code> event.</p><p>Thankfully, the HTML5Shiv tool does that nicely for us.</p><h2>References</h2><ul><li>The HTML5Shiv tool: <a
href="https://github.com/aFarkas/html5shiv">https://github.com/aFarkas/html5shiv</a></li><li>The story of the HTML5 Shiv: <a
href="http://paulirish.com/2011/the-history-of-the-html5-shiv/">http://paulirish.com/2011/the-history-of-the-html5-shiv/</a></li></ul><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/html5-older-browsers-and-the-shiv/feed/</wfw:commentRss> <slash:comments>9</slash:comments> </item> <item><title>What&#8217;s New in jQuery 2.0</title><link>http://www.sitepoint.com/whats-new-in-jquery-2-0/</link> <comments>http://www.sitepoint.com/whats-new-in-jquery-2-0/#comments</comments> <pubDate>Sat, 20 Apr 2013 08:30:51 +0000</pubDate> <dc:creator>Craig Buckler</dc:creator> <category><![CDATA[Ajax]]></category> <category><![CDATA[HTML]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[jQuery]]></category> <category><![CDATA[Libraries]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <category><![CDATA[javascript]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65533</guid> <description><![CDATA[jQuery 2.0 has been released. There is one new feature, but it's a big change. Craig provides the details...]]></description> <content:encoded><![CDATA[<p></p><p>The second branch of the web&#8217;s most popular JavaScript library was released on April 18, 2013. <a
href="http://jquery.com/download/">jQuery 2.0 is now available for download</a> but don&#8217;t rush off and install it just yet &#8212; there is one major change&hellip;</p><h2>No More Support for IE6/7/8</h2><p><em>(or IE9 and IE10 if they&#8217;re used in &#8220;Compatibility View&#8221; mode).</em></p><p>I still think it&#8217;s a little premature to abandon IE8 but the team couldn&#8217;t wait any longer. jQuery 2.0 removes all the legacy IE code for node selection, DOM manipulation, event handling and Ajax.</p><p>This has resulted in a 11% file size reduction from 32,819 bytes to 29,123 bytes. That&#8217;s 3.6Kb for the gzipped minified version &#8212; it&#8217;s unlikely to be noticeable even on a dial-up connection. Admittedly, the team hoped to save more but discovered that Android/Webkit 2.x browsers still required many workarounds.</p><p>If you need to support IE8 and below, stick with jQuery 1.9.x for now. You could conditionally load version 2.0 in all other browsers, but:</p><ol><li>conditional loading will offset any gains in file size reduction and processing, and</li><li>you may encounter differences between the two jQuery branches. The team has pledged to minimize API divergence but there will almost certainly be issues. jQuery 1.10 will address known compatibility problems.</li></ol><h2>Custom Builds</h2><p>The custom build feature has been refined in version 2.0 so you can exclude any of 12 unused modules and shrink jQuery below 10Kb. The modules which can be omitted are:</p><ul><li><strong>ajax</strong>: all Ajax functionality, transports, and event shorthands.</li><li><strong>ajax/xhr</strong>: XMLHTTPRequest Ajax transport only.</li><li><strong>ajax/script</strong>: <code>&lt;script&gt;</code> Ajax transport only.</li><li><strong>ajax/jsonp</strong>: JSONP Ajax transport only (depends on ajax/script).</li><li><strong>css</strong>: The <code>.css()</code> method plus non-animated <code>.show()</code>, <code>.hide()</code> and <code>.toggle()</code>.</li><li><strong>deprecated</strong>: deprecated methods (currently <code>.andSelf()</code> only).</li><li><strong>dimensions</strong>: <code>.width()</code> and <code>.height()</code> methods, including <code>inner-</code> and <code>outer-</code> variations.</li><li><strong>effects</strong>: the <code>.animate()</code> method and its shorthands such as <code>.slideUp()</code>.</li><li><strong>event-alias</strong>: event attaching/triggering shorthands such as <code>.click()</code>.</li><li><strong>offset</strong>: the <code>.offset()</code>, <code>.position()</code>, <code>.offsetParent()</code>, <code>.scrollLeft()</code>, and <code>.scrollTop()</code> methods.</li><li><strong>wrap</strong>: the <code>.wrap()</code>, <code>.wrapAll()</code>, <code>.wrapInner()</code>, and <code>.unwrap()</code> methods.</li><li><strong>sizzle</strong>: the Sizzle selector engine. When this module is excluded, it is replaced by a rudimentary selector engine based on the native <code>querySelectorAll</code> method which does not support some advanced jQuery selectors.</li></ul><p>For example, if you are using CSS3 animations rather than jQuery methods, you could omit the <strong>effects</strong> module and possibly <strong>dimensions</strong>, <strong>offset</strong>, <strong>wrap</strong> and <strong>sizzle</strong>.</p><p>Creating your own custom build is not for the faint-hearted and you&#8217;ll require some knowledge of Git, Node.js and Grunt. <a
href="https://github.com/jquery/jquery/#readme">Full instructions are available</a> but, longer term, I hope the team can implement an online build process similar to <a
href="http://modernizr.com/download/">Modernizr</a>.</p><h2>Should I Upgrade?</h2><p>It&#8217;s important to understand that jQuery 2.0 has API parity with jQuery 1.9. There are a small number of bug fixes but no new features.</p><p>However, if you&#8217;re one of those lucky developers who has dropped support for IE6/7/8, <a
href="http://jquery.com/download/">grab jQuery 2.0</a> and don&#8217;t look back.</p><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/whats-new-in-jquery-2-0/feed/</wfw:commentRss> <slash:comments>22</slash:comments> </item> <item><title>Win Free Tickets to Web Directions Code</title><link>http://www.sitepoint.com/win-free-tickets-to-web-directions-code-2013/</link> <comments>http://www.sitepoint.com/win-free-tickets-to-web-directions-code-2013/#comments</comments> <pubDate>Thu, 18 Apr 2013 15:56:58 +0000</pubDate> <dc:creator>Ricky Onsman</dc:creator> <category><![CDATA[Community]]></category> <category><![CDATA[CSS3]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <category><![CDATA[web directions]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65500</guid> <description><![CDATA[If you're in Melbourne, Australia, May 2-3, and wishing you could go to Web Directions Code, we have not one but TWO $999 tickets to give away.]]></description> <content:encoded><![CDATA[<p></p><p>JavaScript, HTML5, CSS3: it’s a changing landscape. Web Directions brings together practitioners and big thinkers at the leading edge to educate and inspire at a “festival of code”.</p><p>SitePoint is partnering with <a
href="http://code13.webdirections.org/" target="_blank">Web Directions Code 2013</a> in Melbourne, Australia, and we have two free tickets worth $999 each to give away.</p><p>To enter, all you need to do is let us know why would you like to be at this year&#8217;s conference. We will pick the two most creative answers on Wednesday 24th April.</p><h2>Sorry, this promotion is now closed. Winners will be contacted directly.</h2><p>You can still buy tickets to <a
href="http://code13.webdirections.org/" target="_blank">Web Directions Code 2013</a>. Use SITEPOINT as a voucher code to take $150 off the full price.</p><h2 style="clear: both; display:block; padding-top: 30px;">Fine print:</h2><ul><li>Two conference tickets to be given away to the two most creative answers.</li><li>Conference to be held in Melbourne, Australia, 2-3 May, no transport or accommodation included.</li><li>Promotion ends 23th April 2013.</li></ul><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/win-free-tickets-to-web-directions-code-2013/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>3 Neat Tricks with Regular Expressions</title><link>http://www.sitepoint.com/3-neat-tricks-with-regular-expressions/</link> <comments>http://www.sitepoint.com/3-neat-tricks-with-regular-expressions/#comments</comments> <pubDate>Thu, 18 Apr 2013 01:20:15 +0000</pubDate> <dc:creator>James Edwards</dc:creator> <category><![CDATA[Intermediate]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Raw Javascript]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65485</guid> <description><![CDATA[James looks at three cunning things you can do with regular expressions, that provide neat solutions to some very sticky problems.]]></description> <content:encoded><![CDATA[<p></p><p>I&#8217;d like to show you three cunning things you can do with regular expressions, that provide neat solutions to some very sticky problems:</p><ol><li><a
href="#removing-comments">Removing Comments</a></li><li><a
href="#using-replacement-callbacks">Using Replacement Callbacks</a></li><li><a
href="#working-with-invisible-delimiters">Working With Invisible Delimiters</a></li></ol><h2 id="removing-comments">1. Removing Comments</h2><p>Regular expressions make light work of <strong>single-character delimiters</strong>, which is why it&#8217;s so easy to remove markup from a string:</p><pre class="brush: jscript; title: ; notranslate">
str = str.replace(/(&lt;[\/]?[^&gt;]+&gt;)/g, '');
</pre><p>It&#8217;s the negation in the character class that does the real work:</p><pre class="brush: jscript; title: ; notranslate">
[^&gt;]
</pre><p>Which means <em>&#x201c;anything except <code>&lt;&#x201d;</code></em>. So the expression looks for the starting tag-delimiter and possible slash, then anything except the closing tag-delimiter, and then the delimiter itself. Easy.</p><p>However comments are not so simple, because comment delimiters are comprised of <strong>more than one character</strong>. Multi-line comments in <abbr
title="Cascading Style Sheets">CSS</abbr> and JavaScript, for example, start with <code>/*</code> and end with <code>*/</code>, but between those two delimiters there could be <strong>any number of unrelated stars</strong>.<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><p>I often use multiple stars in comments, to indicate the severity of a bug I&#8217;ve just noticed, for example:</p><pre class="brush: jscript; title: ; notranslate">
/*** this is a bug with 3-star severity ***/
</pre><p>But if we tried to parse that with a single negation character, it would fail:</p><pre class="brush: jscript; title: ; notranslate">
str = str.replace(/(\/\*[^\*]+\*\/)/g, '');
</pre><p>Yet it&#8217;s not possible with regular expressions to say: <em>&#x201c;anything except [this sequence of characters]&#x201d;</em>, we can only say: <em>&#x201c;anything except [one of these single characters]&#x201d;</em>.</p><p>So here&#8217;s the regular expression we need instead:</p><pre class="brush: jscript; title: ; notranslate">
str = str.replace(/(\/\*([^*]|(\*+[^*\/]))*\*+\/)/gm, '');
</pre><p>The expression handles unrelated characters by <strong>looking at what comes after them</strong> &#x2014; stars are allowed as long as they&#8217;re not followed by a slash, until we find one that is, and that&#8217;s the end of the comment.</p><p>So it says: <em>&#x201c;<code>/</code> then <code>*</code> (then anything except <code>*</code> OR one or more <code>*</code> followed by anything except <code>*</code> or <code>/</code>)(and any number of instances of that) then one or more <code>*</code> then <code>/</code>&#x201d;</em>.</p><p>(The syntax looks particular convoluted, because <code>"*"</code> and <code>"/"</code> are both special characters in regular expressions, so the ambiguous literal ones have to be escaped. Also note the <code>"m"</code> flag at the end of the expression, which means <strong>multi-line</strong>, and specifies that the regular expression should search across more than one line of text.)</p><p>Using the same principle then, we can adapt the expression to search for any kind of complex delimiters. Here&#8217;s another one that matches <abbr
title="HyperText Markup Language">HTML</abbr> comments:</p><pre class="brush: jscript; title: ; notranslate">
str = str.replace(/(&lt;!\-\-([^\-]|(\-+[^&gt;]))*\-+&gt;)/gm, '');
</pre><p>And here&#8217;s one for <code>CDATA</code> sections:</p><pre class="brush: jscript; title: ; notranslate">
str = str.replace(/(&lt;\!\[CDATA\[([^\]]|(\]+[^&gt;]))*\]+&gt;)/gm, '');
</pre><h2 id="using-replacement-callbacks">2. Using Replacement Callbacks</h2><p>The <code>replace</code> function can also be <strong>passed a callback</strong> as its second parameter, and this is invaluable in cases where the replacement you want can&#8217;t be described in a simple expression. For example:</p><pre class="brush: jscript; title: ; notranslate">
isocode = isocode.replace(/^([a-z]+)(\-[a-z]+)?$/i,
  function(match, lang, country)
  {
    return lang.toLowerCase()
      + (country ? country.toUpperCase() : '');
  });
</pre><p>That example normalizes the capitalisation in language codes &#x2014; so <code>"EN"</code> would become <code>"en"</code>, while <code>"en-us"</code> would become <code>"en-US"</code>.</p><p>The first argument that&#8217;s passed to the callback is always the complete match, then each subsequent argument corresponds with the backreferences (i.e. <code>arguments[1]</code> is what a string replacement would refer to as <code>"$1"</code>, and so on).</p><p>So taking <code>"en-us"</code> as the input, we&#8217;d get the three arguments:</p><ol
start="0"><li><code>"en-us"</code></li><li><code>"en"</code></li><li><code>"-us"</code></li></ol><p>Then all the function has to do is enforce the appropriate cases, re-combine the parts and return them. Whatever the callback returns is what the replacement itself returns.</p><p>But we don&#8217;t actually have to assign the return value (or return at all), and if we don&#8217;t, then the original string will be unaffected. This means we can use <code>replace</code> as a <strong>general-purpose string processor</strong> &#x2014; to extract data from a string without changing it.</p><p>Here&#8217;s another example, that combines the multi-line comment expression from the previous section, with a callback that extracts and saves the text of each comment:</p><pre class="brush: jscript; title: ; notranslate">
var comments = [];
str.replace(/(\/\*([^*]|(\*+[^*\/]))*\*+\/)/gm,
  function(match)
  {
    comments.push(match);
  });
</pre><p>Since nothing is returned, the original string remains unchanged. Although if we wanted to extract <em>and</em> remove the comments, we could simply return and assign an empty-string:</p><pre class="brush: jscript; title: ; notranslate">
var comments = [];
str = str.replace(/(\/\*([^*]|(\*+[^*\/]))*\*+\/)/gm,
  function(match)
  {
    comments.push(match);
    return '';
  });
</pre><h2 id="working-with-invisible-delimiters">3. Working With Invisible Delimiters</h2><p>Extracting content is all very well when it uses standard delimiters, but what if you&#8217;re using <strong>custom delimiters</strong> that only your program knows about? The problem there is that <strong>the string might already contain your delimiter</strong>, literally character for character, and then what do you?</p><p>Well, recently I came up with a very cute trick, that not only avoids this problem, it&#8217;s also as simple to use as the single-character class we saw at the start! The trick is to use <strong>unicode characters that the document can&#8217;t contain</strong>.</p><p>Originally I tried this with <em>undefined</em> characters, and that certainly worked, but it&#8217;s not safe to assume that any such character will always be undefined (or that the document won&#8217;t already contain it anyway). Then I discovered that Unicode actually reserves a set of code-points specifically for this kind of thing &#x2014; so-called <a
href="http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Noncharacters" title="Mapping of Unicode Characters (wikipedia.org)">noncharacters</a>, which will never be used to define actual characters. A valid Unicode document is not allowed to contain noncharacters, but a program can use them internally for its own purposes.</p><p>I was working on <abbr
title="Cascading Style Sheets">CSS</abbr> processor, and I needed to remove all the comments before parsing the selectors, so they wouldn&#8217;t confuse the selector-matching expressions. But they had to be replaced in the source with something that took up the same number of lines, so that the line-numbers would remain accurate. Then later they would have to be added back to the source, for final output.</p><p>So first we use a regex callback to extract and save the comments. The callback returns a copy of the match in which all non-whitespace is converted to space, and which is delimited with a noncharacter either side:</p><pre class="brush: jscript; title: ; notranslate">
var comments = [];
csstext = csstext.replace(/(\/\*([^*]|(\*+([^*\/])))*\*+\/)/gm,
  function(match)
  {
    comments.push(match);
    return '\ufddf' + match.replace(/[\S]/gim, ' ') + '\ufddf';
  });
</pre><p>That creates an array of comments in the same source-order as the spaces they leave behind, while the spaces themselves take-up as many lines as the original comment.</p><p>Then the originals can be restored simply by replacing each delimited space with its corresponding saved comment &#x2014; and since the delimiters are single characters, we only need a <strong>simple character class</strong> to match each pair:</p><pre class="brush: jscript; title: ; notranslate">
csstext = csstext.replace(/(\ufddf[^\ufddf]+\ufddf)/gim,
  function()
  {
    return comments.shift();
  });
</pre><p>How easy is that!</p><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/3-neat-tricks-with-regular-expressions/feed/</wfw:commentRss> <slash:comments>5</slash:comments> </item> <item><title>Using CSS Grid Layout and Blend 5 to Build a Game</title><link>http://www.sitepoint.com/using-css-grid-layout-and-blend-5-to-build-a-game/</link> <comments>http://www.sitepoint.com/using-css-grid-layout-and-blend-5-to-build-a-game/#comments</comments> <pubDate>Wed, 17 Apr 2013 01:14:51 +0000</pubDate> <dc:creator>David Rousset</dc:creator> <category><![CDATA[CSS]]></category> <category><![CDATA[CSS3]]></category> <category><![CDATA[Gaming]]></category> <category><![CDATA[HTML]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[HTML5 Dev Center]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65462</guid> <description><![CDATA[David Rousset reveals a long-held Microsoft secret about what drove the development of CSS Grid Layout. Tetris is involved.]]></description> <content:encoded><![CDATA[<p></p><p>I’d like to share with you a mysterious internal secret kept within Microsoft for a long time. It’s the real story behind the concept of the <a
href="http://www.w3.org/TR/css3-grid-layout/">CSS Grid Layout</a> imagined by Microsoft for IE10 and Windows Store Apps.</p><p>Most of you probably think that this specification was designed to give developers a better layout engine for their websites and applications. But the original motivation was completely different. The very first aim was to be able to create a Tetris-like game in an easy way!</p><p>I’m sure you’re not convinced yet. That’s why I’m going to prove it to you using Blend 5 as a companion. Ok, let’s go!</p><p><strong><span
style="text-decoration: underline;">Pre-requisites:</span></strong> to follow this tutorial, you need first to:</p><ol><li>Download/buy &amp; install <strong>Windows 8 RTM</strong> on your machine: <a
href="http://msdn.microsoft.com/en-US/windows/apps/br229516.aspx">http://msdn.microsoft.com/en-US/windows/apps/br229516.aspx</a></li><li>Download &amp; install the free edition of <strong>Visual Studio 2012 Express RTM </strong>for Windows 8: <a
href="http://msdn.microsoft.com/en-US/windows/apps/br229516.aspx">http://msdn.microsoft.com/en-US/windows/apps/br229516.aspx</a> that includes Expression Blend 5 for Visual Studio or use the higher versions.</li></ol><h2>Step 1: discover the secret behind the CSS Grid Layout thanks to Blend 5</h2><p>Launch Expression Blend 5 and create a new HTML (Windows Store) project of type Blank App. Name it “<em><strong>TheRealCSSGridStory</strong></em>”:<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><p><img
alt="image" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/2502.image_5F00_thumb_5F00_00459BE1.png" width="450" height="352" border="0" /></p><p>Replace:</p><pre class="code"><span style="color: blue;">&lt;</span><span style="color: maroon;">p</span><span style="color: blue;">&gt;</span>Content goes here<span style="color: blue;">&lt;/</span><span style="color: maroon;">p</span><span style="color: blue;">&gt;
</span></pre><p>With:</p><pre class="code"><span style="color: blue;">&lt;</span><span style="color: maroon;">div </span><span style="color: red;">class</span><span style="color: blue;">="mainGrid"&gt;
&lt;/</span><span style="color: maroon;">div</span><span style="color: blue;">&gt;
</span></pre><p>Let’s create a grid containing 10 columns and 20 lines, whatever the screen’s resolution, by using fraction units. For that, add this CSS rule:</p><pre class="code"><span style="color: maroon;">.mainGrid </span>{
    <span style="color: red;">display</span>: <span style="color: blue;">-ms-grid</span>;
    <span style="color: red;">width</span>: <span style="color: blue;">100%</span>;
    <span style="color: red;">height</span>: <span style="color: blue;">100%</span>;
    <span style="color: red;">-ms-grid-columns</span>: <span style="color: blue;">(1fr)[10]</span>;
    <span style="color: red;">-ms-grid-rows</span>: <span style="color: blue;">(1fr)[20]</span>;
}</pre><p>Select in the Live DOM the <em>&lt;div&gt; mainGrid</em> element and you should obtain this:</p><p><img
alt="image" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/7220.image_5F00_thumb_5F00_1173967B.png" width="640" height="418" border="0" /></p><p>Let’s draw a shape inside this beautiful grid. Add this block of HTML inside the main grid:</p><pre class="code"><span style="color: blue;">&lt;</span><span style="color: maroon;">div </span><span style="color: red;">class</span><span style="color: blue;">="shape1"&gt;
&lt;/</span><span style="color: maroon;">div</span><span style="color: blue;">&gt;
</span></pre><p>And insert this CSS associated with it:</p><pre class="code"><span style="color: maroon;">.shape1 </span>{
    <span style="color: red;">-ms-grid-column</span>: <span style="color: blue;">4</span>;
    <span style="color: red;">-ms-grid-row</span>: <span style="color: blue;">3</span>;
    <span style="color: red;">-ms-grid-column-span</span>: <span style="color: blue;">3</span>;
    <span style="color: red;">-ms-grid-row-span</span>: <span style="color: blue;">2</span>;
    <span style="color: red;">background-color</span>: <span style="color: blue;">red</span>;
}</pre><p>You should now see that in Blend 5:</p><p><img
alt="image" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/5556.image17_5F00_thumb_5F00_4CB47F2B.png" width="450" height="276" border="0" /></p><p>Cool, but nothing yet looks like to a Tetris gaming piece. Let’s work on that. Add these two DIVs inside the shape1:</p><pre class="code"><span style="color: blue;">&lt;</span><span style="color: maroon;">div </span><span style="color: red;">class</span><span style="color: blue;">="line1shape1"&gt;&lt;/</span><span style="color: maroon;">div</span><span style="color: blue;">&gt;
&lt;</span><span style="color: maroon;">div </span><span style="color: red;">class</span><span style="color: blue;">="line2shape1"&gt;&lt;/</span><span style="color: maroon;">div</span><span style="color: blue;">&gt;
</span></pre><p>and replace the previous <em>.shape1</em> rule with this block of CSS:</p><pre class="code"><span style="color: maroon;">.shape1 </span>{
    <span style="color: red;">-ms-grid-column</span>: <span style="color: blue;">4</span>;
    <span style="color: red;">-ms-grid-row</span>: <span style="color: blue;">3</span>;
    <span style="color: red;">-ms-grid-column-span</span>: <span style="color: blue;">3</span>;
    <span style="color: red;">-ms-grid-row-span</span>: <span style="color: blue;">2</span>;
    <span style="color: red;">display</span>: <span style="color: blue;">-ms-grid</span>;
    <span style="color: red;">-ms-grid-columns</span>: <span style="color: blue;">1fr 1fr 1fr</span>;
    <span style="color: red;">-ms-grid-rows</span>: <span style="color: blue;">1fr 1fr</span>;
    <span style="color: red;">width</span>: <span style="color: blue;">100%</span>;
    <span style="color: red;">height</span>: <span style="color: blue;">100%</span>;
}
<span style="color: maroon;">.line1shape1 </span>{
    <span style="color: red;">-ms-grid-column-span</span>: <span style="color: blue;">2</span>;
    <span style="color: red;">background-color</span>: <span style="color: blue;">red</span>;
}
<span style="color: maroon;">.line2shape1 </span>{
    <span style="color: red;">-ms-grid-column</span>: <span style="color: blue;">2</span>;
    <span style="color: red;">-ms-grid-row</span>: <span style="color: blue;">2</span>;
    <span style="color: red;">-ms-grid-column-span</span>: <span style="color: blue;">2</span>;
    <span style="color: red;">background-color</span>: <span style="color: blue;">red</span>;
}</pre><p>The shape1 is currently spanning on three columns and two rows. I’ll then create a new grid inside this area defined by three columns and two rows in order to have cells having exactly the same size as the cells of the main grid, whatever the resolution might be.</p><p>Once done, I’ll create two lines in order to mimic the Z shape of the Tetris game. You should now have this result:</p><p><img
alt="z-shape in Tetris" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/0334.image_5F00_thumb_5F00_520EF183.png" width="450" height="274" border="0" /></p><p>Even better, play with the various views available in the Device tab and you’ll see that our game is already implementing a <a
href="http://en.wikipedia.org/wiki/Responsive_web_design">responsive design</a>! This is freaking cool, isn’t it?</p><p>Here, for instance, is the outputs for the snapped view and the portrait view:</p><p><img
alt="" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/1488.image_5F00_thumb_5F00_544E09F5.png" width="550" height="262" border="0" /><br
/> <img
alt="z-shape in landscape view" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/8345.image_5F00_thumb_5F00_119670D0.png" width="325" height="286" border="0" /></p><p>Let’s now resolve another problem.</p><p>The Tetris grid gaming grid is composed of squares. Our current responsive design is stretching 100% width. Building a Windows 8 application for the Windows Store will most of the time meet widescreen monitors (current tablets are 1366&#215;768 or 1920&#215;1080 and most desktop PC have a 16/9 ratio). Let’s then assume that targeting a widescreen ratio is addressing almost all cases. To compute the proper responsive width, you need to do: 9/16 * 10/20 (the ratio of the main gaming grid) which equals to: <strong>28.125%.</strong></p><p>Add this rule to target the main grid in full screen landscape mode:</p><pre class="code"><span style="color: blue;">@media </span>screen and (-ms-view-state: fullscreen-landscape) {
    <span style="color: maroon;">.mainGrid </span>{
        <span style="color: red;">width</span>: <span style="color: blue;">28.125%</span>;
        }
}</pre><p>Let’s now center the gaming grid by using… the CSS Grid Layout again! (And you should now start to believe it was truly designed for Tetris!)</p><p>Switch the <em>body</em> element to <em>–ms-grid</em> made of one column and one row:</p><pre class="code"><span style="color: maroon;">body </span>{
    <span style="color: red;">display</span>: <span style="color: blue;">-ms-grid</span>;
    <span style="color: red;">-ms-grid-columns</span>: <span style="color: blue;">1fr</span>;
    <span style="color: red;">-ms-grid-rows</span>: <span style="color: blue;">1fr</span>;
    <span style="color: red;">width</span>: <span style="color: blue;">100%</span>;
    <span style="color: red;">height</span>: <span style="color: blue;">100%</span>;
}</pre><p>Now simply add this attribute to the CSS associated to the the main grid:</p><pre class="code"><span style="color: red;">-ms-grid-column-align</span>: <span style="color: blue;">center</span>;</pre><p>And the grid is now centered:</p><p><img
alt="" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/2553.image_5F00_thumb_5F00_110B1794.png" width="640" height="332" border="0" /></p><p>At this stage, you’re probably already shocked. &#8220;<em>How could I have missed this incredible secret?</em>&#8221; you’re wondering to yourself.</p><p>Take a breath.</p><p>Now that you know THE secret, let’s continue this tutorial together to discover other awesome possibilities delivered by the CSS specifications combined together.</p><h2>Step 2: moving or rotating a shape</h2><p>My first idea was to try avoiding JS by using as much CSS as possible. I then first tried to use <a
href="http://blogs.msdn.com/b/davrous/archive/2011/12/06/introduction-to-css3-animations.aspx">CSS3 Animations</a> to move and animate the shape on the various rows/columns. But the bad news is that you can’t change the <em>–ms-grid-column</em> or <em>–ms-grid-row</em> values via CSS3 animations. This will then be the job of some JavaScript code.</p><p>I then started to think about how I will rotate the shape. <a
href="http://www.w3.org/TR/css3-transforms/">CSS Transforms</a> seemed to be perfectly adapted for that. Let’s check that by doing some experiments. Blend 5 is really cool for that as you can directly see live the outcome of your changes.</p><p>Add a rotation of 90 degrees on <em>shape1</em> by adding this class to its DIV element:</p><pre class="code"><span style="color: maroon;">.shape1rotated </span>{
    <span style="color: red;">transform</span>: <span style="color: blue;">rotate(90deg)</span>;
}</pre><p>I’m sure you weren’t expecting this:</p><p><img
title="image" alt="Rotated shape" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/4666.image_5F00_thumb_5F00_15CE3F67.png" width="240" height="122" /></p><p>Problem: it’s not properly aligned to the gaming grid. To align our shape to the grid, we need some small adjustments:</p><pre class="code"><span style="color: maroon;">.shape1rotated </span>{
    <span style="color: red;">transform-origin</span>: <span style="color: blue;">33% 50%</span>;
    <span style="color: red;">transform</span>: <span style="color: blue;">rotate(90deg) translateX(-33%)</span>;
}</pre><p>And we now have the same rotation as a Tetris-like game. Here are two screenshots before/after the rotation:</p><p><img
alt="z-shape before rotation" src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/3414.image_5F00_22C81F78.png" /><br
/> <img
alt="z-shape after rotation" src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/8272.image_5F00_0E4560AB.png" /></p><p>We can even go further than that by using a transition set on shape1 via this:</p><pre class="code"><span style="color: red;">transition</span>: <span style="color: blue;">all 1s ease-out</span>;</pre><p>And now, removing/adding the <em>.shape1rotated</em> class on the shape1 DIV will trigger a smooth rotation animation.</p><p>Check out the result inside Blend 5 thanks to this short video:</p><div
class="video-js-box"><video
id="MSPointerEventsArticleVideo" class="video-js" poster="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo1.jpg" preload="preload" controls="controls" width="800" height="450"><source
type="video/mp4; codecs=&quot;avc1.42E01E, mp4a.40.2&quot;" src="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo1.mp4" /><source
type="video/webm; codecs=&quot;vp8, vorbis&quot;" src="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo1.webm" /><object
id="flash_fallback_1" class="vjs-flash-fallback" data="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf"<br /> width="800" type="application/x-shockwave-flash" height="450"><param
name="movie" value="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf" /><param
name="allowfullscreen" value="true" /><param
name="flashvars" value="config={&quot;playlist&quot;:[&quot;http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo1.jpg&quot;, {&quot;url&quot;: &quot;http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo1.mp4&quot;,&quot;autoPlay&quot;:false,&quot;autoBuffering&quot;:true}]}" /> <img
title="No video playback capabilities." alt="Poster Image" src="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo1.jpg" width="800" height="450" /> </object></video></p><p
class="vjs-no-video"><strong>Download Video:</strong> <a
href="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo1.mp4">MP4</a>, <a
href="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo1.webm">WebM</a>, <a
href="http://videojs.com">HTML5 Video Player</a> by VideoJS</p></div><p>At this stage, we could think that this approach is the good one to build our game. But this is unfortunately not yet the case. Here’s why. Try moving the shape by simply changing its <em>–ms-grid-column</em> property. Blend 5 will reflect the changes directly. When not rotated, the shape can be moved up to the 8th column:</p><p><img
alt="z-shape moved to the 8th column" src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/0118.image_5F00_4D7445B9.png" /></p><p>So far so good. But when you’re rotating it by adding the <em>.shape1rotated</em> class to the DIV:</p><p><img
alt="z-shape rotated to the 8th column" src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/0525.image_5F00_5A70B67B.png" /></p><p>You see that there is still 1 row available on the right for the shape’s move. If you think we simply need to move it to the 9th row, you’re wrong! Indeed, here is what we’ll obtain on the 9th row:</p><p><img
alt="z-shape gets compressed when moved to the 9th row" src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/1185.image_5F00_27A340B8.png" /></p><p>You probably forgot that we’re currently moving a DIV element displaying a grid layout of three columns and two rows matching exactly the underlying gaming grid. When moving it, we really have the feeling that this is a block part of the main grid we’re moving. But for this trick to work, we need at least three columns available to contain our shape element. If it’s contained in two columns (when set to the 9th column) or less, it will be “compressed”, as in the screenshot.</p><p>There are two ways to resolve that.</p><p>1 – Stop using CSS Transforms and draw the rotated shape using another grid definition. For instance, using three divs inside the shape instead of two. But using such an approach will prevent us having the nice CSS Animations in place.<br
/> 2 – Redefined the main grid to work on 12 columns instead of 10 and we’ll use only the columns from 2 to 11 (a sort of clipping area if you wish). This will resolve our “overflow” problem.</p><p>Let’s implement the second solution.</p><p>Redefine the main grid using this:</p><pre class="code"><span style="color: maroon;">.mainGrid </span>{
    <span style="color: red;">display</span>: <span style="color: blue;">-ms-grid</span>;
    <span style="background-color: #ffff00;"><span style="color: red;">-ms-grid-columns</span>: <span style="color: blue;">(1fr)[12]</span>;</span>
    <span style="color: red;">-ms-grid-rows</span>: <span style="color: blue;">(1fr)[20]</span>;
    <span style="color: red;">-ms-grid-column-align</span>: <span style="color: blue;">center</span>;
    <span style="color: red;">width</span>: <span style="color: blue;">100%</span>;
    <span style="color: red;">height</span>: <span style="color: blue;">100%</span>;
}</pre><p>To also have the proper ratio, you need to update the associated media query:</p><pre class="code"><span style="color: blue;">@media </span>screen and (-ms-view-state: fullscreen-landscape) {
    <span style="color: maroon;">.mainGrid </span>{
        <span style="background-color: #ffff00;"><span style="color: red;">width</span>: <span style="color: blue;">33.75%</span>;</span>
        }
}</pre><p>33.75% = 9/16 *12/20</p><p>Let’s also add a “virtual grid” to delimitate the space where we will be able to move the shapes.  Inside the main grid DIV, insert this one:</p><pre class="code"><span style="color: blue;">&lt;</span><span style="color: maroon;">div </span><span style="color: red;">class</span><span style="color: blue;">="virtualGrid"&gt;
&lt;/</span><span style="color: maroon;">div</span><span style="color: blue;">&gt;
</span></pre><p>Associated with this block of CSS:</p><pre class="code"><span style="color: maroon;">.virtualGrid </span>{
    <span style="color: red;">-ms-grid-column</span>: <span style="color: blue;">2</span>;
    <span style="color: red;">-ms-grid-column-span</span>: <span style="color: blue;">10</span>;
    <span style="color: red;">-ms-grid-row-span</span>: <span style="color: blue;">20</span>;
    <span style="color: red;">border-right-style</span>: <span style="color: blue;">dashed</span>;
    <span style="color: red;">border-left-style</span>: <span style="color: blue;">dashed</span>;
    <span style="color: red;">background-color</span>: <span style="color: blue;">#505A5A</span>;
}</pre><p>It will help to delimit the gaming area with a grey background and some dashed border lines.</p><p>Now, if I’m moving the Z shape on column 9, row 2, here is the result:</p><p><img
alt="z-shape at column 9" src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/4544.image_5F00_21CDE152.png" /></p><p>If I’m rotating it with CSS Transforms, I can move it correctly on column 10:</p><p><img
alt="Rotated z-shape moved correctly to column 9" src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/4048.image_5F00_07FC3BD4.png" /></p><h4>Bonus – handling the Portrait mode:</h4><p>If you’d like to support the portrait mode (which is even better for the Tetris grid game), add this CSS Media Query definition:</p><pre class="code"><span style="color: blue;">@media </span>screen and (-ms-view-state: fullscreen-portrait) {
        <span style="color: maroon;">.mainGrid </span>{
        <span style="color: red;">width</span>: <span style="color: blue;">106.66%</span>;
        }
}</pre><p>As the ratio need to be computed as = 16/9 * 12/20 = 106,66%.</p><p><img
alt="Tetris grid in landscape" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/1411.image_5F00_32C79A6A.png" /></p><h2>Step 3: adding some code to handle part of the game logic</h2><p>Now that we’ve solved the graphics part of the game using only some pure CSS &amp; HTML code, we need the help of JavaScript to move/rotate the shape in the gaming area. We’re going to re-implement the CSS logic via a JS object that’s going to be defined thanks to <a
href="http://msdn.microsoft.com/fr-fr/library/windows/apps/br229776.aspx">WinJS.Class</a>.</p><p>Open the &#8220;<em>TheRealCSSGridStory</em>&#8221; in Visual Studio 2012.</p><p>Create a <em>TetrisShapeZ.js</em> file in the JS directory and copy/paste this code:</p><pre>(<span style="color: blue;">function </span>() {
    <span style="color: maroon;">"use strict"</span>;
    <span style="color: blue;">var </span>ShapeZ = WinJS.Class.define(
    <span style="color: #006400;">/// Constructor - columnIndex is optional. If provided, defines the column the shape starts in
        </span><span style="color: blue;">function </span>(columnIndex) {
            <span style="color: #006400;">// We're creating the equivalent of this HTML block :
            // &lt;div class="shape1 "&gt;
            //     &lt;div class="line1shape1"&gt;&lt;/div&gt;
            //     &lt;div class="line2shape1"&gt;&lt;/div&gt;
            // &lt;/div&gt;
            </span><span style="color: blue;">this</span>._shape1 = document.createElement(<span style="color: maroon;">"div"</span>);
            <span style="color: blue;">var </span>line1 = document.createElement(<span style="color: maroon;">"div"</span>);
            <span style="color: blue;">var </span>line2 = document.createElement(<span style="color: maroon;">"div"</span>);
            <span style="color: blue;">this</span>._shape1.className = <span style="color: maroon;">"shape1"</span>;
            line1.className = <span style="color: maroon;">"line1shape1"</span>;
            line2.className = <span style="color: maroon;">"line2shape1"</span>;
            <span style="color: blue;">this</span>._shape1.appendChild(line1);
            <span style="color: blue;">this</span>._shape1.appendChild(line2);
            <span style="color: #006400;">// Boolean to indicate if the shape is in its default orientation mode or not
            // True means not rotated, false means rotated
            </span><span style="color: blue;">this</span>._defaultOrientation = <span style="color: blue;">true</span>;
            <span style="color: #006400;">// Setting the column position in the main grid
            </span><span style="color: blue;">if </span>(columnIndex) {
                <span style="color: blue;">this</span>._currentColPos = columnIndex;
                <span style="color: blue;">this</span>._shape1.style.msGridColumn = <span style="color: blue;">this</span>._currentColPos;
            }
            <span style="color: blue;">else </span>{
                <span style="color: blue;">this</span>._currentColPos = 1;
            }
            <span style="color: #006400;">// We always start at line 1
            </span><span style="color: blue;">this</span>._currentLinePos = 1;
            <span style="color: #006400;">// Boolean to know if the shape can be move/rotate or not
            // If true, this means we've reached the last line possible
            </span><span style="color: blue;">this</span>._fixed = <span style="color: blue;">false</span>;
        },
        {
            <span style="color: #006400;">/// Specify in which HTML element displayed in CSS Grid you'd like to work with
            /// width is the number of columns of the grid &amp; height is the number of lines
            </span>insertIntoGrid: <span style="color: blue;">function </span>(element, width, height) {
                element.appendChild(<span style="color: blue;">this</span>._shape1);
                <span style="color: blue;">this</span>._gridWidth = width;
                <span style="color: blue;">this</span>._gridHeight = height;
                <span style="color: #006400;">// These are the left &amp; bottom max limit for this shape
                // when displayed in default orientation mode
                </span><span style="color: blue;">this</span>._maxLeft = width - 3;
                <span style="color: blue;">this</span>._maxBottom = height - 1;
            },
            <span style="color: #006400;">/// Rotate the Z shape 90 degrees anti/clockwise using CSS Transforms
            /// by simply removing/adding the shape1rotated class
            </span>rotate: <span style="color: blue;">function </span>() {
                <span style="color: blue;">if </span>(!<span style="color: blue;">this</span>._fixed &amp;&amp; <span style="color: blue;">this</span>._defaultOrientation) {
                    <span style="color: #006400;">// rotating 90 degrees clockwise, it will trigger also the CSS Transition
                    </span>WinJS.Utilities.addClass(<span style="color: blue;">this</span>._shape1, <span style="color: maroon;">"shape1rotated"</span>);
                    <span style="color: blue;">this</span>._defaultOrientation = <span style="color: blue;">false</span>;
                    <span style="color: #006400;">// the left limit is now +1 vs the default orientation
                    </span><span style="color: blue;">this</span>._maxLeft = <span style="color: blue;">this</span>._gridWidth - 2;
                }
                <span style="color: blue;">else </span>{
                    <span style="color: blue;">if </span>(!<span style="color: blue;">this</span>._fixed &amp;&amp; <span style="color: blue;">this</span>._currentColPos &lt; <span style="color: blue;">this</span>._maxLeft) {
                        <span style="color: #006400;">// removing the shape1rotated will automatically reset the shape
                        // to its initial matrix and again the CSS Transition will do the
                        // animation for you
                        </span>WinJS.Utilities.removeClass(<span style="color: blue;">this</span>._shape1, <span style="color: maroon;">"shape1rotated"</span>);
                        <span style="color: blue;">this</span>._defaultOrientation = <span style="color: blue;">true</span>;
                        <span style="color: blue;">this</span>._maxLeft = <span style="color: blue;">this</span>._gridWidth - 3;
                    }
                }
            },
            <span style="color: #006400;">// Internal function called by public moveLeft/moveRight
            </span>_moveHorizontally: <span style="color: blue;">function </span>(direction) {
                <span style="color: blue;">if </span>(!<span style="color: blue;">this</span>._fixed &amp;&amp; (<span style="color: blue;">this</span>._currentColPos &lt; <span style="color: blue;">this</span>._maxLeft || direction === -1) &amp;&amp;
                (<span style="color: blue;">this</span>._currentColPos &gt; 2 || direction === 1)) {
                    <span style="color: blue;">this</span>._currentColPos = <span style="color: blue;">this</span>._currentColPos + direction;
                    <span style="color: blue;">this</span>._shape1.style.msGridColumn = <span style="color: blue;">this</span>._currentColPos;
                }
            },
            <span style="color: #006400;">/// Move the shape on the immediate left column
            /// Test if you've reached the left limit or not
            </span>moveLeft: <span style="color: blue;">function </span>() {
                <span style="color: blue;">this</span>._moveHorizontally(-1);
            },
            <span style="color: #006400;">/// Move the shape on the immediate right column
            /// Test if you've reached the right limit or not
            </span>moveRight: <span style="color: blue;">function </span>() {
                <span style="color: blue;">this</span>._moveHorizontally(1);
            },
            <span style="color: #006400;">/// Move the shape down on the immediate below line
            /// Test if you've reached the bottom limit or not
            </span>moveDown: <span style="color: blue;">function </span>() {
                <span style="color: blue;">if </span>(!<span style="color: blue;">this</span>._fixed) {
                    <span style="color: blue;">this</span>._currentLinePos = <span style="color: blue;">this</span>._currentLinePos + 1;
                    <span style="color: blue;">this</span>._shape1.style.msGridRow = <span style="color: blue;">this</span>._currentLinePos;
                    <span style="color: blue;">if </span>(<span style="color: blue;">this</span>._currentLinePos === <span style="color: blue;">this</span>._maxBottom) {
                        <span style="color: blue;">this</span>._fixed = <span style="color: blue;">true</span>;
                    }
                }
            }
        }
    );
    WinJS.Namespace.define(<span style="color: maroon;">"CSSTetris"</span>, { ShapeZ: ShapeZ });
} ());</pre><p>Simply read the code to understand what it’s doing. It should be commented enough to be self-explicit.</p><p>Add a reference to this script file in <em>default.html </em>and keep only this block of HTML inside the body:</p><pre class="code"><span style="color: blue;">&lt;</span><span style="color: maroon;">div </span><span style="color: red;">class</span><span style="color: blue;">="mainGrid"&gt;
    &lt;</span><span style="color: maroon;">div </span><span style="color: red;">class</span><span style="color: blue;">="virtualGrid"&gt;
    &lt;/</span><span style="color: maroon;">div</span><span style="color: blue;">&gt;
&lt;/</span><span style="color: maroon;">div</span><span style="color: blue;">&gt;
</span></pre><p>Jump into <em>default.js</em>.</p><p>The cool part of having well-documented code is that we now have interesting IntelliSense details like for the <strong>constructor</strong>:</p><p><img
alt="Intellisense for ShapeZ(columnIndex) constructor" src="http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/0574.image_5F00_3C57958D.png" /></p><p>or the <strong>rotate</strong> function:</p><p><img
alt="Intellisense for the rotate function" src="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-10-46-metablogapi/0160.image_5F00_1BD2E68C.png" /></p><p>To properly use this code, add this block of JS just after <em>processAll</em> call:</p><pre class="code">document.addEventListener(<span style="color: maroon;">"keydown"</span>, OnKeyDown, <span style="color: blue;">false</span>);
mainGrid = document.getElementsByClassName(<span style="color: maroon;">"mainGrid"</span>)[0];
myShape = <span style="color: blue;">new </span>CSSTetris.ShapeZ(4);
myShape.insertIntoGrid(mainGrid, 12, 20);
init();</pre><p>And add these two functions:</p><pre class="code"><span style="color: blue;">function </span>init() {
    setInterval(<span style="color: blue;">function </span>() {
        myShape.moveDown();
    }, 1000);
}
<span style="color: blue;">function </span>OnKeyDown(event) {
    <span style="color: blue;">switch </span>(event.keyCode) {
        <span style="color: blue;">case </span>KEYCODE_X:
            myShape.rotate();
            <span style="color: blue;">break</span>;
        <span style="color: blue;">case </span>KEYCODE_LEFT:
            myShape.moveLeft();
            <span style="color: blue;">break</span>;
        <span style="color: blue;">case </span>KEYCODE_RIGHT:
            myShape.moveRight();
            <span style="color: blue;">break</span>;
        <span style="color: blue;">case </span>KEYCODE_DOWN:
            myShape.moveDown();
            <span style="color: blue;">break</span>;
    }
}</pre><p>And we’re done! We now have a very basic game using CSS Grid Layout coupled with CSS Transforms &amp; Animations for the graphics part and a couple of JS lines of code to have the beginning of the basics of a Tetris-like game.</p><p>Here is a short video demonstrating the final result:</p><div
class="video-js-box"><video
id="MSPointerEventsArticleVideo" class="video-js" poster="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo2.jpg" preload="preload" controls="controls" width="705" height="450"><source
type="video/mp4; codecs=&quot;avc1.42E01E, mp4a.40.2&quot;" src="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo2.mp4" /><source
type="video/webm; codecs=&quot;vp8, vorbis&quot;" src="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo2.webm" /><object
id="flash_fallback_1" class="vjs-flash-fallback" data="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf"<br /> width="705" type="application/x-shockwave-flash" height="450"><param
name="movie" value="http://releases.flowplayer.org/swf/flowplayer-3.2.1.swf" /><param
name="allowfullscreen" value="true" /><param
name="flashvars" value="config={&quot;playlist&quot;:[&quot;http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo2.jpg&quot;, {&quot;url&quot;: &quot;http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo2.mp4&quot;,&quot;autoPlay&quot;:false,&quot;autoBuffering&quot;:true}]}" /><br
/> <img
title="No video playback capabilities." alt="Poster Image" src="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo2.jpg" width="705" height="450" /><br
/> </object></video></p><p
class="vjs-no-video"><strong>Download Video:</strong> <a
href="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo2.mp4">MP4</a>, <a
href="http://david.blob.core.windows.net/videos/TheRealCSSGridStoryVideo2.webm">WebM</a>, <a
href="http://videojs.com">HTML5 Video Player</a> by VideoJS</p></div><p>You can download the final Visual Studio solution corresponding to the three steps of this tutorial here: <a
title="http://david.blob.core.windows.net/win8/TheRealCSSGridStory.zip" href="http://david.blob.core.windows.net/win8/TheRealCSSGridStory.zip">http://david.blob.core.windows.net/win8/TheRealCSSGridStory.zip</a></p><p>So, are you now convinced that CSS Grid Layout was made to simplify the creation of Tetris-like games?</p> <footer><hr
/><p>This article is part of the HTML5 tech series from the Internet Explorer team. Try-out the concepts in this article with 3 months of free BrowserStack cross-browser testing @ <a
href="http://modern.IE">http://modern.IE</a>.</p><hr
/><p>This article originally appeared on David Rousset&#8217;s MSDN blog, Coding4Fun on <a
href="http://blogs.msdn.com/b/davrous/archive/2013/02/12/coding4fun-build-a-tetris-like-game-using-css-grid-layout-amp-blend-5.aspx">12 Feb 2013</a>.</p> </footer><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/using-css-grid-layout-and-blend-5-to-build-a-game/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Skimr: The Inside Story</title><link>http://www.sitepoint.com/skimr-the-inside-story/</link> <comments>http://www.sitepoint.com/skimr-the-inside-story/#comments</comments> <pubDate>Tue, 16 Apr 2013 13:46:02 +0000</pubDate> <dc:creator>Petr Kral</dc:creator> <category><![CDATA[CSS]]></category> <category><![CDATA[HTML]]></category> <category><![CDATA[JavaScript]]></category> <category><![CDATA[Responsive Web Design]]></category> <category><![CDATA[skimr]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65448</guid> <description><![CDATA[Petr Kral, co-founder of simplified RSS reader Skimr, gives us the lowdown on how the project came about, what it took to make it work and how it came to take off. ]]></description> <content:encoded><![CDATA[<p></p><p>I met my two <a
href="http://skimr.co/">Skimr</a> co-founders, Josf and Davd, at my current employer &#8211; Seznam.cz. It&#8217;s basically the Czech Yahoo &#8211; email, maps, news, search, classifieds, etc. We are part of a small team (skunk works or Google X, if you will) where we explore new product ideas, create prototypes, experiment &#8230; some of these make it into production.</p><p>BTW, my colleagues&#8217; real names are Josef and David. The shortened versions, Josf and Davd, come from VentureBeat&#8217;s Dylan Tweney. He thought my name was Peter and I had shortened my name in the same way we shortened the Skimr name (instead of Skimmer). So then we applied that to my colleagues&#8217; names. We think it&#8217;s pretty funny, so we keep referring to them as Josf and Davd.</p><p><a
href="http://www.skimr.co"><img
class="alignnone size-full wp-image-65449" alt="Davd, Petr and Josf" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/04/team.png" width="512" height="384" /></a></p><p>I&#8217;ve been using Google Reader for a long time. Basically since the day it launched. I then moved to the popular Reeder app. My favorite subscriptions have always been TechCrunch, Techmeme, etc. However, due to the time difference between Europe and US, the majority of articles were published during the night. So in the morning, I had a gazillion of new unread posts &#8211; impossible to read them all.<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><p>So I was thinking of a way to literally skim through the headlines and quickly see what&#8217;s new. I realized the title alone was sometimes not enough, but a combination of the title plus the first one or two sentences was typically enough to get the basic story. If it was interesting enough, I could click on it and read the full article.</p><p>After I realized this, I asked Josf if we could take Techmeme&#8217;s RSS feed and display it on a web page in this format. A few days later, I was using this web page all the time. I showed it to my friends and they all asked if they could add their own feeds.</p><p>I put together some mockups, and Josf started coding them. Then, I asked my other colleague Davd, who is a great designer, to help me make the mockups look nice. You know, fonts, colors, proportions, etc. At this stage, we were still building a small tool for ourselves and our friends. So the backend was very basic, only able to handle tens of unique RSS feeds, max.</p><p>A few weeks later, Skimr was up and running and all our friends were using it. Daily. Many times a day. It was unbelievable. It encouraged me to post it on a few sites and see what other people had to say. And that&#8217;s when it started to get complicated &#8230;</p><p><a
href="http://www.skimr.co"><img
class="alignnone size-full wp-image-65450" alt="skimr screen shot" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/04/sitepoint.png" width="600" height="560" /></a></p><p>We didn&#8217;t really know any journalists or bloggers outside of the Czech Republic who we could ask to write a review. So, I went to Quora and found a few posts there that were listing websites where people could submit their projects. After submitting Skimr to approx 20 websites, I decided I was not going to continue this way because it had no effect at all.</p><p>There was one site left on my list &#8211; Web.Appstorm.net. So, I pushed myself and submitted Skimr there too. Five minutes later, I hade an email from their editor, Matthew Guay, saying he was just looking for something interesting to write about for the upcoming weekend and out of nowhere, Skimr landed in his mailbox. He liked the service, so he immediately wrote a <a
href="http://web.appstorm.net/reviews/media-reviews/skimr-a-slick-and-simple-rss-reader/">review</a>.</p><p>And then on Monday, more reviews started to pop up.</p><p>I was extremely happy. Davd, too. Only Josf was furious. His backend was absolutely not built for a larger user base. Fortunately, we were running Skimr on AWS, so there was always an option to rent more instances. Also, we have learned there is a devastating conversion rate between tweet-article-visitor-registered user.</p><p>Let&#8217;s say a blogger writes an article and promotes it to his 10,000 followers on Twitter. 10% of them will click on the link and read the article. That&#8217;s 1,000 people. 10% of them will click on the link within the article and actually visit your site. That&#8217;s 100 people. And then 10% of them sign up, that&#8217;s 10 people.</p><p>So, a few weeks later, we had a small user base of people who really liked the service, were sending us emails about features they wanted us to add and telling their friends their friends about Skimr. They were adding a few unique feeds an hour, so no big deal.</p><p>But then, Google announced they were going to shut down their Reader. All of a sudden, people all around the world started looking for alternatives. Instead of a few unique RSS feeds being added to Skimr every hour, there were 100 unique feeds added every minute. And then 1,000 feeds every minute. We kept upgrading our AWS instances, launching more and more download robots, renting more storage, etc.</p><p>It was fascinating. But we knew it was only a matter of time when our tiny system would break under such a heavy load.</p><p>What&#8217;s more, journalists became very responsive. I started hearing back from them. Even Walt Mossberg replied to my email to him. Coming from a small country, it was amazing.</p><p>Fortunately, over the weekend, people calmed down a bit. We immediately contacted my long time friend Radm who is an excellent backend programmer to help us out. The following paragraphs come from him:</p><blockquote><p>The current version is build on LAMP technology + JavaScript based front end. This solution works perfectly as a proof of concept and is able to handle traffic up to a certain degree.</p><p>After Google announced the end of its Reader, the traffic grew significantly and we handled this situation by adding new AWS servers. This solution had one big disadvantage &#8211; it was pretty expensive and the PHP backend was not really using the servers efficiently. To solve this challenge as a first step we decided to migrate the site from AWS to our own dedicated servers (based in Prague). This saved us approximately 2/3 of the operational costs.</p><p>As a next step we worked on a rewrite of the site to a more efficient technology &#8211; to the same technology we have been successfully using for several years now at <a
href="http://www.agentka.cz/">www.agentka.cz</a> (a popular local job site). Usually, one of the bottlenecks on heavily used sites is the DB which is under high load. This is a paradox as most of the sites are not really using advanced (sometimes even basic) SQL database features the way they were designed &#8211; heavy loaded databases are usually denormalized and serves only as a persistent storage. The situation is even worse when it is required to perform full-text searches over all stored data.</p><p>In the past, this observation led us to drop SQL database and use a special indexer that stores documents with a very loose structure and provides very fast full-text search features and, of course, provides support for atomic operations. This indexer is the heart of the site and it is used for storing all data, it also allows us to scale the site the way we need to. After we migrate the current Skimr implementation off LAMP to the described one we will be able to provide a lot of nice features to our users &#8211; for example full text search in the feeds. This work is in progress and we expect to have a new version in several weeks’ time.</p></blockquote><p>Launching a web project always brings this dilemma &#8211; launch quickly and risk the system breaking if it becomes popular; or build it properly first and be ready. If we had chosen the second approach, we would have completely missed the window of opportunity that appeared when Google anounced the Reader closure.</p><p>Let&#8217;s go from the backend to seeing how the frontend works. It has two basic parts:</p><ol><li>Static pages generated on the server via PHP, e.g. <a
href="http://skimr.co/about">http://skimr.co/about</a></li><li>Dynamic pages generated on the client in javascript, e.g. <a
href="http://skimr.co/techcrunch">http://skimr.co/techcrunch</a></li></ol><p>Interesting is the second part – dynamic pages. The core of the application is just a vanilla JavaScript build with static object literals (single modules for homepage, articles, updater, etc). We don’t use any big JavaScript library like jQuery. We want to have full control with a focus on speed and simplicity.</p><p>Rendering of HTML is done strictly by Dust.js with precompiled templates on the server. JavaScripts don’t contain any HTML code or any direct manipulation of the DOM. We just supply data from the server via the JSON API, process them and send them to a render function that generates HTML for an active page.</p><p>Working with URLs is done by HTML5 function <code>pushState()</code> with a fallback to hash. This is very important and the main key to a really &#8220;responsive&#8221; application, because you are able to “switch” pages instantly.</p><p>All in all, the frontend is quite simple and completely responsive &#8211; works nicely on desktops, tablets and smartphones, out of the box. Everything is done just in CSS, we don’t have any application logic to support responsive function of the application. We are also using LESS in style sheets. We can definitely recommend LESS for any kind of web or application.</p><p>Regarding design, I wanted Skimr to be as simple as possible. I&#8217;ve always thought traditional RSS readers were a bit too complicated for the average users. But I liked the concept of pulling in content via RSS and showing it in a unified form. Yet, I thought it was fair for us to only show a small part of the original content, not the whole article. If people liked it, they could click and read it on the publisher&#8217;s website.</p><p>The biggest problem of traditional websites is the number of elements people try to put on them. All the menus, options, contextual content, etc. I find them quite distracting. The point of any website should be to solve a specific need and then let go.</p><p>When it comes to news sites, they should tell their users what&#8217;s new and that&#8217;s about it. So I made sure there was a clear purpose as to why Skimr existed &#8211; to quickly see what&#8217;s new. After a few iterations, I&#8217;ve realized there was a simpler way than a traditional RSS reader &#8211; just a simple feed. Latest entries on the top, older below. Nothing more, nothing less. It sounds so obvious now, but back then, it meant ditching the majority of features a good RSS reader would have.</p><p>Another interesting aspect of building Skimr is that of native vs. web apps. I had an <a
href="http://www.zdnet.com/do-ios-users-really-want-mobile-web-applications-7000013326/">interview with ZDNet’s Dave Morgenstern</a> on this topic.</p><p>Basically, I think very soon, people won’t be able to tell the difference between native and web apps. The latest developments in CSS, JS and HTML 5 enable app designers to create almost identical user experiences to traditional native apps.</p><p>So, I thought web app would be totally enough. But I was wrong.</p><p>One of the main requests from our users are native apps. They think native apps offer better performance. Since it’s basically a distribution channel, we have decided to build these for them.</p><p>Speaking of new feature requests, folders are an interesting story. A long time ago, I used to be subscribed to many websites in Google Reader. Over time, I reduced the number to approximately 10. On that principle, we&#8217;ve designed Skimr for users with few feeds. It turned out very quickly that there are many people all around the world who still consume hundreds or maybe even thousands of feeds. In this scenario, folders are necessary to organize these feeds. So, lesson learned: we&#8217;ve developed <a
href="http://blog.skimr.co/2013/04/folders-are-here.html">folders for our users</a>.</p><p>We have many ideas as to how to make Skimr even better for our users, for example native apps, search (within a user’s RSS feeds or even all the RSS feeds in the system). But it is very important to always think twice before implementing a user request.</p><p>Since we are not building a traditional RSS reader, some of our users (with all due respect), don’t quite get this, so they ask for features they are used to on Google Reader. Our path is different from Google Reader, Feedly, etc., so we might not make everyone happy. The benefit of this approach, however, is that the majority of our users are actually very happy we are offering something different. We have found a niche, where people are fed up by the complex readers out there and are happy with the simplicity Skimr offers. They are even sending us emails asking us to keep Skimr as is and not add any new features at all. Amazing!</p><p>Going forward, we can expect some spikes in traffic around June, when Google will definitely sunset Reader. We will do our best to prepare for them properly, mainly rewriting the backend to the no-DB stuff.</p><p>But, in general, we are more interested in organic growth, rather than overnight success. It helps to create a bond with the users, listen to what they have to say and carefully improve the product. After all, Skimr is a hobby project for us. We all have full time jobs.</p><p>Having said that, there are obvious business models we could try, especially Freemium (some special features for a small fee).</p><p>So far, the Skimr project has been full of surprises, so who know what comes next?</p><p><a
href="http://www.skimr.co"><img
class="alignnone size-full wp-image-65451" alt="skimr logo" src="http://www.sitepoint.com/wp-content/uploads/1/files/2013/04/skimr.png" width="258" height="328" /></a></p><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/skimr-the-inside-story/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>Creating a Notepad app with indexedDB</title><link>http://www.sitepoint.com/creating-a-notepad-app-with-indexeddb/</link> <comments>http://www.sitepoint.com/creating-a-notepad-app-with-indexeddb/#comments</comments> <pubDate>Sun, 14 Apr 2013 07:07:14 +0000</pubDate> <dc:creator>Sandeep Panda</dc:creator> <category><![CDATA[APIs]]></category> <category><![CDATA[HTML5]]></category> <category><![CDATA[JavaScript]]></category> <guid
isPermaLink="false">http://www.sitepoint.com/?p=65425</guid> <description><![CDATA[In this tutorial, Sandeep Panda gives an overview of indexedDB API and explains how to use it to create a simple notepad application.]]></description> <content:encoded><![CDATA[<p></p><p>indexedDB, which is new in HTML5, allows developers to persist data within the web browser. As a result your app runs both online and offline with powerful query facilities. indexedDB is different from traditional relational databases in that it is an <code>objectStore</code> instead of a collection of rows and columns. You just create an <code>objectStore</code> in indexedDB and store JavaScript objects in that store. Furthermore, it’s very easy to perform CRUD operations on your stored objects. This tutorial gives an overview of indexedDB API and explains how to use this to create a simple notepad application.</p><p>Before we get started note that indexedDB API specification has not stabilized yet. But if you have the latest Firefox or Google Chrome installed on your machine you are good to go. To know which browser versions support the API see the <a
href="http://caniuse.com/indexeddb">compatibility table</a>.</p><p>There are two types of APIs in indexedDB spec: synchronous and asynchronous. However, we will focus on the asynchronous API as currently this is the only API that’s supported by browsers. Asynchronous means you perform an operation on the database and receive the result in a callback through a DOM event.<div
id='div-gpt-ad-1328644474660-10' style='width:728px; height:90px;'> <script type='text/javascript'>googletag.cmd.push(function() { googletag.display('div-gpt-ad-1328644474660-10'); });</script> </div></p><p>In any note-making app there are four simple functions: Create, Read, Update and Delete. indexedDB provides very simple APIs to perform these operations. But before doing anything we need to create a database and open it.</p><h2>Setting Up:</h2><p>Since the specification has not stabilized yet, different browsers use prefixes in their implementations. So, you need to check correctly to ensure that a browser supports indexedDB. Use the following code to ensure that a browser supports indexedDB.</p><pre>window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB ||
    window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction ||
    window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
if (!window.indexedDB) {
    alert("Sorry!Your browser doesn't support IndexedDB");
}</pre><h2>Opening/Creating a Database:</h2><p>Opening an indexedDB database is a matter of calling <code>indexedDB.open()</code>.</p><p><code>var request = window.indexedDB.open("notepad",1);</code></p><p><code>indexedDB.open()</code> accepts two parameters. The first one represents the database name. If the database does not already exist it creates a new one. The second parameter is the version number. If you need to update the database schema in future you need to call <code>indexedDB.open()function</code> with a version number higher than the previous one. In that case you will need to implement <code>onupgradeneeded</code> callback where you can update the database schema and create/delete <code>objectStores</code>.</p><p>Creating the database is the first step. But to store something you need to have an <code>objectStore</code>. When you create a database, you will probably want to create an <code>objectStore</code> as well. When a database is initially created <code>onupgradeneeded</code> callback is fired where you can create an <code>objectStore</code>.</p><pre>var database;
var request = window.indexedDB.open("notepad",1);
request.onerror = function(event) {
console.log(event.target.errorCode);
};
request.onsuccess = function(event) {
    database=request.result;
};
request.onupgradeneeded = function(event) {
    var db = event.target.result;
    var objectStore = db.createObjectStore("notes", { keyPath:  "id",autoIncrement:true});
};</pre><p>In the above code sample we call <code>indexedDB.open()</code> with database name notepad and version number 1. The method returns an  <code>IDBOpenDBRequest</code>. When the database open request succeeds the <code>request.onsuccess</code> callback is fired. The result property of request is an instance of <code>IDBDatabase</code> which we assign to variable database for later use.</p><p>Inside <code>onupgradeneeded</code> callback we get a reference to the database and use it to create a new <code>objectStore</code> called <code>notes</code>. <code>createObjectStore()</code> function also accepts a second parameter.  In this case we have defined a <code>keyPath</code> called <code>id</code> that uniquely identifies an object in our store. In addition we also want the id to be <code>autoIncrementing</code>.</p><h2>Adding/Updating an Item in the objectStore:</h2><p>Let’s say we want to persist a note in the store. The object should have fields like: title, body and a creation date. To store the object use the following code:</p><pre>var note={title:”Test Note”, body:”Hello World!”, date:”01/04/2013”};
var transaction = database.transaction(["notes"], "readwrite");
var objectStore = transaction.objectStore("notes");
var request=objectStore.put(note);
request.onsuccess = function(event) {
    //do something here
};</pre><p><code>database.transaction()</code> takes an array as first parameter which represents the names of <code>objectStores</code> this transaction spans. The second parameter determines the type of transaction. If you don’t pass a second argument you will get a read only transaction. Since we want to add a new item we pass <code>readwrite</code> as the second argument. As a result of this call we get a transaction object. <code>transaction.objectStore()</code> selects an <code>objectStore</code> to operate on.   Finally <code>objectStore.put()</code> adds the object to the store. You can also use <code>objectStore.add()</code> to add an object. But the former will update an object in the store if we attempt to add a new object with an <code>id</code> the same as the <code>id</code> of an existing entry.</p><h2>Deleting an Item from the store:</h2><p>Deleting an object from the store is pretty simple and straightforward.</p><pre>var request = database.transaction(["notes"], "readwrite") .objectStore("notes").delete(20);
request.onsuccess = function(event) {
    //handle success
};</pre><p>The above code deletes an object from the store which has an <code>id</code> of 20.</p><h2>Querying all objects in store:</h2><p>For any database driven app it’s very common to display all the stored entries.  In indexedDB you can get the objects stored in the store and iterate through them with the help of a cursor.</p><pre>var objectStore = database.transaction("notes").objectStore("notes");
objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if (cursor) {
        alert("Note id: "+cursor.key+", Title: "+cursor.value.title);
        cursor.continue();
    }
};</pre><p>The above code is very simple to understand.  The <code>openCursor()</code> function can accept several arguments. You can control the range of results returned and the direction of iteration by passing appropriate parameters. The cursor object is the result of the request. <code>cursor.continue()</code> should be called if you expect multiple objects and want to iterate through them. This means as long as there is more data <code>onsuccess</code> callback is fired, provided you call <code>cursor.continue()</code>.</p><p>So, this is all you should know before developing the notepad app using indexedDB.  Now, I will show how to create the app step by step.</p><h2>Initial HTML markup:</h2><pre>&lt;html&gt;
&lt;head&gt;&lt;title&gt;Simple Notepad Using indexedDB&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id="container"&gt;
    &lt;h3 id="heading"&gt;Add a note&lt;/h3&gt;
    &lt;input type="hidden" value="0" id="flag"/&gt;
    &lt;a href="#" id="add"&gt;&lt;img src="add.png" onclick="createNote(0)"/&gt; New&lt;/a&gt;
    &lt;a href="#" id="back"&gt;&lt;img src="back.png" onclick="goBack()"/&gt;&lt;/a&gt;
    &lt;div id="notes"&gt;&lt;/div&gt;
    &lt;div id="editor" contenteditable="true"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</pre><h3>Explanation:</h3><p>We have two divs: notes and editor. The first one is used for showing a list of existing notes and the second one is used as an editor to write a note.  The editor div is initially invisible. When the user clicks on the <strong>add</strong> button we hide the notes div and show the editor div. You should keep in mind that by setting <code>contenteditable="true"</code> we are making a div editable. We also have a hidden input field with id flag. This will be used later in the tutorial.</p><h2>The JavaScript:</h2><pre>&lt;script type="text/javascript"&gt;
var database;
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB ||
    window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction ||
    window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
if (!window.indexedDB) {
    alert("Sorry!Your browser doesn't support IndexedDB");
}
function init(){
    var request = window.indexedDB.open("notepad",1);
    request.onerror = function(event) {
    console.log(event.target.errorCode);
};
request.onsuccess = function(event) {
    database=request.result;
    showNotes();
};
request.onupgradeneeded = function(event) {
    var db = event.target.result;
    var objectStore = db.createObjectStore("notes", { keyPath: "id",autoIncrement:true});
    };
}
function createNote(id){
    document.getElementById("editor").style.display="block";
    document.getElementById("editor").focus();
    document.getElementById("back").style.display="block";
    document.getElementById("add").style.display="none";
    document.getElementById("notes").style.display="none";
    if(parseInt(id)!=0){
    database.transaction("notes").objectStore("notes").get(parseInt(id))
    .onsuccess = function(event) {
document.getElementById("editor").innerHTML=event.target.result.body;
    document.getElementById("flag").value=id;
};
}
}
function goBack(){
    var note={};
    note.body=document.getElementById("editor").innerHTML;
    note.title=getTitle(note.body);
    note.date=getDate();
    var flag=parseInt(document.getElementById("flag").value);
    if(flag!=0)
      note.id=flag;
    if(note.title.trim()==="")
        window.location.href="index.html";
    else
        addNote(note);
    }
function getDate(){
    var date=new Date();
    var month=parseInt(date.getMonth())+1;
    return date.getDate()+"/"+month+"/"+date.getFullYear();
}
function getTitle(body){
    var body = body.replace(/(&lt;([^&gt;]+)&gt;)/ig,"");
    if(body.length &gt; 20) body = body.substring(0,20)+". . .";
        return body;
}
function addNote(note){
    var transaction = database.transaction(["notes"], "readwrite");
    var objectStore = transaction.objectStore("notes");
    var request=objectStore.put(note);
    request.onsuccess = function(event) {
        document.getElementById("flag").value="0";
        window.location.href="index.html";
        };
    }
function showNotes(){
var notes="";
var objectStore = database.transaction("notes").objectStore("notes");
objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if (cursor) {
        var link="&lt;a class=\"notelist\" id=\""+cursor.key+"\" href=\"#\"&gt;"+cursor.value.title+"&lt;/a&gt;"+"
        &lt;img class=\"delete\" src=\"delete.png\" height=\"30px\" id=\""+cursor.key+"\"/&gt;";
        var listItem="&lt;li&gt;"+link+"&lt;/li&gt;";
        notes=notes+listItem;
        cursor.continue();
    }
    else
    {
    document.getElementById("notes").innerHTML="&lt;ul&gt;"+notes+"&lt;/ul&gt;";
    registerEdit();
    registerDelete();
    }
};
}
function deleteNote(id){
var request = database.transaction(["notes"], "readwrite")
        .objectStore("notes")
        .delete(id);
request.onsuccess = function(event) {
    window.location.href="index.html";
};
}
function registerEdit(){
var elements = document.getElementsByClassName('notelist');
for(var i = 0, length = elements.length; i &lt; length; i++) {
    elements[i].onclick = function (e) {
        createNote(this.id);
    }
}
}
function registerDelete(){
var deleteButtons = document.getElementsByClassName('delete');
    for(var i = 0, length = deleteButtons.length; i &lt; length; i++){
        deleteButtons[i].onclick=function(e){
        deleteNote(parseInt(this.id));
        }
    }
}
window.addEventListener("DOMContentLoaded", init, false);
&lt;/script&gt;</pre><h3>Explanation:</h3><p>The <code>init</code> method does the necessary initialization. It creates/opens the database and also creates an <code>objectStore</code> when the database is first created.  After the database is successfully opened we get a reference to it and store it in a database variable.</p><p><code>showNotes()</code> function displays a list of notes created by the user. We start a transaction and get the note objects that are present in the store. Then we create an unordered list of the note titles and finally display it in the div having <code>id</code> notes. We also call two functions <code>registerEdit()</code> and <code>registerDelete()</code>. The first function attaches a click event listener to the note titles which are simple links having class notelist so that the notes can be edited when someone clicks on the title. The latter function adds a click event listener to the delete buttons (simple images) that are present beside the note titles. By doing this we can delete a note when someone clicks on delete button. The <code>deleteNote()</code> function performs the delete operation.</p><p>The <code>createNote()</code> function displays an editor to create a new note or update an existing one . It accepts one argument. If it’s 0, we know that we want to create a new note. Otherwise we start a transaction to get the content of an existing note. We pass the <code>id</code> of the note to <code>objectStore.get()</code> and in <code>onsuccess</code> we fetch the body of the note. Then we populate the editor with the fetched note body. In addition we set our hidden input flag to the id which is used in <code>goBack()</code> function.  This method is fired when the user wants to go back after writing a note. This is where we save a note in the store.</p><p>In the <code>goBack()</code> function we create a new object and set its title, body and date property. The title is taken as the first 20 characters of the body. Then find out the value of hidden flag. If it’s not 0 we know we want to update an existing note. Therefore, we set the <code>id</code> property of the created object. Otherwise there is no need of an <code>id</code> property as the object is going to be a new entry in the store. In the end <code>addNote()</code> function is called with the note object as argument. The <code>addNote()</code> function simply starts a transaction that adds/updates an object in the store. If the transaction succeeds we take the user back where he/she can see a list of created notes.</p><p>You can try out the demo app <a
href="http://extremecss.com/demos/notepad">here</a>. I have tested the app in Chrome 25 and Firefox 20.</p><p>indexedDB is a great API in HTML5 and when used with app cache can be very powerful. <a
href="https://developer.mozilla.org/en-US/docs/IndexedDB">Mozilla</a> has some interesting and very useful information about indexedDB. Do check them out if you want to know more.</p><p>If you are not able to get something let me know in the comments.</p><div
class='after-content-widget-1'><div
id="sitepointcontextualcontentmanagerwidget-5" class="widget widget_sitepointcontextualcontentmanagerwidget">&nbsp;</div></div>]]></content:encoded> <wfw:commentRss>http://www.sitepoint.com/creating-a-notepad-app-with-indexeddb/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using memcached
Database Caching 38/74 queries in 0.140 seconds using memcached
Object Caching 1937/2097 objects using memcached

Served from: www.sitepoint.com @ 2013-05-13 13:31:26 --