lambdaway
::
whynot_fr
3
|
list
|
login
|
load
|
|
{uncover data/amelie_poulain.jpg 100 1500 Amélie Poulain adore la lambada.} _h1 pourquoi pas ? {center ([[english version|?view=whynot]])} _p Le projet '{lambda way} est un éditeur de texte, '{lambda tank}, un wiki opérant en fine surcouche de tout navigateur web, équipé d'un langage de programmation, '{lambda talk}, pour écrire, styliser et coder sur le web. {{title} _h2 Bon sang, c'est quoi ça ? ... } {{content} _p « {i S'il vous plait, noooon ! Il y a des centaines de moteurs wiki et des centaines de langages ! Pourquoi un nouveau wiki et un nouveau langage que personne n'utilisera jamais ?} » _p On pourrait peut-être en parler un peu ! } {{title} _h2 1) c'est un carnet de notes ... } {{content} _p '{lambda tank} est un carnet de notes où les pages peuvent être ajoutées à volonté. On écrit le texte d'une page dans un cadre d'édition, dans l'esprit du champ d'édition d'un tableur classique, Excel ou OpenOffice.calc. On fait apparaître cet éditeur en cliquant sur le nom grisé à droite des quatre points {b ::} tout en haut de la page. On le fait disparaître de la même façon. _p Si vous écrivez dans l'éditeur {pre Hello world } _p la page affiche {{show} Hello world } _p Vous allez me dire : {i Pourquoi faire simple quand on peut faire compliqué ?} _p Je suis bien d'accord mais savez-vous que si vous écrivez {pre '{b Hello world} } _p vous obtenez le texte en gras {{show} {b Hello world}} _p N'est-ce pas merveilleux ? _p Vous allez encore une fois rétorquer : {i Dites-donc cher ami, avec mon vieux traitement de texte standard les choses sont bien plus simples, je sélectionne le texte et clique sur le bouton {b [B]}, pourquoi devrais-je en revenir au Moyen Âge ?} _p Patience s'il vous plait. Si vous écrivez {pre '{b 1 2 3 4 5 6} } _p vous dites en fait "{i {b Met en gras} la séquence de nombres 1 2 3 4 5 6}" et vous obtenez {{show} {b 1 2 3 4 5 6} } _p Maintenant si vous écrivez {pre '{* 1 2 3 4 5 6} } _p vous dites "{i {b Multiplie} les nombres de cette séquence}" et obtenez {{show} {* 1 2 3 4 5 6} } _p C'est là une commande qu'un traitement de texte standard ne peut comprendre ! _p C'est une commande que '{lambda tank} comprend et exécute parfaitement, dans la mesure où elle est écrite dans la syntaxe '{lambda talk}, un véritable langage de programmation. Explorons quelques unes de ses fonctionnalités. } {{title} _h2 2) où l'on peut construire des fonctions ... } {{content} _p Les fonctions sont le "couteau suisse" des langages de programmation. Leur fonctionnement apparaît souvent proche de la magie. Dans ce qui suit nous allons les présenter comme de simples machines à remplacer du texte, des machines à "protéger" des expressions qui ne sont pas en l'état calculables, à repousser leur évaluation. Il est évident qu'une expression comme {code '{* hello world}} est difficilement calculable. Qu'en faire donc ? _h3 2.1) ce qui peut être calculé est calculé _p '{lambda talk} sait donc calculer ce qui est calculable, par exemple {code '{* 1 2 3 4 5 6}}. Intéressons-nous maintenant à une expression un peu plus complexe. L'hypoténuse d'un triangle rectangle de côtés a=3 et b=4 est donnée par la formule bien connue de pythagore, {code c{sup 2} = a{sup 2} + b{sup 2}}, ou son équivalent {code c = √(a{sup 2}+b{sup 2})}. En voici l'équivalent dans la syntaxe '{lambda talk} : {pre '{sqrt {+ {* 3 3} {* 4 4}}} } _p où {b sqrt} signifie "square root" soit en français "racine carrée". Notez au passage que la forme classique {code c = √(a{sup 2}+b{sup 2})} utilise un opérateur "préfixe", {b √}, un opérateur "infixe", {b +}, et un opérateur {b 2} en "exposant", {b x{sup 2}}, ainsi que des règles de priorité entre opérateurs qui sont implicites et supposées connues. On comprend pourquoi tant de gens ont du mal avec les mathématiques ! Alors qu'en notation '{lambda talk}, d'apparence inhabituelle, l'expression se lit {i en bon français} et sans aucune ambiguïté : "{i calcule la racine carrée de la somme du produit de 3 par 3 et du produit de 4 par 4.} " _p Et donc, face à une telle expression emboîtée - remarquez la "balance" des accolades - il est facile pour '{lambda talk} - et pour ma concierge - de rechercher les {b formes ne contenant pas d'accolades}, c'est tout ce qu'on lui demande. Il trouve donc {code '{* 3 3}} et {code '{* 4 4}} qu'il évalue facilement à {b 9} et {b 16} et recommence jusqu'à ce qu'il n'y ait plus d'accolade. Ecrivons jusqu'à son terme la suite de ces opérations : {pre °° {sqrt {+ {* 3 3} {* 4 4}}} -> {sqrt {+ 9 16}} -> {sqrt 25} -> 5 °°} _p Cette façon de procéder est systématique, '{lambda talk} évalue les expressions {b de l'intérieur vers l'extérieur} jusqu'à ce que toutes les accolades aient disparu. Une telle expression {pre '{* {S.serie 1 6}} } _p ne devrait pas poser des problèmes de compréhension. Cette expression se lit ainsi : " calcule le produit de la séquence de nombres de {b 1} à {b 6} - soit {b 1 2 3 4 5 6} - et on sait déjà que le résultat de {code '{* 1 2 3 4 5 6}} est {b 720}. _p En somme '{lambda talk} est une calculette qui {i pense} comme nous ! _h3 2.2) ce qui ne peut être calculé est différé _p Essayons maintenant de calculer l'hypoténuse d'un triangle rectangle dont on ne précise pas les valeurs de {b a} et de {b b}. Si l'on écrit {pre '{sqrt {+ {* a a} {* b b}}} } _p on obtient {{show} {sqrt {+ {* a a} {* b b}}} } _p '{lambda talk} n'aime pas du tout la plaisanterie et répond {b Not a Number}. Voici ce qui s'est passé : {pre °° {sqrt {+ {* a a} {* b b}}} -> {sqrt {+ NaN NaN}} // a et b ne sont pas des nombres :( -> {sqrt NaN} // NaN + NaN est égal à NaN :(( -> NaN // √ NaN est égal à NaN, stop :((( °°} _p Tout simplement parce que les fonctions/opérateurs [{b *, +, sqrt}] attendent des nombres et ne peuvent rien faire avec des mots qui ne peuvent pas être interprêtés comme nombres. _p Il y a une solution : {b ce qui ne peut pas être calculé doit être différé !} _p Voici comment on pourrait décrire le calcul de {code '{sqrt {+ {* a a} {* b b}}}} en attente des valeurs 3 et 4 : {pre {i s'il te plait, avant de faire tes calculs,} {b remplace} a et b {b dans} '{sqrt {+ {* a a} {* b b}}} {b par} 3 et 4 } _p C'est on ne peut plus clair mais le problème est que '{lambda talk} ne sait rien faire de phrases sybilines comme "{i s'il te plait, avant de faire tes calculs}", de conjonctions comme {b et, dans, par}, des retours à la ligne et des alignements divers. Il ne reconnait que des combinaisons d'opérateurs et de nombres emboîtés entre accolades. _p Voici donc une écriture similaire, {b sténographique}, compacte, codifiée, qui serait susceptible d'être comprise par '{lambda talk} : {pre '{{remplace {a b} {sqrt {+ {* a a} {* b b}}}} 3 4} } _p où l'on voit que les conjonctions {b et, dans, par} ont été remplacées par un jeu d'accolades bien balancées. _p Mais si l'on donne en pâture une telle expression à '{lambda talk} on n'obtient absolument pas le résultat attendu. Nous avons vu que '{lambda talk} évalue les expressions emboîtées {i en commençant par celles qui ne contiennent pas d'accolades}, les premières formes qu'il tentera d'évaluer seront {code '{a b}}, {code '{* a a}} et {code '{* b b}} et on sait déjà que les deux dernières au moins retourneront {b NaN}. _p Ce n'est donc pas la bonne voie. Il faudrait qu'on puisse {b retarder l'évaluation} des expressions internes à la commande {code '{remplace ...}}. _p C'est ici qu'apparaît la {b forme spéciale} {code '{lambda ...}}, qui est spéciale en ce qu'elle est spécialement construite pour préserver de toute évaluation les expressions internes qui sont "inévaluables en l'état" ... jusqu'à ce que les remplacements futurs les aient rendues évaluables, dans le cas présent que les mots {b a & b} aient été remplacés par {b 3 & 4}. _p C'est ainsi que notre expression différée s'écrira finalement {pre '{{lambda {a b} {sqrt {+ {* a a} {* b b}}}} 3 4} } _p au besoin en une seule ligne {pre '{{lambda {a b} {sqrt {+ {* a a} {* b b}}}} 3 4} } _p et que '{lambda talk}, ayant remplacé {b avant évaluation} l'expression {code '{sqrt {+ {* a a} {* b b}}}} par {code '{sqrt {+ {* 3 3} {* 4 4}}}} affichera la valeur attendue {{show} {{lambda {a b} {sqrt {+ {* a a} {* b b}}}} 3 4} } _p Rien de bien magique donc, la forme spéciale {code '{lambda ...}} inverse temporairement le mode d'évaluation des formes standards et le tour est joué ! _p Bien sûr ceci fonctionne pour d'autres valeurs que {b 3 & 4}, et écrire : {pre '{{lambda {a b} {sqrt {+ {* a a} {* b b}}}} 1 1} } _p affichera {{show} {{lambda {a b} {sqrt {+ {* a a} {* b b}}}} 1 1} } _p Vous avez bien sûr reconnu la valeur de {b √2}, la diagonale du carré unité. _p {i Bon, je veux bien, mais pourquoi avoir choisi un mot aussi bizarre que {b lambda} et ne pas garder {b remplace} tout simplement ? } _p On aurait pu. Mais bon, le verbe {b remplace} fait un peu {i plouc} dans le monde des {i geeks}, et il faut bien se la péter un peu, alors on a choisi {b lambda}, le caractère grec {b λ}, en souvenir d'Alonso Church qui inventa le {b λ-calculus} dans les années 30, dix ans avant l'ère des ordinateurs. Et puis franchement '{lambda talk} ça a quand même plus d'allure que {b '{remplace parlote}}, n'est-ce pas ? _p {i Bien, tout cela est bien beau mais je ne vois toujours pas à quoi ça sert ? N'est-il pas plus simple d'écrire directement {code '{sqrt {+ {* 3 3} {* 4 4}}}} ou {code '{sqrt {+ {* 1 1} {* 1 1}}}} ? Au fond je commençais à m'y faire à cette calculette, qui me suffit bien. } _p Vous avez aimé la calculette, vous allez aimer {b le langage}. C'est ici qu'intervient une seconde forme spéciale : {b def}. La syntaxe de cette commande est très simple. Ecrire {pre '{def nom n'importe quoi} } _p enregistrera {b nom} dans un dictionnaire et l'affichera tout simplement {{show} {def nom n'importe quoi} } _p Pour obtenir la valeur de l'expression associée à {b nom} il suffira de l'encadrer entre deux accolades, écrire {pre '{nom} } _p affichera bien {{show} {nom} } _p C'est déjà bien utile pour définir des constantes, par exemple le {b nombre d'or} {pre '{def φ {/ {+ 1 {sqrt 5}} 2}} -> {def φ {/ {+ 1 {sqrt 5}} 2}} } _p qui vaut {code '{φ}} = {{show} {φ} } _p Et pour en revenir à notre longue expression calculant l'hypotenuse, on pourra donner le nom {b hypotenuse} à la forme lambda : {pre '{def hypotenuse {lambda {a b} {sqrt {+ {* a a} {* b b}}}}} -> {def hypotenuse {lambda {a b} {sqrt {+ {* a a} {* b b}}}}} } _p {i oublier} l'expression assez illisible et {i appliquer} facilement son {i alias} à autant de valeurs qu'on le souhaite, par exemple {pre '{hypotenuse 3 4} } _p affiche {{show} {hypotenuse 3 4} } _p et {pre '{hypotenuse 1 1} } _p affiche {{show} {hypotenuse 1 1} } _p Chemin faisant nous venons d'enrichir le dictionnaire d'une fonction supplémentaire, {b hypotenuse}, qui s'utilise de la même façon que toutes celles que nous avons déjà rencontrées, {b b, +, *, sqrt, S.serie, ...}. Nous sommes entrés dans un nouveau monde, le monde des langages de programmation extensibles à souhait. Il est désormais possible d'écrire tous les algorithmes possibles et imaginables. En théorie du moins. _p {b Bienvenue dans le monde du code !} _p Ouvrez donc l'éditeur de cette page, parcourez sans crainte le code, et écrivez n'importe où, disons en tête de la page, l'expression {code '{lib}} pour voir affichées les {b 185} fonctions pré-définies dans '{lambda talk} plus les quelques fonctions que nous venons de créer, [{b nom, hypotenuse, φ,...}], en sachant que {b nom} et {b φ} sont des fonctions un peu particulières, des fonctions sans arguments, des {b constantes} au même titre que la constante prédéfinie {b PI} dont la valeur est {code '{PI}} = {PI}. _p Au fond il n'y a là aucune magie, il suffit de se rappeler que les fonctions sont des {b machines à remplacer du texte} et tout s'éclaire. _p {i Mais encore une fois, à quoi ça sert ? Calculer la valeur d'une hypotenuse ou la valeur de {b PI} ne me passionne pas du tout.} _p Vous aimez peut-être écrire des pages de textes bien présentés, les structurer et les styliser, sachez que '{lambda talk} sait faire bien plus que mettre en gras du texte en écrivant des expressions comme {code '{b Hello World}}. _p Le premier exemple est donné dans cette page même : ouvrez l'éditeur et parcourez le code, peu importe si le détail vous échappe pour l'instant. Vous y trouvez une multitude d'expressions entre accolades et quelques mots inconnus comme {b _{span}h1} et {b _{span}p}. Sachez pour l'instant que ces mots sont une facilité d'écriture (il y en d'autres) évitant l'écriture de {b '{h1 ...}} et {b '{p ...}}, la première définissant le style d'un titre de niveau 1, la seconde un paragraphe. On reste donc bien dans la syntaxe systématique {code '{func mots}} qui ne souffre d'aucune exception. Cette unité fait la force du langage. _p On peut ainsi comprendre pourquoi écrire {code '{b Hello World}} pour afficher {b Hello World} n'est pas un retour au Moyen Age, mais un simple entrainement, une mise en bouche préparant à des manipulations plus complexes, comme nous allons le voir bientôt. _p Mais avant d'aller plus loin il y a encore un point à régler. Imaginons que nous ayons envie d'enjoliver notre fonction {b hypotenuse}, par exemple ainsi : {pre '{def hypotenuse {lambda {a b} {sqrt {+ {* a a} {* b b}}} }} } _p qui devient {pre '{def oops_hypotenuse {lambda {a b} L'hypoténuse d'un triangle rectangle de côtés a et b est égale à {sqrt {+ {* a a} {* b b}}} }} -> {def oops_hypotenuse {lambda {a b} L'hypoténuse d'un triangle rectangle de côtés a et b est égale à {sqrt {+ {* a a} {* b b}}} }} } _p Le résultat n'est pas tout à fait ce que l'on désirait car si l'on écrit {pre '{oops_hypotenuse 3 4} } _p on obtient {{show} {oops_hypotenuse 3 4} } _p Voyez-vous le problème ? La fonction a bien fait ce qui était demandé, remplacer {b } par {b 3} et {b b} par {b 4} ... mais elle l'a fait {b partout}, y compris à l'intérieur des mots {b triangle, rectangle et égale}. Afin d'éviter ce comportement logique mais indésirable, on conviendra de "préfixer" systématiquement les mots variables comme {b a} et {b b} par un caractère d'échappement comme "{b :}". On écrira ainsi, avec une petite amélioration rendue maintenant possible {pre '{def smart_hypotenuse {lambda {:a :b} L'hypoténuse d'un triangle rectangle de côtés a=:a et b=:b est égale à {sqrt {+ {* :a :a} {* :b :b}}} }} -> {def smart_hypotenuse {lambda {:a :b} L'hypoténuse d'un triangle rectangle de côtés a=:a et b=:b est égale à {sqrt {+ {* :a :a} {* :b :b}}} }} } _p et écrire {pre '{smart_hypotenuse 3 4} } _p affichera bien {{show} {smart_hypotenuse 3 4} } _p Avez-vous noté la légère amélioration ? En tout cas c'est en suivant cette convention de systématiquement préfixer les mots variables d'une fonction que nous allons poursuivre l'exploration. } {{title} _h2 3) où l'on peut styliser du texte ... } {{content} _p '{lambda talk} s'appuie sur les balises HTML pour structurer du texte et sur les règles CSS pour le styliser. Vous avez à votre disposition toute la puissance de ces deux langages reconnus par tous les navigateurs ainsi que toutes leurs documentations et les multiples tutoriaux dont regorge le net. Apprendre à utiliser '{lambda talk} pour écrire des pages web et produire des documents papiers ne peut être une perte de temps. Même si vous abandonnez '{lambda talk} vous aurez acquis de solides connaissances en HTML/CSS. Il faudra dans ce cas que vous oubliez le confort d'êcriture apporté par '{lambda talk} et la puissance d'un véritable langage, avec des constantes et des fonctions. Par exemple en HTML vous devrez écrire des lourds emboîtements de balises comme ceci {pre <{span}b><{span}i><{span}u>Hello World<{span}/u><{span}/i><{span}/b> } _p au lieu de {pre '{b {i {u Hello World}}} } _p Vous ne pourrez pas donner un nom à une phrase et la répéter cent fois comme ceci {prewrap '{def HI Hello World} -> {def HI Hello World} '{S.map HI {S.serie 1 100}} -> Hello World Hello World Hello World ... 100 fois ... Hello World } _p Ce qui est bien dommage, convenez-en. De plus, il n'existe pas en HTML de commande permettant de colorer facilement du texte. Avec '{lambda talk} il est possible de créer une fonction, {b '{color couleur texte}}, appliquant à un texte une couleur choisie {prewrap '{def color {lambda {:col :text} {span {@ style="color::col"}:text}}} -> {def color {lambda {:col :text} {span {@ style="color::col"}:text}}} } _p qui pourra être utilisée ainsi {pre '{color #f00 hello world} // ou rgb(255,0,0) ou simplement red '{color #0f0 hello world} '{color #00f hello world} } _p pour afficher {{show} {br}{color #f00 hello world} {br}{color #0f0 hello world} {br}{color #00f hello world} } _p Ce qui donne tout de suite l'envie d'aller plus loin dans le jeu des couleurs. La fonction suivante {pre '{def randcol {lambda {:w} {span {@ style="color:rgb({round {* {random} 255}}, {round {* {random} 255}}, {round {* {random} 255}})"}:w}}} -> {def randcol {lambda {:w} {span {@ style="color:rgb({round {* {random} 255}}, {round {* {random} 255}}, {round {* {random} 255}})"}:w}}} } _p utilise de nombreuses balises HTML, des règles CSS et quelques fonctions mathématiques. Peu importe le détail, il y a des web designers et des matheux qui peuvent écrire une telle fonction pour vous. Vous n'aurez plus qu'à écrire {pre '{S.map randcol {S.serie 1 100}} } _p se qui se lit "applique la fonction {b randcol} à la série de nombres de 1 à 100 pour les voir s'afficher en couleurs aléatoires {{show} {S.map randcol {S.serie 1 100}} } _p Il ne vous reste plus qu'à en imaginer d'autres. Ce wiki en contient de nombreuses et les pages elles-mêmes sont des exemples d'utilisation de '{lambda talk} pour créer des pages structurées, stylisées et interactives. _p Pour finir cette rapide introduction vous aurez noté que les titres de cette page passent au rouge quand le curseur les survole. Cliquez-donc et observez. Voici comment se code l'affichage progressif utilisé dans cette page: {pre '{{title} ... le titre ... } '{{content} ... le contenu ... } } _p C'est aussi simple que çà. Les curieux pourront analyser le code qui se cache derrière ces deux fonctions, {code title} et {code content}, en ouvrant l'éditeur de la page. } {{title} _h2 4) où l'on peut faire des maths ... } {{content} _p Pour les allergiques aux maths, vous pouvez passer à la conclusion. _p Pour les autres je propose de calculer la somme et le produit des cent premiers nombres naturels. Nous avons déjà rencontré la fonction {code S.serie}, on peut donc écrire simplement {pre '{+ {S.serie 1 100}} -> {+ {S.serie 1 100}} '{* {S.serie 1 100}} -> {* {S.serie 1 100}} } _p Le dernier résultat est un très grand nombre dont la valeur approximative est calculée avec un nombre de chiffres limité à 15. '{lambda talk} dispose d'une fonction, {code long_mult}, capable de calculer le produit de deux grands nombres {pre '{* 123456789123456789 123456789123456789} -> {* 123456789123456789 123456789123456789} // valeur approchée '{long_mult 123456789123456789 123456789123456789} -> {long_mult 123456789123456789 123456789123456789} // valeur exacte } _p Mais contrairement à l'opérateur {code *} qui sait multiplier une séquence de nombres, la fonction {code long_mult} ne reconnait rien au-delà de 2 nombres {pre '{long_mult {S.serie 1 100}} -> {long_mult {S.serie 1 100}} // produit des deux premiers, les suivants sont oubliés } _p '{lambda talk} dispose d'une fonction de haut niveau, {code S.reduce}, capable d'itérer l'application d'une fonction sur une série de valeurs {prewrap '{S.reduce long_mult {S.serie 1 100}} -> '{S.reduce {{lambda {:a :b} {long_mult :a :b}} 1} {S.serie 1 100}} } _p Nous terminerons ce rapide tour de '{lambda talk} en définissant une fonction récursive, {code calc}, utilisant différents opérateurs, par exemple {code [+,*,long_mult,...]} : {prewrap '{def calc {lambda {:op :n} {if {= :n 1} then 1 else {:op :n {calc :op {- :n 1}}}}}} -> {def calc {lambda {:op :n} {if {= :n 1} then 1 else {:op :n {calc :op {- :n 1}}}}}} } _p La voici développée {pre '{def calc // nom d'une fonction de deux {lambda {:op :n} // arguments op & n retournant {if {= :n 1} // si n = 1 then 1 // alors 1 else {:op // sinon op appliqué à :n // n {calc // et à calc appliqué à :op // op {- :n 1} // et n-1 } // termine calc } // termine op } // termine if } // termine lambda } // termine def } _p Et utilisée {prewrap '{calc + 100} -> {calc + 100} '{calc * 100} -> {calc * 100} '{calc long_mult 100} -> {calc long_mult 100} } _p Les fonctions récursives sont la "perle" des langages de programmation. Toutes les structures itératives peuvent être construites de façon récursive. '{lambda talk} pourrait même se passer de la forme conditionnelle {code '{if ... then ... else ...}} et de la forme {code '{def nom ...}}, se rapprochant ainsi du langage le plus pur qui soit, le {b lambda calcul}, qui peut se résumer en cette courte définition : {center x := w | '{λ {w} x} | '{x x} {hr} {br}古池や {br}蛙飛込む {br}水の音 } _p Voyez le comme un [[haïku (俳句)|https://fr.wikipedia.org/wiki/Ha%C3%AFku]], un petit poème extrêmement bref visant à dire et célébrer l'évanescence des choses. Tout le reste est bruit, sinon vain. } {{title} _h2 5) et bien d'autres choses ... } {{content} _p Comme vous le constatez le code peut devenir "difficile" quand on s'engage dans des choses complexes. Mais il ne faut pas oublier que le code est simple pour ce qui reste simple. Et la syntaxe est systématique, réductible à une composition sans limite de formes {code '{function words}} _p En tant qu'auteur de contenu (textes, images, ...) vous n'aurez à utiliser qu'une poignée de fonctions, aucun besoin d'entrer dans leur "cuisine interne". Et ce que vous aurez produit pourra être mis en musique par un "web designer" maîtrisant le code HTML/CSS et un "codeur" qui pourra concocter de bien utiles fonctions à la demande. Dans un langage partagé par les trois. C'était ce que souhaitait {b John Mc Carthy}, le créateur du Lisp, qui se désolait de voir à quel point les outils du web étaient devenus disparates, une vraie Tour de Babel, et qui pensait que « {i An environment where the markup, styling and scripting is all s-expression based would be nice.} » Le projet '{lambda way} en est une esquisse. Le chemin est long mais la voie est libre. _p Si vous êtes un peu curieux, vous aurez une intoduction plus déraillée [[ici|http://lambdaway.free.fr/lambdaspeech/]] et pour les plus courageux ici [[fromroots2canopy]]. } _p {i Alain Marty (2020/11/21-24)} ;; coder's corner, keep away {div {@ style="position:fixed; top:0; left:0;"} {input {@ type="button" value="display sections" onclick="show_hide(this)"}} } {{hide} {def title div {@ class="title" onclick="this.nextSibling.nextSibling.style.transform= (this.nextSibling.nextSibling.style.transform==='scale(0)')? 'scale(1)' : 'scale(0)'; this.nextSibling.nextSibling.style.height= (this.nextSibling.nextSibling.style.height==='0px')? 'auto' : '0px';"}} {def content div {@ class="content" style="transform:scale(0); height:0px; transition:all 1s;"}} {def show div {@ style="box-shadow:0 0 8px #000; padding:5px; background:#fff; color:#000"}} } {style body, #page_frame, #page_content, .page_menu, pre { border:0 solid #444; box-shadow:0 0 0; background:#444; color:#fff; } .title { cursor:pointer; color:#ff0; } .title:hover { color:red; } pre { background:#eee; color:#000; margin: 5px; padding:5px; } } {script var show_hide = function(obj) { var contents = document.getElementsByClassName('content'); for (var i in contents) { if (contents[i].style.height === "0px") { contents[i].style.height = "auto"; contents[i].style.transform = "scale(1)"; obj.value = "hide sections" } else { contents[i].style.height = "0px"; contents[i].style.transform = "scale(0)" obj.value = "display sections" } } }; }
lambdaway v.20211111