Sjungar med Sinatra Recall App

Välkommen till Track 2 of Singing med Sinatra. I del ett granskade vi rutor, hur man arbetar med URI-parametrar, arbetar med formulär, och hur Sinatra skiljer rutter med den HTTP-metod som de begärdes av. Idag ska vi utöka vår kunskap om Sinatra genom att bygga en liten databasdriven app, "Recall", för att ta anteckningar / göra en att göra-lista.

Vi ska använda en SQLite-databas för att lagra anteckningarna, och vi använder DataMapper RubyGem för att kommunicera med databasen. Kör följande inuti ett skal för att installera relevanta Gems:

geminstallation sqlite3 datamapper dm-sqlite-adapter

Beroende på hur du har skapat RubyGems på ditt system, kan du behöva prefix gem installation med sudo.


Uppvärmningen

Låt oss hoppa direkt in genom att skapa en ny katalog för projektet och skapa applikationsfilen, recall.rb. Starta det genom att kräva de relevanta ädelstenarna:

kräver "rubygems" kräver "sinatra" kräver "datamapper"

Notera: Om du kör Ruby 1.9 (som du borde vara) kan du släppa "behöva rubygems" -linjen, eftersom Ruby automatiskt laddar RubyGems i alla fall.

Och konfigurera databasen med följande:

DataMapper :: setup (: default, "sqlite3: // # Dir.pwd /recall.db") klass Notera inkluderar DataMapper :: Resursegenskap: id, seriell egendom: innehåll, text,: krävs => sann egendom: komplett, booleskt: krävs => true,: default => false property: created_at, DateTime-egenskap: updated_at, DateTime-slut DataMapper.finalize.auto_upgrade!

På första raden skapar vi en ny SQLite3-databas i den aktuella katalogen, namngiven recall.db. Nedan lägger vi faktiskt upp ett Notes-bord i databasen.

Medan vi ringer klassen "Notera", skapar DataMapper tabellen som "Notes". Detta överensstämmer med en konvention som Ruby on Rails och andra ramverk och ORM-moduler följer.

Inom klassen ställer vi upp databasschemat. Tabellen Notes har 5 fält. En id fält som kommer att vara ett heltal primärtangent och automatisk inkrementering (detta är vad seriell betyder). en innehåll fält som innehåller text, en booleska komplett fält och två datetime fält, skapad vid och updated_at.

Den allra sista raden instruerar DataMapper att automatiskt uppdatera databasen för att innehålla tabeller och fält som vi har ställt och att göra det igen om vi gör några ändringar i schemat.


Hemsidan

Nu, låt oss skapa vår hemsida:

Överst är ett formulär för att lägga till en ny anteckning, och nedan är det alla anteckningar i databasen. För att komma igång lägger du till följande i programfilen, recall.rb:

få '/' do @notes = Note.all: .order =>: id.desc @title = 'Alla anteckningar' erb: hemänden

Viktig notering: Ta bort pricken ('.') i :.ordning. (WordPress störa kodprovet.)

På andra raden ser du hur vi hämtar alla anteckningar från databasen. Om du har använt ActiveRecord (den ORM som används i Rails) tidigare, kommer DataMapper-syntaxen att känna sig mycket bekant. Anteckningarna är tilldelade till @notes instansvariabel. Det är viktigt att använda instansvariabler (det är variabler som börjar med en @) så att de kommer att vara tillgängliga från visningsfilen.

Vi ställer in @titel instansvariabel och ladda visningar / home.erb visa fil via ERB-parsern.

Skapa visningar / home.erb visa fil och starta av med följande:

<% # display notes %>

Vi har en enkel formulär som POST till hemsidan ('/'), och under det är en del ERB-kod som tjänstgör som platshållare för nu.


layouter

HTML-standardpartiet bland dig kan ha drabbats av en mindre stroke efter att ha sett att vår hemvyfil inte innehåller någon doktyp eller andra HTML-taggar. Tja, det finns en anledning till det. Skapa en layout.erb filen i din visningar / katalog som innehåller följande:

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

Återkallelse

för att du är för upptagen att komma ihåg

<%= yield %>

En app för Nettuts+.

