Chanter avec Sinatra l'application de rappel

Bienvenue sur la piste 2 de Singing with Sinatra. Dans la première partie, nous avons examiné Routes, comment utiliser les paramètres d'URI, les formulaires et comment Sinatra différencie les itinéraires en fonction de la méthode HTTP qui leur a été demandée. Aujourd'hui, nous allons élargir notre connaissance de Sinatra en créant une petite application basée sur une base de données, "Recall", permettant de prendre des notes / de créer une liste de tâches..

Nous allons utiliser une base de données SQLite pour stocker les notes, et nous utiliserons le DataMapper RubyGem pour communiquer avec la base de données. Exécutez ce qui suit dans un shell pour installer les gems pertinents:

gem installer sqlite3 datamapper dm-sqlite-adapter

Selon la configuration de RubyGems sur votre système, vous devrez peut-être préfixer bijou installer avec sudo.


L'échauffement

Passons directement en créant un nouveau répertoire pour le projet et en créant le fichier d'application, rappel.rb. Commencez par demander aux gemmes pertinentes:

nécessite 'rubygems' nécessite 'sinatra' nécessite 'datamapper'

Remarque: Si vous utilisez Ruby 1.9 (ce que vous devriez être), vous pouvez supprimer la ligne "require 'rubygems" "car Ruby charge automatiquement RubyGems quand même..

Et configurez la base de données avec les éléments suivants:

DataMapper :: setup (: default, "sqlite3: // # Dir.pwd /recall.db"), classe Remarque: inclut la propriété DataMapper :: Resource, id, Propriété série: content, Texte,: required => true property: complete, Boolean,: required => true,: default => false propriété: created_at, propriété DateTime: updated_at, dateTime end DataMapper.finalize.auto_upgrade!

Sur la première ligne, nous configurons une nouvelle base de données SQLite3 dans le répertoire en cours, nommée rappel.db. En dessous, nous mettons en place une table 'Notes' dans la base de données..

Pendant que nous appelons la classe 'Note', DataMapper créera la table en tant que 'Notes'. Ceci est conforme à la convention suivie par Ruby on Rails et d’autres frameworks et modules ORM..

