/**
* This file is part of Sesatheque.
* Copyright 2014-2015, Association Sésamath
*
* Sesatheque is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation.
*
* Sesatheque is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Sesatheque (LICENCE.txt).
* @see http://www.gnu.org/licenses/agpl.txt
*
*
* Ce fichier fait partie de l'application Sésathèque, créée par l'association Sésamath.
*
* Sésathèque est un logiciel libre ; vous pouvez le redistribuer ou le modifier suivant
* les termes de la GNU Affero General Public License version 3 telle que publiée par la
* Free Software Foundation.
* Sésathèque est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE,
* sans même la garantie tacite de QUALITÉ MARCHANDE ou d'ADÉQUATION à UN BUT PARTICULIER.
* Consultez la GNU Affero General Public License pour plus de détails.
* Vous devez avoir reçu une copie de la GNU General Public License en même temps que Sésathèque
* (cf LICENCE.txt et http://vvlibri.org/fr/Analyse/gnu-affero-general-public-license-v3-analyse
* pour une explication en français)
*/
'use strict'
var log = require('../utils/log')
var hasProp = require('../index').hasProp
/**
* Ajoute une css dans le <head> de la page
*
* Déclaré par init (dès son chargement)
* @param {string} file Chemin du fichier css (mis dans href tel quel)
*/
function addCss (file) {
var head = window.document.getElementsByTagName('head')[0]
var links = head.getElementsByTagName('link')
var dejala = false
for (var i = 0; i < links.length; i++) {
if (links[i].href === file) {
dejala = true
break
}
}
if (dejala) {
log(file + " était déjà présent, on ne l'ajoute pas")
} else {
var elt = window.document.createElement('link')
elt.rel = 'stylesheet'
elt.type = 'text/css'
elt.href = file
head.appendChild(elt)
}
}
/**
* Ajoute un js à la fin du body et appelle la callback quand il est chargé
* @param {string} file Chemin du fichier jss (mis dans src tel quel)
*/
function addJs (file, cb) {
function callCb () {
cb()
elt.removeEventListener('load', callCb)
}
/**
* @private
* @type {HTMLElement}
*/
var body = window.document.getElementsByTagName('body')[0]
var elt = window.document.createElement('script')
elt.type = 'text/javascript'
// pour que ça marche mieux partout, il vaut mieux mettre le listener onload après avoir mis l'élément dans le dom
body.appendChild(elt)
elt.addEventListener('load', callCb)
// et ensuite indiquer le fichier à charger
elt.src = file
}
/**
* Ajoute un élément html de type tag à parent
* @param {HTMLElement} parent
* @param {string} tag
* @param {Object=} attrs Les attributs
* @param {string=} content
* @returns {HTMLElement} L'élément ajouté
*/
function addElement (parent, tag, attrs, content) {
var elt = getElement(tag, attrs, content)
parent.appendChild(elt)
return elt
}
/**
* Ajoute un élément html juste après element
* @param {HTMLElement} element
* @param {string} tag
* @param {Object=} attrs Les attributs
* @param {string=} content
* @returns {HTMLElement} L'élément ajouté
*/
function addElementAfter (element, tag, attrs, content) {
var newElt = getElement(tag, attrs, content)
var parent = element.parentNode
// pas de insertAfter, si nextSibling est null ça le mettra à la fin, cf https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore
if (parent) parent.insertBefore(newElt, element.nextSibling)
else log.error(new Error("Navigateur incompatible (pas de parentNode), impossible d'ajouter l'élément"))
return newElt
}
/**
* Ajoute un élément html juste avant element
* @param {HTMLElement} element
* @param {string} tag
* @param {Object=} attrs Les attributs
* @param {string=} content
* @returns {HTMLElement} L'élément ajouté
*/
function addElementBefore (element, tag, attrs, content) {
var newElt = getElement(tag, attrs, content)
var parent = element.parentNode
if (parent) parent.insertBefore(newElt, element)
else log.error(new Error("Navigateur incompatible (pas de parentNode), impossible d'insérer l'élément"))
return newElt
}
/**
* Ajoute un élément html comme premier enfant de parent
* @param {HTMLElement} parent
* @param {string} tag
* @param {Object=} attrs Les attributs
* @param {string=} content
* @returns {HTMLElement} L'élément ajouté
*/
function addElementFirstChild (parent, tag, attrs, content) {
var newElt = getElement(tag, attrs, content)
parent.insertBefore(newElt, parent.firstChild)
return newElt
}
/**
* Ajoute un élément html comme frère aîné de elementRef
* @param {HTMLElement} elementRef
* @param {string} tag
* @param {Object=} attrs Les attributs
* @param {string=} content
* @returns {HTMLElement} L'élément ajouté
*/
function addElementFirstSibling (elementRef, tag, attrs, content) {
var newElt = getElement(tag, attrs, content)
elementRef.parentNode.insertBefore(newElt, elementRef.parentNode.firstChild)
return newElt
}
/**
* Ajoute du texte dans un élément
*
* Déclaré par init (dès son chargement)
* @param {HTMLElement} elt
* @param {string} text
*/
function addText (elt, text) {
elt.appendChild(window.document.createTextNode(text))
}
/**
* Vide un élément html de tous ses enfants
*
* Déclaré par init (dès son chargement)
* @param {HTMLElement} element
*/
function empty (element) {
if (element && element.firstChild) {
while (element.firstChild) element.removeChild(element.firstChild)
}
}
/**
* Retourne un élément html de type tag (non inséré dans le dom)
*
* Déclaré par init (dès son chargement)
* @param {string} tag
* @param {Object=} attrs Les attributs
* @param {string=} txtContent
*/
function getElement (tag, attrs, txtContent) {
var elt = window.document.createElement(tag)
var attr
try {
if (attrs) {
for (attr in attrs) {
if (hasProp(attrs, attr)) {
if (attr === 'class') elt.className = attrs.class
else if (attr === 'className') elt.className = attrs.className
else if (attr === 'style') setStyles(elt, attrs.style)
else elt.setAttribute(attr, attrs[attr])
}
}
}
} catch (error) {
log('plantage dans getElement ' + tag + ' avec les attributs ', attrs, error)
}
if (txtContent) addText(elt, txtContent)
return elt
}
/**
* Retourne la taille de la fenêtre
* @returns {{width: number, height: number}}
*/
function getSize () {
// suivant que l'on est en mode standard ou quirk faut prendre l'un ou l'autre
var body = (window.document.compatMode === 'CSS1Compat') ? window.document.documentElement : window.document.body
// en cas de zoom, window.innerWidth est plus petit, et c'est la bonne valeur
return {
width: Math.floor(Math.min(body.clientWidth, window.innerWidth)),
height: Math.floor(Math.min(body.clientHeight, window.innerHeight))
}
}
/**
* Retourne un id qui n'existe pas encore dans le dom (mais ne le créé pas)
*/
var getNewId = (function () {
// au dela de 10000 id dans un dom y'a un pb !
var max = 10000
// une closure pour conserver la valeur de cette variable privée entre 2 appels
var lastId = 0
var id = 'sesa' + lastId
return function getNewId () {
while (window.document.getElementById(id) && lastId < max) {
lastId++
id = 'sesa' + lastId
}
if (lastId === max) throw Error('Max de ' + max + ' id générés atteint')
return id
}
})()
/**
* Affecte des styles à un élément html (on peut pas affecter elt.style directement car read only, faut faire du elt.style.foo = bar)
* sans planter en cas de pb (on le signale juste en console)
*
* Déclaré par init (dès son chargement)
* @param {HTMLElement} elt
* @param {string|object} styles
*/
function setStyles (elt, styles) {
try {
if (elt && elt.style) {
if (typeof styles === 'string') {
styles = styles.split(';')
styles.forEach(function (paire) {
paire = /([\w]+):(.+)/.exec(paire)
if (paire && paire.length === 3) {
var key = paire[1]
elt.style[key] = paire[2]
}
})
} else if (typeof styles === 'object') {
for (var prop in styles) {
if (hasProp(styles, prop)) {
elt.style[prop] = styles[prop]
}
}
}
}
} catch (error) {
log.error(error)
}
}
/**
* Fonctions génériques pour manipuler le dom
* @service sesajstools/dom
*/
module.exports = {
// faut rester en es5 :-/ (uglifyJs aime pas si on répète pas `prop: prop`)
addCss: addCss,
addJs: addJs,
addElement: addElement,
addElementAfter: addElementAfter,
addElementBefore: addElementBefore,
addElementFirstChild: addElementFirstChild,
addElementFirstSibling: addElementFirstSibling,
addText: addText,
empty: empty,
getElement: getElement,
getSize: getSize,
getNewId: getNewId,
setStyles: setStyles
}