gRaphaël pie chart animated to change the values [JavaScript]

I wanted to be able to change the values for a pie chart created with gRaphaël. After some tries I finally created a new method called changeValues that is available in my fork of gRaphaël.
Below is an example:

Move your mouse over one slice from the first chart to see the second one that change.

JavaScript APIs you’ve never heard of (And some you have)

From this video I’ve discovered several JS API I didn’t know. Let’s list them:

1) Use children instead of childNodes to list the children nodes of a DOM element:

<ul>
  <li>Hello</li>
  <li>world</li>
  <li>!</li>
</ul>
<script>
document.getElementById('test').childNodes.length; // 7 -> because it also returns the text nodes
document.getElementById('test').children.length; // 3
</script>

Support: all browsers, but from IE6 to IE8 it will also return the comments nodes <!-- -->

2) Use nextElementSibling / previousElementSibling / firstElementChild / lastElementChild instead of nextSibling / previousSibling / firstChild / lastChild, because the second one will return the text nodes, but no the first one:

<script>
document.getElementById('test').firstChild; // #textNode
document.getElementById('test').firstElementChild; // LI
</script>

Support: IE9+, FF3.5+, Chrome.

3) Use contains() to indicates whether a node is a descendent of a given node:

<body>
<ul>
  <li>
  <li id="test">
  <li>
</ul>
</body>
<script>
document.body.contains(document.getElementById('test')); // TRUE
</script>

Support: all browsers.

4) Insert an HTML element with insertAdjacentHTML (see on MDN):

<div id="one">one</div>
<script>
// the first param of insertAdjacentHTML can be :
// <!-- beforebegin --><div id="one"><!-- afterbegin -->one<!--beforeend --></div><!-- afterend -->
document.getElementById('one').insertAdjacentHTML('afterend', '
two
'); // At this point, the new structure is: //
one
two
</script>

Support: all browsers.

5) If you want to replace a node you can either use replaceChild or outerHTML. See outerHTML on MDN.

Support: all browsers.

6) Input.setSelection() sets the start and end positions of the current text selection in an <input> element. That could be useful to put the caret in a particular place (see on MDN):

var textbox=document.getElementById('my-input');
textbox.setSelectionRange(textbox.value.length,textbox.value.length); // the caret will be at the end of the input text

Support: All browsers, except for IE (only from IE9).

7) The document.activeElement returns the currently focused element (see details on MDN):

document.getElementById('my-input').focus();
document.activeElement; // #my-input

Support: all browsers.

8) The new FormData is part of the XMLHttpRequest Level 2 and permits to easily construct a set of key/value pairs representing form fields and their values and then send them with the send() command (see more on MDN):

var data=new FormData();
data.append("name","Paul");
data.append("age",15);
var xhr = new XMLHttpRequest();
xhr.open("post", "/submit", true);
xhr.send(data);

Support: it’s quite well supported, except from IE from version 10 only

Note: the video gives more functions regarding the XMLHttpRequest Level 2

9) Element.matchesSelector() permits to test a CSS selector over an element. It’s prefixed so we’ll create a function:

<div><span class="title" id="test">Hello world!</span></div>
if (!Element.prototype.matchesSelector) {
  Element.prototype.matchesSelector = function(css) {
    if (this.webkitMatchesSelector) return this.webkitMatchesSelector(css)
    if (this.mozMatchesSelector) return this.mozMatchesSelector(css)
    if (this.msMatchesSelector) return this.msMatchesSelector(css)
    if (this.oMatchesSelector) return this.oMatchesSelector(css)
  } 
}

var span=document.getElementById('test');
span.matchesSelector('#test'); // true
span.matchesSelector('.title'); // true
span.matchesSelector('div > span'); // true

Support: all browsers, except for IE starting from IE9.

10) getBoundingClientRect permits to know the size and the position of an element in the viewport:

var div=document.getElementById('test');
var rect=div.getBoundingClientRect();
// in pixels, relative to the viewport
rect.left;
rect.top;
rect.right; // relative to left
rect.bottom; // relative to top
rect.width;
rect.height;

Support: all browsers, however with IE<8 there is a bug that adds 2 to the different positions.

11) document.elementFromPoint(x,y) returns the element that is in the (x,y) coordinates. For example, this is useful to know over which element the mouse is.

Support: all browsers.

Raphaëljs plugins for different charts [gRaphaël]

