Highcharts Symbols Inverted Axis

As I sit here and write this article to help others, I wonder if my efforts were based on my inability to let things go or an uncontrollable urge to get this right? I think a little of both would suffice the question just asked.

I will admit that this answer starts with this link and eventually leads to me discovering how to set attributes based on other attributes being set. How I arrived at the solution was more of me adapting to what was not working. For most of the time I was just trying to get the icons to render, then getting the icons to render vertically and eventually trying to figure out how to get the icons to show properly once the chart redrew itself. I Googled quite hard in seeing what other people were doing as well as stumbling along other links which explained my issue.

So lets get down to the business of properly displaying a font awesome icon inside of a Highchart element.

First we must extend the SVGRenderer symbol method


/*
This experiment explores how to use FontAwesome iconic fonts as markers in Highcharts. Three steps are required:

1. Supply the plugin code below to handle text symbols.
2. In each series' marker.symbol setting, add the font icon as Unicode string on the format 
   "text:\uf182". The Unicode corresponding to icons can be found at 
   https://github.com/FortAwesome/Font-Awesome/blob/master/less/variables.less

Compatibility notes:
- IE6, 7, and 8 don't support rgba colors (fall back to solid rgb)
*/

/* Highcharts plugin to handle text symbols */
(function (H) {
    function symbolWrap(proceed, symbol, x, y, w, h, options) {
        if (symbol.indexOf('text:') === 0) {
            var text = symbol.split(':')[1],
                svgElem = this.text(text, x, y)
            .attr({
            	translateY: h,
                translateX: -1
            })
                .css({
                    fontFamily: 'FontAwesome',
                    fontSize: h * 2
                });
            
            if (svgElem.renderer.isVML) {
                svgElem.fillSetter = function (value, key, element) {
                    element.style.color = H.Color(value).get('rgb');
                };
            }
            return svgElem;
        }
        return proceed.apply(this, [].slice.call(arguments, 1));
    }
    H.wrap(H.SVGRenderer.prototype, 'symbol', symbolWrap);
    if (H.VMLRenderer) {
        H.wrap(H.VMLRenderer.prototype, 'symbol', symbolWrap);
    }
    
    // Load the font for SVG files also
    H.wrap(H.Chart.prototype, 'getSVG', function (proceed) {
        var svg = proceed.call(this);
        svg = '' + 
            svg;
        return svg;
    });
}(Highcharts));

Next we need to add proper translation and rotation whenever this chart resizes. This is important as once the chart redraws itself the icons will be moved to it’s new location based on the final size of the chart. We used the little know class of MutationObserver to take care of placing the icon appropriately.


var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

var height = (h/2) + y;
var width = (w/2) + x;
var transformParams = “translate(-1, ” + h + “) rotate(90 ” + width + ” ” + height + “)”;

svgElem.element.setAttribute(“transform”, transformParams);

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        if(mutation.target.style.fontFamily === ‘FontTW’) {
            var element = mutation.target;
            var x = parseInt(element.attributes[‘x’].value);
            var y = parseInt(element.attributes[‘y’].value);
            var params = “translate(-1, ” + parseInt(element.attributes[‘height’].value) + “) rotate(90 ” + x + ” ” + y + “)”;
            
            mutation.target.setAttribute(“transform”, params);
        }
    });
});

observer.observe(svgElem.element, {
    attributeFilter: [‘x’, ‘y’] //configure it to listen to attribute changes
});

When we put everything together we end up with this script


/*
This experiment explores how to use FontAwesome iconic fonts as markers in Highcharts. Three steps are required:

1. Supply the plugin code below to handle text symbols.
2. In each series’ marker.symbol setting, add the font icon as Unicode string on the format
  “text:\uf182”. The Unicode corresponding to icons can be found at
  https://github.com/FortAwesome/Font-Awesome/blob/master/less/variables.less

Compatibility notes:
- IE6, 7, and 8 don’t support rgba colors (fall back to solid rgb)
*/

