Tracking mailto, anchors and external links with Google Analytics

With this simple snippet, all clicks on page-internal anchors, external links (http…) and mailto-links are tracked as events in Google Analytics:

$(function(){
    $("a[href*='http']").each(function() {
                    $(this).click(function (ev) {
                        var pageView = '/outgoing/' + $(this).attr('href');
                        _gat._getTrackerByName()._trackEvent('Outbound Links', pageView);
                        var _href = $(this).attr('href');
                        setTimeout(function() {
                            location.href = _href;
                        }, 100);
                        ev.preventDefault();
                        return false;
                    });
        });
      $("a[href*='mailto']").each(function() {
                    $(this).click(function (ev) {
                        var pageView = '/mailto/' + $(this).attr('href').substring(7);
                        _gat._getTrackerByName()._trackEvent('Mailto', pageView);
                    });
        });
      $("a[href*='#']").each(function() {
                    $(this).click(function (ev) {
                        var pageView = '/anchor/' + $(this).attr('href').substring(1);
                        _gat._getTrackerByName()._trackEvent('Anchors', pageView);
                    });
        });
});
Advertisement

Announcing jQuery Live-Ready 1.0 Release

jQuery Live-Ready is a combination of jQuery.ready and jQuery.live. Often jQuery-Plugins manipulate content by just registering to $.ready and then go modify the DOM. But whenever additional content is added to a page, that won’t benefit from the registrations.

With $.live in turn, you can subscribe to events even for elements to be added in the future. But live does support the ready-event.

This is where liveReady comes in.

Getting Ready

With liveReady, instead of registering to $.ready, you register your callback to $.liveReady like this:

$.liveReady(function () {
  // this is $(document), or the element(s) added
  this.find('a').css('color', 'red');
});

The registered function will be called on Document-ready and whenever the Extension liveReady() is called on jQuery elements.

Attention: When ‘a’ is the top-level element added, this.find(‘a’) will not return anything. Have a look at the ‘Filtering to a Selector’ section.

Now, when you create new content, you can call liveReady() on the new elements before or after adding them to the dom:

// before (no parent)
$(document).append($('<p>Some <a href="#">Link</a></p>').liveReady())

// after, recommended
var $content = $('<p>Some <a href="#">Link</a></p>');
$(document).append($content);
$content.liveReady()

Filtering to a Selector

In many cases, you only do something with a subset of DOM elements within the ready-Event. With liveReady you then register your function to a specific jQuery selector:
$.liveReady('a', function () {
  // this is $(document), or the element(s) added
  this.css('color', 'red');
});

Now, whenever $(‘…’).liveReady() is executed on new content, the registered function will run with the element itself, if it is an ‘a’, or with all ‘a’-elements liveReady finds within.

Download and Live Hosting

The project is in it’s initial release 1.0. The project code is hosted at Bitbucket. If you find any issues feel free to add them there.

I have also submitted the plugin to jQuery Plugins.

You can download the release archive including a demo here. Or just check out the jQuery Live-Ready 1.0 Demo.

Live Hosting from bitbucket CDN is also available:

<!-- full version -->
<script type="text/javascript" src="https://bitbucket.org/larscorneliussen/jquery.liveready/downloads/jquery.liveready-1.0.js"></script>

<!-- minified version -->
<script type="text/javascript" src="https://bitbucket.org/larscorneliussen/jquery.liveready/downloads/jquery.liveready-1.0-min.js"></script>

 

Happy coding.

beautyOfCode – jQuery plugin for Syntax Highlighter 2.0 by Alex Gorbatchev

One of my most-visited posts is the jQuery plugin “beautyOfCode” for the version 1.5 of Alex’ Syntax Highlighter. The main point of doing a wrapper was, that version 1.5 did require invalid html code.

In the new version of Syntax Highlighter this is fixed. But still, people ask for a new version of my code, too.

I finally released a new version with some more features:

  • Uses online hosting of styles, scripts and brushes. This means you only need to reference jQuery and beautyOfCode!
  • Supports the new features as smart-tab, html-script, themes, line-wrapping and much more
  • Still uses a code-tag inside a pre-tag as well as a more css-like configuration with classes instead of property syntaxes

Referencing  the plugin and configuring brushes

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script type="text/javascript" src="Scripts/jquery.beautyOfCode.js"></script>

<script type="text/javascript">
    $.beautyOfCode.init({
      brushes: ['Xml', 'JScript', 'CSharp', 'Plain', 'Php']
    }); 
</script>

Detection, default behaviour

By default, beautyOfCode will beautify all elements matching ‘pre.code:has(code[class])”:

<pre class="code">
  <code class="javasript">
    alert("Hello, World!");
  </code>
