[SPIP] +spip v3.0.17
[lhc/web/clavette_www.git] / www / plugins-dist / porte_plume / porte_plume_fonctions.php
diff --git a/www/plugins-dist/porte_plume/porte_plume_fonctions.php b/www/plugins-dist/porte_plume/porte_plume_fonctions.php
new file mode 100644 (file)
index 0000000..26e8792
--- /dev/null
@@ -0,0 +1,830 @@
+<?php\r
+/**\r
+ * Fonctions utiles pour le Porte Plume\r
+ * \r
+ * @plugin Porte Plume pour SPIP\r
+ * @license GPL\r
+ * @package SPIP\PortePlume\BarreOutils\r
+ */\r
+\r
+if (!defined("_ECRIRE_INC_VERSION")) return;\r
+\r
+/**\r
+ * Objet contenant les différents paramètres definissant une barre d'outils\r
+ * Markitup et permettant d'agir dessus\r
+ *\r
+ * @example\r
+ *     $barre = new Barre_Outil($description);\r
+ * \r
+ * @package SPIP\PortePlume\BarreOutils\r
+ */\r
+class Barre_outils{\r
+       /**\r
+        * Identifiant HTML de la barre\r
+        * @todo À supprimer car non utilisé !\r
+        * @var string */\r
+       public $id = "";\r
+\r
+       /**\r
+        * Nom de la barre d'outil\r
+        * @var string */\r
+       public $nameSpace = "";\r
+\r
+       /**\r
+        * Langue\r
+        * @todo À supprimer car non utilisé !\r
+        * @var string */\r
+       public $lang = "";\r
+\r
+       /**\r
+        * Option de markitup : rafraîchir la prévisu ?\r
+        * @todo À supprimer car non utilisé !\r
+        * @var bool */\r
+       public $previewAutoRefresh = false;\r
+\r
+       /**\r
+        * Option de markitup : nom de la fonction de prévisu\r
+        * @todo À supprimer car on le redéfini dans l'appel javascript !\r
+        * @var bool */\r
+       public $previewParserPath = "";\r
+\r
+       /**\r
+        * Option de markitup : que faire sur l'appuie de Entrée ?\r
+        * @var array */\r
+       public $onEnter = array();\r
+       \r
+\r
+       /**\r
+        * Option de markitup : que faire sur l'appuie de Shift+Entrée ?\r
+        * @example array('keepDefault'=>false, 'replaceWith'=>"\n_ ")\r
+        * @var array */\r
+       public $onShiftEnter = array();\r
+\r
+       /**\r
+        * Option de markitup : que faire sur l'appuie de Control+Entrée ?\r
+        * @var array */\r
+       public $onCtrlEnter = array();\r
+\r
+       /**\r
+        * Option de markitup : que faire sur l'appuie d'une tabulation ?\r
+        * @var array */\r
+       public $onTab = array();\r
+\r
+       /**\r
+        * Option de markitup : Code JS à exécuter avant une insertion\r
+        * @var string */\r
+       public $beforeInsert = "";\r
+\r
+       /**\r
+        * Option de markitup : Code JS à exécuter après une insertion\r
+        * @var string */\r
+       public $afterInsert = "";\r
+\r
+       /**\r
+        * Description des outils/boutons et leurs sous boutons éventuels\r
+        * @var array */\r
+       public $markupSet = array();\r
+\r
+       /**\r
+        * Fonctions JS supplémentaires à écrire après la déclaration JSON\r
+        * des outils. Ces fonctions peuvent servir aux boutons.\r
+        * @var string */\r
+       public $functions = "";\r
+\r
+       /**\r
+        * Liste des paramètres valides pour une description d'outils (markupSet)\r
+        * @var array */\r
+       private $_liste_params_autorises = array(\r
+       \r
+               'replaceWith',\r
+               'openWith',\r
+               'closeWith',\r
+               'openBlockWith', // sur multiline, avant les lignes selectionnees\r
+               'closeBlockWith', // sur multiline, apres les lignes selectionnees\r
+               'placeHolder', // remplace par ce texte lorsqu'il n'y a pas de selection\r
+               \r
+               'beforeInsert', // avant l'insertion\r
+               'afterInsert', // apres l'insertion\r
+               'beforeMultiInsert',\r
+               'afterMultiInsert',\r
+\r
+               'dropMenu', // appelle un sous menu\r
+               \r
+               'name', // nom affiche au survol\r
+               'key', // raccourcis clavier\r
+               'className', // classe css utilisee\r
+               'lang', // langues dont le bouton doit apparaitre - array\r
+               'lang_not', // langues dont le bouton ne doit pas apparaitre - array\r
+               'selectionType', // '','word','line' : type de selection (normale, aux mots les plus proches, a la ligne la plus proche) \r
+               'multiline', // open/close sur chaque ligne (mais replace est applique sur l'ensemble de la selection)\r
+               'forceMultiline', // pour faire comme si on faisait systematiquement un control+shift (et replace est applique sur chaque ligne de la selection)\r
+               \r
+               'separator',\r
+               \r
+               'call',\r
+               'keepDefault',\r
+               \r
+               // cacher ou afficher facilement des boutons\r
+               'display',\r
+               // donner un identifiant unique au bouton (pour le php)\r
+               'id',\r
+       );\r
+\r
+       /**\r
+        * Constructeur\r
+        * \r
+        * Initialise la barre avec les paramètres transmis\r
+        * en n'adressant que les paramètres effectivement valides\r
+        *\r
+        * @api\r
+        * @param array $params Paramètres de la barre d'outil\r
+        * @return void\r
+        */\r
+       function Barre_outils($params=array()){\r
+               foreach ($params as $p=>$v) {\r
+                       if (isset($this->$p)) {\r
+                               // si tableau, on verifie les entrees\r
+                               if (is_array($v)) {\r
+                                       $v = $this->verif_params($p,$v);\r
+                               }\r
+                               $this->$p = $v;\r
+                       }\r
+               }\r
+       }\r
+\r
+\r
+       /**\r
+        * Vérifie que les paramètres d'une clé existent\r
+        * et retourne un tableau des paramètres valides\r
+        * \r
+        * @param string $nom\r
+        *     Clé à vérifier (ex: 'markupSet')\r
+        * @param array $params\r
+        *     Paramètres de cette clé (description des boutons ou sous boutons)\r
+        * @return array\r
+        *     Paramètres, soustrait de ceux qui ne sont pas valides\r
+        */\r
+       function verif_params($nom, $params=array()) {\r
+               // si markupset, on boucle sur les items\r
+               if (stripos($nom, 'markupSet')!==false) {\r
+                       foreach ($params as $i=>$v) {\r
+                               $params[$i] = $this->verif_params($i, $v);\r
+                       }\r
+               } \r
+               // sinon on teste la validite\r
+               else {\r
+                       foreach ($params as $p=>$v) {\r
+                               if (!in_array($p, $this->_liste_params_autorises)) {\r
+                                       unset($params[$p]);\r
+                               }\r
+                       }\r
+               }\r
+               return $params;\r
+       }\r
+\r
+       /**\r
+        * Permet d'affecter des paramètres à un élément de la barre\r
+        * \r
+        * La fonction retourne les paramètres, de sorte qu'on peut s'en servir\r
+        * pour simplement récupérer ceux-ci.\r
+        * \r
+        * Il est possible d'affecter des paramètres avant/après l'élément trouvé\r
+        * en definisant une valeur différente pour le $lieu : 'dedans','avant','apres'\r
+        * par defaut 'dedans' (modifie l'élément trouvé).\r
+        * \r
+        * Lorsqu'on demande d'insérer avant ou après, la fonction retourne\r
+        * les paramètres inserés\r
+        * \r
+        * @param array $tableau\r
+        *     Tableau ou chercher les elements (sert pour la recursion)\r
+        * @param string $identifiant\r
+        *     Identifiant du bouton a afficher\r
+        * @param array $params\r
+        *     Paramètres à affecter à la trouvaille (ou avant ou après).\r
+        *     Peut être un tableau clé/valeur ou un tableau de tableaux\r
+        *     clé/valeur (sauf pour $lieu = dedans)\r
+        * @param string $lieu\r
+        *     Lieu d'affectation des paramètres (dedans, avant, apres)\r
+        * @param bool $plusieurs\r
+        *     Définit si $params est une forme simple (tableau cle/valeur)\r
+        *     ou comporte plusieurs boutons (tableau de tableaux cle/valeur).\r
+        * @return array|bool\r
+        *     Paramètres de l'élément modifié ou paramètres ajoutés\r
+        *     False si l'identifiant cherché n'est pas trouvé\r
+        */\r
+       function affecter(&$tableau, $identifiant, $params=array(), $lieu='dedans', $plusieurs=false){\r
+               static $cle_de_recherche = 'id'; // ou className ?\r
+               \r
+               if ($tableau === null) // utile ?\r
+                       $tableau = &$this->markupSet;\r
+\r
+               if (!in_array($lieu, array('dedans','avant','apres'))) \r
+                       $lieu = 'dedans';\r
+\r
+               // present en premiere ligne ?\r
+               $trouve = false;\r
+               foreach ($tableau as $i=>$v){\r
+                       if (isset($v[$cle_de_recherche]) and ($v[$cle_de_recherche] == $identifiant)) {\r
+                               $trouve = $i;\r
+                               break;\r
+                       }\r
+               }\r
+               // si trouve, affectations\r
+               if (($trouve !== false)) {\r
+                       if ($params) {\r
+                               // verifier que les insertions sont correctes\r
+                               $les_params = ($plusieurs ? $params : array($params));\r
+                               foreach ($les_params as $i=>$un_params) {\r
+                                       $les_params[$i] = $this->verif_params($identifiant, $un_params);\r
+                               }\r
+       \r
+                               // dedans on merge ($params uniquement tableau cle/valeur)\r
+                               if ($lieu == 'dedans' && !$plusieurs) {\r
+                                       return $tableau[$trouve] = array_merge($tableau[$trouve], $les_params[0]);\r
+                               }\r
+                               // avant ou apres, on insere ($params peut etre tableau cle/valeur ou tableau de tableaux cle/valeur)\r
+                               elseif ($lieu == 'avant') {\r
+                                       array_splice($tableau, $trouve, 0, $les_params);\r
+                                       return $params;\r
+                               }\r
+                               elseif ($lieu == 'apres') {\r
+                                       array_splice($tableau, $trouve+1, 0, $les_params);\r
+                                       return $params;\r
+                               }\r
+                       }\r
+                       return $tableau[$trouve];\r
+               }\r
+\r
+               // recursivons sinon !\r
+               foreach ($tableau as $i=>$v){\r
+                       if (is_array($v)) {\r
+                               foreach ($v as $m=>$n) {\r
+                                       if (is_array($n) AND ($r = $this->affecter($tableau[$i][$m], $identifiant, $params, $lieu, $plusieurs))) \r
+                                               return $r;\r
+                               }\r
+                       }\r
+               }\r
+               return false;\r
+       }\r
+\r
+       \r
+       /**\r
+        * Permet d'affecter des paramètres à tous les éléments de la barre\r
+        * ou à une liste d'identifiants d'éléments indiqués.\r
+        * \r
+        * @param array $tableau\r
+        *     Tableau où chercher les éléments\r
+        * @param array $params\r
+        *     Paramètres à affecter aux éléments\r
+        * @param array $ids\r
+        *     Tableau d'identifiants particuliers à qui on affecte les paramètres.\r
+        *     Si vide, tous les identifiants seront modifiés\r
+        * @return bool\r
+        *     false si aucun paramètre à affecter, true sinon.\r
+        */\r
+       function affecter_a_tous(&$tableau, $params=array(), $ids=array()){\r
+               if (!$params)\r
+                       return false;\r
+               \r
+               if ($tableau === null)\r
+                       $tableau = &$this->markupSet;\r
+\r
+               $params = $this->verif_params('divers', $params);\r
+\r
+               // merge de premiere ligne\r
+               foreach ($tableau as $i=>$v){\r
+                       if (!$ids OR in_array($v['id'], $ids)) {\r
+                               $tableau[$i] = array_merge($tableau[$i], $params);\r
+                       }\r
+                       // recursion si sous-menu\r
+                       if (isset($tableau[$i]['dropMenu'])) {\r
+                               $this->affecter_a_tous($tableau[$i]['dropMenu'], $params, $ids);\r
+                       }\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Affecte les valeurs des paramètres indiqués au bouton demandé\r
+        * et retourne l'ensemble des paramètres du bouton (sinon false)\r
+        *\r
+        * @api\r
+        * @param string|array $identifiant\r
+        *     Identifiant du ou des boutons.\r
+        * @param array $params\r
+        *     Paramètres de l'ajout (tableau paramètre=>valeur)\r
+        * @return bool|array\r
+        *     false si l'identifiant n'a pas été trouvé\r
+        *     true si plusieurs identifiants,\r
+        *     array sinon : description de l'identifiant cherché.\r
+        */\r
+       function set($identifiant, $params=array()) {\r
+               // prudence tout de meme a pas tout modifier involontairement (si array)\r
+               if (!$identifiant) return false;\r
+       \r
+               if (is_string($identifiant)) {\r
+                       return $this->affecter($this->markupSet, $identifiant, $params);\r
+               }\r
+               elseif (is_array($identifiant)) {\r
+                       return $this->affecter_a_tous($this->markupSet, $params, $identifiant);\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       /**\r
+        * Retourne les parametres du bouton demande\r
+        *\r
+        * @api\r
+        * @param string|array $identifiant\r
+        *     Identifiant du ou des boutons.\r
+        * @return bool|array\r
+        *     false si l'identifiant n'est pas trouvé\r
+        *     array sinon : Description de l'identifiant cherché.\r
+        */\r
+       function get($identifiant) {\r
+               if ($a = $this->affecter($this->markupSet, $identifiant)) {\r
+                       return $a;\r
+               }\r
+               return false;\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Affiche le ou les boutons demandés\r
+        *\r
+        * @api\r
+        * @param string|array $identifiant\r
+        *     Identifiant du ou des boutons\r
+        * @return bool|array\r
+        *     false si l'identifiant n'a pas été trouvé\r
+        *     true si plusieurs identifiants,\r
+        *     array sinon : description de l'identifiant cherché.\r
+        */\r
+       function afficher($identifiant){\r
+               return $this->set($identifiant,array('display'=>true));\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Cache le ou les boutons demandés\r
+        *\r
+        * @api\r
+        * @param string|array $identifiant\r
+        *     Identifiant du ou des boutons\r
+        * @return bool|array\r
+        *     false si l'identifiant n'a pas été trouvé\r
+        *     true si plusieurs identifiants,\r
+        *     array sinon : description de l'identifiant cherché.\r
+        */\r
+       function cacher($identifiant){\r
+               return $this->set($identifiant, array('display'=>false));\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Affiche tous les boutons\r
+        *\r
+        * @api\r
+        * @return bool\r
+        *     false si aucun paramètre à affecter, true sinon.\r
+        */\r
+       function afficherTout(){\r
+               return $this->affecter_a_tous($this->markupSet, array('display'=>true));\r
+       }\r
+       \r
+       /**\r
+        * Cache tous les boutons\r
+        *\r
+        * @api\r
+        * @return bool\r
+        *     false si aucun paramètre à affecter, true sinon.\r
+        */\r
+       function cacherTout(){\r
+               return $this->affecter_a_tous($this->markupSet, array('display'=>false));\r
+       }\r
+       \r
+\r
+       /**\r
+        * Ajoute un bouton ou quelque chose, avant un autre déjà présent\r
+        *\r
+        * @api\r
+        * @param string $identifiant\r
+        *     Identifiant du bouton où l'on doit se situer\r
+        * @param array $params\r
+        *     Paramètres de l'ajout.\r
+        *     Description d'un bouton (tableau clé/valeurs).\r
+        * @return array|bool\r
+        *     Paramètres ajoutés avant\r
+        *     False si l'identifiant cherché n'est pas trouvé\r
+        */\r
+       function ajouterAvant($identifiant, $params){\r
+               return $this->affecter($this->markupSet, $identifiant, $params, 'avant');\r
+       }\r
+\r
+       /**\r
+        * Ajoute plusieurs boutons, avant un autre déjà présent\r
+        *\r
+        * @api\r
+        * @param string $identifiant\r
+        *     Identifiant du bouton où l'on doit se situer\r
+        * @param array $tableau_params\r
+        *     Paramètres de l'ajout.\r
+        *     Description de plusieurs boutons (tableau de tableaux clé/valeurs).\r
+        * @return array|bool\r
+        *     Paramètres ajoutés avant\r
+        *     False si l'identifiant cherché n'est pas trouvé\r
+        */\r
+       function ajouterPlusieursAvant($identifiant, $tableau_params){\r
+               return $this->affecter($this->markupSet, $identifiant, $tableau_params, 'avant', true);\r
+       }\r
+\r
+       /**\r
+        * Ajoute un bouton ou quelque chose, après un autre déjà présent\r
+        *\r
+        * @api\r
+        * @param string $identifiant\r
+        *     Identifiant du bouton où l'on doit se situer\r
+        * @param array $params\r
+        *     Paramètres de l'ajout.\r
+        *     Description d'un bouton (tableau clé/valeurs).\r
+        * @return array|bool\r
+        *     Paramètres ajoutés après\r
+        *     False si l'identifiant cherché n'est pas trouvé\r
+        */\r
+       function ajouterApres($identifiant, $params){\r
+               return $this->affecter($this->markupSet, $identifiant, $params, 'apres');\r
+       }\r
+\r
+       /**\r
+        * Ajoute plusieurs boutons, après un autre déjà présent\r
+        *\r
+        * @api\r
+        * @param string $identifiant\r
+        *     Identifiant du bouton où l'on doit se situer\r
+        * @param array $tableau_params\r
+        *     Paramètres de l'ajout.\r
+        *     Description de plusieurs boutons (tableau de tableaux clé/valeurs).\r
+        * @return array|bool\r
+        *     Paramètres ajoutés après\r
+        *     False si l'identifiant cherché n'est pas trouvé\r
+        */\r
+       function ajouterPlusieursApres($identifiant, $tableau_params){\r
+               return $this->affecter($this->markupSet, $identifiant, $tableau_params, 'apres', true);\r
+       }\r
+       \r
+       /**\r
+        * Ajoute une fonction JS qui pourra être utilisée par les boutons\r
+        *\r
+        * @api\r
+        * @param string $fonction Code de la fonction JS\r
+        * @return void\r
+        */\r
+       function ajouterFonction($fonction){\r
+               if (false === strpos($this->functions, $fonction)){\r
+                       $this->functions .= "\n" . $fonction . "\n";\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Supprimer les éléments non affichés (display:false)\r
+        * Et les séparateurs (li vides) selon la configuration\r
+        * \r
+        * @param array $tableau Tableau de description des outils\r
+        * @return void\r
+        */\r
+       function enlever_elements_non_affiches(&$tableau){\r
+               if ($tableau === null) // utile ?\r
+                       $tableau = &$this->markupSet;\r
+               \r
+               foreach ($tableau as $p=>$v) {\r
+\r
+                       if (isset($v['display']) AND !$v['display']) {\r
+                               unset($tableau[$p]);\r
+                               $tableau = array_values($tableau); // remettre les cles automatiques sinon json les affiche et ça plante.\r
+                       }\r
+                       // sinon, on lance une recursion sur les sous-menus\r
+                       else {\r
+                               if (isset($v['dropMenu']) and is_array($v['dropMenu'])) {\r
+                                       $this->enlever_elements_non_affiches($tableau[$p]['dropMenu']);\r
+                                       // si le sous-menu est vide\r
+                                       // on enleve le sous menu.\r
+                                       // mais pas le parent ($tableau[$p]), qui peut effectuer une action.\r
+                                       if (!$tableau[$p]['dropMenu']) {\r
+                                               unset($tableau[$p]['dropMenu']);\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Enlève les séparateurs pour améliorer l'accessibilité\r
+        * au détriment du stylage possible de ces séparateurs.\r
+        *\r
+        * Le bouton précédent le séparateur reçoit une classe CSS 'separateur_avant'\r
+        * Celui apres 'separateur_apres'\r
+        * \r
+        * @param array Tableau de description des outils \r
+        * @return void\r
+       **/\r
+       function enlever_separateurs(&$tableau) {\r
+               if ($tableau === null) // utile ?\r
+                       $tableau = &$this->markupSet;\r
+\r
+               foreach ($tableau as $p=>$v) {\r
+                       if (isset($v['separator']) and $v['separator']) {\r
+                               if (isset($tableau[$p-1])) {\r
+                                       $tableau[$p-1]['className'] .= " separateur_avant";\r
+                               }\r
+                               if (isset($tableau[$p+1])) {\r
+                                       $tableau[$p+1]['className'] .= " separateur separateur_apres $v[id]";\r
+                               }\r
+                               unset($tableau[$p]);\r
+                               $tableau = array_values($tableau); // remettre les cles automatiques sinon json les affiche et ça plante.\r
+                       }\r
+                       // sinon, on lance une recursion sur les sous-menus\r
+                       else {\r
+                               if (isset($v['dropMenu']) and is_array($v['dropMenu'])) {\r
+                                       #$this->enlever_separateurs($tableau[$p]['dropMenu']);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Supprime les éléments vides (uniquement à la racine de l'objet)\r
+        * et uniquement si chaîne ou tableau.\r
+        * \r
+        * Supprime les paramètres privés\r
+        * Supprime les paramètres inutiles a markitup/json dans les paramètres markupSet\r
+        * (id, display, icone)\r
+        */\r
+       function enlever_parametres_inutiles() {\r
+               foreach($this as $p=>$v){\r
+                       if (!$v) {\r
+                               if (is_array($v) or is_string($v)) {\r
+                                       unset($this->$p);\r
+                               }\r
+                       } elseif ($p == 'functions') {\r
+                               unset($this->$p);\r
+                       }\r
+               }\r
+               foreach($this->markupSet as $p=>$v) {\r
+                       foreach ($v as $n=>$m) {\r
+                               if (in_array($n, array('id', 'display'))) {\r
+                                       unset($this->markupSet[$p][$n]);\r
+                               }\r
+                       }\r
+               }\r
+               unset ($this->_liste_params_autorises);\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Crée la sortie json pour le javascript des paramètres de la barre\r
+        * \r
+        * @return string Déclaration json de la barre\r
+        */\r
+       function creer_json(){\r
+               $barre = $this;\r
+               $type = $barre->nameSpace;\r
+               $fonctions = $barre->functions;\r
+\r
+               $barre->enlever_elements_non_affiches($this->markupSet);\r
+               $barre->enlever_separateurs($this->markupSet);\r
+               $barre->enlever_parametres_inutiles();\r
+               \r
+               $json = Barre_outils::json_export($barre);\r
+\r
+               // on lance la transformation des &chose; en veritables caracteres\r
+               // sinon markitup restitue &laquo; au lieu de « directement\r
+               // lorsqu'on clique sur l'icone\r
+               include_spip('inc/charsets');\r
+               $json = unicode2charset(html2unicode($json));\r
+               return "\n\nbarre_outils_$type = ".$json . "\n\n $fonctions";\r
+       }\r
+\r
+       /**\r
+        * Transforme une variable PHP dans un équivalent javascript (json)\r
+        * \r
+        * Copié depuis ecrire/inc/json, mais modifié pour que les fonctions\r
+        * JavaScript ne soient pas encapsulées dans une chaîne (string)\r
+        * \r
+        * @access private\r
+        * @param mixed the variable\r
+        * @return string js script | boolean false if error\r
+        */\r
+       function json_export($var) {\r
+               $asso = false;\r
+               switch (true) {\r
+                       case is_null($var) :\r
+                               return 'null';\r
+                       case is_string($var) :\r
+                               if (strtolower(substr(ltrim($var),0,8))=='function')\r
+                                       return $var;\r
+                               return '"' . addcslashes($var, "\"\\\n\r") . '"';\r
+                       case is_bool($var) :\r
+                               return $var ? 'true' : 'false';\r
+                       case is_scalar($var) :\r
+                               return $var;\r
+                       case is_object( $var) :\r
+                               $var = get_object_vars($var);\r
+                               $asso = true;\r
+                       case is_array($var) :\r
+                               $keys = array_keys($var);\r
+                               $ikey = count($keys);\r
+                               while (!$asso && $ikey--) {\r
+                                       $asso = $ikey !== $keys[$ikey];\r
+                               }\r
+                               $sep = '';\r
+                               if ($asso) {\r
+                                       $ret = '{';\r
+                                       foreach ($var as $key => $elt) {\r
+                                               $ret .= $sep . '"' . $key . '":' . Barre_outils::json_export($elt);\r
+                                               $sep = ',';\r
+                                       }\r
+                                       return $ret ."}\n";\r
+                               } else {\r
+                                       $ret = '[';\r
+                                       foreach ($var as $elt) {\r
+                                               $ret .= $sep . Barre_outils::json_export($elt);\r
+                                               $sep = ',';\r
+                                       }\r
+                                       return $ret ."]\n";\r
+                               }\r
+               }\r
+               return false;\r
+       }\r
+       \r
+}\r
+\r
+\r
+/**\r
+ * Crée le code CSS pour les images des icones des barres d'outils\r
+ * \r
+ * S'appuie sur la description des jeux de barres disponibles et cherche\r
+ * une fonction barre_outils_($barre)_icones pour chaque barre et\r
+ * l'exécute si existe, attendant alors en retour un tableau de couples :\r
+ * nom de l'outil => nom de l'image\r
+ *\r
+ * @pipeline_appel porte_plume_lien_classe_vers_icone\r
+ * \r
+ * @return string Déclaration CSS des icones\r
+ */\r
+function barre_outils_css_icones(){\r
+       // recuperer la liste, extraire les icones\r
+       $css = "";\r
+       \r
+       // liste des barres\r
+       if (!$barres = barre_outils_liste()) \r
+               return null;\r
+               \r
+       // liste des classes css et leur correspondance avec une icone\r
+       $classe2icone = array();\r
+       foreach ($barres as $barre) {\r
+               include_spip('barre_outils/' . $barre);\r
+               if ($f = charger_fonction($barre . '_icones', 'barre_outils', true)) {\r
+                       if (is_array($icones = $f())) {\r
+                               $classe2icone = array_merge($classe2icone, $icones);\r
+                       }\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Permettre aux plugins d'étendre les icones connues du porte plume\r
+        *\r
+        * On passe la liste des icones connues au pipeline pour ceux qui\r
+        * ajoutent de simples icones à des barres existantes\r
+        *\r
+        * @pipeline_appel porte_plume_lien_classe_vers_icone\r
+        * @var array $classe2icone\r
+        *     Couples identifiant de bouton => nom de l'image (ou tableau)\r
+        *     Dans le cas d'un tableau, cela indique une sprite : (nom de l'image , position haut, position bas)\r
+        *     Exemple : 'outil_header1' => array('spt-v1.png','-10px -226px')\r
+        */\r
+       $classe2icone = pipeline('porte_plume_lien_classe_vers_icone', $classe2icone);\r
+       \r
+       // passage en css\r
+       foreach ($classe2icone as $n=>$i) {\r
+               $pos="";\r
+               if (is_array($i)){\r
+                       $pos = "background-position:".end($i);\r
+                 $i = reset($i);\r
+               }\r
+               if (file_exists($i))\r
+                       $file = $i;\r
+               else\r
+                       $file = find_in_path("icones_barre/$i");\r
+               if ($file)\r
+                       $css .= "\n.markItUp .$n>a>em {background-image:url(".protocole_implicite(url_absolue($file)).");$pos}";\r
+       }\r
+\r
+       return $css;\r
+}\r
+\r
+\r
+/**\r
+ * Retourne une instance de Barre_outils\r
+ * crée à partir du type de barre demandé\r
+ *\r
+ * Une fonction barre_outils_{type}_dist() retournant la barre doit\r
+ * donc exister.\r
+ * \r
+ * @param string $set\r
+ *     Type de barre (ex: 'edition')\r
+ * @return Barre_Outils|bool\r
+ *     La barre d'outil si la fonction a été trouvée, false sinon\r
+ */\r
+function barre_outils_initialiser($set){\r
+       if ($f = charger_fonction($set, 'barre_outils')) {\r
+               // retourne une instance de l'objet Barre_outils\r
+               return $f();\r
+       }\r
+       return false;\r
+}\r
+\r
+/**\r
+ * Retourne la liste des barres d'outils connues\r
+ *\r
+ * @return array|bool\r
+ *     Tableau des noms de barres d'outils trouvées\r
+ *     False si on ne trouve aucune barre.\r
+ */\r
+function barre_outils_liste(){\r
+       static $sets = -1;\r
+       if ($sets !== -1) \r
+               return $sets;\r
+\r
+       // on recupere l'ensemble des barres d'outils connues\r
+       if (!$sets = find_all_in_path('barre_outils/','.*[.]php')\r
+       or !is_array($sets)) {\r
+               spip_log("[Scandale] Porte Plume ne trouve pas de barre d'outils !");\r
+               $sets = false;\r
+               return $sets;\r
+       }\r
+\r
+       foreach($sets as $fichier=>$adresse) {\r
+               $sets[$fichier] = substr($fichier,0,-4); // juste le nom\r
+       }\r
+       return $sets;\r
+}\r
+\r
+/**\r
+ * Filtre appliquant les traitements SPIP d'un champ\r
+ *\r
+ * Applique les filtres prévus sur un champ (et eventuellement un type d'objet)\r
+ * sur un texte donné. Sécurise aussi le texte en appliquant safehtml().\r
+ *\r
+ * Ce mécanisme est à préférer au traditionnel #TEXTE*|propre\r
+ * \r
+ * traitements_previsu() consulte la globale $table_des_traitements et\r
+ * applique le traitement adequat. Si aucun traitement n'est trouvé,\r
+ * alors propre() est appliqué.\r
+ *\r
+ * @package SPIP\PortePlume\Fonctions\r
+ * @see champs_traitements() dans public/references.php\r
+ * @global table_des_traitements\r
+ * \r
+ * @param string $texte\r
+ *     Texte source\r
+ * @param string $nom_champ\r
+ *     Nom du champ (nom de la balise, en majuscules)\r
+ * @param string $type_objet\r
+ *     L'objet a qui appartient le champ (en minuscules)\r
+ * @param string $connect\r
+ *     Nom du connecteur de base de données\r
+ * @return string\r
+ *     Texte traité avec les filtres déclarés pour le champ.\r
+ */\r
+function traitements_previsu($texte, $nom_champ='', $type_objet='', $connect=null) {\r
+       include_spip('public/interfaces'); // charger les traitements\r
+\r
+       global $table_des_traitements;\r
+       if (!strlen($nom_champ) || !isset($table_des_traitements[$nom_champ])) {\r
+               $texte = propre($texte, $connect);\r
+       }\r
+       else {\r
+               include_spip('base/abstract_sql');\r
+               $table = table_objet($type_objet);\r
+               $ps = $table_des_traitements[$nom_champ];\r
+               if (is_array($ps)) {\r
+                       $ps = $ps[(strlen($table) && isset($ps[$table])) ? $table : 0];\r
+               }\r
+               if (!$ps) {\r
+                       $texte = propre($texte, $connect);\r
+               } else {\r
+                       // [FIXME] Éviter une notice sur le eval suivant qui ne connait\r
+                       // pas la Pile ici. C'est pas tres joli...\r
+                       $Pile = array( 0 => array() );\r
+                       // remplacer le placeholder %s par le texte fourni\r
+                       eval('$texte=' . str_replace('%s', '$texte', $ps) . ';');\r
+               }\r
+       }\r
+       // il faut toujours securiser le texte prévisualisé car il peut contenir n'importe quoi\r
+       // et servir de support a une attaque xss ou vol de cookie admin\r
+       // on ne peut donc se fier au statut de l'auteur connecté car le contenu ne vient pas\r
+       // forcément de lui\r
+       return safehtml($texte);\r
+}\r
+?>\r