/* Highcharts plugin to handle text symbols */
(function (H) {
   function symbolWrap(proceed, symbol, x, y, w, h, options) {
       var test = proceed.apply(this, [].slice.call(arguments, 1));
       var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;

       if (symbol.indexOf(‘text:‘) === 0) {
           var text = symbol.split(‘:’)[1],
               svgElem = this.text(text, x, y + h)
               .attr({
                   translateY: h,
                   translateX: -1
               })
               .css({
                   fontFamily: ‘FontTW’,
                   fontSize: h * 2 + “px”
               });

           var height = (h/2) + y;
           var width = (w/2) + x;
           var transformParams = “translate(-1, ” + h + “) rotate(90 ” + width + ” ” + height + “)”;

           svgElem.element.setAttribute(“transform”, transformParams);

           var observer = new MutationObserver(function(mutations) {
             mutations.forEach(function(mutation) {
               if(mutation.target.style.fontFamily === ‘FontTW’) {
                   var element = mutation.target;
                   var x = parseInt(element.attributes[‘x’].value);
                   var y = parseInt(element.attributes[‘y’].value);
                   var params = “translate(-1, ” + parseInt(element.attributes[‘height’].value) + “) rotate(90 ” + x + ” ” + y + “)”;

                   mutation.target.setAttribute(“transform”, params);
               }
             });
           });

           observer.observe(svgElem.element, {
             attributeFilter: [‘x’, ‘y’] //configure it to listen to attribute changes
           });

           if (svgElem.renderer.isVML) {
               svgElem.fillSetter = function (value, key, element) {
                   element.style.color = H.Color(value).get(‘rgb’);
               };
           }
           return svgElem;
       }


       return test;
   }
   H.wrap(H.SVGRenderer.prototype, ‘symbol’, symbolWrap);
   if (H.VMLRenderer) {
       H.wrap(H.VMLRenderer.prototype, ‘symbol’, symbolWrap);
   }

   // Load the font for SVG files also
   H.wrap(H.Chart.prototype, ‘getSVG’, function (proceed) {
       var svg = proceed.call(this);
       svg = ‘’ +
           svg;
       return svg;
   });
}(Highcharts));

How long did this take approximately 20 hours of researching, testing and correcting all the intricate details. I hope you can take these code snippets and copy and paste the final result. Although this took me forever and made me question my own abilities as a developer I’m glad that I’m able to share this with the rest of the world until they fix this lowly ranked issue in Highcharts.

Felicia is now known as NPM and nurse is known as Yarn

Bye NPM, Hello Yarn.

If you haven’t heard of Yarn you are missing out. Yarn is the new node package manager that the cool kids are using these days. Don’t you want to be a part of the cool kids? I know the label cool kids can be a misnomer and a term that can bring up bad school memories. However I ask you to push those negative thoughts to the side and come over and check out Yarn.

I will admit I haven’t had a whole lot of experience using the Yarn package manager, but I will tell you a few things that drew me towards using it in my own personal pet project and would even take the risk of persuading others to use it as well.

I will start with the most attractive feature, in my own opinion, is the folder management. If you are currently using NPM for your project take a look at the node_modules folder in the root of your project. If you take a few minutes and go through a few packages you will find another node_modules folder within a package. If you look inside that node_modules folder chances are you will find another package that contains a node_modules folder. And if you look inside that node_modules folder… I’m sure at this point you get the picture. This creates a very messy and complex folder structure. Also, side note for all you Windows users, you may run into the filename character limit.

Yarn untangles the messy folder structure and puts all the packages that are needed in the root of the node_modules folder. When you look inside of each package using Yarn you will not see another node_modules folder.

Second, parallel processing of dependencies. Instead of waiting for one package to finish downloading before the other one starts, all dependencies download at once. Even if one dependency fails it will retry again. This is pretty cool in my opinion and is something that has been needed for over a long time. So great job Facebook and Google for making this feature happen.

Third, the transition from NPM to Yarn is a breeze. All I had to do was type two commands

npm install -g yarn
yarn

