diff --git a/servicenow-kb-toc.user.js b/servicenow-kb-toc.user.js
index b88028e..506a095 100644
--- a/servicenow-kb-toc.user.js
+++ b/servicenow-kb-toc.user.js
@@ -1,441 +1,441 @@
// ==UserScript==
// @name ServiceNow - Table of Contents
// @namespace it.epfl.ch
-// @version 0.4
+// @version 0.5
// @description Runs when a knowledge article is edited. Add a button in the Wysiwyg editor (TinyMCE) "Create or update table of contents". When pressed, creates unique ids on titles contained in the text editor and then insert a table of content with anchor links.
// @author Laurent Indermühle, Julien Grondier, Frederik Künstner
// @match https://it.epfl.ch/backoffice/kb_knowledge.do*
// @match https://it-test.epfl.ch/backoffice/kb_knowledge.do*
// @match https://epfldev.service-now.com/kb_knowledge.do*
// @grant none
// ==/UserScript==
/**
* COMPATIBILITY
*
* V0.5 : ServiceNow Helsinki
* V0.4 : ServiceNow Fuji
*/
/**
* LOG
*
* 2016.10.12 L.Indermühle: Bump to version 0.5
* 2016.08.09 L.Indermühle: Bump to version 0.4
* 2016.08.09 L.Indermühle: Bump to version 0.3
* 2016.08.09 L.Indermühle: Bump to version 0.2
* 2016.08.09 L.Indermühle: Published on C4Science.ch
* 2016.08.08 L.Indermühle: Initial version
*/
(function() {
'use strict';
/**
* Window = kb_knowledge.do (because of the @match in head of this script)
*
* The wysiwyg editor's iframe container has the id #kb_knowledge.text_ifr
* ServiceNow Fuji (early 2016) : window.frames[1]
* This script is in the scope of a parent iframe called "gsft_main".
*/
function getDocument() {
var frame_id = 'kb_knowledge.text_ifr';
for (var i = 0; i < window.frames.length; i++) {
if (window.frames[i].frameElement.id == frame_id) {
return window.frames[i].document;
}
}
}
/**
* @return string 'fr' or 'en'
*/
function getLang() {
return document.getElementById('kb_knowledge.language').value;
}
/**
* tocTitle prints the header of the navigation bloc
* @return {string} [description]
*/
function tocTitle() {
if (getLang() == 'fr') {
return 'Sommaire';
}
else if (getLang() == 'en') {
return 'Table of contents';
}
}
/**
* Generate a unique hash
*/
function generateUID() {
return (
"0000" +
(Math.random()*Math.pow(36,4) << 0).toString(36)).slice(-4);
}
/**
* Count
*/
function countH1(nodelist) {
var h1count = 0;
for (var i of nodelist) {
if (i.tagName === 'H1') {
h1count += 1;
}
}
return h1count;
}
/**
* Get all titles html element
* @param {NodeList} body
* @return {NodeList or null}
*/
function getTitles(body) {
var b = body.querySelectorAll("h1, h2, h3, h4, h5, h6");
if (b.length === 0) {
throw new Error("Titles not found.");
}
else {
if (countH1(b) === 0) {
throw new Error("No H1 title found.");
}
else if (countH1(b) > 1) {
throw new Error("More than one H1 title found.");
}
else {
return b;
}
}
}
/**
* Generate ids based on the title text. Example:
* Mon premier titre => monpremiertitre-4gu2
* Also strip specials characters:
* Ça va être-épique ! => cavaetreepique-9u1o
*/
function generateId(title) {
title = title.replace(/[ë|é|è|ê]/gi, 'e');
title = title.replace(/[ï|í|ì|î]/gi, 'i');
title = title.replace(/[ü|ú|ù|û]/gi, 'u');
title = title.replace(/[ä|á|à|â]/gi, 'a');
title = title.replace(/[ç|Ç]/g, 'c');
title = title.replace(/\s|\'|\|\.|\;|\:"/g, ''); // \s = space
title = title.replace(/^([^a-z])/i, 'a'); // first char must be alpha
title = title.replace(/[^a-z|^0-9]/gi, '');
return title + "-" + generateUID();
}
/* Tiny add a br for every element that you add. They do that
* because Firefox don't let you move the caret on empty elements in
* a text field text field.
* That's fine but the problem is that this behavior also trigger when
* we add an id. I you look in the source-code provided by Tiny
,
* the one you see when pressing the <> button, you'll see:
* ...
* And if you console.log(title.id):
* MyId
* So an HTML entitized function happen. We now know what to search for.
*/
function removeMceBogus(string) {
return string.replace('', '');
}
/**
* If the title already has an id, we keep it.
* So if external referers still works.
* In regards to duplicates ids, we had a unique hash at the end, so if
* duplicates ids already exists in the article, it's not our fault.
*/
function insertTitlesIds(titles) {
for (var title of titles) {
if (title.id === "") {
title.id = generateId(title.textContent);
} else {
title.id = removeMceBogus(title.id);
}
}
}
/**
* Table of Contents looks like that:
*
*
*
[Sommaire|Table of contents]
*
*
*/
function buildToC() {
var n = getDocument().createElement('div');
n.id = 'psdn_toc_container';
var t = getDocument().createElement('p');
t.setAttribute('style', 'font-weight:bold;');
t.textContent = tocTitle();
n.appendChild(t);
var u = getDocument().createElement('ul');
u.id = 'psdn_toc_top_ul';
n.appendChild(u);
return n;
}
/**
* ==> 2
*/
function extractLevel(html_tag) {
return parseInt(html_tag.replace(/[^\d]/i, ""));
}
/**
* higher level means smallest digit, because is top level
*
* This will serve to construct the hierarchy, especially in the case
* the first title is not a top level one :
* Titre
* Un autre titre
* Un autre sous titre
* In this case, the loop will begin with a title level 3, then receive a
* level 2 but can't shift left because we should have add an
* extra