</pre>

Explicit

If auto detection is not desired, the plugin has to be initialized differently:

<script type="text/javascript">
    $.beautyOfCode.init({
      brushes: ['Xml', 'JScript'],
      ready: function() {
        $('#mycode').beautify('javascript');
      }
    }); 
</script>

This would then only beautify following code:

<pre id="mycode">
  <code>
    alert("Hello, World!");
  </code>
</pre>

Initialization Options

These are the default options, which can be overridden by $.jQuery.beautyOfCode.init:

settings: {
    // should the syntax highlighter and brushes
    // be loaded dynamically
    autoLoad: true,
    
    // the base url to alex' hosted sources
    // http://alexgorbatchev.com/wiki/SyntaxHighlighter:Hosting
    baseUrl: 'http://alexgorbatchev.com/pub/sh/2.0.320/',
    
    // the baseurl for the hosted scripts
    scripts: 'scripts/',
    
    // the baseurl for the hosted styles
    styles: 'styles/',
    
    // themes from http://alexgorbatchev.com/wiki/SyntaxHighlighter:Themes
    theme: 'Default',
    
    // the brushes that should be loaded - case sensitive!
    // http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes
    brushes: ['Xml', 'JScript', 'CSharp', 'Plain'],
    
    // overrides for configurations and defaults
    // http://alexgorbatchev.com/wiki/SyntaxHighlighter:Configuration
    config: {},
    defaults: {},
    
    // function to be called, when all scripts are loaded
    ready: function() {
        jQuery.beautyOfCode.beautifyAll();
    }
}

Example with current version, Django-theme and no gutter

<script type="text/javascript">
    $.beautyOfCode.init({
        theme: 'Django',
        baseUrl: 'http://alexgorbatchev.com/pub/sh/current/'
        defaults: { gutter: false },
        brushes: ['Xml', 'JScript']
    }); 
</script>

Options

Find a reference for all options on SyntaxHighlighter Configuration.

The options can either be specified as defaults in init, on the beautify-call or as css classes.

Specify options in code:

$('#myCode').beautify('javascript', {'wrap-lines':true, 'first-line:2'});

Specify options in html:

In this case the gutter would not be shown and the lines 3 and 6 (5 and 8 in Listing) would be highlighted.

<pre class="code">
  <code class="javasript boc-no-gutter boc-highlight&#91;3,6&#93;">
    alert("Hello, World!");
    
    function x() {
        if (true)
            x();
    }    
  </code>
</pre>

Syntax for options in html:

  • The flags smart-tabs, ruler, gutter, toolbar, collapse, auto-links, wrap-lines and html-script mean true, then prefixed with boc-, and false, when prefixed with boc-no-.
  • class-name, first-line and tab-size are prefixed with boc- and followed by their values in square brackets. For example first-line[3] means, the gutter starts with a three.
  • highlight is also prefixed with a boc- and followed by a comma-separated list of line numbers in square brackets that are to be highlighted.

Sources

Find the plugin source code and a demo page on the bitbucked beautyOfCode project page. If you like the plugin, please also vote on the jQuey plugin site.

Feel free to submit issues and patches. The code is not very well tested, since I don’t use it in any production environment today.

beautyOfCode: jQuery Plugin for Syntax Highlighting

UPDATE: Support for Syntax Highlighter 2.0 + jQuery Listed + bitbucket Repository

The Syntaxhighlighter by Alex Gorbatchev is the best I have seen so far. As you see in this post, wordpress.com uses it, too.

But the required syntax is not xhtml-compliant (name on pre is forbidden).

<pre name="code" class="javascript">
  // my code
</pre>

In order to enable compliant xhtml I had to rewrite a small part anyway, so I just made a small jQuery-Plugin that just uses Alex’s scripts right away (thanks Alex!).

Beautify explicitely

<pre id="myCode">
  <code>
     // my code
  </code>
</pre>

Using beautyOfCode you don’t have to decorate your html in order to enable syntax highlighting. Just call beautifyCode on any selected pre or textarea

$(function(){
    $.beautyOfCode.init('clipboard.swf');
    $("#myCode").beautifyCode('javascript');
});

Beautify all

If you prefer to decorate your html and then just let the highlighter take care of the rest, you may use following notation.

<pre class="code">
  <code class="javascript">
     // my code
  </code>
</pre>

<pre class="code">
  <code class="css boc-nocontrols">
    body {
      font-size: 2em;
    }
  </code>
</pre>

Make sure you have these few lines

$(function(){
    $.beautyOfCode.init('clipboard.swf');
    $.beautyOfCode.beautifyAll();
});

Settings

Default Settings

