User:Ciencia Al Poder-fduser/CharInsert/en

'''This is a translated version. The original version is in Spanish. This version may be outdated'''.

This is a utility that allows a highly customization to the characters that usually are put below the edit box to give an easy way to insert some strange or repeatedly pieces of text into the edit box.

Features

 * Is fully functional in Mozilla Firefox, Opera and Internet Explorer, and with any browser that supports the standards DOM Level 1 and DOM Level 2 Events.
 * You can define groups of characters in both the HTML of the page and with JavaScript with an easy manner.
 * The different groups of characters are not displayed unless you select one of them with a select input. The select input only is displayed when there's more than one group of characters.
 * When selecting one of the groups with the select input, the group selected is stored in a temporary cookie that reminds the last group selected and automatically switches to it when the page is reloaded.
 * Module-based load: The character groups defined by JavaScript are loaded in the document only when they are selected in the select input, not before. This reduces the time to load the page, in case that there was a lot of character groups to load.
 * The design is fully customizable with CSS
 * Object-oriented programming designed as an individual class, that avoids conflicts with functions and variables with common names.

License
http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later.

Pre-requisites
To insert text in the edit box, this utility uses the insertTags function, defined in the file wikibits.js (Commonly included in all MediaWiki installations)

JavaScript
There must be declared the unique global variable of this utility (in MediaWiki:Common.js, user Special:Mypage/monobook.js or similar) before that any of the code of this utility is executed:

var charInsert = {groups: {}};

After that, you can put the implementations of the functions of this utility and JavaScript-defined character groups (as described in basic customization), without care of the order, but it's recommended to load the JavaScript-defined character groups after that any user-JavaScript file is loaded, so the user could overwrite the character groups defined in the site. As an example of JavaScript Character group definition:

 charInsert.groups["MediaWiki"] = [ '~', [], [,], [], ['#REDIRECT ',], [' '], ' ', [' ',' '], [' ',' '], ['&lt;nowiki>','&lt;/nowiki>'], [], ['',''], ''];

Also, you must define the ID of the element where this utility would be initiated, where the character groups will be inserted and activating the characters defined in the HTML.

Once doing that, the JavaScript functions of this utility must be declared, as follows:

// Author: Jesús Martínez Novo (Ciencia Al Poder) // Licencia/License: http://www.gnu.org/copyleft/gpl.html GNU General Public Licence 2.0 or later // To add groups of special characters, add new identifiers to the object charInsert.groups as array elements. If an element is an other array, then it will be treated as a double element. // sample: charInsert.groups["Name of the group"] = ['a','b','c', ['d','e'], ['f','g'] ]; // To disable storing in a cookie the chargroup selected // charInsert.disableCookie = true;

charInsert.version = '1.1';

charInsert.bindId = 'charinsert-blocks'; // ID of the element where this utility would be placed. Change if necessary