and viola I had yarn implemented into my project. As Yarn was doing the transition from NPM it showed me several warnings that indicated my packages were out of date. Updating those packages were easy as well.

Bonus and probably the most important reason why I am writing this post is the speed at which my application runs. My personal pet project uses EmberCLI. Whenever you are first starting this tool on your machine it takes a few seconds to get the build to complete. Before, using NPM, it took around 11 seconds to build. Granted this is a very small application that is around 143MB. I know what you are thinking here with the size, but trust me the EmberCLI creates a very large project for such a small application during development. I would like to see what the size of the project when it’s ready for production. Anyways, with Yarn it now takes 8 seconds to build. A three second decrease may not seem much, but if you are working on a large project your build times will decrease on a more dramatic scale. Less time waiting for builds means more time for development.

Double Bonus for all you people still developing on Windows machines. One, I have a heart for you and would first tell you out of respect to consider switching to a Mac. However, with that being said, I know many of you will not for various reasons. I do really respect that, because I used to be one of you. In the meantime you would really be doing yourself a disservice by not switching over from NPM to Yarn. Whenever I was developing a web application using my old Windows machine it was evident to me that it took some time to create a build from Grunt, Gulp and/or EmberCLI. These build managers were taking a tremendous toll on my machine in time and how hot the machine ran. I cannot guarantee that your Windows machine will not run hot during these builds, but I can guarantee that it will be a faster process. The speed and efficiency gained is worth the minimal effort in creating a faster development environment.

Word Cloud with Responsiveness

I use a lot of different JavaScript libraries to accomplish many things in my job. I am often impressed with the tools that are readily available and free to accomplish these many tasks. However as a developer in 2016 some of these libraries forget that we are now a society that doesn’t look at things on a singular resolution. You would think that these developers would think about responsiveness whenever they create their libraries. If they were to do so it would make my job easier along with other developers.

As a professional it is my civic duty to not only create sites that not only function well, but they must also look good at almost any resolution. At certain times i will judge a site on how it looks on smaller screens, whether they used WordPress, Angular (yuck, EmberJS is better) and css for certain UI elements . There is very little excuse to not put out a quality product to the world.

I will say in the defense of some of those who create content that developing a JavaScript library can be a huge process. I cannot overly complain about the quality of work done by Jason Davies with his world cloud generator. It functionally works flawlessly for my use cases.

However, my biggest complaint about the library was its inability to render properly when the screen size changes. This may have been an oversight during development or this still may be a future feature we do not know about. As of today there isn’t a solution available online until today.

There are two things we must focus upon when setting up the component. The first being container size. As a user shrinks and grows their browser the parent container which holds the word cloud will change in size as well. In order to respond to such changes we must add an event listener to the windows resize event.