{
   noGutter: false, // hide line numbers?
   addControls: true, // copy, view plain, ...
   collapse: false, // show controls with expand link
   showColumns: false, // show column numbers
   firstLine: 1 // start with another line number?
}

Change Settings with Javascript

$(function(){
  $.beautyOfCode.init('clipboard.swf', {
    // add global settings here
    firstLine: 4,
    collapse: true
  });

  // change options per listing
  $("#myCode").beautifyCode('javascript', {
    noGutter: true
  });
});

Change Settings in Element-Decoration

Settings with in the html are added as additional classes on the code-element.

<pre class="code">
  <!-- You dont have to specify default values. -->
  <code class="javascript boc-firstline&#91;3&#93;
    boc-nocontrols
    boc-nogutter
    boc-showcolumns
    boc-collapse">
     // my code
  </code>
</pre>

kick it on DotNetKicks.com

Source Code

Notice: beautyOfCode is using Syntaxhighlighter. You need to link in SyntaxHighlighter.css, shCore.js and the brushes you want to use.

jQuery.beautyOfCode = {
  initialized: false,

  settings: {

    // hide line numbers?
    noGutter: false,

    // show copy, plain, ... links
    addControls: true,

    // collapse to control bar. cant be used
    // with addControls set to false
    collapse: false,

    // show column numbers
    showColumns: false,

    // start with another line number?
    firstLine: 1
  },

  brushByAlias: {},

  init: function (clipboardSwf, settings) {
    dp.SyntaxHighlighter.ClipboardSwf = clipboardSwf;

    if (settings)
      jQuery.extend(jQuery.beautyOfCode.settings, settings);

    if (jQuery.beautyOfCode.isInitialized)
      return;

    // creates a map of each registered brush by alias
    jQuery.each(dp.sh.Brushes, function (i, brush) {
      var aliases = brush.Aliases;

      if(aliases == null)
       return;

      jQuery.each(aliases, function (ii, alias) {
        jQuery.beautyOfCode.brushByAlias[alias] = brush;
      });
    });

    jQuery.beautyOfCode.isInitialized = true;
  },

  addCssForBrush: function (brush, highlighter) {
    if (brush.isCssInitialized)
      return;

    var headNode = $("head")[0];
    if(highlighter.Style && headNode)
    {
      var styleNode = document.createElement('style');
      styleNode.setAttribute('type', 'text/css');

      if(styleNode.styleSheet) // for IE
        styleNode.styleSheet.cssText = highlighter.Style;
      else // for everyone else
        $(styleNode).text(highlighter.Style);

      headNode.appendChild(styleNode);
    }

    brush.isCssInitialized = true;
  },

  beautifyAll: function() {
    jQuery("pre.code:has(code[class])").each(function (i, item) {

      function getOptionValue(name, list)
      {
        var regex = new RegExp('^' + name + '\\[(\\w+)\\]$', 'gi');
        var matches = null;

        for(var i = 0; i < list.length; i++)
         if((matches = regex.exec(list[i])) != null)
          return matches[1];

        return null;
      }

      var $item = jQuery(item);
      var $code = $item.children("code");
      var code = $code[0];

      var options = code.className.split(" ");
      var language = options[0];

      var settings = {};

      if ($code.hasClass("boc-nogutter"))
        settings.noGutter = true;

      if ($code.hasClass("boc-nocontrols"))
        settings.addControls = false;

      if ($code.hasClass("boc-showcolumns"))
        settings.showColumns = true;

      if ($code.hasClass("boc-collapse"))
        settings.collapse = true;        

      var firstLine = getOptionValue("boc-firstline", options, 1);
      if (firstLine)
        settings.firstLine = firstLine;

      $item.beautifyCode(language, settings);
    });
  }
};

jQuery.fn.beautifyCode = function (language, settings) {

  var saveLanguage = language;
  var saveSettings = settings;

  // iterate all elements
  this.each( function (i, item) {
    var $item = jQuery(item);

    var settings = jQuery.extend({}, jQuery.beautyOfCode.settings, saveSettings);

    var brush = jQuery.beautyOfCode.brushByAlias[saveLanguage];

    if (!brush)
      return;

    // instantiate brush
    highlighter = new brush();

    // set brush options
    jQuery.extend(highlighter, settings);

    jQuery.beautyOfCode.addCssForBrush(brush, highlighter);

    // IE Bug?: code in pre has to be skipped
    // in order to preserver line breaks.
    if ($item.is("pre") && ($code = $item.children("code")))
      $item.text($code.text());

    highlighter.Highlight($item.html());
    highlighter.source = item;

    $item.replaceWith(highlighter.div);
  });
}
[/sourcecode]