charInsert.activaCaracteresEspeciales = function { if (!document.createTextNode) return; // No es DOM compatible var divSC = document.getElementById(charInsert.bindId); if (!divSC) return; // 1. Añadimos una caja de selección segn los conjuntos de caracteres que hay var select = document.createElement('select'); select.id = 'chargroup-select'; // 1.1 Conjuntos de caracteres que ya haya en el código var listaCharGrp = divSC.getElementsByTagName('div'); for (var i = 0; i < listaCharGrp.length; i++){ var p = listaCharGrp[i]; if ((' chargroup ').indexOf(' '+p.className+' ') != -1){ var option = document.createElement('option'); option.value = p.title; option.groupRef = p;     option.appendChild(document.createTextNode(p.title)); select.appendChild(option); p.title = ''; // Borramos el título para que no aparezca al pasar el mouse por encima // IE Fix: la selección actual en el textarea se pierde si se hace clic en un elemento que no sea un 'a' o un elemento de formulario ('input', etc) if (document.selection && !is_gecko){ var el = p.firstChild; if (el) { do { if (el.nodeType == 1 && el.tagName.toLowerCase == 'span') { var inel = el.firstChild; if (!inel) continue; var a = document.createElement('a'); a.href = '#'; do { var refinel = inel; inel = inel.nextSibling a.appendChild(refinel); } while (inel); el.appendChild(a); }         } while (el = el.nextSibling); }     }      // Fin IE Fix } }  // 1.2 Conjuntos de caracteres definidos en charInsert.groups (custom) if (typeof charInsert.groups == typeof {}){ for (grupo in charInsert.groups){ var option = document.createElement('option'); option.value = grupo; option.groupRef = null; option.groupArray = charInsert.groups[grupo]; option.appendChild(document.createTextNode(grupo)); select.appendChild(option); } }

if (select.options.length > 1){ divSC.insertBefore(select, divSC.firstChild); // 2. Capturamos el evento de cambio charInsert.addEvent(select, 'change', charInsert.eSelectChanged); // 3. Seleccionamos grupo por defecto var selectedGrp = select.options[0].value; if (charInsert.disableCookie == undefined || charInsert.disableCookie != true){ var cookie = document.cookie; var cookiePos = cookie.indexOf('chargroup='); if (cookiePos > -1) { cookiePos += 10; var endPos = cookie.substring(cookiePos,cookie.length).indexOf(';'); if (endPos == -1) endPos = cookie.length; selectedGrp = decodeURIComponent(document.cookie.substr(cookiePos,endPos)); }   }    select.value = selectedGrp; charInsert.selectChargroup(select.options[select.selectedIndex]); }else{ delete select; }

// 4. Asignamos un evento para todo el area charInsert.addEvent(divSC, 'click', charInsert.specialCharClick); };

charInsert.eSelectChanged = function(event){ var targetElement = charInsert.eventTargetElement(event); charInsert.selectChargroup(targetElement.options[targetElement.selectedIndex]); if (charInsert.disableCookie == undefined || charInsert.disableCookie != true) document.cookie = 'chargroup='+encodeURIComponent(targetElement.options[targetElement.selectedIndex].value); };

charInsert.selectChargroup = function(item){ var divSC = document.getElementById(charInsert.bindId); if (!divSC) return;

var listaCharGrp = divSC.getElementsByTagName('div'); for (var i = 0; i < listaCharGrp.length; i++){ var p = listaCharGrp[i]; if ((' '+p.className+' ').indexOf(' chargroup ') != -1){ if ((p.isSameNode && p.isSameNode(item.groupRef)) || p == item.groupRef){ //DOM || IE       p.style.display = 'inline'; }else{ p.style.display = 'none'; }   }  }  if (!item.groupRef && item.groupArray){ var p = charInsert.addGroup(item.groupArray); item.groupRef = p;   p.style.display = 'inline'; } };

charInsert.addGroup = function(group){ var divSC = document.getElementById(charInsert.bindId); if (!divSC) return; var bloque = document.createElement('div'); bloque.className = 'chargroup'; for (var i = 0; i < group.length; i++){ // IE Patch if (document.selection && !is_gecko){ var car = document.createElement('a'); car.href = "#"; } else // END IE Patch var car = document.createElement('span'); if (typeof group[i] == typeof ''){ car.appendChild(document.createTextNode(group[i])); }else if(typeof group[i] == typeof [] && group[i].length == 2){ var c1 = document.createElement('span'); c1.appendChild(document.createTextNode(group[i][0])); car.appendChild(c1); var c2 = document.createElement('span'); c2.appendChild(document.createTextNode(group[i][1])); car.appendChild(c2); }   // IE Patch if (document.selection && !is_gecko){ var ospan = document.createElement('span'); ospan.appendChild(car); bloque.appendChild(ospan); } else // END IE Patch bloque.appendChild(car); bloque.appendChild(document.createTextNode(' ')); } divSC.appendChild(bloque); return bloque; };

charInsert.specialCharClick = function(event){ var charEl = charInsert.eventTargetElement(event); // Obtenemos el span más externo posible, pero que descienda directamente del div de class 'chargroup' // Si lo capta un textNode (no debería), buscamos su span. if (charEl.nodeType == 3){ // text node. if (charEl.parentNode.nodeType == 1 && charEl.parentNode.tagName.toLowerCase == 'span') charEl = charEl.parentNode; else return; } // For IE patch if (charEl.parentNode.tagName.toLowerCase == 'a') charEl = charEl.parentNode; if (charEl.tagName.toLowerCase == 'a'){ charEl = charEl.parentNode; event.returnValue = false; } // End IE  if (charEl.nodeType != 1 || charEl.tagName.toLowerCase != 'span') return;

if ((' '+charEl.parentNode.className+' ').indexOf(' chargroup ') == -1){ // span interno? if ((' '+charEl.parentNode.parentNode.className+' ').indexOf(' chargroup ') == -1) return; else charEl = charEl.parentNode; }

var spans = charEl.getElementsByTagName('span'); if (spans.length < 2) insertTags(charInsert.getElementText(charEl), , ); else insertTags(charInsert.getElementText(spans[0]), charInsert.getElementText(spans[1]), ''); };

charInsert.getElementText = function(element){ if (element.textContent) return element.textContent; else if (element.innerText) return element.innerText; };

charInsert.eventTargetElement = function(event){ if (event.target) return event.target; else if(event.srcElement) //IE return event.srcElement; else // ?? return null; }

charInsert.addEvent = function(element, hookName, hookFunct) { if (element.addEventListener) element.addEventListener(hookName, hookFunct, false); else if (element.attachEvent) element.attachEvent('on' + hookName, hookFunct); }

To start this utility, the function charInsert.activaCaracteresEspeciales must be executed. You must have declared all the functions before of executing this function. When executed, it will find an element with the id attribute set as defined in charInsert.bindId (it could be changed) where initialize it.

In a wiki powered by MediaWiki it could be done putting the following instruction below of all of the utility's code:

addOnloadHook(charInsert.activaCaracteresEspeciales);

CSS
The interface appearance is customizable using Cascading Style Sheets (CSS). For example, to make the active characters an appearance of a keyboard, you can put this code in MediaWiki:Common.js, Special:Mypage/monobook.css or similar:

Note: If tou changed the charInsert.bindId variable, you must replace the identifier associated to the following CSS rules (in this case "#charinsert-block") to match your change.

padding: 2px; border: 1px solid #dfe6ff; background: #efefef; color: #000000; line-height: 1.8em; }
 * 1) charinsert-block {


 * 1) charinsert-block select { margin-right: 0.5em; }

padding: 0 2px; border: 1px solid #afafaf; background: #dddddd; cursor: pointer; white-space: nowrap; }
 * 1) charinsert-block .chargroup span {


 * 1) charinsert-block .chargroup > span:hover { background: #ffffff; }

background: transparent; border: none; }
 * 1) charinsert-block .chargroup span span {

text-decoration: none; color: black; }
 * 1) charinsert-block .chargroup span a {

Pages
This utility will appear in pages where a div which has the same id</tt> attribute defined in charInsert.bindId</tt>.

Technically, it has no need to contain nothing —it could be a div without content. It's not recommended to put nothing in it except if you want to include a group of characters in the HTML code. The div usually must be placed in MediaWiki:Edittools.

Note: Until the rest of the explanation, the id</tt> attribute of the div</tt> container will have their value set as charinsert-block</tt> as an example, and must be defined in charInsert.bindId</tt>. To use other value you must change both, the id attribute in the HTML, in the JavaScript property and in the CSS style rules that could have been defined for that id.

Example without any group defined in the HTML, loading only the characters defined in charInsert.groups</tt> (refer to Basic customization for further information):

To preload a group of characters in the HTML of he page, that will be avaliable although JavaScript is disabled —in this case the characters would appear but there wouldn't be any action associated to mouse clicks:

<pre style="overflow:auto"> ~ –  —  …   «  »

The behavior would be the following:

Each div</tt> element with class attribute set to chargroup</tt> inside an other div</tt> of id defined in charInsert.groups</tt> would be a character group. The group's name would be defined by it's title</tt> attribute. When more than one group (defined in HTML and JavaScript) a select input will appear to show only one of the groups.

Each span</tt> element inside the group will insert its text content inside the edit box when clicked. If there are 2 nested span</tt> in the same span</tt> element, the contents of the first element would be inserted before the caret position or text selection, and the second's content after the caret position or text selection.

Per-user installation
This utility works also if installed only for the current user, not for all users (unprivileged mode), in the user's Monobook.js (or similar).

For doing this, the code would be the same, but the difference is that we don't have access to MediaWiki:Edittools, so we need an existing div element in that place with an unique id attribute, and change the charInsert.groups</tt> value to match that id. If not, then you'll need to create it using JavaScript before this utility loads. In the latter case, you must add the following code before the addOnloadHook(charInsert.activaCaracteresEspeciales);</tt>, or adapt it to fit your purpose.

To add it below the first textarea:

addOnloadHook(function{ var txts = document.getElementsByTagName('textarea');  if (txts.length < 1) return;  var div = document.createElement('div');  div.id = charInsert.bindId; // This specify the same id defined in charInsert.bindId  txts[0].parentNode.insertBefore(div, txts); });

An example of user customized code: User:Ciencia Al Poder/monobook.js

Appearance
The appearance of the characters is fully customizable using CSS rules. Only is forced with JavaScript the visibility of the groups, allowing customize the rest of the appearance.

If you insert groups in HTML, you can establish some in-line styles. For example, you can hide the groups so them won't show if JavaScript isn't enabled or until the JavaScript is loaded.

Customized text
To add groups of customized characters or arbitrary text with JavaScript you must add to the object <tt>charInsert.groups</tt> new properties. The property name will be the name of the character group that will show in the select input (only visible if more than one group is defined). The property value must be a string array. Each array element would be a clickable element.

The array elements that are other array elements will make a complex element, that would make the first element of the nested array to be inserted before the caret position and the second after the caret (or selection).

charInsert.groups[ "Group name" ] = [ 'as', 'df' , [ '«' , '»' ] ];


 * You can add as many groups as you want.
 * The group name is what will show in the select input (if there are more than one group). It could contain whitespace. If you define more than one with the same name, the last will overwrite the previous one.
 * The simple elements of the array will be text strings that will be inserted in the caret position.
 * The complex elements of the array (a nested array) will make a unique clickable text region, but it would insert the first element of the nested array before the caret position and the second after the caret (or selection). If there are more elements in the nested array, they will be ignored.

Also, you can add elements in the HTML, adding inside a <tt>div</tt> element with the same <tt>id</tt> that the defined in the variable <tt>charInsert.bindId</tt>, one or more <tt>div</tt> with the <tt>class</tt> attribute set to <tt>chargroup</tt>. Inside the group there must be as many <tt>span</tt> elements as clickable elements you want. If there are two nested <tt>span</tt> elements inside an other <tt>span</tt>, they will be inserted (when clicked) to both sides of the selection, or in the caret position if there's no selection:

<div class="chargroup" title=" Group name "> as    df        «  »


 * <tt>  </tt>: (The same id specified in <tt>charInsert.bindId</tt>) The place when this utility would be placed. There must be only one in a page. If there isn't that element, the utility would not appear.
 * <tt> <div class="chargroup" title=" Group name ">  </tt>: Character group. As explained above, only will appear the selected group. The rest will remain hidden. You can select one or other with the select input. The title of each group will be defined by the <tt>title</tt> attribute of each <tt>div</tt>. There could be more than one group.
 * The <tt>    </tt> elements that only contains text would be the text that will be inserted when clicked. Note: To insert MediaWiki special characters you must use <tt>&lt;nowiki&gt;</tt> or character references (like <tt>&amp;lt;</tt> as the &lt; character, etc).
 * If there are two nested <tt>span</tt> elements inside a <tt> span </tt> element, then the outer <tt>span</tt> element will be an unique clickable element that will insert the contents of the nested elements between the selected text in the textarea.

Important note: The <tt>   </tt> elements in the HTML must be placed in the same line, without carriage returns, because for some reason the MediaWiki software will insert automatically them inside a <tt>    </tt> element and the utility wouldn't work.

Example:

<pre style="overflow:auto;"> charInsert.groups["MediaWiki"] = [ '~', [], [,], ['#REDIRECT ',], [' '], ' ', ''];

It's the same as write in MediaWiki:Edittools the following:

<pre style="overflow:auto;"> ~            #REDIRECT

Note that there's easier to add groups with JavaScript than in the HTML, and the amount of code to type is smaller. Also, the code in MediaWiki:Edittools is loaded in every page load, while the JavaScript code is cached and is loaded only once until the cache expires.

Disable cookies
If there are more than one group, a select input will appear to allow you to select one group, hiding the rest. When selecting one of the groups, the group is stored in a cookie to remind the group selected when the page is reloaded. This only happens if cookies are enabled. The cookie will be deleted when the browser is closed.

If you have configured the browser to ask every time a cookie is stored, you can disable the use of cookies of this utility adding this JavaScript code after the definition of the <tt>charInsert</tt> object:

charInsert.disableCookie = true;

The cookies won't be written when a group is selected, nor read when the utility is loaded.

Using custom functions
If you have customized the JavaScript of the wiki, or your personal JavaScript, maybe you have some functions that do the same functions that the ones used in this utility. For example, is possible that you had defined a function that returns the text content of an element, or the element that started an event. In this case you can replace the repeated functions with a reference to your custom function:

charInsert.eventTargetElement = window.eventTargetElement;

charInsert.addEvent = window.addEvent;

Test this utility
WikiDex has installed this utility. You can test it's functionality editing the sandbox.

The code has been split in various parts: w:c:es.pokemon:MediaWiki:Edittools, w:c:es.pokemon:MediaWiki:Common.js, w:c:es.pokemon:MediaWiki:Edit.js, w:c:es.pokemon:MediaWiki:Common.css.

Change log

 * Version 1.0
 * First version


 * Version 1.1
 * Fixed a bug when reading the selected group from the cookie
 * New variable <tt>charInsert.bindId</tt> where you can specify the id of the element where the utility is placed.