var frequency_list = [{"text":"study","size":40},{"text":"motion","size":15},{"text":"forces","size":10},{"text":"electricity","size":15},{"text":"movement","size":10},{"text":"relation","size":5},{"text":"things","size":10},{"text":"force","size":5},{"text":"ad","size":5},{"text":"energy","size":85},{"text":"living","size":5},{"text":"nonliving","size":5},{"text":"laws","size":15},{"text":"speed","size":45},{"text":"velocity","size":30},{"text":"define","size":5},{"text":"constraints","size":5},{"text":"universe","size":10},{"text":"physics","size":120},{"text":"describing","size":5},{"text":"matter","size":90},{"text":"physics-the","size":5},{"text":"world","size":10},{"text":"works","size":10},{"text":"science","size":70},{"text":"interactions","size":30},{"text":"studies","size":5},{"text":"properties","size":45},{"text":"nature","size":40},{"text":"branch","size":30},{"text":"concerned","size":25},{"text":"source","size":40},{"text":"google","size":10},{"text":"defintions","size":5},{"text":"two","size":15},{"text":"grouped","size":15},{"text":"traditional","size":15},{"text":"fields","size":15},{"text":"acoustics","size":15},{"text":"optics","size":15},{"text":"mechanics","size":20},{"text":"thermodynamics","size":15},{"text":"electromagnetism","size":15},{"text":"modern","size":15},{"text":"extensions","size":15},{"text":"thefreedictionary","size":15},{"text":"interaction","size":15},{"text":"org","size":25},{"text":"answers","size":5},{"text":"natural","size":15},{"text":"objects","size":5},{"text":"treats","size":10},{"text":"acting","size":5},{"text":"department","size":5},{"text":"gravitation","size":5},{"text":"heat","size":10},{"text":"light","size":10},{"text":"magnetism","size":10},{"text":"modify","size":5},{"text":"general","size":10},{"text":"bodies","size":5},{"text":"philosophy","size":5},{"text":"brainyquote","size":5},{"text":"words","size":5},{"text":"ph","size":5},{"text":"html","size":5},{"text":"lrl","size":5},{"text":"zgzmeylfwuy","size":5},{"text":"subject","size":5},{"text":"distinguished","size":5},{"text":"chemistry","size":5},{"text":"biology","size":5},{"text":"includes","size":5},{"text":"radiation","size":5},{"text":"sound","size":5},{"text":"structure","size":5},{"text":"atoms","size":5},{"text":"including","size":10},{"text":"atomic","size":10},{"text":"nuclear","size":10},{"text":"cryogenics","size":10},{"text":"solid-state","size":10},{"text":"particle","size":10},{"text":"plasma","size":10},{"text":"deals","size":5},{"text":"merriam-webster","size":5},{"text":"dictionary","size":10},{"text":"analysis","size":5},{"text":"conducted","size":5},{"text":"order","size":5},{"text":"understand","size":5},{"text":"behaves","size":5},{"text":"en","size":5},{"text":"wikipedia","size":5},{"text":"wiki","size":5},{"text":"physics-","size":5},{"text":"physical","size":5},{"text":"behaviour","size":5},{"text":"collinsdictionary","size":5},{"text":"english","size":5},{"text":"time","size":35},{"text":"distance","size":35},{"text":"wheels","size":5},{"text":"revelations","size":5},{"text":"minute","size":5},{"text":"acceleration","size":20},{"text":"torque","size":5},{"text":"wheel","size":5},{"text":"rotations","size":5},{"text":"resistance","size":5},{"text":"momentum","size":5},{"text":"measure","size":10},{"text":"direction","size":10},{"text":"car","size":5},{"text":"add","size":5},{"text":"traveled","size":5},{"text":"weight","size":5},{"text":"electrical","size":5},{"text":"power","size":5}];

function resize() {
    document.getElementById("word-cloud").empty();
    setup();
}

function fontSize(d) { return d.size; }