Dans la classe, nous configurons le schéma de base de données. La table 'Notes' aura 5 champs. Un identifiant champ qui sera une clé primaire entière et à incrémentation automatique (c'est ce que signifie "Série"). UNE contenu champ contenant du texte, un booléen Achevée champ et deux champs datetime, créé à et updated_at.

La toute dernière ligne indique à DataMapper de mettre à jour automatiquement la base de données pour qu'elle contienne les tables et les champs que nous avons définis, et de le faire de nouveau si nous apportons des modifications au schéma..


La page d'accueil

Maintenant, créons notre page d'accueil:

Au sommet se trouve un formulaire pour ajouter une nouvelle note, et en dessous se trouvent toutes les notes de la base de données. Pour commencer, ajoutez ce qui suit au fichier d’application, rappel.rb:

get '/' do @notes = Note.all: .order =>: id.desc @title = 'Toutes les notes' erb: home end

Note importante: Enlevez le point ('.') dans :.ordre. (WordPress interfère avec l'exemple de code.)

Sur la deuxième ligne, vous voyez comment nous récupérons toutes les notes de la base de données. Si vous avez déjà utilisé ActiveRecord (l'ORM utilisé dans Rails), la syntaxe de DataMapper vous semblera très familière. Les notes sont affectées à la @Remarques variable d'instance. Il est important d’utiliser des variables d’instance (c’est-à-dire des variables commençant par un @) afin qu'ils soient accessibles depuis le fichier de vue.

Nous avons mis le @Titre variable d'instance, et chargez le views / home.erb afficher le fichier via l'analyseur ERB.

Créer le views / home.erb afficher le fichier et le démarrer avec les éléments suivants:

<% # display notes %>

Nous avons un formulaire simple qui affiche à la page d'accueil ('/'), et au-dessous, un code ERB servant d’espace réservé pour le moment.


Layouts

Le groupe de normes HTML parmi vous a peut-être subi une légère attaque après avoir constaté que notre fichier de vue initiale ne contient aucun type de document ni aucune autre balise HTML. Eh bien, il y a une raison à cela. Créer un layout.erb déposer dans votre vues / répertoire contenant les éléments suivants:

    <%= @title + ' | Recall' %>      

Rappel

Parce que tu es trop occupé pour te souvenir

<%= yield %>

Une application pour Nettuts+.

Les deux parties intéressantes ici sont les lignes 5 et 18. Sur la ligne 5, vous voyez la première utilisation du <%=? %> Tags ERB. <%= est différent de l'ordinaire <% comme il imprime ce qui est à l'intérieur. Donc, ici, nous affichons ce qui est dans le @Titre variable d'instance suivie de | Rappel pour la page </code> étiquette.</p> <p>La ligne 18 est <code><%= yield %></code>. Sinatra affichera cette <code>layout.erb</code> fichier sur tous les itinéraires. Et le contenu réel de cette route sera inséré partout où le <code>rendement</code> est. <code>rendement</code> est un terme qui signifie essentiellement "arrête-toi ici, insère tout ce qui est en attente, puis continue".</p> <p>Démarrer le serveur avec <code>Rappel de fusil de chasse.rb</code> dans la coquille, et jetez un oeil à la page d'accueil du navigateur. Vous devriez voir le contenu du fichier de mise en page et le formulaire du fichier <code>home.erb</code> vue.</p> <img src="//accentsconagua.com/img/images_26_3/singing-with-sinatra-the-recall-app_2.png"> <hr> <h2>CSS</h2> <p>Dans le fichier de mise en page, nous avons inclus deux fichiers CSS. Sinatra peut charger des fichiers statiques (votre CSS, vos images JS, etc.) à partir d’un dossier nommé <code>Publique/</code> dans le répertoire racine. Donc, créez ce répertoire, et à l'intérieur de deux fichiers: <code>reset.css</code> et <code>style.css</code>. La réinitialisation contient la réinitialisation CSS de Boilerplate HTML5:</p> <pre>/ * HTML5? Boilerplate style.css contient une réinitialisation, une normalisation de police et quelques styles de base. le crédit est laissé là où le crédit est dû. yui.yahooapis.com/2.8.1/build/base/base.css camendesign.com/design/ praegnanz.de/weblog/htmlcssjs-kickstart * / / * html5doctor.com Réinitialiser la feuille de style ( Réinitialisation d'Eric Meyer Reloaded + HTML5) v1.6.1 2010-09-17 | Auteurs: Eric Meyer et Richard Clark html5doctor.com/html-5-reset-stylesheet/ * / html, body, div, span, objet, iframe, h1, h2, h4, h5, h6, p, blockquote, pre abbr, adresse, cite, code, del, dfn, em, img, ins, kbd, q, samp, petit, fort, sous, sup, var, b, i, dl, dt, dd, ol, ul, li fieldset, formulaire, étiquette, légende, tableau, légende, tbody, tfoot, thead, tr, th, td, article, à part, toile, détails, figcaption, figure, pied de page, en-tête, hgroup, menu, navigation, section, résumé , heure, marque, audio, vidéo margin: 0; rembourrage: 0; bordure: 0; taille de police: 100%; font: hériter; alignement vertical: ligne de base; article, aparté, détails, figcaption, figure, pied de page, en-tête, hgroup, menu, navigation, section display: block; blockquote, q quotes: none; blockquote: before, blockquote: after, q: before, q: after content: "; content: none; ins background-color: # ff9; color: # 000; text-decoration: none; mark background -color: # ff9; color: # 000; police-style: italic; police-poids: gras; del text-decoration: ligne-travers; abbr [title], dfn [title] border-bottom: 1px pointillé; curseur: aide; table border-collapse: collapse; border-espacement: 0; hr affichage: bloc; hauteur: 1px; bordure: 0; border-top: 1px solide #ccc; marge: 1em 0; padding: 0; input, selectionnez normal-align: middle; / * END RESET CSS * / / * Normalisation de la police inspirée de la police fonts.css de la librairie YUI: developer.yahoo.com/yui/ * / body font : 13px / 1.231 sans-serif; * taille de police: petit; / * hack conservé pour préserver la spécificité * / select, input, textarea, button font: 99% sans-serif; / * normaliser la taille des espaces monospaces * en. wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome * / pre, code, kbd, samp font-family: monospace, sans-serif; / * * minimal b ase styles * / body, select, input, textarea / * # 444 a un meilleur rendu que le noir: twitter.com/H_FJ/statuses/11800719859 * / color: # 444; / * définissez votre police de base ici, pour appliquer uniformément * / / * police-famille: Georgia, serif; Les en-têtes * / / * (h1, h2, etc.) n'ont pas de taille de police ni de marge par défaut. définissez-les vous-même. * / h1, h2, h3, h4, h5, h6 poids de la police: gras; / * force toujours une barre de défilement en non-IE: * / html overflow-y: scroll; / * traitement ciblé accessible: people.opera.com/patrickl/experiments/keyboard/test * / a: survolez, a: actif contour: aucun; a, a: actif, a: visité color: # 607890; a: survol color: # 036; ul, ol margin-left: 2em; ol list-style-type: decimal; / * supprimer les marges pour les listes de navigation * / nav ul, nav li margin: 0; style de liste: aucun; liste-style-image: aucune; petit taille de la police: 85%; fort, e font-weight: gras; td vertical-align: top; / * set sub, sup sans affecter line-height: gist.github.com/413930 * / sub, sup font-size: 75%; hauteur de ligne: 0; position: relative; sup top: -0.5em; sub bottom: -0.25em; pre / * www.pathf.com/blogs/2008/05/formatting-quoted-code-in-blog-posts-css21-white-space-pre-wrap/ * / white-space: pre; espace blanc: préemballage; espace blanc: pré-ligne; word-wrap: mot de rupture; rembourrage: 15px; textarea overflow: auto; / * www.sitepoint.com/blogs/2010/08/20/ie-remove-textarea-scrollbars/ * / .ie6 légende, .ie7 légende margin-left: -7px; / * thnx ivannikolic! * / / * aligner les cases à cocher, les radios, les entrées de texte avec leur étiquette par: Thierry Koblentz tjkdesign.com/ez-css/css/base.css * / input [type = "radio"] vertical-align: text-bottom; input [type = "case à cocher"] vertical-align: bottom; .ie7 input [type = "case à cocher"] vertical-align: baseline; .ie6 input vertical-align: text-bottom; / * curseur de la main sur les éléments d'entrée cliquables * / label, entrée [type = "bouton"], entrée [type = "submit"], entrée [type = "image"], bouton curseur: pointeur; / * Les navigateurs Webkit ajoutent une marge de 2 pixels hors du chrome des éléments de formulaire * / button, entrée, sélection, textarea margin: 0; / * couleurs pour la validité du formulaire * / input: valide, textarea: valide input: invalide, textarea: invalide border-radius: 1px; -moz-box-shadow: 0px 0px 5px rouge; -webkit-box-shadow: 0px 0px 5px rouge; box-shadow: 0px 0px 5px rouge; Entrée .no-boxshadow: invalide, .no-boxshadow textarea: invalid background-color: # f0dddd; / * Ces déclarations de sélection doivent être séparées. No text-shadow: twitter.com/miketaylr/status/12228805301 Aussi: rose vif. * / :: - moz-selection background: # FF5E99; couleur: #fff; texte-ombre: aucun; :: selection background: # FF5E99; couleur: #fff; texte-ombre: aucun; / * j.mp/webkit-tap-highlight-color * / a: link -webkit-tap-highlight-color: # FF5E99; / * permet aux boutons de bien jouer dans IE: www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ * / button width: auto; débordement: visible; / * redimensionnement bicubique pour les IMG non natifs: code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ * / .ie7 img -ms-interpolation-mode: bicubic; </pre> <p>Et <code>style.css</code> contient quelques styles de base pour rendre l'application jolie:</p> <pre>corps marge: auto 35px; largeur: 640px; header text-align: center; marge: 0 0 20px; en-tête h1 display: inline; taille de police: 32px; en-tête h1 a: lien, en-tête h1 a: visité color: # 444; texte-décoration: aucun; en-tête h2 font-size: 16px; style de police: italique; couleur: # 999; #main margin: 0 0 20px; #add margin: 0 0 20px; #add textarea height: 30px; largeur: 510px; rembourrage: 10px; bordure: 1px solide #ddd; #add input height: 50px; largeur: 100px; marge: -50px 0 0; bordure: 1px solide #ddd; fond blanc; #edit textarea height: 30px; largeur: 480px; rembourrage: 10px; bordure: 1px solide #ddd; #edit input [type = submit] hauteur: 50px; largeur: 100px; marge: -50px 0 0; bordure: 1px solide #ddd; fond blanc; #edit input [type = case à cocher] height: 50px; largeur: 20px; article border: 1px solid #eee; bordure supérieure: aucune; rembourrage: 15px 10px; article: premier du type border: 1px solid #eee; article: nth-child (pair) background: #fafafa; article.complete background: # fedae3; article span font-size: 0.8em; p margin: 0 0 5px; .meta font-size: 0.8em; style de police: italique; couleur: # 888; .links font-size: 1.8em; hauteur de ligne: 0.8em; Flotter à droite; marge: -10px 0 0; . lie un display: block; texte-décoration: aucun; </pre> <p>Actualisez la page dans votre navigateur et tout devrait être plus stylé. Ne vous inquiétez pas trop pour ce CSS; ça rend les choses un peu plus jolies!</p> <img src="//accentsconagua.com/img/images_26_3/singing-with-sinatra-the-recall-app_3.png"> <hr> <h2>Ajout d'une note à la base de données</h2> <p>À l'heure actuelle, si vous essayez de soumettre le formulaire sur la page d'accueil, vous obtiendrez une erreur de route. Créons maintenant l'itinéraire POST pour la page d'accueil:</p> <pre>post '/' do n = Note.new n.content = params [: content] n.created_at = Time.now n.updated_at = Time.now n.save redirect '/' end</pre> <p>Alors, quand une demande de publication est faite sur la page d'accueil, nous créons un nouvel objet Note dans <code>n</code> (grâce à l'ORM DataMapper, <code>Note.nouveau</code> représente une nouvelle ligne dans la <code>Remarques</code> table dans la base de données). le <code>contenu</code> champ est défini sur les données soumises de la zone de texte et de la <code>créé à</code> et <code>updated_at</code> Les champs datetime sont définis sur l'horodatage actuel.</p> <p>La nouvelle note est ensuite sauvegardée et l'utilisateur est redirigé vers la page d'accueil où la nouvelle note sera affichée..</p> <hr> <h2>Affichage des notes</h2> <p>Nous avons donc ajouté une nouvelle note, mais nous ne la voyons pas encore sur la page d’accueil car nous n’avons pas écrit le code correspondant. À l'intérieur de <code>views / home.erb</code> voir le fichier, remplacez le <code><%# display notes %></code> aligner avec:</p> <pre><% @notes.each do |note| %> <article <%= 'class="complete"' if note.complete %>> <p> <%= note.content %> <span>"> [modifier]</span> </p> <p> / complete ">? </p> <p>Créé: <%= note.created_at %></p> </article> <% end %></pre> <p>Sur la première ligne, nous commençons une boucle à travers chacun des <code>@Remarques</code> (alternativement, nous aurions pu écrire <code>pour note dans @notes</code>, mais utiliser un bloc, comme nous sommes ici, est une meilleure pratique). Sur la ligne 2, nous donnons la <code><article></code> une classe de <code>Achevée</code> si la note actuelle est réglée sur <code>Achevée</code>. Le reste devrait être assez simple.</p> <hr> <h2>Editer une note</h2> <p>Nous pouvons donc ajouter et afficher des notes. Maintenant, nous avons juste besoin de pouvoir les éditer et les supprimer.</p> <p>Vous avez peut-être remarqué que dans notre <code>home.erb</code> voir que nous mettons un <code>[modifier]</code> lien pour chaque note à ce qui est essentiellement <code>/: id</code>, alors créons cette route maintenant:</p> <pre>get '/: id' do @ note = Note.get paramètres [: id] @title = "Modifier la note ## params [: id]" "erb: modifier fin</pre> <p>Nous récupérons la note demandée dans la base de données à l’aide de l’ID fourni, mettons en place un <code>@Titre</code> variable, et chargez le <code>views / edit.erb</code> afficher le fichier via l'analyseur ERB.</p> <p>Entrez ce qui suit pour le <code>views / edit.erb</code> vue:</p> <pre><% if @note %> <form action="/<%= @note.id %>"méthode =" post "> <input type="hidden" name="_method" value="put"> <textarea name="content"><%= @note.content %></textarea> <input type="checkbox" name="complete" <%= "checked" if @note.complete %>> <input type="submit"> </form> <p>/ delete "> Supprimer</p> <% else %> <p>Note non trouvée.</p> <% end %></pre> <p>C'est une vue assez simple. Un formulaire qui pointe vers la page actuelle, une zone de texte contenant le contenu de la note et une case à cocher qui est cochée si la note est définie sur <code>Achevée</code>.</p> <p>Mais regardez la troisième ligne. Mystérieux. Pour expliquer cela, nous devons éviter un peu.</p> <h3>Services RESTful</h3> <p>Vous avez entendu parler des deux termes GET et POST.</p> <ul> <li> <strong>OBTENIR: </strong>Le plus commun. C'est généralement pour demander une page, et peut être marqué.</li> <li> <strong>POSTER: </strong> Utilisé pour la soumission de données et ne peut pas être mis en signet.</li> </ul> <p>Mais GET et POST ne sont pas les seuls "verbes HTTP" - il y en a deux autres que vous devriez connaître: PUT et DELETE..</p> <p>Techniquement, POST ne devrait être utilisé que pour créer quelque chose - comme créer une nouvelle note dans votre nouvelle application Web géniale, par exemple..</p> <p>PUT est le verbe pour modifier quelque chose. Et DELETE, vous l’avez deviné, sert à supprimer quelque chose.</p> <p>Avoir ces quatre verbes est un excellent moyen de séparer une application. C'est logique Malheureusement, les navigateurs Web ne prennent pas en charge les requêtes PUT ou DELETE, c'est pourquoi vous n'en avez probablement jamais entendu parler auparavant..</p> <p>Donc, pour revenir sur la bonne voie ici, si nous voulons diviser logiquement notre application (ce que Sinatra encourage), nous devons simuler ces demandes PUT et DELETE. Vous verrez notre formulaire <code>action</code> est réglé sur <code>poster</code>. Le caché <code>_méthode</code> champ de saisie que nous avons défini sur <code>mettre</code> sur la troisième ligne, Sinatra simule cette requête PUT tout en utilisant un POST Les rails, entre autres, font les choses de la même manière.</p> <hr> <h2>Laissez-nous mettre</h2> <p>Maintenant que nous avons simulé notre requête PUT, nous pouvons lui créer un itinéraire:</p> <pre>put '/: id' do n = Note.get params [: id] n.content = params [: content] n.complete = params [: complete]? 1: 0 n.updated_at = Time.now n.save redirect '/' end</pre> <p>C'est tout simple. Nous obtenons la note pertinente en utilisant l'ID de l'URI, définissons les champs sur les nouvelles valeurs, sauvegardons et redirigeons vers la maison. Remarquez comment, sur la quatrième ligne, nous utilisons un opérateur ternaire pour définir <code>n.complete</code> à <code>1</code> si <code>params [: complete]</code> existe, ou <code>0</code> autrement. En effet, la valeur d'une case à cocher n'est soumise avec un formulaire que si elle est cochée, nous en vérifions simplement l'existence..</p> <hr> <h2>Supprimer une note</h2> <p>Dans notre <code>edit.erb</code> vue, nous avons ajouté un lien "Supprimer" à ce qui est essentiellement le chemin <code>/: id / delete</code>. Ajoutez ceci à votre dossier de candidature:</p> <pre>get '/: id / delete' do @note = Note.get params [: id] @title = "Confirmer la suppression de la note ## params [: id]" "erb: delete end</pre> <p>Sur cette page, nous obtiendrons la confirmation de l'utilisateur qu'il souhaite réellement supprimer cette note. Créez le fichier de vue à <code>views / delete.erb</code> avec ce qui suit:</p> <pre><% if @note %> <p>Êtes-vous sûr de vouloir supprimer la note suivante: <em>"<%= @note.content %>"</em>?</p> <form action="/<%= @note.id %>"méthode =" post "> <input type="hidden" name="_method" value="delete"> <input type="submit" value="Yes, Delete It!"> "> Annuler </form> <% else %> <p>Note non trouvée.</p> <% end %></pre> <p>Notez que, tout comme nous avons simulé une demande PUT en définissant un paramètre caché <code>_méthode</code> champ de saisie, nous simulons maintenant une demande DELETE.</p> <hr> <h2>La route DELETE</h2> <p>Je suis sûr que vous comprenez cela maintenant. L'itinéraire supprimé est:</p> <pre>supprimer '/: id' do n = Note.get params [: id] n.destroy redirect '/' end</pre> <p>Essaye le! Vous devriez maintenant pouvoir voir, ajouter, éditer et supprimer des notes. Il n'y a plus qu'une chose? </p> <hr> <h2>Marquer une note comme "complète"</h2> <p>En ce moment si vous voulez définir une note comme <code>Achevée</code> vous devez aller dans la vue Edition et cocher la case sur cette page. Rendons ce processus un peu plus simple.</p> <p>Lorsque nous avons créé la page d’accueil principale, nous avons inclus un <code>/: id / complete</code> lien sur chaque note. Faisons maintenant cette route, qui va simplement définir une note comme complète (ou incomplète si elle était déjà définie pour terminer):</p> <pre>get '/: id / complete' do n = Note.get paramètres [: id] n.complete = n.complete? 0: 1 # retourne n.updated_at = Time.now n.save redirect '/' end</pre> <hr> <h2>Conclusion</h2> <p>Vous et Sinatra réussissez un duo de crackin! Vous avez très rapidement écrit une application Web simple qui exécute toutes les opérations CRUD auxquelles vous vous attendez. Il est écrit en code Ruby super-sexy-clean, et est séparé en ses parties logiques.</p> <p>Dans la dernière partie de Singing with Sinatra, Encore, nous allons améliorer la gestion des erreurs, sécuriser l'application depuis XSS et créer un flux RSS pour les notes..</p> <p><strong>Remarque:</strong> Vous pouvez parcourir les fichiers du projet final pour ce tutoriel sur GitHub.</p> <div class="rek-block"> <center> <ins class="adsbygoogle" style="display:inline-block;width:580px;height:400px" data-ad-client="ca-pub-3810161443300697" data-ad-slot="9434875811"></ins> <script> (adsbygoogle = window.adsbygoogle || []).push({}); </script> </center> </div> <div class="h-alltags"> <a href="articles/code">Code</a> </div> </div> </div> </div> </div> <div class="next_posts clearfix"> <div class="n_post"> <div class="next_posts-h1 left_nh1"><a href="/articles/code/singing-with-sinatra.html">Chanter avec Sinatra</a></div> <div class="next_posts-img" style="background-image: url('//accentsconagua.com/img/images_26_3/singing-with-sinatra.jpg');"></div> </div> <div class="n_post"> <div class="next_posts-h1 right_nh1"><a href="/articles/code/singing-with-sinatra-the-encore.html">Chanter avec Sinatra - The Encore</a></div> <div class="next_posts-img" style="background-image: url('//accentsconagua.com/img/images_26_3/singing-with-sinatra-the-encore.jpg');"></div> </div> </div> <footer> <div class="container"> <div class="footer-langs"> <ul class="site-langs-list"> <li><a href="https://www.accentsconagua.com"><i class="flag flag-DE"></i>Deutsch</a></li> <li><a href="https://fr.accentsconagua.com"><i class="flag flag-FR"></i>Français</a></li> <li><a href="https://nl.accentsconagua.com"><i class="flag flag-NL"></i>Nederlands</a></li> <li><a href="https://no.accentsconagua.com"><i class="flag flag-NO"></i>Norsk</a></li> <li><a href="https://sv.accentsconagua.com"><i class="flag flag-SE"></i>Svenska</a></li> <li><a href="https://it.accentsconagua.com"><i class="flag flag-IT"></i>Italiano</a></li> <li><a href="https://es.accentsconagua.com"><i class="flag flag-ES"></i>Español</a></li> <li><a href="https://ro.accentsconagua.com"><i class="flag flag-RO"></i>Românesc</a></li> </ul> </div> <div class="h-block"><a href="/">fr.accentsconagua.com</a><div class="h-block-a"></div></div> <div class="footer-text"> Informations intéressantes et conseils utiles sur la programmation. Développement de sites Web, conception de sites Web et développement Web. Tutoriels Photoshop. Création de jeux informatiques et d'applications mobiles. Devenez un programmeur professionnel à partir de zéro. </div> </div> </footer> <div class="search"> <img class="searchico" src="//accentsconagua.com/img/search.svg" alt=""> </div> <div class="modal"> <div class="modal-content"> <span class="close-button">×</span> <input class="searchmain" type="text" id="search-input" placeholder="Chercher..."> <ul class="searchli" id="results-container"></ul> </div> </div> <link rel="stylesheet" href="css/flags.css"> <link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.css" /> <script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js"></script> <script> window.addEventListener("load", function(){ window.cookieconsent.initialise({ "palette": { "popup": { "background": "#edeff5", "text": "#838391" }, "button": { "background": "#4b81e8" } }, "theme": "classic", "position": "bottom-right" })}); </script> <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> <script src="js/scripts.min.js"></script> <script src="js/common.js"></script> <link rel="stylesheet" href="css/fontawesome-all.min.css"> <script> var modal = document.querySelector(".modal"); var trigger = document.querySelector(".search"); var closeButton = document.querySelector(".close-button"); function toggleModal() { modal.classList.toggle("show-modal"); } function windowOnClick(event) { if (event.target === modal) { toggleModal(); } } trigger.addEventListener("click", toggleModal); closeButton.addEventListener("click", toggleModal); window.addEventListener("click", windowOnClick); </script> <script src="https://unpkg.com/simple-jekyll-search@1.5.0/dest/simple-jekyll-search.min.js"></script> <script> SimpleJekyllSearch({ searchInput: document.getElementById('search-input'), resultsContainer: document.getElementById('results-container'), json: '/search.json', searchResultTemplate: '<li><a href="{url}">{title}</a></li>' }) </script> <script src="jquery.unveil2.min.js"></script> <script> $('img').unveil(); </script> </body> </html>