There is gRaphaël that provides several useful plugins to create some different charts like the pie charts, the line charts and so on, based on the Raphaël JS library. unfortunately, the owner doesn’t update his files very often, even if there are several pull requests from the users. So I decided to fork the main repository to fix the issues that I found and that others found: gRaphaël fork

How to trigger the window.resize event on IE8

For the modern browsers it’s easy to fire the window.resize event, but not for IE8…. After several hours of searching, I didn’t find anything.
The only solution I found is the one to resize the HTML that will trigger the event:

function triggerEvent(element, eventName) {
  var event;
  if (document.createEvent) {
    event = document.createEvent("HTMLEvents");
    event.initEvent(eventName, true, true);
  } else {
    event = document.createEventObject();
    event.eventType = eventName;
  }

  event.eventName = eventName;
  if (document.createEvent)
    element.dispatchEvent(event);
  else {
    if (eventName==="resize") {
      var savedWidth=document.documentElement.style.width;
      document.documentElement.style.width="99.999999%";
      setTimeout(function() { document.documentElement.style.width=savedWidth }, 50);
    } else
      element.fireEvent("on" + event.eventType, event);
  }
}
triggerEvent(window,'resize')

WebPart doesn’t work with IE8 in standard mode under Sharepoint 2010

The Microsoft developers are not really good, and we can see it if you use IE8 in standard mode with Sharepoint 2010: the web parts don’t work anymore due to an error with the wpadder.js file.
After few hours trying to find the problem I’ve finally discovered that the WPAdder class uses the for..in statement without testing the hasOwnProperty (more about that problem), then it causes some issues…

Here is how I fixed it (this code must be added after the DOM is loaded in the master page to be called by all the pages) :