De två intressanta delarna här är linjerna 5 och 18. På linje 5 ser du den första användningen av <%=? %> ERB-taggar. <%= skiljer sig från det vanliga <% som det skriver ut vad som är inuti. Så här visar vi vad som helst i @titel instansvariabel följt av | Återkallelse för sidans </code> märka.</p> <p>På linje 18 är <code><%= yield %></code>. Sinatra kommer att visa detta <code>layout.erb</code> fil på alla rutter. Och det faktiska innehållet för den rutten kommer att införas varhelst <code>avkastning</code> är. <code>avkastning</code> är en term som väsentligen betyder "stanna här, sätt in vad som väntar, fortsätt sedan på".</p> <p>Starta servern med <code>shotgun recall.rb</code> i skalet och ta en titt på hemsidan i webbläsaren. Du borde se innehållet från layoutfilen och formuläret från själva <code>home.erb</code> se.</p> <img src="//accentsconagua.com/img/images_26_3/singing-with-sinatra-the-recall-app_2.png"> <hr> <h2>CSS</h2> <p>I layoutfilen inkluderade vi två CSS-filer. Sinatra kan ladda statiska filer (t.ex. din CSS, JS, bilder etc.) från en mapp som heter <code>offentlig/</code> i rotkatalogen. Så skapa den katalogen, och inuti den två filer: <code>reset.css</code> och <code>style.css</code>. Återställningen innehåller återställning av HTML5 Boilerplate CSS:</p> <pre>/ * HTML5? Boilerplate style.css innehåller en återställning, normalisering av teckensnitt och vissa basstilar. kredit är kvar där kredit är förfallen. mycket inspiration togs från dessa projekt: yui.yahooapis.com/2.8.1/build/base/base.css camendesign.com/design/ praegnanz.de/weblog/htmlcssjs-kickstart * / / * html5doctor.com Återställ stilark ( Eric Meyer Reset Reloaded + HTML5 baseline) v1.6.1 2010-09-17 | Författare: Eric Meyer & Richard Clark html5doctor.com/html-5-reset-stylesheet/ * / html, kropp, div, span, objekt, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, före abbr adress cite kod del dfn em img ins kbd q samp liten stark sub sup var var jag dl dt dd ul ol fältet, form, etikett, legend, tabell, bildtext, tbody, tfoot, thead, tr, th, td, artikel, åt sidan, kanfas, detaljer, figcaption, figur, footer, header, hgroup, meny, nav, avsnitt, sammanfattning , tid, markera, ljud, video margin: 0; padding: 0; kant: 0; font-size: 100%; typsnitt: ärv; vertikal-align: baslinje; artikeln, åtgärd, detaljer, figcaption, figur, footer, header, hgroup, meny, nav, avsnitt display: block; blockquote, q quotes: none; blockquote: före, blockquote: after, q: before, q: efter content: "; content: none; ins bakgrundsfärg: # ff9; färg: # 000; textdekoration: none; markera bakgrund -färg: # ff9; färg: # 000; teckensnittstil: kursiv; teckensnitt-vikt: bold; del text-decoration: line-through; abbr [titel], dfn [titel] gränsbotten: 1px punkterad, markör: hjälp; tabell gränshoppning: kollaps; gränsbild: 0; hr display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; input, välj vertical-align: middle; / * END RESET CSS * / / * font normalisering inspirerad av från YUI bibliotekets fonts.css: developer.yahoo.com/yui/ * / body font : 13px / 1.231 sans-serif; * fontstorlek: liten; / * hack behålls för att bevara specificitet * / välj, inmatning, textarea, knapp font: 99% sans-serif; / * normalisera monospace limning * sv. wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome * / pre, kod, kbd, samp font-family: monospace, sans-serif; / * * minimal b ase styles * / body, select, input, textarea / * # 444 ser bättre ut än svart: twitter.com/H_FJ/statuses/11800719859 * / color: # 444; / * Ange din basfontör här, för att tillämpa jämnt * / / * font-family: Georgia, serif; * / / * rubriker (h1, h2, etc) har ingen standard teckenstorlek eller marginal. definiera dem själv. * / h1, h2, h3, h4, h5, h6 font-weight: bold; / * tvinga alltid en rullningsrad i icke-IE: * / html overflow-y: scroll; / * tillgänglig fokusbehandling: people.opera.com/patrickl/experiments/keyboard/test * / a: svävar, a: aktiv disposition: none; a, a: aktiv, a: besökte färg: # 607890; a: svävar färg: # 036; ul, ol margin-left: 2em; ol list-stil-typ: decimal; / * ta bort marginaler för navigeringslistor * / nav ul, nav li margin: 0; list-style: none; list-style-image: none; liten font-size: 85%; starkt, th font-weight: bold; td vertical-align: top; / * set sub, sup utan att påverka linjehöjd: gist.github.com/413930 * / sub, sup font-size: 75%; linjehöjd: 0; position: relativ; sup topp: -0.5em; sub botten: -0,25em; pre / * www.pathf.com/blogs/2008/05/formatting-quoted-code-in-blog-posts-css21-white-space-pre-wrap/ * / white-space: pre; white-space: pre-wrap; white-space: pre-line; word-wrap: break-word; vaddering: 15px; textarea översvämning: auto; / * www.sitepoint.com/blogs/2010/08/20/ie-remove-textarea-scrollbars/ * / .ie6 legend, .ie7 legend margin-left: -7px; / * thnx ivannikolic! * / / * justera kryssrutor, radio, textinmatningar med deras etikett av: Thierry Koblentz tjkdesign.com/ez-css/css/base.css * / input [typ = "radio"] vertikaljustering: textbotten; inmatning [typ = "kryssrutan"] vertikal-align: bottom; .ie7 inmatning [typ = "kryssrutan"] vertikaljustering: baslinje; .6 inmatning vertikal-align: text-bottom; / * handmarkör på klickbara inmatningselement * / etikett, inmatning [typ = "knapp"], inmatning [typ = "submit"], inmatning [typ = "bild"], knapp markör: pekare; / * webkit-webbläsare lägger till en 2px-marginal utanför formelementets krom * / knapp, inmatning, välj textarea margin: 0; / * Färger för formgiltighet * / Inmatning: Giltig, Textarea: Giltig Inmatning: Ogiltig, Textarea: Ogiltig Räckvidd: 1px; -moz-box-skugga: 0px 0px 5px röd; -webkit-box-skugga: 0px 0px 5px röd; boxskugga: 0px 0px 5px röd; .no-boxshadow-inmatning: ogiltig, .no-boxshadow textarea: ogiltig bakgrundsfärg: # f0dddd; / * Dessa urvalsdeklarationer måste vara separata. Ingen textskugga: twitter.com/miketaylr/status/12228805301 Också: hetrosa. * / :: - moz-selection background: # FF5E99; färg: #fff; textskugga: ingen; :: urval bakgrund: # FF5E99; färg: #fff; textskugga: ingen; / * j.mp/webkit-tap-highlight-color * / a: länk -webkit-tap-highlight-färg: # FF5E99; / * gör knappar bra i IE: www.viget.com/inspire/styling-the-utton-element-in-internet-explorer/ * / button width: auto; överflöde: synligt; / * bicubisk resizing för IMG: s icke-inhemska storlek: 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>Och <code>style.css</code> innehåller några grundläggande styling för att få appen att se bra ut:</p> <pre>kropp margin: 35px auto; bredd: 640px; header text-align: center; marginal: 0 0 20px; header h1 display: inline; typsnittstorlek: 32px; rubrik h1 a: länk, rubrik h1 a: besökte färg: # 444; text-dekoration: ingen; header h2 font-size: 16px; typsnittstyp: kursiv; färg: # 999; #main margin: 0 0 20px; #add margin: 0 0 20px; #add textarea höjd: 30px; bredd: 510px; vaddering: 10px; gränsen: 1px solid #ddd; # lägg in höjd: 50px; bredd: 100px; marginal: -50px 0 0; gränsen: 1px solid #ddd; bakgrund: vit; #edit textarea höjd: 30px; bredd: 480px; vaddering: 10px; gränsen: 1px solid #ddd; #edit input [typ = submit] höjd: 50px; bredd: 100px; marginal: -50px 0 0; gränsen: 1px solid #ddd; bakgrund: vit; #edit input [typ = checkbox] höjd: 50px; bredd: 20px; artikel border: 1px solid #eee; border-top: none; vaddering: 15px 10px; artikel: förstklassig border: 1px solid #eee; artikel: nth-barn (jämn) bakgrund: #fafafa; article.complete background: # fedae3; artikelsträcka font-size: 0.8em; p marginal: 0 0 5px; . meta font-size: 0.8em; typsnittstyp: kursiv; färg: # 888; .links font-size: 1.8em; linjehöjd: 0,8em; float: right; marginal: -10px 0 0; .links ett display: block; text-dekoration: ingen; </pre> <p>Uppdatera sidan i din webbläsare och allt ska vara snyggare. Oroa dig inte för denna CSS för mycket; det gör bara sakerna lite snyggare!</p> <img src="//accentsconagua.com/img/images_26_3/singing-with-sinatra-the-recall-app_3.png"> <hr> <h2>Lägga till en anteckning i databasen</h2> <p>Just nu om du försöker skicka in formuläret på hemsidan kommer du att få ett rutfel. Låt oss skapa POST-rutten för hemsidan nu:</p> <pre>posta '/' gör n = Note.new n.content = params [: innehåll] n.created_at = Time.now n.updated_at = Time.now n.save omdirigering '/' slutet</pre> <p>Så när en postförfrågan görs på hemsidan skapar vi ett nytt Objekt-objekt i <code>n</code> (tack vare DataMapper ORM, <code>Note.new</code> representerar en ny rad i <code>anteckningar</code> tabell i databasen). De <code>innehåll</code> fältet är inställt på de inlämnade data från textarea och <code>skapad vid</code> och <code>updated_at</code> datetime fält är inställda på aktuell tidstämpel.</p> <p>Den nya noten sparas sedan, och användaren omdirigerades till hemsidan där den nya noten kommer att visas.</p> <hr> <h2>Visar noterna</h2> <p>Så vi har lagt till en ny anteckning, men vi kan inte se den på hemsidan ändå eftersom vi inte har skrivit koden för den. Inuti <code>visningar / home.erb</code> visa fil, ersätt <code><%# display notes %></code> linje med:</p> <pre><% @notes.each do |note| %> <article <%= 'class="complete"' if note.complete %>> <p> <%= note.content %> <span>"> [Redigera]</span> </p> <p> / Komplett ">? </p> <p>Skapad: <%= note.created_at %></p> </article> <% end %></pre> <p>På första raden börjar vi en slinga genom var och en av <code>@notes</code> (Alternativt kan vi ha skrivit <code>för notering i @notes</code>, men med ett block, som vi är här, är en bättre övning). På linje 2 ger vi <code><article></code> en klass av <code>komplett</code> om nuvarande anteckning är inställd på <code>komplett</code>. Resten ska vara ganska rakt framåt.</p> <hr> <h2>Redigering av en anteckning</h2> <p>Så vi kan lägga till och visa anteckningar. Nu behöver vi bara möjlighet att redigera och ta bort dem.</p> <p>Du kanske har märkt det i vår <code>home.erb</code> visa vi satte en <code>[redigera]</code> länk för varje anteckning till vad som är väsentligen <code>/: Id</code>, så låt oss skapa den vägen nu:</p> <pre>få '/: id' do @note = Note.get params [: id] @title = "Redigera anteckning ## params [: id]" erb: redigera slutet</pre> <p>Vi hämtar den begärda noten från databasen med hjälp av det angivna ID-kortet, ställa in en <code>@titel</code> variabel och ladda <code>visningar / edit.erb</code> visa fil via ERB-parsern.</p> <p>Ange följande för <code>visningar / edit.erb</code> se:</p> <pre><% if @note %> <form action="/<%= @note.id %>"metod =" 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 "> Ta bort</p> <% else %> <p>Not not found.</p> <% end %></pre> <p>Detta är en ganska enkel vy. En blankett som pekar tillbaka till den aktuella sidan, en textarea innehållande anteckningen och en kryssruta som kontrolleras om anteckningen är inställd på <code>komplett</code>.</p> <p>Men titta på den tredje raden. Mystisk. För att förklara detta behöver vi sidospår lite.</p> <h3>RESTful tjänster</h3> <p>Du har hört talas om de två termerna GET och POST.</p> <ul> <li> <strong>SKAFFA SIG: </strong>Den vanligaste. Det är allmänt för att begära en sida och kan bokmärkas.</li> <li> <strong>POSTA: </strong> Används för att skicka in data och kan inte bokmärkas.</li> </ul> <p>Men GET och POST är inte de enda "HTTP-verben" - det finns två mer du borde veta om: PUT och DELETE.</p> <p>Tekniskt sett bör POST endast användas för att skapa något - till exempel skapa en ny anteckning i din fantastiska nya webbapp, till exempel.</p> <p>PUT är verbet för att ändra något. Och DELETE, du gissade det, är för att ta bort något.</p> <p>Att ha dessa fyra verb är ett bra sätt att skilja en app upp. Det är logiskt. Tyvärr stöder webbläsare inte faktiskt PUT eller DELETE-förfrågningar, det är därför du aldrig har hört talas om dem tidigare.</p> <p>Så, om vi vill logga upp vår app uppåt (vilken Sinatra uppmuntrar), måste vi förfalska dessa PUT och DELETE förfrågningar. Du ser vår formulär <code>verkan</code> är satt till <code>posta</code>. Det gömda <code>_metod</code> inmatningsfält som vi har ställt in på <code>sätta</code> På den tredje raden låter Sinatra falska denna PUT-förfrågan, samtidigt som man använder en POST. Rails, bland andra ramar, gör saker på ett liknande sätt.</p> <hr> <h2>Låt oss ställa</h2> <p>Nu har vi faked vår PUT-förfrågan, vi kan skapa en rutt för den:</p> <pre>put '/: id' gör n = Note.get params [: id] n.content = params [: innehåll] n.complete = params [: complete]? 1: 0 n.updated_at = Time.now n.save omdirigering '/' slutet</pre> <p>Det är allt ganska enkelt. Vi får den relevanta anteckningen med hjälp av ID: n i URI: n, sätter fälten till de nya värdena, sparar och omdirigerar hem. Lägg märke till hur vi på fjärde raden använder en ternär operatör för att ställa in <code>n.complete</code> till <code>1</code> om <code>params [: kompletta]</code> existerar, eller <code>0</code> annat. Det beror på att värdet på en kryssruta endast lämnas in med ett formulär om det är markerat, så vi bara söker efter att det finns det.</p> <hr> <h2>Radera en anteckning</h2> <p>I vår <code>edit.erb</code> Visa, vi lade till en "Radera" länk till vad som i huvudsak är sökvägen <code>/: Id / delete</code>. Lägg till detta i din ansökningsfil:</p> <pre>få '/: id / delete' gör @note = Note.get params [: id] @title = "Bekräfta radering av anteckning ## params [: id]" erb: radera slutet</pre> <p>På den här sidan får vi bekräftelse från användaren att de faktiskt vill ta bort denna anteckning. Skapa visningsfilen på <code>visningar / delete.erb</code> med följande:</p> <pre><% if @note %> <p>Är du säker på att du vill radera följande anteckning: <em>"<%= @note.content %>"</em>?</p> <form action="/<%= @note.id %>"metod =" post "> <input type="hidden" name="_method" value="delete"> <input type="submit" value="Yes, Delete It!"> "> Avbryt </form> <% else %> <p>Not not found.</p> <% end %></pre> <p>Observera att precis som hur vi faktade en PUT-förfrågan genom att ställa in en dold <code>_metod</code> Inmatningsfältet, vi fakar nu en DELETE-förfrågan.</p> <hr> <h2>DELETE Route</h2> <p>Jag är säker på att du hänger med det här just nu. Radera raden är:</p> <pre>delete '/: id' gör n = Note.get params [: id] n.destroy omdirigera '/' slutet</pre> <p>Testa! Du ska nu kunna visa, lägga till, redigera och ta bort anteckningar. Det finns bara en sak till? </p> <hr> <h2>Markera en anteckning som "komplett"</h2> <p>Just nu om du vill ange en anteckning som <code>komplett</code> Du måste gå in i Redigera och markera rutan på den sidan. Låt oss göra processen lite enklare.</p> <p>Tillbaka när vi startade huvudsidan inkluderade vi a <code>/: Id / komplett</code> länk på varje anteckning. Låt oss göra den rutten nu, som helt enkelt ska ange en anteckning som fullständig (eller ofullständig om den redan var klar att slutföra):</p> <pre>få '/: id / complete' gör n = Note.get params [: id] n.complete = n.complete? 0: 1 # flip it n.updated_at = Time.now n.save omdirigering '/' slutet</pre> <hr> <h2>Slutsats</h2> <p>Du och Sinatra drar av en crackin duet! Du har väldigt snabbt skrivit en enkel webbapp som utför alla CRUD-operationer du förväntar dig att en app ska göra. Den är skriven i super-sexig ren Ruby-kod och är åtskild i sina logiska delar.</p> <p>I den sista delen av Singing med Sinatra, Encore förbättrar vi felhantering, skyddar appen från XSS och skapar ett RSS-flöde för anteckningarna.</p> <p><strong>Notera:</strong> Du kan bläddra i de slutliga projektfilerna för denna handledning på 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">Koda</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">Sjungar med 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">Sjungar med 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="/">ro.accentsconagua.com</a><div class="h-block-a"></div></div> <div class="footer-text"> Informații interesante și sfaturi utile privind programarea. Dezvoltarea de site-uri, web design si dezvoltare web. Tutoriale Photoshop. Crearea de jocuri pe calculator și aplicații mobile. Deveniți un programator profesionist de la zero. </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="Căutare..."> <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>