function setup() { 
    var color = d3.scale.linear()
            .domain([0,1,2,3,4,5,6,10,15,20,100])
            .range(["#ddd", "#ccc", "#bbb", "#aaa", "#999", "#888", "#777", "#666", "#555", "#444", "#333", "#222"]);

    d3.layout.cloud().size([800, 300])
            .words(frequency_list)
            .rotate(0)
            .fontSize(fontSize)
            .on("end", draw)
            .start();

    function draw(words) {
        d3.select("word-cloud").append("svg")
                .attr("width", 850)
                .attr("height", 350)
                .attr("class", "wordcloud")
                .append("g")
                // without the transform, words words would get cutoff to the left and top, they would
                // appear outside of the SVG area
                .attr("transform", "translate(320,200)")
                .selectAll("text")
                .data(words)
                .enter().append("text")
                .style("font-size", function(d) { return d.size + "px"; })
                .style("fill", function(d, i) { return color(i); })
                .attr("transform", function(d) {
                    return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
                })
                .text(function(d) { return d.text; });
}

setup();
window.addEventListener("resize", resize());

The second item we need to focus upon is after the resize is complete. Once the resize is complete you will notice that the font size may not be relative to the size of the parent’s container. In other words you will get something like this: Of course this may work in some cases, but I doubt that your user will like what they are viewing in their browser. In order to work around this we must scale our font sizes. The scaling of the the fonts is inexact science and may take some tweaking on your part. The overall goal is to create sizes for your fonts based upon the the size of the parent container of the word cloud.

var frequency_list = [{"text": "sample", "size": "xl"}, {"text": "demo", "size": "m"}]

function fontSize(d) {
    var fontSizes = {
        "s":[8, 10, 12, 14],
        "m":[16, 20, 24, 28],
        "l":[36, 42, 48, 54],
        "xl":[48, 56, 64, 72]
    },
    width = document.getElementById("word-cloud").clientWidth,
    fontScale = 0;

    if(width < 400) {
        fontScale = 0;
    }
    else if() {
        fontScale = 1;
    }
    else if() {
        fontScale = 2;
    }
    else {
        fontScale = 3;
    }

    return fontSizes[d.size][fontScale];
}

The third and final point is the position of the word cloud svg container itself. This was probably the most difficult thing to accomplish so my hard work is your reward. A few notes on what is happening during the resize and initial setup. Once the initial setup is complete and drawn on the screen the library place the svg container way off center from its parent div.

 var svgWidth = document.getElementById("word-cloud").getElementByTagName("svg").getElementByTagName("g").getBoundingClientRect().width,
     svgHeight = document.getElementById("word-cloud").getElementByTagName("svg").getElementByTagName("g").getBoundingClientRect().height;

var svgLeftEdge = document.getElementById("word-cloud").getElementByTagName("svg").borderEdge.left,
    gLeftEdge = document.getElementById("word-cloud").getElementByTagName("svg").getElementByTagName("g").borderEdge.left;

var svgTopEdge = document.getElementById("word-cloud").getElementByTagName("svg").borderEdge.top,
    gTopEdge = document.getElementById("word-cloud").getElementByTagName("svg").getElementByTagName("g").borderEdge.top;

var wHeight = document.getElementById("word-cloud").getElementByTagName("svg").getBoundingClientRect().height;
var wWidth = document.getElementById("word-cloud").getElementByTagName("svg").getBoundingClientRect().width;

var tHeight = (svgTopEdge - gTopEdge) + ((wHeight - svgHeight)/2);
var tWidth = (svgLeftEdge + gLeftEdge.abs()) + ((wWidth - svgWidth)/2);

document.getElementById("word-cloud").getElementByTagName("svg").getElementByTagName("g").attributes['transform'] = "translate("+ tWidth + "," + tHeight + ")";

Upon resizing you will notice that the words will scramble around. This is something I haven’t had the time to figure out nor, do I see a quick fix for this action on resizing. Maybe someone will piggy back off this solution and figure it out. Until then this is as good as it gets.

Dart JS Event Handler

I will admit that I am not overly eager in using frameworks in general let alone ones that seem to place overhead and headaches over productivity. I will preface this blog post with my opinion on strongly type javascript languages such as CoffeeScript and Typescript are weak compared to most. In fact I have yet to use either languages and up to this point in my career have no desire to learn or investigate the reasoning behind these languages truly exist. I understand that these aforementioned languages solve problems that many other developers encounter rather frequently. What those issues may involve is not for discussion of this post.

Even though I am not a fan of using CoffeeScript and TypeScript does not mean I am against using such frameworks. My current employer uses Dart as its preferred strongly typed javascript language. After using Dart for almost a year I would say that it’s not drastically different than plain old javascript. There are a few caveats here and there, but nothing to write home about.

The biggest difference between Dart and javascript would be actually interacting with a javascript library. This page describes what you need to do in order to setup and call pure javascript methods using Dart. It’s pretty simple once you get the hang of it, but there is a tricky method in creating event handlers for  javascript libraries.

Below is a code snippet of how you actually accomplish this in normal javascript.

 d3.layout.cloud().size([800, 300])
            .words(frequency_list)
            .rotate(0)
            .fontSize(function(d) { return d.size; })
            .on("end", draw)
            .start();

 

This is how you would accomplish the same exact thing using Dart

 context['d3']['layout'].callMethod('cloud')
  .callMethod('size', [new JsArray.from([800, 300])])
  .callMethod('words', [new JsObject.jsify(frequency_list)]) 
  .callMethod('rotate', [0]) 
  .callMethod('fontSize', [fontSize()]) 
  .callMethod('on', new JsArray.from(['end', draw()])) 
  .callMethod('start');

 

I am sure you will notice the all of the callMethod calls that are being made in the Dart code. If you aren’t familiar with dart or the dart:js library then I would consider you studying up on the library itself before continuing.

For the most part this is pretty standard except whenever you are setting the event handler for the end event. I was pretty clueless at first and tried several different attempts, but the dart code snippet above works in all browsers.

If you are to set the event handler as so:

 .callMethod('on', ['end', draw()])

 

It will work in Chromium, but not your standard browser. It will also not show an error or any other type of warning and will definitely cause some frustration. I can speak from experience because it took me a few hours and lots of Googling to finally find a solution that works in all browsers. I hope this helps some people out there who are developing with Dart. If you are using Dart drop me a line and maybe we could sync-up sometime.

Responsive Highcharts

I was recently tasked with creating some charts using Highcharts. Up to this point I hadn’t the opportunity to deal with the other JavaScript chart libraries that are available. I had no idea what was to come of this new adventure I was to embark upon for several days. I will preface with the following that I was trying to use Highcharts using the Dart language. I will write another post about that, but for now lets help the people out there trying to get their Highcharts to render within flexboxes.

Out of the box Highcharts only responds to the growth of an HTML element. It fails miserably at the shrinking. I was bouncing ideas off other people (just my boss) I thought I was going to have modify the source code and use that version for my charts. This was the last thing I wanted to do and my boss agreed that this would be the worse case scenario.

For those of you wondering why this would be the worse case scenario I will explain. The others who fully understand the impact of having a custom version of a library in your code please go to the next paragraph. Modifying and creating your own JavaScript library is a potential dangerous situation. Unless you have personally developed this library yourself, you have no idea what the ramifications of your changes will have on other parts of the library. As a younger developer I had once modified the JavaScript library locally for bootstrap. Now why did I do this? I do not remember why, but I can tell you that it was specifically so I could do something that was conflicting with my code. In short my change to the local JavaScript file prevented an expected action to fail miserably. I had been moved off to another project and my co-worker was frustrated with an issue that had nothing to do with his abilities. It was my own naiveness and ignorance of what happens when you change code. It caused my co-worker to spend a few frustrating hours and plenty of cursing at the screen.

So after googling for a few hours looking for solutions and praying that someone figured this out I eventually stumbled upon this article. Finally I found something that allowed me to modify the framework, but yet do so in a way that worked with the current framework. The wrap utility allows developers to write code for public functions that occur before the specified method, after it and even in replacement of the method. After trying a few things I was able to come up with this solution.


(function (H) {
    H.wrap(H.Chart.prototype, 'reflow', function (proceed, e) {

        //before the original method
        var chart = this;

        chart.container.style.display = "none";
        chart.container.style.height = "0px";
        chart.container.style.width = "0px";

        //original method
        proceed.apply(this, Array.prototype.slice.call(arguments, 1));

        //after original method
        chart.container.style.display = "block";
    });
}(Highcharts));

The above code wraps the reflow function that is attached to the window.resize function. The first portion of the code hides the chart container from the user, but it is still in the DOM. This allows the chart container to size properly. Next we call the original method and it calculates the size of the chart. Once this is done we re-show the chart to the user.

I would like to come up with a solution that just re-sizes to the proper size without having to shrink it down to nothing. It is not perfect because you have to hide and shrink the chart to nothing, allow Highcharts to calculate the container and eventually re-show the chart. At times you may not see the chart, but that’s because it’s caught between stages and hasn’t updated yet. This will go away once you re-size the screen again. It occurs more frequently when you are slowly shrinking the screen. Other than the rare hiccups I would call this an effective solution for placing Highcharts inside of flexboxes and percentage sized HTML elements.