Vérifier du code HTML en PHP

Supposons que vos utilisateurs peuvent entrer du code HTML et que vous souhaitiez vous assurer que les tags sont correctement fermés. Alors vous pouvez utiliser PHP pour faire ça :

  $codeUtilisateur = '<p><b>Mon texte</b>'; // il manque '</p>'
  $doc = new DOMDocument(); // on utilise DOMDocument qui est installé par défaut avec PHP
  $doc->loadHTML($codeUtilisateur);
  $codeRetour = preg_replace('~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>\s*~i', '', $doc->saveHTML()); // on va obtenir : '<p><b>Mon texte</b></p>'

Changer la synthèse vocale par défaut sous Windows 7

Si vous êtes sous Windows 7 64bits, les paramètres de la synthèse vocale ne vous propose que la voix américaine d’Anna quand bien même vous auriez installé la voix de Virginie en français par exemple… C’est qu’il faut passer par une commande spéciale qui ouvre les synthèses vocales en 32bits. Pour cela il suffit d’ouvrir le fichier qui se trouve là : C:\Windows\SysWOW64\Speech\SpeechUX\sapi.cpl

Vous verrez les autres langues que vous avez installées.

Sharepoint 2010 and IE in standard mode (issues with IE9/IE10)

I wanted to have a masterpage for Sharepoint 2010 with a HTML5 Doctype… unfortunately Sharepoint 2010 has been coded without standards, so lot of things are now broken.

I spent some time to find the broken functions. I’ve created a file that is called just after <SharePoint:ScriptLink language="javascript" name="core.js" OnDemand="true" runat="server"/>, and you can download it here (.js file).

I overwrite some functions to make the « Multiple lines of text » to work and same thing for the « People Picker », and other stuff. That could be useful for someone else.

Also I found that the Gantt View doesn’t work in Standard Mode. So I’ve also overwritten some functions to make it OK in IE8, IE9 and IE10: fix_grid_sp2010.js. This file must be called at the bottom of your masterpage (in my case it’s just after the </form>) with something like :

<script type="text/javascript">
// <![CDATA[
if (typeof SP !== "undefined" && typeof SP.JsGrid !== "undefined") document.write('<script type="text/javascript" src="/dir/_catalogs/masterpage/My_Standard/js/fix_grid_sp2010.js"></'+'script>');
// ]]>
</script>

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.