EnsureScript("WPAdderClass", undefined, function() {
  if (typeof loadWPAdderCallback === "function") {
    var _loadWPAdderCallback=loadWPAdderCallback;
    loadWPAdderCallback = function() {
      _WPAdder.prototype._layout = function (a) {
        ULSior: ;
        var l = this._saveDescriptionForLayout();
        if (this._lastLayoutWidth == -1) {
          var g = this._getCategoryContainer();
          for (var b in this._cats) {
            if (this._cats.hasOwnProperty(b)) g.appendChild(this._cats[b].el)
          }
        }
        if (this._table.clientHeight != this._lastLayoutHeight) {
          this._selCat != -1 && this._removeItemHover();
          this._layoutCategoryColumn();
          this._getNavigationTable().parentNode.colSpan = 1;
          var d = 0,
          f = 0,
          c = document.createElement("DIV"),
          g = this._itemContainerTemplate.cloneNode(true);
          c.style.whiteSpace = "nowrap";
          c.style.width = "1px";
          c.style.height = "1px";
          c.style.overflow = "auto";
          c.innerHTML = "Quick Brown Fox!
It jumped!"; this._getFirstChild(g).appendChild(c); this._getItemContainer().appendChild(g); var j = c.scrollWidth, i = c.scrollHeight; this._removeElement(g); if (a && (parseInt(a[0][0]) != j || parseInt(a[0][1]) != i || parseInt(a[0][2]) != this._table.clientHeight)) a = null; if (a) { d = parseInt(a[0][3]); for (var b = 0; b < this._cats.length; b++) { if (this._cats.hasOwnProperty(b)) { var e = this._fillCachedItems(this._cats[b].items, this._itemContainerTemplate, a[b + 1]); if (e.length > f) f = e.length; this._cats[b].itemCols = e } } } else { a = ""; for (var b in this._cats) { if (this._cats.hasOwnProperty(b)) { var e = [], h = this._fillItems(e, this._cats[b].items, this._getItemContainer(), this._itemContainerTemplate, _WPAdder_maximumItemWidth); if (h[1] > d) d = h[1]; if (e.length > f) f = e.length; this._cats[b].itemCols = e; a += ";" + h[0] } } a = j + "," + i + "," + this._table.clientHeight + "," + d + a; this._getHiddenField("layout").value = this._layoutHash + ";" + a } for (var b in this._cats) { if (this._cats.hasOwnProperty(b)) { var k = this._cats[b].itemCols; for (var m in k) { if (k.hasOwnProperty(m)) this._finishColumn(k[m], d) } } } for (var b in this._dummyCols) { if (this._dummyCols.hasOwnProperty(b)) this._finishColumn(this._dummyCols[b], d); } this._maxCols = f; this._widestColumn = d }(this._table.clientWidth != this._lastLayoutWidth || this._table.clientHeight != this._lastLayoutHeight) && this._calculateVisibleItemColumns(); this._restoreDescriptionAndLayout(l); this._lastLayoutWidth = this._table.clientWidth; this._lastLayoutHeight = this._table.clientHeight }; _WPAdder.prototype._removeItemHover = function () { ULSior: ; var a = this._getItems(); for (var b in a) { if (a.hasOwnProperty(b)) a[b].removeHover() } }; _loadWPAdderCallback(); } } })

Actually, when we load the WPAdder class, then I override the two functions that are buggy in adding the hasOwnProperty. That’s it.

Citrix Receiver: impossible d’ajouter un compte [configuration]

J’utilise Citrix Receiver dans le cadre de mon travail. Après avoir installé la dernière version sur mon ordinateur, il me demande d’entrer une adresse email professionnel, ou bien une adresse HTTPS du serveur. Lorsque je rentre l’URL sécurisé du serveur j’avais le droit à un laconique message disant « impossible d’ajouter un compte »…. Après avoir cherché sur quelques forums j’ai découvert qu’il faut indiquer l’adresse suivante : https://votre_serveur/Citrix/PNAgent/config.xml et, magie, ça fonctionne !

Aussi vous pourriez avoir une erreur en rapport avec un « Allow HotKey » qui n’a pas été trouvé. Il faut ouvrir la base de registre (en tapant « regedit » dans la zone de recherche du menu de Windows) puis faire une recherche sur « EnableLockdown » une fois dans la base de registre. Il faudra changer la valeur de 1 à 0.

Détection et polyfill pour émuler mouseenter et mouseleave [JavaScript]

Il semblerait qu’à l’heure où j’écris ces lignes, les événements « mouseenter » et « mouseleave » ne soient pas encore supportés par la dernière version stable de Chrome (alors que FF16 et IE les supportent).
D’autres plus anciennes versions de Firefox peuvent aussi être impactées.
Mais heureusement il existe un polyfill pour ça.

Tout d’abord voici une fonction pour détecter si l’événement est supporté (source) :

// source: http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
function isMouseEventSupported(eventName) {
  var el = document.createElement('div');
  eventName = 'on' + eventName;
  var isSupported = (eventName in el);
  if (!isSupported) {
    el.setAttribute(eventName, 'return;');
    isSupported = typeof el[eventName] == 'function';
  }
  el = null;
  return isSupported;
}

isMouseEventSupported("mouseenter"); // --> true ou false

Maintenant voici le polyfill :

// inspired by : http://blog.stchur.com/2007/03/15/mouseenter-and-mouseleave-events-for-firefox-and-other-non-ie-browsers/

// bindEvent is an easy way to create a native binding events
// @param {DOMElement} element The DOM element related to the event
// @param {String} eventName An event name just like 'click'
// @param {Function} fct The function that will be triggered by the event
function bindEvent(element, eventName, fct) {
  if (!element) throw new ErrorType("The element doesn't exist");
  
  if (element.addEventListener) {
    // if it's mouseenter or mouseleave, we want to check the compatibility
    if (eventName === "mouseenter" || eventName === "mouseleave") {
      if (!isMouseEventSupported(eventName)) {
        switch (eventName) {
          case "mouseleave": eventName="mouseout"; break;
          case "mouseenter": eventName="mouseover"; break;
        }
        fct=fixMouseEnter(fct);
      }
    }
    element.addEventListener(eventName, fct, false);
  }
  else
    element.attachEvent("on"+eventName, fct);
}

function fixMouseEnter(fct) {
  return function(evt) {
    var relTarget = evt.relatedTarget;
    if (this === relTarget || isChildOf(this, relTarget)) return;
    fct.call(this, evt);
  }
}
function isChildOf(parent, child) {
  if (parent === child) return false;
  while (child && child !== parent) child = child.parentNode
  return child === parent;
}

Voir tout le code JavaScript présent dans une page [Astuce]

Il peut arriver qu’on ait besoin de voir tout le code JavaScript d’une page dans un unique endroit. Pour cela vous pouvez utiliser ce bout de code. Grâce à la partie « bookmarklet » vous pouvez créer un nouveau marque-page et utiliser le code fourni comme URL du marque-page. Ensuite il suffit d’aller sur la page voulue et d’appeler le marque-page : cela ouvrira une nouvelle page avec tout le code JavaScript, qui sera « unminifié » grâce à jsbeautifier.org. Cependant, seuls les scripts qui sont sur le même domaine pourront être visualisés.