Category: Scripts

  • HTML5 Dynamic Charting

    I recently attended an HTML5 workshop held by
    Mr Remy Sharp. I was the only honest schmo in the room to admit to having already
    purchased the HTML5 book on offer. I was waiting for the free T-shirt that
    never transpired 🙁 The workshop itself was really interesting and I left with
    the usual ‘I’m going to change the world with this new knowledge’ excitement.
    A few months have passed and I’ve finally got around to writing this blog (the
    changing the world part will have to wait).

    The part of the day that really got my juices flowing was the Canvas tag
    stuff. It’s perhaps embarrassing to admit but I’d not had any dealings with it
    beforehand. The bit that really blew my top was the traversing through each
    pixel of a transposed image on the canvas and altering each one. Simply
    amazing. During my lunch hours at work, I wanted to investigate the Canvas tag
    in more depth. I wanted to create a simple table of data, that using
    JavaScript, could be used to generate a simple line graph. I wasn’t even sure
    if it was possible.

    Just a quick mention of
    Zen-Coding
    which I was also introduced to at the Workshop. Imagine being able to write
    entire blocks of HTML shorthand and then convert them at the press of a
    button. So

    div.class-name becomes
    <div class="class-name"></div>and

    ul>li*3 becomes
    <ul><li></li><li></li><li></li></ul>
    (Tabbed in reality)Back to the point…

    Generating a line graph on the fly

    The final example can be seen
    here. I started off with a basic table of data within my HTML5 document.

    Day Jokes Learnt Girls Numbers
    Day 1 5 1
    Day 2 7 1
    Day 3 10 3
    Day 4 20 4
    Day 5 40 7

    Here we are studying the highly scientific effect of comedy on the fairer sex.
    The next thing I added was the container for my X and Y labels and a key
    holder which explained what the line represented.

    
      <div id="y-axis">
        <ul id="y-axis"> 
          <li id="label-1"></li> 
          <li id="label-2"></li> 
          <li id="label-3"></li> 
          <li id="label-4"></li> 
        </ul>
      </div> 
        <div id="canvas-holder"></div> 
        <div id="key-holder"> 
          <table id="key"> 
            <tbody> 
              <tr>
                <th>
                  Key
                </th> 
                <th>
                  Value
                </th> 
              </tr> 
            </tbody>
          </table> 
        </div>
      <div id="x-axis"> </div>
    
    

    I learnt at the workshop about document.querySelector and
    document.querySelectorAll. Both return the corresponding DOM
    elements of the queried parameters. I wanted this HTML file to be
    self-sufficient and not require any 3rd party JavaScript libraries. So using
    these DOM selectors I did the following.

      
        var can = document.querySelector('canvas'); 
        
        // Traverse each header to work out the keys required
        var thList = document.querySelector('#data').querySelectorAll('th');
        var dataKey = new Array();
        // Start count at 1 to avoid our blank cell
        for (i = 1; i < thList.length; i++) {
          dataKey[i] = thList[i].innerHTML;
        }
      
    

    The first line assigns our canvas element to the variable
    can. Next up we use the headers to define the number of lines
    we need on our graph and what they represent. Notice that the count starts at
    1 to ignore the first th cell which is empty. We assign these
    variables to an array (dataKey).

      
    // Use the td content to derive the data array
    var tdList = document.querySelector('#data').querySelectorAll('td');
    var dataArray = {};
    var dataCount = 0;
    var currentKey = '';
    
    // Variables for our X and Y labels
    var smallestFigure = 0;
    var largestFigure = 0;
    var xLabels = [];
    var xlableCnt = 0;
    
    // Traverse the td to derive our data array
    for (i = 0; i < tdList.length; i++) {
      // Set smallest/largest if value is a number
      if (!isNaN(tdList[i].innerHTML)) {
        var testNumber = parseFloat(tdList[i].innerHTML);
        if (!smallestFigure && !largestFigure) {
          smallestFigure = testNumber;
          largestFigure = testNumber;
        } else {
          if (testNumber < smallestFigure) {
            smallestFigure = testNumber;
          }
          if (testNumber > largestFigure) {
            largestFigure = testNumber;
          }
        }
      }
    
      // Col 1 is the key for this row
      if (dataCount == 0) {
        currentKey = tdList[i].innerHTML;
        dataArray[currentKey] = {};
        xLabels[xlableCnt] = tdList[i].innerHTML;
        xlableCnt++;
      } else {
        dataArray[currentKey][dataKey[dataCount]] = tdList[i].innerHTML;
      }
    
      // Count starts at zero
      if (dataCount == (dataKey.length - 1)) {
        dataCount = 0;
      } else {
        dataCount++;
      }
    }
    
    

    OK, this may look slightly overwhelming. Just take some deep breaths and I'll
    talk you through it. What this code is trying to do is to use the content of
    the table to form a data array. Firstly the tdList becomes a
    holder for all of the content within the td tags. Next, we
    define all of the variables we are going to require to get our array. We loop
    through all of the td DOM elements. For each
    td element, we check whether the content is a valid number.
    If it is we check the value against previous values to determine the largest
    and smallest numbers. This will be used to determine our Y axis range. Our X
    axis labels use the first (vertical) column in our table. Each label will also
    be used as our array key to determine the values plotted on the canvas.

    
      // Each value will be worth a units worth of pixel
      var unit = can.height / largestFigure;
    
      // Lets now get our side label
      document.querySelector('#label-1').innerHTML = largestFigure;
      document.querySelector('#label-2').innerHTML = (largestFigure / 4) * 3;
      document.querySelector('#label-3').innerHTML = (largestFigure / 4) * 2;
      document.querySelector('#label-4').innerHTML = (largestFigure / 4) * 1;
    
      // Our bottom label
      var xWidth = can.width / xLabels.length;
    
      // Lets fill our x labels
      for (i = 0; i < xLabels.length; i++) {
        xSpan = document.createElement('span');
        xSpan.innerHTML = xLabels[i];
        xSpan.className = 'spanBlock';
        xSpan.style.width = xWidth + 'px';
        document.querySelector('#ul-x-axis').appendChild(xSpan);
      }
    
    

    Next up we use our assigned variables to determine the X and Y axis labels. We
    find our unit (what each value is in pixels) by taking the length of the
    canvas and dividing it by the largest figure we have. The Y axis is split into
    4 so is not very dynamic, I'm sure you can do better. Also, you may have noted
    I've picked some nice round numbers in this example, barely the case in
    reality! Our X-axis uses the xLabels variable we assigned to
    early. We create a new span DOM element and set the inner HTML to match the
    value of the label. Now for the fun part...

    
      // Check whether we can use the canvas 
      if (can.getContext){ 
        // Get our object
        var ctx = can.getContext('2d');
        
        // Black out back ground 
        ctx.fillStyle = '#000'; 
        ctx.fillRect(0,0, ctx.canvas.width, ctx.canvas.height); 
        ctx.fillStyle = '#fff'; 
        ctx.strokeStyle = '#fff'; 
        
        // default drawing style 
        ctx.lineWidth = 5; 
        ctx.lineCap = 'round'; 
        ctx.save(); 
        
        // Define array of fill colours first element is 
        // blank so that our keys can be used 
        strokeArr =new Array('','#fff','#0000FF');
        
    

    The first line checks that we can actually use the
    getContext method of the object. We set up our ctx object
    which allows us to draw on our canvas tag. We create our black rectangle which
    covers the canvas, this is the base for our chart. The
    lineWidth and lineCap set up how our line
    graph will look on the canvas. Our strokeArr sets the colours
    for each of our lines. This isn't at all dynamic. You may ask
    'why is the first element blank' and
    'what if someone adds another column'. To which my response will be
    'Shut the hell up!'. Anyway, we are now ready to start drawing our
    lines.

    
      for (i=0;i<dataKey.length;i++) { 
        if (dataKey[i] != undefined) { 
          // Set Starting point 
          ctx.strokeStyle = strokeArr[i]; 
          ctx.beginPath(); 
          ctx.moveTo(0,500); 
          
          // Set defualt points 
          xPoint = 0; 
          yPoint = 500; 
          
          // Item in object
          xCount = 0; 
          
          // Loop through items 
          for (o in dataArray) { 
            // X point moves along with the count 
            xPoint  = xCount * xWidth; 
            
            // Use the unit calculated earlier to calc the y point 
            if (dataArray[o][dataKey[i]]) {
              yPoint = 500 - (dataArray[o][dataKey[i]] * unit); 
            } else { 
              yPoint = 500;
            } 
            
            // Start new line or join existing 
            if (xCount == 0) { 
              ctx.moveTo(xPoint,yPoint); 
            } else { 
              ctx.lineTo(xPoint, yPoint);
            } 
            
            xCount++; 
          } 
          
        // Add stroke
        ctx.stroke(); 
        
        // Add this row to key 
        keyTr = document.createElement('tr');
        keyTd = document.createElement('td'); 
        keyTd.style.backgroundColor = strokeArr[i]; 
        keyTr.appendChild(keyTd); 
        keyTd2 = document.createElement('td'); 
        keyTd2.innerHTML = dataKey[i];
        keyTr.appendChild(keyTd2);
        document.querySelector('#keytbody').appendChild(keyTr);
      } 
    }
    
    

    We are now ready to use our data array which we generated earlier. We loop
    through each item (line on the chart) which contains an object of points on
    the map. The strokeArr value is the colour of the line. The
    beginPath method is the equivalent of taking your pen off of
    the canvas. The moveTo method puts the pen back on the canvas
    at the correct point, in this case where the X and Y axis meet. We start
    looping the points on the canvas we need to draw.

    The xPoint (where on the X axis we need to plot our point) is
    calculated by using the xCount (number of iterations through
    our X-axis) times the xWidth (number of pixels between each X
    label). The Y axis is a little more complicated as the lowest Y point isn't 0
    it's 500 (because the canvas start at the top left not the bottom left). We
    use our data value and times it by the unit which gives us the number of
    pixels up we need to be. We have our X and Y points so we now just need to set
    them. We check whether this is a new line or an existing line and plot the
    point accordingly. The ctx.stroke() fills the line for us.
    Now we have our line we need to create a record in our key table. The first
    td is filled with the same colour of the line while the
    second td holds the label for that line. We append the new
    table row to the key table.

    And that's that. I was very impressed with the ease with which we can do this
    funky stuff and look forward to more of the same!

  • Google Marker Events

    My latest project involves using google maps. So far I’ve been very impressed with the API. There are plenty of examples and explanations of how everything works. However, I came across a problem that seemed like a common enough scenario yet the answer could not be found. Eventually, I found this post which helped point me in the right direction.

    I had a similar problem where I had an array of longitudes and latitudes that all required their own events. For each marker, I needed a mouseover and mouseout click function. Again the issue was that the last array items were the values being used regardless of which marker’s event is fired. I needed a way to identify which marker the event was applicable to. The solution was found by using the ‘this’ variable within the event function. From ‘this’ you can use the this.position.c  to find the longitude and the this.position.b to find the latitude of that particular marker. Hope this helps!

      
    // Loop through our list of longs and lats
    for (i=0;i<initialMarkerHolder.length;i++) {
    	google.maps.event.addListener(marker, 'click',function() {
    		alert('log = ' + this.position.c);
    		alert('lat = ' + this.position.b);
    	});
    }
    
    
  • SVN Magic

    My company recently hired a new developer which brought the number of developers to….(drum roll)…Four! OK, not a really significant amount but enough for us to need a more effective code change reporting system. We needed a way to alert developers of any changes made to their files. The system I managed to put in place has been running for about a week and I thought I would do a blog post to maybe help other small development teams to introduce something similar. The solution I set out to achieve was this …

    A daily email is sent to every developer with a list of files that have been changed the previous day. The list will only include files that this particular developer had previously added or changed. Attached to the email is an HTML file containing a more detailed view of the changes made to each file.

    If you think this would be useful for you then continue reading. The first step was to store the details of each SVN commit in a DB table. This was accomplished by creating a script that was called after every commit. SVN comes with a selection of ‘hooks’ which are executable bash scripts that run after (or before ) a specific SVN event occurs. Simply go to your SVN hooks directory and copy the template file post-commit.tmpl as post-commit. Add the shell commands required to call your script and make it executable.

    chmod +x post-commit
    

    My script uses the Repository and Revision variables passed via SVN to carry out an svnlook on the repository for that revision. I use the PHP system call to store the output in a variable (use square quotes “). I then use the PHP explode on the new lines (n) to generate a nice neat array of variables. I simply store each of the files involved in the commit in my DB table along with the revision number, the author, the comment, and the DateTime.

    My second script does the main leg work. I firstly added the script to our crontab to run every morning. The first thing the script does is return all of the commits for the previous day. For each file, it runs svn log and stores the output in a variable. By using some exploding and other PHP string functions the scripts gets the output into a manageable format. From this information, we determine who previously changed this particular file. We use the author name as the main KEY of a large array we’ll use later. In the array, we store all of the information we think may be useful to include in our final email.

    Now for the cool part. We export the previous revision of this particular file to a temporary directory and also the latest version of this file.

    svn export --revision 120 file:///my_repos/blog.html temp/temp1.txt
    svn export --revision 121 file:///my_repos/blog.html temp/temp2.txt
    

    We use a 3rd party Pear class to give us a comparison array of the two files (showing new code snippets, changed code snippets, and deleted code snippets). We add some final gloss to our email by using some 3rd party syntax highlighting.

    OK, at this point we should have our enormous array of SVN usernames and file details. All we need to do now is loop through each SVN username key and create our email. I have a template system in place that takes care of displaying the diffs between the files. I simply write this to an HTML file and attach it to the email. I have a list of email addresses in the database assigned to each SVN username. And you’re done.

    Please let me know if you would like any further details on this and I’ll elaborate on those points. Good Luck!