• posted

    These days it's hard to call yourself a web developer if you do not have at least some jQuery experience. jQuery is very helpful for many many things such as browser compatibility, ajax calls, DOM manipulation, utility, and more. It helps us lazy web developers do these common operations simply - but one thing it does not help us do, is write good code.

    Not so long ago (ok, maybe a while ago) I wrote a post titled How jQuery and "unobtrusive javascript" can be poisonous. This can be thought of as a bit of a follow up to that post.

    The Problem

    Here is how to structure the "Front End Layers" of a web application.

    The desired separation: Presentation (CSS), Behavior (Javascript), Data/Structure (HTML)

    Source: Maintainable Javascript 2012 via Nicholas C. Zakas (1)

    There you have it. It's as easy as that. Except for, you know, it not being that easy.

    It's easy to say, well, just keep the data in the HTML, and the behavior in javascript, and anything UI related in CSS, and thus you will have good separation.

    Well, turns out in practice this doesn't always work out so well. If you're not careful, anything more then elementary client-side interactivity done through jQuery will result in some pretty terrible spaghetti code. Worse than that, jQuery in practice is often extremely coupled to the structure of your HTML. This makes it extremely hard to maintain, refactor, and understand.

    Can't I just use one of the fancy new MVC/MV* Javascript Frameworks?

    Well, yeah, actually — if that's an option...

    This is where I tell you if you haven't heard of frameworks such as KnockoutJS, BackboneJS, Ember.js, AngularJS, Spine, and BatmanJS, you should check them out. Many of them are great options. I only have extensive experience (extensive meaning, say above ToDo App level) with KnockoutJS and BackboneJS.

    My suggestion is that you check out at least 2 of them to get an idea of the variety/flavors. I personally use knockout the most, although if I was building a web project on top of a RESTful API, I would choose Backbone instead (since it has built-in RESTful conventions).

    The big problem with these frameworks (in my opinion) is that they are completely and utterly incompatible with SEO friendly web design.

    If you want to use one of the MV* frameworks mentioned above, you pretty much have to send/retrieve data in JSON format. This is how it should be (hell, it DOES seems natural to have Javascript Objects be stored in JavaScript Object Notation).

    Well this seems to change the organizational chart above, doesn't it? Now, HTML is essentially just our View, while our Data lives in server-generated JSON. Usually comminicated asynchronously via AJAX.

    Google looks at HTML. Plain and simple. No JSON.

    Thus our problem. If you are trying to build an SEO-friendly web app, for the most part you are required to push out the data in HTML format, not in JSON format which then gets rendered client-side.

    So thus, we are back where we started.

    Our data lives in HTML.

    Let's assume for now that all of the fun MVC javascript frameworks are out. We want to build a fully-functional, client-side web application, meanwhile keeping everything friendly and crawlable for Google. We pick jQuery as the backbone of our client-side application.

    How do we build a Maintainable Javascript applicaiton with jQuery?

    This is a good question. I am going to demonstrate a "pattern" to do this which has worked for me. I will do so by building a faux application which has the basic behavior of a commenting system. I feel like this is a richer demonstration than the TODO applications, as there is generally more complex user interaction, and more server-side communication which needs to take place.

    enter image description here

    (Bear with me on the Visio diagrams - they aren't great but I think they help)

    The idea is, in a javascript-disabled world, the application essentially still works. Note: If you have javascript disabled, I pity you - this is primarily for it to "work" for google - not for people who have js turned off. Although it helps them too so win/win.

    We will talk more later about how that HTML might look, but first let's see what the basic structure of the application is:

    Basic JS Structure:

    "use strict";
    (function($, application, window) {
    
        application.CommunicationLayer = function (spec) {
            var self = this;
    
            // Communicate with server + business-level logic
    
            return self;
        };
    
        application.performBinding = function (app, selector) {
            //Handle all HTML-specific code here
    
            //DOM wrapper element, all event handlers are bound to this element
            var $wrapper = $(selector || window.document);
    
            //bind all events
            $wrapper
                //PRIMARY BINDINGS
                .on('click', '.-delete', function() {
                    //user clicked delete
                    //handle UI changes
                    //get necessary data
                    //call appropriate method on app
                    app.delete(data);
                })
                .on('click', '.-search', function () {
                    //user clicked search
                    //handle UI changes
                    //get necessary data
                    //call appropriate method on app
                    app.search(data);
                })
                // ...
            ;
        };
    
    })(jQuery, window.MyApp || (window.MyApp = {}), window);
    

    Then, your HTML page will have the following:

    <div id="application-wrapper">
        <!-- rendered HTML/Data -->
    </div>
    
    <script src="Scripts/App.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function() {
            MyApp.performBinding(new MyApp.CommunicationLayer(), "#application-wrapper");
        });
    </script>
    

    Let's try this out. As promised, I'll create a simple commenting app as an example. Comments can get pretty complex in the web 2.0 age, so how about we specify some requirements/constraints.

    • comments can have children comments
    • users can delete comments
    • users can flag comments
    • users can edit comments
    • users can reply to comments

    The page will have the following dependencies:

    • Handlebars.js (templating engine) *
    • jQuery (this post IS about jQuery after all)

      *It is important to note that Handlebars is being used ONLY to generate HTML based on actions of the user. All of the original HTML is generated server side. Generating the HTML is considered outside the scope of this post.

    I am going to make the following assumptions about the HTML:

    • all comments are contained by an element with the class -comment
    • comments have child elements with classes -reply, -update, -delete, and -flag which are clickable
    • the comment content (text) is contained in a child element of -comment with the class -body
    • child comments are contained by an element with a class -child-comments
    • the -comment element has a data-app attribute like so: data-app="id: 123, parentId: 456"

    enter image description here

    (Assumptions about the HTML structure)

    The important thing to note here is that we aren't tying ourselves to the structure of the HTML too much. The assumptions made are essentially about the structure of the data. For example, Comments have child comments - thus it seems logical that in the HTML-representation of this data, a node representing a comment, would have, somewhere down the DOM tree, a child node representing a list of child comments. This does limit things slightly, but is not disabling. The coupling between the HTML and the javascript should still be pretty small relatively speaking. I will demonstrate this point later.

    Conventions

    I have followed a convention where any class names prefixed with a - (e.g., -comment) are class names for Javascript. The idea is to not use any of these classes for CSS styling — this allows for a clean separation over presentation and behavior. All class names not prefixed can be safely assumed to be tied to CSS rules. This allows developers to easily and cleanly navigate your view code, knowing whether or not you changing something will affect the behavior of the javascript or not. This, in my mind, is an invaluable feature.

    With that in mind, let's build our communication layer...

    application.CommunicationLayer = function (spec) {
        var self = this;
        //hardcode author as "Current User"
        //normally, you might pull this from a cookie or something
        self.author = "Current User";
    
        self.insert = function (comment, callback) {
            //send data to server via AJAX
            //server returns the inserted comment ID
            //trigger callback method with comment data
    
            //emulate inserted comment by triggering callback
            //with a dummy ID
            callback({
                id: comment.parentId + 1000,
                body: comment.body,
                date: new Date(),
                author: self.author
            });
        };
    
        self.delete = function(id) {
            //delete comment
        };
    
        self.update = function(toUpdate, callback) {
            //update comment
    
            callback();
        };
    
        self.flag = function (id, reason) {
            //flag comment
        };
    
        return self;
    };
    

    Since this is just a demo, the communication layer is pretty empty, but it should be pretty clear how one would code this, and where business logic and communication logic would go.

    We need to decide how our HTML is going to look. Let's make some Handlebars templates.

    Comment Template:

    <li class="-comment" data-app="id:{{id}}, parentId: {{parentId}}">
        <div>
            <span class="comment-author">{{author}}</span> at
            <span class="comment-date">{{date}}</span>
        </div>
        <div class="comment-body -body">{{body}}</div>
        <div class="comment-tools">
            <a class="-reply">Reply</a>
            <a class="-update">Update</a>
            <a class="-delete">Delete</a>
            <a class="-flag">Flag</a>
        </div>
    
        <ul class="-child-comments"></ul>
    </li>
    

    We are also going to need some templates for replying and editing comments.

    Add comment Template:

    <li class="-add-comment-template">
        <form class="-add-comment-form" data-app="parentId: {{parentId}}">
            <input type="text" class="-body" />
            <input class="btn" type="submit" value="Submit"/>
            <a class="btn -cancel">Cancel</a>
        </form>
    </li>
    

    Update Comment Template:

    <form class="-update-comment-form">
        <input type="hidden" class="-original" value="{{body}}" />
        <textarea class="-value">{{body}}</textarea>
        <button class="btn" type="submit">Save</button>
        <a class="btn -cancel">Cancel</a>
    </form>
    

    Note: as a benefit to the conventions we have used, at the end of this I will demonstrate how one would be able to completely change the design, and HTML structure of the comments, without altering a single line of javascript!

    Now comes the binding code.

    application.bindCommentApp = function(app, selector) {
        var $wrapper = $(selector || window.document);
    
        //templates - for user-created comments
        var addCommentTemplate = Handlebars.compile($("#add-comment-template").html());
        var commentTemplate = Handlebars.compile($("#comment-template").html());
        var commentUpdateTemplate = Handlebars.compile($("#comment-update-template").html());
    
        $wrapper
            //PRIMARY BINDINGS
            .on('click', '.-delete', function() {
                var $comment = $(this).closest(".-comment"),
                    data = $comment.data("app");
                var proceed = confirm("Are you sure you would like to delete this comment?");
                if (!proceed) { return; }
                app.delete(data.id);
                $comment.remove();
            })
            .on('click', '.-flag', function () {
                var data = $(this).closest(".-comment").data("app");
                var reason = window.prompt("Why would you like to flag this comment?");
                if (!reason) { return; }
                app.flag(data.id, reason);
            })
            .on('click', '.-reply', function () {
                var $comment = $(this).closest(".-comment"),
                    data = $comment.data("app");
    
                var $children = $comment.find(".-child-comments").first();
    
                //insert the add comment form template into children list
                $children.prepend(addCommentTemplate({parentId: data.id}));
    
            })
            .on('click', '.-update', function () {
                var $comment = $(this).closest(".-comment"),
                    data = $comment.data("app");
    
                var $body = $comment.find(".-body").first();
                var body = $body.text();
                $body.empty();
                $body.html(commentUpdateTemplate({ body: body }));
    
            })
    
    
            //TEMPLATE RELATED BINDINGS
            .on('submit', '.-add-comment-form', function () {
                var $this = $(this),
                    $tmpl = $this.closest(".-add-comment-template"),
                    $children = $this.closest(".-child-comments"),
                    data = $this.data("app"),
                    body = $this.find(".-body").val();
    
                $tmpl.remove();
                app.insert({ parentId: data.parentId, body: body }, function (comment) {
                    $children.prepend(commentTemplate(comment));
                });
    
                return false; //prevent post
            })
            .on('click', '.-add-comment-form .-cancel', function () {
                var $tmpl = $(this).closest(".-add-comment-template");
    
                $tmpl.remove();
            })
    
            .on('submit', '.-update-comment-form', function () {
                var $tmpl = $(this).closest(".-update-comment-form"),
                    $comment = $tmpl.closest(".-comment"),
                    $body = $comment.find(".-body").first(),
                    data = $comment.data("app"),
                    body = $tmpl.find(".-value").val();
    
                $body.empty();
                app.update({ id: data.id, body: body }, function () {
                    $body.text(body);
                });
    
                return false; //prevent post
            })
            .on('click', '.-update-comment-form .-cancel', function () {
                var $tmpl = $(this).closest(".-update-comment-form"),
                    $body = $tmpl.closest(".-comment").find(".-body").first(),
                    originalValue = $tmpl.find(".-original").val();
                $body.text(originalValue);
                $tmpl.remove();
    
            })
    
    
        ;
    };
    

    Clearly, this snippet is a bit longer - but it is also fully functioning. Take some time to read over it and understand what it is doing. The general pattern is this:

    //Typical logic for event handler
    $wrapperElement.on('{event}', '{element triggering event}', function () {
        //Step 1: gather contextual data. (Usually an identifier of entity 
        //        being acted on + user-created data)
    
        //Step 2: If needed, confirm action with user (ie, "Are you sure?")
    
        //Step 3: Call communicaiton layer with appropriate data to perform 
        //        Business logic + communicate with server
    })
    

    To maintain flexibility, I typically store contextual data in a data- HTML attribute. This is helpful to gather more complex contextual data since it will be typed (rather than parsing it from html). I have also written about ways to make this simpler.

    Using just this code here and a little bit of CSS, you can try out a working demo in the jsFiddle below:

    Try it out:



    Also, as promised, here is an alternate version using Html Tables instead of HTML lists to present the comments. Note that by keeping the structure of the javascript -classname selectors the same, this was relatively easy to accomplish.

    The point here is to show that even with dramatically altered HTML representations of the data (one using a list, one using a table), I was able to refactor the design successfully without changing ANY javascript code. This is fairly significant and will save much time once your application gets sufficiently complex.



    A final Note:

    There are many ways to structure javascript applications. I don't pretend to have the right way. There are many very smart people thinking about how to do to this properly. Over the years I have come up with conventions and structure to battle common problems that I have had with maintaining large Javascript code bases, and have found this to work pretty well.

    I hope that this can help those that are struggling with the same problems. If you have any comments or suggestions on how to improve on this, I would love to hear them.

    Also, if you made it this far and actually read everything: bravo. I'll try to keep my posts shorter in the future.

    Additional Sources:

  • posted

    I'm currently working on a project which will be a developer/tech community. I am building out a framework for users to post tutorials and general blog-type postings, and decided to use Markdown syntax on all user-created content.

    If you don't know what Markdown is, it is a simple and efficient syntax for marking up text to HTML. It has become popular throughout the developer community, largely due to it's use on StackOverflow and GitHub.

    Since the specification of Markdown was sort of defined in an ad-hoc manner by John Gruber, there have been several subtly different implementations of it. Jeff Atwood is proposing to standardize it, but whether or not that will happen any time soon is unknown.

    Anyway, going against the very direction that Jeff Atwood is calling for above (sorry Jeff), I realized that a common desire of mine is to embed a jsFiddle into a post. Such a feature has been discussed for use in StackOverflow multiple times, and has had a fairly positive reception (first post got 67 upvotes...)

    If you don't know what jsFiddle is, and you are a web developer, you are missing out. jsFiddle is a purely awesome service that makes communicating with code snippets significantly easier.

    With Fully-capable markdown, embedding fiddles is somewhat simple, since full HTML syntax is allowed. However, on sites like StackOverflow and GitHub, it would be unwise to allow full HTML support. This would technically allow users to embed whatever nasty javascript they wanted onto a certain page, and because of this concern, one usually decides to whitelist only certain HTML elements so that a user cannot post any malicious content.

    Of course, it seems clear that <iframe> HTML elements should not be included in the whitelisted group of elements. I sought out, however, to include a simple syntax for including embedded jsFiddle iframes into the markdown specification to make writing about javascript, HTML, or CSS that much easier (not to mention interactive).

    Convention

    The simplest convention I could come up with was the following:

    // render jsFiddle embed iframe with default options
    $[http://jsfiddle.net/uzMPU/]
    
    // render jsFiddle embed iframe with specific tabs
    $[http://jsfiddle.net/uzMPU/][result,js,css]
    

    In addition, following similar convention to links and images, referencing the link by id will also be valid:

    // render jsFiddle embed iframe with default options
    $[1]
    
    // render jsFiddle embed iframe with specific tabs
    $[1][result,js,css]
    
      [1]: http://jsfiddle.net/uzMPU/
    

    This would result in the following iframes being rendered as the following:

    <iframe src="http://jsfiddle.net/uzMPU/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
    
    <iframe src="http://jsfiddle.net/uzMPU/embedded/result,js,css/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
    

    (In reality, I have modified this slightly, and included a style tag: width: 100%; height: 300px; by default)

    In addition to the $[] syntax, I have made it a requirement that the $[] be entirely on it's own line. As a result, writing the following:

    This is an inline $[http://jsfiddle.net/uzMPU] fiddle looking link
    

    will not result in an iframe being rendered.

    Implementation

    Since I am working in .Net, I am using MarkdownSharp to process the markdown into HTML, and also using the same Sanitizer that (I think) is used on StackOverflow. Uncommon to most parsers, MarkdownSharp is implemented largely using Regular Expressions. I tried to follow similar conventions as were being used in the MarkdownSharp project, but I simply added the following code into the Markdown.cs file:

    private static Regex _fiddlesRef = new Regex(@"
                        ^               # must be at start of line
                        (               # wrap whole match in $1
                        \$\[
                            ([^\]]*?)       # id = $2, match anything except for endbracket
                        \]
                        (
                            \[
                            ([a-zA-Z,]*)    # tab options in $4
                            \]
                        )?              # tab options are optional
                        )$              # must be entire line
    ", RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline | RegexOptions.Compiled);
    
    /// <summary>
    /// Turn Markdown fiddle shortcuts into embedded iframes
    /// </summary>
    /// <remarks>
    /// $[link to fiddle]
    /// 
    /// OR
    /// 
    /// $[link to fiddle][fiddle tab options]
    /// 
    /// OR
    /// 
    /// $[1]
    ///   [1]: {link to fiddle}
    /// </remarks>
    private string DoFiddles(string text)
    {
        return _fiddlesRef.Replace(text, new MatchEvaluator(FiddleEvaluator));
    }
    
    private string FiddleEvaluator(Match match)
    {
        string idOrLink = match.Groups[2].Value;
        string tabOptions = match.Groups[4].Length > 0 ? match.Groups[4].Value : null;
        string linkID = idOrLink.ToLowerInvariant();
    
        string url = _urls.ContainsKey(linkID) ? _urls[linkID] : idOrLink;
        url = EncodeProblemUrlChars(url);
        url = EscapeBoldItalic(url);
        return CreateFiddle(url, tabOptions);
    
    }
    
    private static string CreateFiddle(string url, string tabOptions = null)
    {
        url = CreateFiddleUrl(url, tabOptions); // creates safe and valid fiddle embed URL. returns empty if not valid.
    
        if (string.IsNullOrEmpty(url))
        {
            return ""; //if empty, don't render fiddle
        }
    
        return String.Format(
            @"<iframe style=""width: 100%; height: 300px"" src=""{0}"" allowfullscreen=""allowfullscreen"" frameborder=""0""></iframe>",
            url
        );
    }
    

    I wanted it to be simple to add a fiddle, and one way of making it simple is to make it so that I could be on any fiddle page in my browser, and simply Copy-and-paste the current browser url into a bracketed $[] and have it work. In order to properly embed the fiddle, it is required that the url have the /embedded/ flag at the end of the url. In addition to wanting this to handle a default fiddle URL without the /embedded/ flag, I thought it would be good to make sure the URL was a proper fiddle URL, and not some malicious URL which could produce an unsafe iframe. This magic currently happens in the CreateFiddleUrl(url, tabOptions); method.

    public static string CreateFiddleUrl(string originalUrl, string tabOptions = null)
    {
        var match = _fiddlesUrl.Match(originalUrl);
    
        if (!match.Success)
        {
            return "";
        }
    
        return string.Format(
            @"http://jsfiddle.net/{0}/{1}/{2}{3}{4}{5}",
            match.Groups[5].Value, //username
            match.Groups[6].Value, //fiddle id
            match.Groups[7].Length > 0 ? match.Groups[7].Value + "/" : "", //version number if present
            "embedded/",
            tabOptions != null ?
                tabOptions + "/" :
                (match.Groups[9].Length > 0 ?
                    match.Groups[9].Value + "/" :
                    ""), //if tab options are included, use them
            match.Groups[10].Value //any additional options at end of url
            );
    
    }
    
    private static Regex _fiddlesUrl = new Regex(@"
        ^                       # url starts string
        (                       # wrap whole match in $1
        (https?://)?            # optional http:// or https:// in $2
        (www\.)?                # allows www. even though jsfiddle doesn't use it in $3
        (jsfiddle\.net)         # required jsfiddle.net in $4
        /([a-zA-Z0-9_]*)        # required username of fiddle creator in $5
        /([a-zA-Z0-9]*)         # required fiddle ID in $6
        /([0-9]*)?              # optional fiddle version number in $7
        /?(embedded)?           # embedded option - not required. will add manually if not present in $8
        /?([a-zA-Z,]*)?         # fiddle tabs options csv list of tabs in $9
        /?(.*)                  # other options are possible, put them all in $10
        )
    ", RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
    

    Thus, the following urls are all valid:

    Markdown.CreateFiddleUrl("http://jsfiddle.net/username/AbCdEf/")
    // returns "http://jsfiddle.net/username/AbCdEf/embedded/"
    
    Markdown.CreateFiddleUrl("http://jsfiddle.net/AbCdEf/")
    // returns "http://jsfiddle.net/AbCdEf/embedded/"
    
    Markdown.CreateFiddleUrl("jsfiddle.net/AbCdEf/result,js/")
    // returns "http://jsfiddle.net/AbCdEf/embedded/result,js/"
    
    Markdown.CreateFiddleUrl("http://jsfiddle.net/AbCdEf/embedded/")
    // returns "http://jsfiddle.net/AbCdEf/embedded/"
    
    Markdown.CreateFiddleUrl("http://jsfiddle.net/AbCdEf/", "result,js")
    // returns "http://jsfiddle.net/AbCdEf/embedded/result,js/"
    

    Now I simply hook this into the main RunBlockGamut method:

    /// <summary>
    /// Perform transformations that form block-level tags like paragraphs, headers, and list items.
    /// </summary>
    private string RunBlockGamut(string text, bool unhash = true)
    {
        text = DoHeaders(text);
        text = DoHorizontalRules(text);
        text = DoLists(text);
        text = DoCodeBlocks(text);
        text = DoBlockQuotes(text);
    
        text = DoFiddles(text);
    
        // We already ran HashHTMLBlocks() before, in Markdown(), but that
        // was to escape raw HTML in the original Markdown source. This time,
        // we're escaping the markup we've just created, so that we don't wrap
        // <p> tags around block-level tags.
        text = HashHTMLBlocks(text);
    
        text = FormParagraphs(text, unhash: unhash);
    
        return text;
    }
    

    and all is well!

    Thus, the result is - by me writing the following in markdown on this current post:

    $[http://jsfiddle.net/nGE9q/][result,html,css]
    

    results in the following fiddle being embedded like so:

    Sanitization

    If one is interested in including this syntax into the markdown spec, but still ensuring that safe HTML is always produced, one can use the following Sanitization code (borrowed and extended from here):

    private static Regex _tags = new Regex("<[^>]*(>|$)",
        RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.Compiled);
    private static Regex _whitelist = new Regex(@"
        ^</?(b(lockquote)?|code|d(d|t|l|el)|em|h(1|2|3)|i|kbd|li|ol|p(re)?|s(ub|up|trong|trike)?|ul)>$|
        ^<(b|h)r\s?/?>$",
        RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
    private static Regex _whitelist_a = new Regex(@"
        ^<a\s
        href=""(\#\d+|(https?|ftp)://[-a-z0-9+&@#/%?=~_|!:,.;\(\)]+)""
        (\stitle=""[^""<>]+"")?\s?>$|
        ^</a>$",
        RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
    private static Regex _whitelist_img = new Regex(@"
        ^<img\s
        src=""https?://[-a-z0-9+&@#/%?=~_|!:,.;\(\)]+""
        (\swidth=""\d{1,3}"")?
        (\sheight=""\d{1,3}"")?
        (\salt=""[^""<>]*"")?
        (\stitle=""[^""<>]*"")?
        \s?/?>$",
        RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
    //<iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
    private static Regex _whitelist_fiddle = new Regex(@"
        ^<iframe 
            (\sstyle=""width:\s?\d{1,3}(%|px);\s?height:\s?\d{1,3}(%|px)"")?
            (\ssrc=""http://jsfiddle.net/[a-zA-Z0-9,_/]*"")
            (\sallowfullscreen=""allowfullscreen"")?
            (\sframeborder=""\d"")?
        >$|^</iframe>$
        ",
        RegexOptions.Multiline | RegexOptions.ExplicitCapture | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
    
    /// <summary>
    /// sanitize any potentially dangerous tags from the provided raw HTML input using
    /// a whitelist based approach, leaving the "safe" HTML tags
    /// CODESNIPPET:4100A61A-1711-4366-B0B0-144D1179A937
    /// </summary>
    public static string Sanitize(string html)
    {
        if (String.IsNullOrEmpty(html)) return html;
        string tagname;
        Match tag;
        // match every HTML tag in the input
        MatchCollection tags = _tags.Matches(html);
        for (int i = tags.Count - 1; i > -1; i--)
        {
            tag = tags[i];
            tagname = tag.Value.ToLowerInvariant();
            if (!(_whitelist.IsMatch(tagname) || _whitelist_a.IsMatch(tagname) || _whitelist_img.IsMatch(tagname) || _whitelist_fiddle.IsMatch(tagname)))
            {
                html = html.Remove(tag.Index, tag.Length);
                System.Diagnostics.Debug.WriteLine("tag sanitized: " + tagname);
            }
        }
        return html;
    }
    

    Synopsis

    At the end of the day - I want to be clear: I am not necessarily proposing that this become a markdown standard. Similar to some additions/deviations from the current "standard" that sites like GitHub have taken, I saw a chance to significantly improve the convenience of an often (or what should be often) task in writing developer-focused content, and I took it. If other people have similar ideas, then perhaps they will read this first and choose to implement it in the same way.

    The biggest issue with doing something like this is the markdown syntax becomes dependent on 3rd party services (ie, jsFiddle.net), which is generally not good. At this point, the specification becomes much less portable.

    What this does do, however, is makes it a lot easier for a developer to write interactive (and thus, hopefully better) tutorials by including executable and/or editable code snippets. To me, this is worth it.

    I would love to hear anyone's opinion on ways to make this particular convention better, or even other things to change about the markdown spec which could help improve it's convenience to the developer in this context.

  • posted

    A new javascript library domo-js came out earlier this week. Domo is a new semantic way to generate HTML/CSS via javascript. The API is fairly clean. I still can't think of any project where I would have chosen to use Domo over a templating framework like Mustache, Handlebars, or Hogan - but it is interesting nonetheless and I always like pushing the envelope of js innovation. Here is an example of what the API looks like:

    <!doctype html>
    <script src="domo.js"></script>
    <script>
      function opacity(pct) {
        return {opacity: String(pct / 100), filter: "alpha(opacity=" + pct + ")"}
      }
    
      HTML({lang: "en"},
        HEAD(
          TITLE("Welcome to domo"),
          STYLE({type: "text/css"},
            STYLE.on("body", {textAlign: "center", fontSize: 50}),
            STYLE.on("h1", opacity(50), {background: "#000", color: "#fff"})
          )
        ),
    
        BODY(H1("Welcome to domo"))
      )
    </script>
    

    not exactly saving any space here, but not bad.

    Recently I was working on a project where I needed to sort a list of entities by "popularity". Popularity algorithms can be a deceptively difficult thing to work out. It seems fairly intuitive that popularity should be based somewhat on "Votes" or "Likes" of something, but also on the chronology of the likes, or the age of the entity. Figuring out exactly how those variables interplay is not that easy. I was playing around with some formulas when I stumbled upon a great and thorough post on popularity sorting written by the folks over at linkibol. Though I had never heard of linkibol, the post covers an impressive amount of ground on the ins and outs of these algorithms, and even compares their algorithm with some of the other big online entities such as Hacker News, Reddit, StumbleUpon, and Del.icio.us. Pretty cool - thank's guys!

    I happened to find my way onto Michael O'Church's blog, where I found two very well crafted essays titled What Programmers Want, and Don't waste your time in crappy startup jobs. Although the title of the latter sounds harsh, the important thing to note is that "crappy" is not an all-inclusive modifier here - and that there are some very good "startup jobs" out there - just likely quite a bit more crappy ones. Nevertheless, great advice to anyone thinking on getting into startup-land - either as a founder or an early employee.

    The former article written by Church, I recommend to anyone running a startup or managing technical people (even if you yourself are technical). I am going to continually refer to that piece to evaluate how I can make our work environment better for my employees every day. Although less elegantly crafted, Ryan Carson also recently wrote a piece 3 mistakes I made as a young entrepreneur which may also have some lessons out there for young entrepreneurs such as myself.

    Ever need to collaboratively write with others in real time? Ever want those documents to have markdown-based rendering? Check out socrates.io.

    An interesting javascript framework called springy came out and provides an API to dynamically render interactive graph maps in javascript. By default, the layout engine uses a force-directed algorithm, though the library provides simple implementation steps to build your own layout engine, as well as customize the way nodes are displayed. Pretty cool.

    Springy.js Demo

    I always love TheOatmeal, and this week was no different. Inman certainly has more experience "making things" for the internet than me and pretty much anyone, but the comic hit home with me regardless. It is a worth a look for anyone who creates content for the internet, but even moreso anyone who "creates" anything.

    sometimes comments aren't the greatest

    Sometimes comments aren't the greatest...

    Take a look at 8 things you didn't know about C#. There are some nice gems in there. I wasn't strictly aware of 1, 4 5 and 8... although I would have guessed most of them. Nevertheless, these are always nice posts. Reminds me of the epic stackoverflow thread: Hidden features of C#. On the javascript front, James Padolsey wrote a nice short piece on JS Adolescence describing some of the fun ninja-techniques that he has used in javascript which are generally just bad ideas. Most of his conclusions I agree with. I hate overly long function names, apparently he agrees:

    // Version 1:
    var responseCache = {};
    function generateNextIDForANewResponseCacheItem() {...}
    function deleteResponseCacheItemsThatDoNotEqual(value) {...}
    
    // Version 2:
    function ResponseCache() {
      this.data = {};
    }
    ResponseCache.prototype = {
      genNextID: function() {...},
      remove: function(optionalFnCheck) {...}
    };
    

    I did learn a thing or two about encoding sets which every programmer needs to learn. The TL;DR; I took out if it was mainly the fact that Unicode is not actually an encoding, but rather a mapping from a bunch of numbers to a bunch of characters. The subtle difference, is that an encoding needs to actually specify the length in bytes of a single character - which it does not. And for good reason (turns out you would need 4 bytes to actually represent a single character!). As a result of pragmatism, oftentimes the Unicode mapping is used, but with an encoding that does not incorporate the entire map (e.g., UTF-8), or by using variable-width encodings. It's all a bit messy which is why it is good to learn more about it. Also see the Joel Spolsky's older, almost identitcal article on Unicode

    Ever wonder why we as windows devs never got a better CLI? I mean really, cmd.exe could use a facelift... Join me in backing the Wish CLI kickstarter project. The project is being developed by Thomas Thornton, and he already has a fairly functional prototype available for download here.

    Wish: Re-imagining the windows command line

    Wish Prototype: Re-imagining the windows command line

    I was toying around with a javascript markdown editor this week for a project of mine, and was looking around for how people have done other editors. In the mix I found an editor which I must have overlooked before - but I found it to be quite nice and simple. Very markdown-esque. Check out EpicEditor written by Oscar Godson. What's even better is that the source is incredibly clean and well commented. Nice!

    Chris Coyier gave a good interview discussing some of the difficulties and motivations behind his most recent creation, codepen.

    Microsoft always seems to be getting the short straw. Although their web browser for Windows Phone 8 is very good about following standards, there is much concern over the state of the mobile web right now. Webkit has been the major player in mobile web browsers, and as such, much of the written mobile web is done using webkit-specific APIs with little concern over other mobile (but standards-compliant) browsers. As mobile gets bigger and bigger, this will become a big issue - especially if the Windows phones will make any big entrance into the market (an uphill battle for Microsoft, but one they no doubt are aware of). See here how Microsoft begs web devs not to make webkit the "new IE6".

    I have absolutely no idea how it will be useful, but someone has written the entire Win32 API in javascript. Wow. Atwood's law, anyone?

    Anyone going the 3d-printer route for gifts this year? I know I am thinking about it... hint I want one /hint. Check out shapeways.com for some really cool 3d-printed products.

    Ever wonder what 100,000 stars looks like? Check out this awesomeness.

    Also - Google TV got an update. Google is really working hard on improving their knowledge graph and context-based voice API which is making a big appearance here. I'm excited to think about the day when TV will no longer be like TV is now... I don't know about you guys, but I think it's s%*t.

  • posted

    Sergey Brin hints at his distaste (which we share) for the electoral college and partisanship of the US Government. When can we finally get that electoral college thing ammended? C'mon small states (+ swing states), just do the right thing and give up your disproportional voting privileges and get this country closer to the democracy we so heartily claim that it is.

    Coding for the web has been big for a while, but is coding on the web going to be the next big thing? webshell.io launched recently, providing a free development sandbox in your browser, with a bit heavier a focus on interacting with API's than jsFiddle has. Further, koding.com launched, providing a full IDE, community, and platform for it's users to develop on. It is interesting to think about whether or not the browser will ever be able to compete with the ultra-IDEs such as Visual Studio or IntelliJ IDEA, etc. Would be nice to never have to worry about whether or not your laptop had all the necessary files etc on it to be productive. Koding is invite only right now, and still in beta, but from the looks of it it is a very well done piece of software - though still a ways to go before I will find myself using it as my IDE.

    Speaking of IDEs, Web Essentials 1.7 came out, just about the most important plug-in for Visual Studio 2012. If you use VS2012 and haven't checked it out, I highly suggest you do so.

    On the entrepreneurial front, I enjoyed reading what entrepreneurs wish they had known and finding some new solid inspirational quotes (always looking for some to put on my wall). Of course, one needs only to master machine learning and write a high frequency trading algorithm in order to achieve ultimate success. There is a nice anecdotal on teaching a "highly motivated" non-technical entrepreneur how to program, focusing on a project-oriented learning process. This definitely resonates with me - and I don't think I'm alone. People constantly ask how I learned this or that, and usually the answer is simply - "Well, I had to build <X>, and couldn't do so without learning <Y>". As Philip Guo, the author of the article, puts it:

    His learning was guided by necessity, not by some preset cookie-cutter academic curriculum.

    Exactly. I watch more computer science lectures than most, and they are fun, helpful, and a learning experience - but it still doesn't make you a software engineer - it makes you a computer scientist. Big difference. Software Engineering has a component which computer science does not - translating an idea into a reality.

    Are you a great programmer? Or are you a terrible programmer? Depending on your measure, or definition of what makes a great programmer, you could be both. Dan Shipper talks precisely about this conundrum.

    Also, a bit random, but I was quite impressed by the urine-powered generator that 4 teenage girls in Nigeria engineered. Awesome. As the author puts it well, this isn’t just a bunch of rich people talking about how their apps are going to change the world. Anyone know where I can donate some $ to these girls?

    Finally, ever wonder what a nuclear bomb exploding looks like?

    P.S.: These weekly retrospects are just a quick synopsis of what I found interesting in the past week.

  • posted

    HTML-based emails are one of those evil things in the life of a web-developer that we would all rather never have to do... but alas, it needs to be done. One can do this in a variety of ways, but it is fairly common to use modern web-development technologies to generate the actual email bodies over HTTP before sending. We all know how to make web pages, right? How different could an HTML email be?

    This always seems rather simple in spirit, but the problem is that email clients have a very non-standardized way of rendering HTML. The fact that you must write HTML for emails as if you were living in the late 90's is bad enough, but on top of that - many email clients remove CSS stylesheets from the HTML.

    I have an ASP.net Razor project where I need to generate some HTML emails. I would like them to be maintainable style-wise and share a common layout page and css styles. What would be nice is if I could develop the web pages like normal with CSS stylesheets, but have the CSS inlined automatically.

    CSS parsing does not sound like something I would like to do myself, so naturally, I looked to see what was out there. There are some great tools out there, but one clear winner:

    PreMailer.Net: C# Library for moving CSS to inline style attributes, to gain maximum E-mail client compatibility.

    PreMailer.Net is a C# implementation of Premailer's CSS inlining service built by Martin Normark. This was exactly what I needed! With this library, it should be super easy to do the inlining.

    Martin did a great job with this by utilizing Fizzler, a CSS Selector Engine for HTML documents in .NET, and a CSS parser class from The Dynamic Programmer to do this. I believe a lot of the work was influenced by Premailer, which deserves a mention here:

    Premailer: Pre-flight for HTML email

    Premailer, written by Alex Dunae is the "original" Premailer library written in Ruby. Alex has been kind enough to put it up as a web service on his website, allowing anyone to inline CSS on the go - and provides some other HTML-email-specific type tools which are worth checking out.

    Anyway, I decided to wrap an HttpModule around the PreMailer.Net library to automatically process the HTML from each response in a sub-folder of my application. The HttpModule would look something like this:

    public class InlineCssModule : IHttpModule
    {
        void IHttpModule.Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(context_BeginRequest);
        }
    
        // Add our custome InlineCssFilter to Response.Filter
        static void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            app.Response.Filter = new InlineCssFilter(app.Response.Filter);
        }
    
        private class InlineCssFilter : Stream
        {
            // The PreMailer object to do the CSS inlining magic
            private static PreMailer pm = new PreMailer();
    
            /* Stream Implementation abbreviated */
    
            // Take in the response stream, write out the modified response
            public override void Write(byte[] buffer, int offset, int count)
            {
                byte[] data = new byte[count];
                Buffer.BlockCopy(buffer, offset, data, 0, count);
                string html = System.Text.Encoding.Default.GetString(buffer);
    
                html = pm.MoveCssInline(html, true);
    
                byte[] outdata = System.Text.Encoding.Default.GetBytes(html);
                _sink.Write(outdata, 0, outdata.GetLength(0));
            }
        }
    }  
    

    (you can find the full HttpModule code in a github gist here)

    Now, one simply needs to modify their config file to put it in the ASP.Net Pipeline:

    <system.webServer>
        <modules>
            <add name="InlineCssModule" type="{namespace}.InlineCssModule"/>
        </modules>
    </system.webServer>
    

    As a result, writing this:

    <html>
        <head>
            <style type="text/css">
                div {
                    color: red;
                }
                .blue {
                    color: blue;
                }
            </style>
        </head>
        <body>
            <div class="blue">I should be blue</div>
        </body>
    </html>
    

    renders the following HTML after the filters: (notice the lack of a stylesheet, and the presence of the style tags).

    <html>
        <head>
    
        </head>
        <body>
            <div class="blue" style="color: blue;">I should be blue</div>
        </body>
    </html>
    

    Success!