John Dyer
view rss
Cross-Browser Native Get/Set Properties in JavaScript
2/5/2012 external link
JavaScript Doesn’t Support Properties When I first created MediaElement.js a few years ago, I wanted to make a JavaScript object that fully mimicked the HTML5 Media API, but under the hood it might have a true <video> tag or a Flash object doing the rendering. The problem I quickly found was that the <video> tag has several properties like .src and .volume that can’t be replicated since JavaScript doesn’t have true get/set capability that works in all browsers (by “all browsers” I mean “IE6 and up”). So I had to create methods like .setSrc() and .setVolume() (or use jQuery’s .volume() syntax) to make it work correctly across browsers, but that resulted in a API that didn’t match the HTML5 spec. Ah, But JavaScript Does Support Properties A long time ago, in Firefox’s distant past there was a proprietary way to create properties, using the __defineGetter__ and __defineSetter__methods. Here’s what it looks like: var myObject = {}; myObject.seconds = 22; myObject.__defineGetter__('milliseconds', function() { return this.seconds / 1000;} ); myObject.__defineSetter__('milliseconds', function(value) { this.seconds = value * 1000;}); myObject.milliseconds = 1750; console.log(myObject.seconds); // outputs 1.75; It’s great because it allows you to easily define properties that can do additional calculation work (e.g., from milliseconds to seconds), but it doesn’t work in all other browsers. The method that is more universally available is Object.defineProperty which offers a slightly cleaner syntax: var myObject = {}; myObject.seconds = 22; Object.defineProperty(obj, 'milliseconds', { get: function() { return this.seconds / 1000;}, set: function(value) { this.seconds = value * 1000;} }); myObject.milliseconds = 1750; console.log(myObject.seconds); // outputs 1.75; Looks super useful right? So why doesn’t anyone use it? The problem is that IE6 and IE7 don’t support it, and while IE8 does support the Object.defineProperty method, it sadly only works on DOM objects that are attached to the tree. So, if you really, really needed an object that supported properties, you can create a DOM object, attach it to the document, and then use Object.defineProperty to give it some properties. That’s a lot of trouble just to support IE8, and you still don’t get IE6 or IE7 support, so I don’t know of any libraries that currently use the approach. Hacking IE6 and IE7 I recently came across a technique that I haven’t seen demonstrated before from Jonathan Neal using the onpropertychanged event. IE will fire this event when any property, native or added by a developer, is changed. However, like IE8 this technique only works on a real DOM object, not a plain old {}. Based on Jonathan’s work, here is a function that will add a property and work in IE6 and up. // Super amazing, cross browser property function, based on http://thewikies.com/ function addProperty(obj, name, onGet, onSet) { // wrapper functions var oldValue = obj[name], getFn = function () { return onGet.apply(obj, [oldValue]); }, setFn = function (newValue) { return oldValue = onSet.apply(obj, [newValue]); }; // Modern browsers, IE9+, and IE8 (must be a DOM object), if (Object.defineProperty) { Object.defineProperty(obj, name, { get: getFn, set: setFn }); // Older Mozilla } else if (obj.__defineGetter__) { obj.__defineGetter__(name, getFn); obj.__defineSetter__(name, setFn); // IE6-7 // must be a real DOM object (to have attachEvent) and must be attached to document (for onpropertychange to fire) } else { var onPropertyChange = function (e) { if (event.propertyName == name) { // temporarily remove the event so it doesn't fire again and create a loop obj.detachEvent("onpropertychange", onPropertyChange); // get the changed value, run it through the set function var newValue = setFn(obj[name]); // restore the get function obj[name] = getFn; obj[name].toString = getFn; // restore the event obj.attachEvent("onpropertychange", onPropertyChange); } }; obj[name] = getFn; obj[name].toString = getFn; obj.attachEvent("onpropertychange", onPropertyChange); } } // must be a DOM object (even if it's not a real tag) attached to document var myObject = document.createElement('fake'); document.body.appendChild(myObject); // create property myObject.firstName = 'John'; myObject.lastName = 'Dyer'; addProperty(myObject, 'fullname', function() { return this.firstName + ' ' + this.lastName; }, function(value) { var parts = value.split(' '); this.firstName = parts[0]; this.lastName = (parts.length > 1) ? parts[1] : ''; }); console.log(myObject.fullname); // returns 'John Dyer' Pretty aweseome. Real World Application Mozilla’s April Dev Derby was on using the <audio> tag, so I thought it’d be a fun chance to try this out and make a native looking HTML5 API that could wrap things like a Flash Ogg player or the amazing JsMad library which can play MP3s using pure JavaScript (great for Firefox which can’t play MP3s natively). The result is a fun little audio library called Shimichanga that has functional properties like .src. What’s interesting about the library is that when you set the .src property, the set method loads an appropriate renderer (HTML5, Flash, or JavaScript) depending on the browser’s capability and then sends the value to renderer to use, all seemless to the developer and end-user. Note: Mozilla didn’t allow the flash shim part, so to see a full working demo go to: shimichanga.com. Hopefully, I can back port this into MediaElement.js and make the entire library more dynamic and usable. For now, go check out my hideously styled demo on Mozilla’s site (Shimichanga) and give me some likes! Dev Derby shimichanga.com
HTML5 Video Wrapper for YouTube and Vimeo API – MediaElement.js
28/11/2011 external link
YouTube and Vimeo APIs YouTube and Vimeo have nice APIs to allow JavaScript developers to control the playback of embedded content (oh, and YouTube has a new design). They’ve also updated the APIs to use the newer <iframe> embed style instead requiring <object><embed> flash tags. But as powerful as those APIs are, they are not consistent with each other and neither one conforms to HTML5 <video> properties or events so you can’t leverage your existing skills or code. MediaElement.js HTML5 Wrapper To make the YouTube and Vimeo APIs easier to use, I wrapped the MediaElement.js shim structure around their proprietary APIs to make them feel like HTML5. So for a YouTube video, you can use this <video> markup <video id="youtube1" width="640" height="360"> <source src="http://www.youtube.com/watch?v=nOEw9iiopwI" type="video/youtube" > </video> Then to build a full-fledged player that is CSS skinnable on top of YouTube Chromeless , you can simply call the MediaElementPlayer like this: jQuery(document).ready(function($) { $('#youtube1').mediaelementplayer(); }); Demo Or you can skip MediaElementPlayer’s controls and build your own player using just the MediaElement wrapper which does not require jQuery (MediaElement is the wrapper which shims HTML5 support into old browsers, MediaElementPlayer is the full-fledged jQuery-based control bar built on top of MediaElement). new MediaElement('youtube#', { success: function(media, domNode) { // add HTML5 events to the YouTube API media object media.addEventListener('play', function() { console.log('yeah, it is playing!'); }, false); media.addEventListener('timeupdate', function() { // access HTML5-like properties console.log('current: ' + media.currentTime); }, false); // add click events to control player myMuteButton.addEventListener('click', function() { // HTML5 has a "muted" setter, but we have to use a function here media.setMuted(true); }, false); } }); Once the success event fires, the media object is a JavaScript wrapper that mimics the HTML5 Media API, but under the hood is really a wrapper around YouTube’s API. Nice right? Gotchas There are a few things you should look out for if you want to try it: This code is now in the official 2.4.0 release of MediaElement.js, but it should be considered experimental. I’d like to pull it out of the core and make it a plugin, but this will require some re-architecting. The Vimeo API wrapper is not finished yet. It will simply display the default Vimeo controls. (Also, unlike YouTube’s chromeless option, only Vimeo Pro users can totally remove controls) Some browsers (Chrome, IE) don’t allow HTML to be placed over an <iframe> with Flash inside which makes MediaElementPlayer’s controls not work, so I’m using the pure Flash version of YouTube for desktop browsers Fullscreen: JavaScript can’t initiate Flash’s true fullscreen, and Firefox still has terrible Flash support (if you try to adjust the size of a Flash movie’s surrounding DOM nodes, Firefox reloads it!) Demo Download
Pinbox.js – Photos Then and Now (jQuery plugin)
2/11/2011 external link
With the help of a colleague, I’m gathering images of an area of Dallas, TX just east of downtown around the campus of Dallas Theological Seminary where I work. I wanted to make the photos interactive like the animated GIF you see here. Live Demo Download Code Code Explanation To make this work, I start with a simple <img /> tag and then add some HTML5 data-* attributes that specify the size and position of the inner image: <img src="images/convent-pano.jpg" width="940" height="365" data-small-src="images/convent-pano-inner.jpg" data-small-width="507" data-small-height="264" data-small-left="163" data-small-top="11" class="pinbox" alt="View from Convent Street" /> This ensures the images will still function properly even if the JavaScript doesn’t fire properly. The next step is to include the Pinbox.js library and then use jQuery to instantiate it when the document has loaded. The script takes care of the rest. <script src="src/jquery.js"></script> <script src="src/jquery.pinbox.js"></script> <link rel="stylesheet" href="src/jquery.pinbox.css" /> <script> jQuery(document).ready(function($) { $('.pinbox').pinbox(); }); </script> Live Demo Download Code
Native Fullscreen JavaScript API (plus jQuery plugin)
20/10/2011 external link
HTML5 <video> is great, but when it was first released, one of the big complaints was that it couldn’t do true FullScreen like Flash. Thankfully, this is changing and native FullScreen support is coming to most browsers in the next few months (no word from the Internet Explorer team Update on IE below #5)) The API is still heavily in flux especially since the W3C joined in this week. I spent some time working through the differences to implement FullScreen in MediaElement.js HTML5 video player, and it’s working great in Safari 5.1+, Chrome Canary Chrome 15+, or Firefox Nightly (go to about:config and set full-screen-api.enabled= true) and scheduled for Firefox 10. Below I’m going to try to explain how things evolved, where we are today, and then some code that you can play with. Simple Demo Video Demo A Brief History of the FullScreen API The first native FullScreen implementation appeared in Safari 5.0 (and iOS) added a a webkitEnterFullScreen() function that only worked on <video> tags using Safari’s video controls (see Apple’s HTML5VideoElement). For Safari 5.1, Apple changed the API to be more inline with Mozilla’s FullScreen API proposal (which actually predates Apple’s implementation). Now, webkitRequestFullScreen() can be called on any DOM element which makes that portion of an HTML page go fullscreen. Firefox and Chrome announced that they will add FullScreen API support, and the feature has already arrived in Chrome Canary Chrome 15+ and Firefox Nightly (scheduled for Firefox 10). The Mozilla team has posted some a demo. On October 15, 2011, the W3C released a Fullscreen API proposal (written by a member of the Opera team) which has two main differences from Mozilla’s proposal: Mozilla/Webkit uses a Capital ‘S’ (FullScreen) while W3C does not (Fullscreen) Mozilla/Webkit uses cancelFullScreen while W3C uses exitFullscreen Update (11/15/2011): Ted Johnson from IEBlog says IE10 will not support the FullScreen API (12/05/2011: I misunderstood the first email from Ted) that the IE10 team has not yet decided whether to implement the FullScreen API. He notes however that, “Windows 8 Metro style Internet Explorer is always full screen … and as before, F11 enters full screen mode in desktop versions of IE.” Understanding the FullScreen API Here are the most important parts of the FullScreen API with notes on how things differ among browsers. In general, I’m using the Mozilla/Webkit spelling in the examples below, but I’m also noting the W3C differences where needed. 1. Detecting FullScreen support To detect fullscreen support, you’ll need to use the typeof command to find out if a given browser has support for the FullScreen API methods. There is also boolean property called fullScreenEnabled that tells you if the user has disabled the feature (strangely WebKit does not have the fullScreenEnabled property making it difficult to detect if it’s turned off). // Mozilla's proposed API: in practice, you'll need vendor prefixes (see examples below) if (typeof document.cancelFullScreen != 'undefined' && document.fullScreenEnabled === true) { /* do fullscreen stuff */ } 2. Entering and Exiting FullScreen To enter FullScreen mode, you call requestFullScreen (or requestFullscreen for W3C) on the element want to be viewed in FullScreen. To exit you call cancelFullScreen (or exitFullscreen for W3C) on the document object. // mozilla proposal element.requestFullScreen(); document.cancelFullScreen();  // Webkit (works in Safari and Chrome Canary) element.webkitRequestFullScreen();  document.webkitCancelFullScreen();  // Firefox (works in nightly) element.mozRequestFullScreen(); document.mozCancelFullScreen(); // W3C Proposal element.requestFullscreen(); document.exitFullscreen(); Mozilla has also proposed an alternate requestFullScreenWithKeys() method which would enable the user to use the keyboard in FullScreen mode. With Flash, Adobe always disabled keyboard support in FullScreen to prevent malicious sites from attempting to steal passwords, but it looks like the browser makers are considering making this an option. 3. Fullscreen Event and Current Status To detect when a FullScreen event happens, there is a fullscreeneventchange that fires on the element going FullScreen and a boolean property (fullScreen) on the document object that reports if it’s in FullScreen mode or not. element.addEventListener('fullscreeneventchange', function(e) { if (document.fullScreen) { /* make it look good for fullscreen */ } else { /* return to the normal state in page */ } }, true); Mozilla also mentions the possibility of adding a fullscreendenied event in the future. You should also know that Webkit added an ‘Is’ to their boolean property and that the W3C proposal strangely does not include this property: // Mozilla proposal document.fullScreen; // Firefox (Nightly) document.mozFullScreen; // Webkit (Chrome, Safari) document.webkitIsFullScreen; // note the 'Is' // W3C proposal // None? Why? 4. Styling FullScreen Both Mozilla and the W3C have proposed new pseudo CSS classes for styling elements in FullScreen mode. /* normal state */ .my-container { width: 640px; height: 360px; } /* Mozilla proposal (dash) */ .my-container:full-screen { width:100%; height:100%; } /* W3C proposal (no dash) */ .my-container:fullscreen { width:100%; height:100%; } /* currently working vendor prefixes */ .my-container:-webkit-full-screen, .my-container:-moz-full-screen { width:100%; height:100%; } 5. Embedding FullScreen When you embed content from another site (like a YouTube video) using Flash’s <object><embed> tags, you can specificy whether or not to allow FullScreen to work. This feature has also been added to the <iframe> tag using the allowFullScreen attribute. <!-- content from another site that is allowed to use the fullscreen command --> <iframe src="http://anothersite.com/video/123" width="640" height="360" allowFullScreen></iframe> Putting it All Together To make this work in its current state, you need a wrapper that can help detect the right features. Here’s what I’ve put together to work in Safari 5.1, Chrome Canary Chrome 15+, and Firefox Nightly. I’ll update it if the W3C notation goes through: (function() { var fullScreenApi = { supportsFullScreen: false, isFullScreen: function() { return false; }, requestFullScreen: function() {}, cancelFullScreen: function() {}, fullScreenEventName: '', prefix: '' }, browserPrefixes = 'webkit moz o ms khtml'.split(' '); // check for native support if (typeof document.cancelFullScreen != 'undefined') { fullScreenApi.supportsFullScreen = true; } else { // check for fullscreen support by vendor prefix for (var i = 0, il = browserPrefixes.length; i < il; i++ ) { fullScreenApi.prefix = browserPrefixes[i]; if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined' ) { fullScreenApi.supportsFullScreen = true; break; } } } // update methods to do something useful if (fullScreenApi.supportsFullScreen) { fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange'; fullScreenApi.isFullScreen = function() { switch (this.prefix) { case '': return document.fullScreen; case 'webkit': return document.webkitIsFullScreen; default: return document[this.prefix + 'FullScreen']; } } fullScreenApi.requestFullScreen = function(el) { return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen'](); } fullScreenApi.cancelFullScreen = function(el) { return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen'](); } } // jQuery plugin if (typeof jQuery != 'undefined') { jQuery.fn.requestFullScreen = function() { return this.each(function() { if (fullScreenApi.supportsFullScreen) { fullScreenApi.requestFullScreen(this); } }); }; } // export api window.fullScreenApi = fullScreenApi; })(); This creates an object called fullScreenApi with a boolean property supportsFullScreen and some methods that allow you to do something more universal. Here’s an example usage: if (fullScreenApi.supportsFullScreen) { myButton.addEventListener('click', function() { fullScreenApi.requestFullScreen(someElement); }, true); } You can see it in action below: Simple Demo Video Demo Issues and Updates Since this post, there are some additional things worth mentioning Security concerns – Browser vendors are well aware of the potential security issues with fullscreen. For example, a malicious site could show a full screen Windows or Mac login window and steal a password. That’s why they are disabling keyboard support by default and only enabling by explicitly asking. Internet Explorer support – I have an email from a IE team member saying they are discussing it, but have not made any decisions. As of now, IE10 will not implement the FullScreen API, the IE team has not yet decided if they will implement the FullScreen API. FullscreenEnabled vs. IsFullScreen – The W3C includes the very helpful fullscreenEnabled flag to let your code know if you can use the API, but strangely the W3C does not include an isFullscreen flag. WebKit on the other hand has webkitIsFullScreen, but does not have a webkitFullScreenEnabled equivalent property. Mozilla helpfully includes both.
Getting Counts for Twitter Links, Facebook Likes/Shares, and Google (+1) PlusOnes in C# or PHP
7/6/2011 external link
I am working on a project where I need to know the number of social shares on Facebook, Twitter, and Google +1 (plusone). Facebook and Twitter make this easy with a simple URL that returns clear JSON data, but Google has not offered an official way to do it yet. However, I found someone who tracked down how to do it using Google’s JSON-RPC API, and I’ve repackaged them together in ASP.NET and PHP for anyone who wants to give it a try. Data URLs Here’s where you can find the data Facebook http://graph.facebook.com/?ids=YOURURL Twitter http://urls.api.twitter.com/1/urls/count.json?url=YOURURL Google https://clients6.google.com/rpc [see below for JSON-RPC] ASP.NET C# Note: Since I’m using “dynamic,” this requires .NET 4.0. Also, I’m using the JavaScriptSerializer class which is officially depreciated, but will probably not actually be removed. You could also easily use Regex to get these simple values. int GetTweets(string url) { string jsonString = new System.Net.WebClient().DownloadString("http://urls.api.twitter.com/1/urls/count.json?url=" + url); var json = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(jsonString); int count = (int)json["count"]; return count; } int GetLikes(string url) { string jsonString = new System.Net.WebClient().DownloadString("http://graph.facebook.com/?ids=" + url); var json = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(jsonString); int count = json[url]["shares"]; return count; } int GetPlusOnes(string url) { string googleApiUrl = "https://clients6.google.com/rpc"; //?key=AIzaSyCKSbrvQasunBoV16zDH9R33D88CeLr9gQ"; string postData = @"[{""method"":""pos.plusones.get"",""id"":""p"",""params"":{""nolog"":true,""id"":""" + url + @""",""source"":""widget"",""userId"":""@viewer"",""groupId"":""@self""},""jsonrpc"":""2.0"",""key"":""p"",""apiVersion"":""v1""}]"; System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(googleApiUrl); request.Method = "POST"; request.ContentType = "application/json-rpc"; request.ContentLength = postData.Length; System.IO.Stream writeStream = request.GetRequestStream(); UTF8Encoding encoding = new UTF8Encoding(); byte[] bytes = encoding.GetBytes(postData); writeStream.Write(bytes, 0, bytes.Length); writeStream.Close(); System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse(); System.IO.Stream responseStream = response.GetResponseStream(); System.IO.StreamReader readStream = new System.IO.StreamReader(responseStream, Encoding.UTF8); string jsonString = readStream.ReadToEnd(); readStream.Close(); responseStream.Close(); response.Close(); var json = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(jsonString); int count = Int32.Parse(json[0]["result"]["metadata"]["globalCounts"]["count"].ToString().Replace(".0", "")); return count; } PHP Again, thanks to Tom Anthony for the Google part function get_tweets($url) { $json_string = file_get_contents('http://urls.api.twitter.com/1/urls/count.json?url=' . $url); $json = json_decode($json_string, true); return intval( $json['count'] ); } function get_likes($url) { $json_string = file_get_contents('http://graph.facebook.com/?ids=' . $url); $json = json_decode($json_string, true); return intval( $json[$url]['shares'] ); } function get_plusones($url) { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, "https://clients6.google.com/rpc"); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, '[{"method":"pos.plusones.get","id":"p","params":{"nolog":true,"id":"' . $url . '","source":"widget","userId":"@viewer","groupId":"@self"},"jsonrpc":"2.0","key":"p","apiVersion":"v1"}]'); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/json')); $curl_results = curl_exec ($curl); curl_close ($curl); $json = json_decode($curl_results, true); return intval( $json[0]['result']['metadata']['globalCounts']['count'] ); } Hope that helps someone out there!
bib.ly – ASP.NET MVC Url Shortener for Bible References
2/4/2011 external link
Last week, I turned a URL shortening website called bib.ly which is a play on the word “Bible” and the popular URL shortener bit.ly. It has two main functions: A Bible URL shortener and a Bible version popup detector for any website. 1. URL Shortener bib.ly let’s you easily create short URLs to Bible verses for use on Twitter and Facebook. You can use the interface you see below or just type in something that looks like a Bible verse after http://bib.ly/ When someone clicks a link, they see the Biblical text and get links to several Bible 2. Bible Refernece Popup You can also add bib.ly to a site by adding the following two lines of code to your own site <script src="http://code.bib.ly/bibly.min.js"></script> <link href="http://code.bib.ly/bibly.min.css" rel="stylesheet" /> This script will find all Bible references like these Mark 5:36; John 3:16; Eph 2:8-9 and create links to the main bib.ly site along with a popup so you can read the text. How I Built It I chose to use ASP.NET MVC 3.0 because it makes this kind of URL handling pretty easy and because I wanted the Bible parsing code to look pretty close in both JavaScript and the server-side language (C#). I fooled around with doing the site in node.js, but decided that I wasn’t confident enough in my node.js skills to pull it off and get it properly hosted. So what follows are my notes about how the site works and how ASP.NET MVC made it really easy to build. URL design The site needs to have two kinds of URLs: Fixed pages – I needed a few normal pages like Home, Plugins, and Contact that will look like this: http://bib.ly/plugins http://bib.ly/contact Parsed URLs - All the other URLs will need to be checked to see if they are a valid Bible Reference. Bit.ly has to check if the URL is a valid id in its enormous database, but we need to actually attempt to read the URL and see if it makes sense as a Bible verse. http://bib.ly/John3.16 for John 3:16 http://bib.ly/Gen12:1-3 for Genesis 12:1-3 http://bib.ly/1Chr11.22-ESV for 1 Chronicles 11:22 in the ESV http://bib.ly/asdfasd12 needs to throw a 404 error. Controller Setup Getting this to work was actually really easy once I figured it out. Basically, I hard coded the paths to the static pages in one controller and then setup another controller as a wildcard URL handler. Global.asax To handle the Bible references, I used wildcard mapping. The only problem with that is that it breaks the normal “//” which usually hooks up a root/home page. This means I needed to manually hook up the “Fixed pages” above. // Map the home page routes.MapRoute( "home", "", new { controller = "Home", action = "Index"} ); // Map all sub pages foreach(String route in new string[] { "About", "Contact", "Plugins", "Feedback" }) { routes.MapRoute( "page_" + route, route.ToLower(), new { controller = "Home", action = route } ); } // Send everything else to the Reference controller to parse routes.MapRoute( "verses", "", new { controller = "Verses", action = "Show"} ); HomeController.cs This one is pretty boring. It’s just a list of Views and one post handler for the Contact form. VersesController.cs This controller has two actions: Show and NotFound. The Show action attempts to parse the URL into a Bible reference. If it’s successful, it shows a page. If not, you get a 404 error page. This could have been handled in the Global.asax with a UrlConstraint, but I thought this was clearer. public ActionResult Show(string referencetext) { // parse the reference Reference reference = ReferenceParser.Parse(referencetext); // if it didn’t parse, show the not found View if (reference == null) { Response.StatusCode = 404; return View("NotFound"); } // show the view, using the reference object as the model return View(reference); } public ActionResult NotFound() { Response.StatusCode = 404; return View(); } URLs with a Colon (:) Character Technically, you’re not supposed to use the colon character in a URL without escaping it. But it’s very common to use a colon to separate a chapter and a verse when giving a Bible reference (2 Cor 5:17; Rev. 22:1-4) so I wanted bib.ly to understand the colon character if someone chose to use it. ASP.NET, however, disallows the colon character (and five other no-nos) by default. To allow colons, you just have to edit the following line in the web.config file. Default requestPathInvalidCharacters value <httpRuntime requestPathInvalidCharacters="&lt;,&gt;,*,:,%,&amp;,\"/> With the colon removed. <httpRuntime requestPathInvalidCharacters="&lt;,&gt;,*,:,%,&amp;,\"/> Wrap Up Hope that gives a basic sense of how the site works. There is a little more code on the C# side to help manage the versions and Bible website links, but the rest of the site’s functionality is mostly just simple HTML and CSS controlled by JavaScript utilizing JSONP to get the Biblical text from their content providers.
ffmpeg Settings for HTML5 codecs (h264/mp4, theora/ogg, vp8/webm)
5/1/2011 external link
This is just a quick tip for people encoding video for HTML5 (and using sweet HTML5 players like MediaElement.js). Go download a copy of ffmpeg, and if you’re on Windows, use this guide to set it up. Then use the following commands to create H.264, WebM, and Ogg videos, along with an image you can use as a poster. REM mp4 (H.264 / ACC) "c:\program files\ffmpeg\bin\ffmpeg.exe" -i %1 -b 1500k -vcodec libx264 -vpre slow -vpre baseline -g 30 -s 640x360 %1.mp4 REM webm (VP8 / Vorbis) "c:\program files\ffmpeg\bin\ffmpeg.exe" -i %1 -b 1500k -vcodec libvpx -acodec libvorbis -ab 160000 -f webm -g 30 -s 640x360 %1.webm REM ogv (Theora / Vorbis) "c:\program files\ffmpeg\bin\ffmpeg.exe" -i %1 -b 1500k -vcodec libtheora -acodec libvorbis -ab 160000 -g 30 -s 640x360 %1.ogv REM jpeg (screenshot at 10 seconds) "c:\program files\ffmpeg\bin\ffmpeg.exe" -i %1 -ss 00:10 -vframes 1 -r 1 -s 640x360 -f image2 %1.jpg I save this as a batch file and then just drag and drop videos on it to get everything ready for HTML5.
HTML5 Reset Debug
3/12/2010 external link
I’m a huge fan of using HTML Reset, originally created by Eric Meyer and given HTML5 updates by people like Richard Clark. The only problem is that when you try to use Firebug, WebKit inspector, IE Developer Tools, or Opera Dragonfly, you get tons of declarations. Ever seen something like this? HTML Reset Production It’s a little hard to tell what’s going on. So instead of several large blocks of HTML selectors which are needed in production, I’ve listed each selector only once and explicitly defined all CSS properties and values. This makes the above WebKit screenshot turn into HTML Reset Debug Of course, you should never use this in production because it’s around 11KB compared to .6KB for a minified HTML reset designed for production. But I’ve been finding it helpful for development, and I thought I would share it. Make Your Own CSS De-Compactor Get it HTML5 Reset Debug (my original CSS reset debug)
MediaElement.js – a magic unicorn HTML5 video library
22/9/2010 external link
I”ve posted before about HTML5 video and based on what I found I released an HTML5 <video> framework and player called MediaElement.js. But I never got around to explaining the logic behind the new library so here it goes: The Problems with HTML5 <video> (1) Codecs It has been well documented that browsers supporting HTML5 <video> don’t support the same codecs. Here’s the current breakdown of proposed(*) codec support including mobile browsers and plugins. <IE9 IE9 Firefox Safari Chrome Opera Andriod iOS WP7 Flash Silverlight H.264 - X X X X X X X X OGG - - X X X WebM - X* X X X X* (2) Old Browsers Even if codecs weren’t an issue, older browsers don’t understand the HTML5 <video> tag. This means you have to provide alternatives viewing mechanisms like Flash or Silverlight fallbacks. (3) Style/UI Each browser has a different UI for their HTML5 <video> players. You can write a player in JavaScript/CSS to bring consistency, but then older browsers will get a different UI when they fall back to Flash. Here are the native controls (from Opera’s tutorial) Proposed Solutions (1) Nested HTML Video for Everybody from Kroc Camden is a pure HTML way of serving video that works in just about every OS/browser combination. Pros: No JavaScript requirement:  Video for Everybody is pure HTML with Flash nested inside for browsers that don’t understand the <video> tag. Cons: A lot of HTML: You’re probably going to need some backend code to help manage this since it’s very unruly to type on your own. Requires multiple files: When a browser like IE8 hits the <video> tag which it doesn’t understand, it just skips it and moves on to the nested <object> tag inside. However, when a browser that does understand <video> comes along, it always ignores the nested code even if it can’t play the provided video file. So, if you try to server an OGG file to Safari or a MP4 to Firefox, you just get a black box with an X. This means you have to provide both MP4 and OGG files. (2) JavaScript player with Flash fallbacks This is the solution of every other library. They detect if the browser supports the <video> tag and if the browser can play the given file. If the browser can play the file, then a cool HTML/CSS/JS player is shown. If not, then the JavaScript replaces the <video> tag with an Flash player. You can find a great list of these players from Praegnanz’s HTML5 video list. Pros: Single Video file: This allows you to use a single H.264/MP4 which will work natively on IE9, Safari, Chrome, WM7, Andriod, and iOS. Firefox, Opera, and older browsers get a Flash player. Same UI for HTML5: All browsers with HTML5 get the same UI instead of the browser default. Cons Inconsistent interface(s): While HTML5 browsers get the same UI, other browser get a different UI in Flash. If you just want someone to play a video, this isn’t really a problem. But if you need anything beyond basic video buttons, then you’ll have to build the feature twice, once in HTML/CSS (IE9, WebKit) and again for Flash (Opera, FF, <IE9). JW Player pulls this off, but overall this problem makes HTML5 video seem not worth the effort. MediaElement Solution: Wrap the HTML5 spec around a plugin, then build the player MediaElement.js goes at the problem a bit differently than the other JavaScript libraries. Instead of falling back to a full-fledged Flash video player for browsers that don’t support <video>, MediaElement inserts a tiny Flash object and wraps it with a JavaScript object that mirrors the HTML5 <video> spec. This means that developers can write one set of code to MediaElement.js and have it work in every browser regardless of what it supports. And instead of just supporting Flash, there is also a Silverlight plugin that allows you to support more browsers (or avoid Flash on the Mac) and even put old WMV files that you don’t want to encode in a <video> tag. On top of MediaElement (which is library independent JavaScript) and jQ uery, I built MediaElementPlayer which is a sample HTML/CSS/JavaScript player that is easily skinnable through CSS and provides the same interface to every browser. Here’s how it works: And here’s the result: Pros Single Video file: like the other JavaScript libraries, you can use a single MP4 file for everyone. Also, when Flash starts supporting WebM, you could use that and have Safari use the Flash plugin. Single API: MediaElement gives you a single programming API defined by the W3. Consistent UI: the controls are HTML/CSS even in Flash or Silverlight modes. Bonus! Double your codecs, double your fun: The Flash and Silverlight fallbacks enable the <video> tag to use additional media types like the older FLV (Flash video) format, Flash RMTP streaming, or even Window Media (WMA, WMV). MediaElement will choose the appropriate playback mechanism (native HTML5, Flash, or Silverlight) for your media and browser, and you’ll still get the same player UI. Cons Slight API differences: Because JavaScript doesn’t have native get/set properties, I’ve had to implement some HTML5 media properties as get/set methods. See http://mediaelementjs.com/ for the main changes [e.g. media.src becomes media.setSrc(url)] Future Work One gotcha with HTML5 video is the issue of fullscreen support. HTML5 video does not have a consistent way to do true fullscreen video (in fact, the HTML5 spec discourages it). You can expand the video to fill the browser, but currently only Safari provides a JavaScript API to do real fullscreen video (Chrome has the API, but it’s not implemented yet). Firefox can do fullscreen, but users must select it in a context menu. Also, Flash and Silverlight do not allow you to trigger fullscreen through the JavaScript API. Also, I’d like to provide some example skins that mimic popular players like YouTube and Vimeo and a default theme that’s a little prettier. Implementations MediaElementPlayer If you want a full-fledged player that just works, here’s the code: <!-- requirements --> <script src="jquery.js"></script> <script src="mediaelement.js"></script> <script src="mediaelementplayer.js"></script> <link rel="stylesheet" href="mediaelementplayer.css" /> <!-- video tag --> <video id="v1" src="myvideo.mp4" width="320" height="240"></video> <!-- startup --> <script> $('video').mediaelementplayer(); // OR new MediaElementPlayer('#v1'); </script> MediaElement If you want to build your own player using MediaElement and your own JavaScript, here’s a sample: <!-- requirements --> <script src="mediaelement.js"></script> <!-- video tag --> <video id="v1" src="myvideo.mp4" width="320" height="240"></video> <!-- startup --> <script> var v = document.getElementsByTagName("video")[0]; new MediaElement(v, {success: function(media) { media.play(); }}); </script> References Full description of the API with options at the MediaElement.js site. Github project Download MediaElement See Also HTML5 shim list – Paul Irish’s list of JavaScript libraries that enable features VideoJS – I think this is the best other HTML5 player out there Projekktor – I also like this one quite a bit Video for Everybody - HTML only player HTML5 player list - I’ve learned a lot from these other players Hope you like it. GPL and MIT.
Simple Cross-Browser HTML5 video with a single H.264 file and fallback options
6/6/2010 external link
At first glance, the promise of HTML5 <video> seems awesome. Just a simple <video src="myvideo.mp4" controls></video> and you’re done right? Not quite. In reality there are all kinds of browser inconsistence that make this impossible. Here’s a run down of the problems: 1. Older browsers don’t support <video> Only the most recent versions of the big five (Firefox, Chrome, Safari, IE, Opera) support <video>, so you have to have a fallback solution of some kind if you want to use <video> and still support your audience. 2. Newer browsers don’t support the same codecs Sadly, different browsers support different codecs. H.264 works in Chrome, Safari, and IE9, while Firefox and Opera only support Ogg/Vorbis. All the major browser vendors have pledged support for Google’s recently released WebM format, but this will take some time for the browers and encoding software to catch up. 3. Fallback HTML requires multiple video files The most common solution offered is to use nested HTML to allow browsers that don’t support <video> to fallback to flash. One of the best of these is called Video for Everybody and looks something like this: <video>   <source src="myfile.mp4">   <source src="myfile.ogg">   <object src="flashplayer.swf?file=myfile.mp4">      <embed src="flashplayer.swf?file=myfile.mp4">   </object></video> This works in all browsers, but it has a major problem: You must have two video files. If you attempt to use only MP4, Firefox on Opera will fail. If you remove the <source src=”myfile.ogg”>, Firefox and Opera will not fallback to the Flash <object>. They will just display a big, blank, black box with an X. The same goes if you only use OGG on browsers that don’t support OGG, or attempt to use WebM. The HTML fallback mechanism only works if the browser doesn’t understand the <video> tag. It doesn’t work if it doesn’t understand the <source> file. So if you want to only use one file, then you must use JavaScript. A Simple JavaScript Solution Since I don’t want to maintain two versions of every video, I need a way to fallback to Flash for non-H.264 browsers. Here’s my simple solution using jQuery. <video id="my-video"> <source src="myfile.mp4" type="video/mp4"> </video> <script> (function() { var video = document.createElement("video"); if ( typeof(video.canPlayType) == 'undefined' || // detect browsers with no <video> support video.canPlayType('video/mp4') == '') { // detect the ability to play H.264/MP4 var video = $('#my-video'); var videoUrl = video.find('source').attr('src'); var flashUrl = '/flash/myplayer.swf'; // create flash var flash = '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ' + ' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" ' + ' width="' + video.width() + '" height="' + video.height() + '" id="fallbackplayer">' + ' <param name="allowfullscreen" value="true" /> ' + ' <param name="movie" value="' + flashUrl + '?mediaUrl=' + videoUrl + '" /> ' + ' <embed id="dtsplayer" width="' + video.width() + '" height="' + video.height() + '" allowfullscreen="true" allowscriptaccess="always" ' + ' quality="high" name="fallbackplayer" ' + ' src="' + flashUrl + '?mediaUrl=' + videoUrl + '" ' + ' type="application/x-shockwave-flash" /> ' + '</object>'; // insert flash and remove video video.before(flash); video.detach(); } })(); </script> The code is fairly simple. It just checks if the browser understands the <video> tag by checking for the “canPlayType” function, then it uses canPlayType to determine if the browser can play an H.264/MP4. If not, it inserts Flash as a fallback. Please note: This is a simplified script for example purposes only. It needs flash version detection, and it should be turned into a jQuery extension, but it should still be a nice guide for