Cantando Con Sinatra La Aplicación De Recall

Bienvenido a la Pista 2 de Cantando con Sinatra. En la primera parte, revisamos las rutas, cómo trabajar con los parámetros de URI, cómo trabajar con los formularios y cómo Sinatra diferencia las rutas por el método HTTP por el que fueron solicitadas. Hoy, vamos a ampliar nuestro conocimiento de Sinatra mediante la creación de una pequeña aplicación basada en bases de datos, "Recall", para tomar notas / hacer una lista de tareas pendientes..

Vamos a utilizar una base de datos SQLite para almacenar las notas, y usaremos el DataMapper RubyGem para comunicarnos con la base de datos. Ejecuta lo siguiente dentro de un shell para instalar las gemas relevantes:

gema instalar sqlite3 datamapper dm-sqlite-adapter

Dependiendo de cómo haya configurado RubyGems en su sistema, es posible que necesite un prefijo instalación de gemas con sudo.


El calentamiento

Comencemos de inmediato creando un nuevo directorio para el proyecto y creando el archivo de la aplicación., recordar.rb. Comience por requerir las gemas relevantes:

requieren 'rubygems' requieren 'sinatra' requieren 'datamapper'

Nota: Si está ejecutando Ruby 1.9 (que debería ser), puede eliminar la línea de "requeridos 'rubygems'", ya que Ruby carga automáticamente RubyGems de todos modos.

Y configura la base de datos con lo siguiente:

DataMapper :: setup (: predeterminado, "sqlite3: // # Dir.pwd /recall.db") clase Nota incluye DataMapper :: Resource property: id, Serial property: content, Text,: required => true property: complete, Boolean,: required => true,: default => false property: created_at, DateTime property: updated_at, DateTime final DataMapper.finalize.auto_upgrade!

En la primera línea, estamos configurando una nueva base de datos SQLite3 en el directorio actual, llamada memoria.db. Debajo de eso, en realidad estamos configurando una tabla de 'Notas' en la base de datos.

Mientras llamamos a la clase 'Nota', DataMapper creará la tabla como 'Notas'. Esto está en consonancia con una convención que Ruby on Rails y otros marcos y módulos ORM siguen.

Dentro de la clase, estamos configurando el esquema de base de datos. La tabla 'Notas' tendrá 5 campos. Un carné de identidad campo que será una clave principal entera y un incremento automático (esto es lo que significa 'Serie'). UNA contenido campo que contiene texto, un booleano completar campo y dos campos de fecha y hora, Creado en y updated_at.

La última línea le indica a DataMapper que actualice automáticamente la base de datos para que contenga las tablas y los campos que hemos establecido, y que vuelva a hacerlo si realizamos cambios en el esquema..


La pagina de inicio

Ahora, vamos a crear nuestra página de inicio:

En la parte superior hay un formulario para agregar una nueva nota, y debajo se encuentran todas las notas en la base de datos. Para comenzar, agregue lo siguiente al archivo de la aplicación, recordar.rb:

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

Nota IMPORTANTE: Retire el punto ('.') en :.orden. (WordPress está interfiriendo con el ejemplo de código.)

En la segunda línea puede ver cómo recuperamos todas las notas de la base de datos. Si ha usado ActiveRecord (el ORM usado en Rails) anteriormente, la sintaxis de DataMapper se sentirá muy familiar. Las notas están asignadas a la @notes Instancia variable. Es importante usar variables de instancia (las variables que comienzan con una @) para que sean accesibles desde el archivo de vista.

Establecemos el @título variable de instancia, y cargar el vistas / home.erb ver archivo a través del analizador ERB.

Crear el vistas / home.erb Ver archivo y comenzar con lo siguiente:

<% # display notes %>

Tenemos un formulario simple que se envía a la página de inicio ('/'), y debajo de eso hay un código ERB que sirve como un marcador de posición por ahora.


Diseños

El lote de estándares HTML entre ustedes puede haber sufrido un golpe menor después de ver que nuestro archivo de vista de inicio no contiene ningún tipo de documento ni otras etiquetas HTML. Bueno, hay una razón para eso. Crear un layout.erb archivo en tu puntos de vista/ directorio que contiene lo siguiente:

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

Recordar

porque estas muy ocupado para recordar

<%= yield %>

Una aplicación para Nettuts+.

Las dos partes interesantes aquí son las líneas 5 y 18. En la línea 5 verá el primer uso del <%=? %> Etiquetas ERB. <%= es diferente de lo ordinario <% ya que imprime lo que hay dentro. Así que aquí estamos mostrando lo que hay en el @título variable de instancia seguida de | Recordar para la pagina </code> etiqueta.</p> <p>En la linea 18 esta <code><%= yield %></code>. Sinatra mostrará esto <code>layout.erb</code> archivo en todas las rutas. Y el contenido real para esa ruta se insertará donde sea <code>rendimiento</code> es. <code>rendimiento</code> es un término que esencialmente significa "para aquí, inserte lo que esté esperando, luego continúe".</p> <p>Inicia el servidor con <code>recuerdo de escopeta.rb</code> en el shell, y eche un vistazo a la página de inicio en el navegador. Debería ver el contenido del archivo de diseño y la forma del archivo real. <code>home.erb</code> ver.</p> <img src="//accentsconagua.com/img/images_26_3/singing-with-sinatra-the-recall-app_2.png"> <hr> <h2>CSS</h2> <p>En el archivo de diseño incluimos dos archivos CSS. Sinatra puede cargar archivos estáticos (por ejemplo, su CSS, JS, imágenes, etc.) desde una carpeta llamada <code>público/</code> en el directorio raíz. Así que crea ese directorio, y dentro de él dos archivos: <code>reset.css</code> y <code>style.css</code>. El reinicio contiene el reinicio de HTML5 Boilerplate CSS:</p> <pre>/ * HTML5? Boilerplate style.css contiene un restablecimiento, la normalización de la fuente y algunos estilos básicos. el crédito se deja donde el crédito es debido. se tomó mucha inspiración de estos proyectos: yui.yahooapis.com/2.8.1/build/base/base.css camendesign.com/design/ praegnanz.de/weblog/htmlcssjs-kickstart * / / * html5doctor.com Restablecer Stylesheet ( Eric Meyer Reset Reloaded + HTML5 baseline) v1.6.1 2010-09-17 | Autores: Eric Meyer y Richard Clark html5doctor.com/html-5-reset-stylesheet/ * / html, body, div, span, object, iframe, h1, h2, h3, h5, h5, h6, p, blockquote, pre , abbr, dirección, cite, código, del, dfn, em, img, ins, kbd, q, samp, pequeño, fuerte, sub, sup, var, b, i, dl, dt, dd, ol, ul, li fieldset, formulario, etiqueta, leyenda, tabla, título, tbody, tfoot, thead, tr, th, td, artículo, aparte, lienzo, detalles, figcaption, figura, pie de página, encabezado, hgroup, menú, navegación, sección, resumen , tiempo, marca, audio, video margen: 0; relleno: 0; borde: 0; tamaño de letra: 100%; fuente: heredar; alineación vertical: línea de base; artículo, aparte, detalles, figcaption, figura, pie de página, encabezado, hgroup, menu, nav, 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; font-style: cursiva; font-weight: bold; del text-decoration: line-through; abbr [title], dfn [title] border-bottom: 1px punteado; cursor: help; table border-collapse: collapse; border-spacing: 0; hr display: block; altura: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; input, seleccione vertical-align: middle; / * END RESET CSS * / / * normalización de fuentes inspirada en las fuentes de la Biblioteca YUI.css: developer.yahoo.com/yui/ * / body font : 13px / 1.231 sans-serif; * font-size: small; / * truco retenido para preservar la especificidad * / select, input, textarea, button font: 99% sans-serif; / * normalize monospace sizing * 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 Los estilos ase * / body, select, input, textarea / * # 444 se ve mejor que el negro: twitter.com/H_FJ/statuses/11800719859 * / color: # 444; / * establezca su fuente base aquí, para aplicar uniformemente * / / * font-family: Georgia, serif; Los encabezados * / / * (h1, h2, etc.) no tienen un tamaño o margen de fuente predeterminado. definir esos a ti mismo. * / h1, h2, h3, h4, h5, h6 font-weight: negrita; / * forzar siempre una barra de desplazamiento que no sea IE: * / html overflow-y: scroll; / * tratamiento de enfoque accesible: people.opera.com/patrickl/experiments/keyboard/test * / a: hover, a: active outline: none; a, a: activo, a: visitado color: # 607890; a: hover color: # 036; ul, ol margen izquierdo: 2em; ol tipo de estilo de lista: decimal; / * eliminar márgenes para las listas de navegación * / nav ul, nav li margin: 0; estilo de lista: ninguno; list-style-image: none; pequeño font-size: 85%; fuerte, th font-weight: negrita; td vertical-align: top; / * establecer sub, sup sin afectar la altura de la línea: gist.github.com/413930 * / sub, sup font-size: 75%; línea de altura: 0; posición: relativa; sup arriba: -0.5em; sub inferior: -0.25em; pre / * www.pathf.com/blogs/2008/05/formatting-quoted-code-in-blog-posts-css21-white-space-pre-wrap/ * / white-space: pre; espacio en blanco: pre-envoltura; espacio en blanco: línea previa; ajuste de palabra: palabra de descanso; relleno: 15px; textarea desbordamiento: auto; / * www.sitepoint.com/blogs/2010/08/20/ie-remove-textarea-scrollbars/ * / .ie6 legend, .ie7 legend margin-left: -7px; / * thnx ivannikolic! * / / * alinear casillas de verificación, radios, entradas de texto con su etiqueta por: Thierry Koblentz tjkdesign.com/ez-css/css/base.css * / input [type = "radio"] vertical-align: text-bottom; input [type = "checkbox"] vertical-align: bottom; .ie7 input [type = "checkbox"] vertical-align: baseline; entrada .ie6 vertical-align: text-bottom; / * cursor de la mano en los elementos de entrada en los que se puede hacer clic * / label, input [type = "button"], input [type = "submit"], input [type = "image"], button cursor: pointer; / * los navegadores webkit agregan un margen de 2px fuera del cromo de los elementos del formulario * / button, input, select, textarea margin: 0; / * colores para la validez de formulario * / input: valid, textarea: valid input: invalid, textarea: invalid border-radius: 1px; -moz-box-shadow: 0px 0px 5px rojo; -webkit-box-shadow: 0px 0px 5px rojo; cuadro de sombra: 0px 0px 5px rojo; entrada de .no-boxshadow: no válido, .no-boxshadow textarea: inválido fondo-color: # f0dddd; / * Estas declaraciones de selección tienen que ser separadas. Sin texto de sombra: twitter.com/miketaylr/status/12228805301 También: rosa intenso. * / :: - moz-selection background: # FF5E99; color: #fff; texto-sombra: ninguno; :: selection background: # FF5E99; color: #fff; texto-sombra: ninguno; / * j.mp/webkit-tap-highlight-color * / a: link -webkit-tap-highlight-color: # FF5E99; / * hacer que los botones funcionen bien en IE: www.viget.com/inspire/styling-the-button-element-in-internet-explorer/ * / button width: auto; desbordamiento: visible; / * redimensionamiento bicúbico para IMG de tamaño no nativo: code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ * / .ie7 img -ms-modo-interpolación: bicúbico; </pre> <p>Y <code>style.css</code> contiene un estilo básico para que la aplicación se vea bonita:</p> <pre>cuerpo margen: 35px auto; ancho: 640px; header text-align: center; margen: 0 0 20px; header h1 display: inline; tamaño de fuente: 32px; encabezado h1 a: enlace, encabezado h1 a: visitado color: # 444; texto-decoración: ninguno; header h2 font-size: 16px; estilo de letra: cursiva; color: # 999; #main margin: 0 0 20px; #add margen: 0 0 20px; #add textarea altura: 30 px; ancho: 510px; relleno: 10px; borde: 1px sólido #ddd; #add input altura: 50 px; ancho: 100px; margen: -50px 0 0; borde: 1px sólido #ddd; fondo: blanco; #edit textarea altura: 30 px; ancho: 480px; relleno: 10px; borde: 1px sólido #ddd; #edit input [type = submit] height: 50px; ancho: 100px; margen: -50px 0 0; borde: 1px sólido #ddd; fondo: blanco; #edit input [type = checkbox] height: 50px; ancho: 20px; article border: 1px solid #eee; borde superior: ninguno; relleno: 15px 10px; artículo: primero de tipo borde: 1px sólido #eee; artículo: nth-child (even) background: #fafafa; article.complete background: # fedae3; artículo span font-size: 0.8em; p margen: 0 0 5px; .meta font-size: 0.8em; estilo de letra: cursiva; color: # 888; .links font-size: 1.8em; altura de la línea: 0.8em; flotar derecho; margen: -10px 0 0; .links a display: block; texto-decoración: ninguno; </pre> <p>Actualiza la página en tu navegador y todo debería estar más estilizado. No te preocupes demasiado por este CSS; solo hace que las cosas se vean un poco más bonitas!</p> <img src="//accentsconagua.com/img/images_26_3/singing-with-sinatra-the-recall-app_3.png"> <hr> <h2>Agregar una nota a la base de datos</h2> <p>Ahora mismo, si intenta enviar el formulario en la página de inicio, obtendrá un error de ruta. Vamos a crear la ruta POST para la página de inicio ahora:</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>Entonces, cuando se realiza una solicitud de publicación en la página de inicio, creamos un nuevo objeto Note en <code>norte</code> (Gracias al DataMapper ORM, <code>Nota.nuevo</code> representa una nueva fila en el <code>notas</code> tabla en la base de datos). los <code>contenido</code> campo se establece en los datos enviados desde el área de texto y la <code>Creado en</code> y <code>updated_at</code> Los campos de fecha y hora se establecen en la marca de tiempo actual.</p> <p>La nueva nota se guarda y el usuario vuelve a dirigir a la página de inicio donde se mostrará la nueva nota..</p> <hr> <h2>Mostrando las notas</h2> <p>Así que hemos agregado una nueva nota, pero aún no podemos verla en la página de inicio ya que no hemos escrito el código para ella. Dentro de <code>vistas / home.erb</code> ver archivo, reemplazar el <code><%# display notes %></code> linea con:</p> <pre><% @notes.each do |note| %> <article <%= 'class="complete"' if note.complete %>> <p> <%= note.content %> <span>"> [editar]</span> </p> <p> / completar ">? </p> <p>Creado: <%= note.created_at %></p> </article> <% end %></pre> <p>En la primera línea comenzamos un bucle a través de cada uno de los <code>@notes</code> (Alternativamente, podríamos haber escrito <code>para nota en @notes</code>, pero usar un bloque, como estamos aquí, es una mejor práctica). En la línea 2, le damos la <code><article></code> una clase de <code>completar</code> si la nota actual se establece en <code>completar</code>. El resto debe ser bastante sencillo..</p> <hr> <h2>Editando una nota</h2> <p>Así que podemos agregar y ver notas. Ahora solo necesitamos la posibilidad de editarlos y borrarlos..</p> <p>Usted puede haber notado que en nuestro <code>home.erb</code> vista establecemos un <code>[editar]</code> enlace para cada nota a lo que es esencialmente <code>/:carné de identidad</code>, Así que vamos a crear esa ruta ahora:</p> <pre>get '/: id' do @note = Note.get params [: id] @title = "Edit note ## params [: id]" erb: edit end</pre> <p>Recuperamos la nota solicitada de la base de datos utilizando el ID proporcionado, configuramos un <code>@título</code> variable, y cargar el <code>vistas / edit.erb</code> ver archivo a través del analizador ERB.</p> <p>Ingrese lo siguiente para el <code>vistas / edit.erb</code> ver:</p> <pre><% if @note %> <form action="/<%= @note.id %>"method =" 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>/ eliminar "> Eliminar</p> <% else %> <p>Nota no encontrada.</p> <% end %></pre> <p>Esta es una vista bastante simple. Un formulario que apunta a la página actual, un área de texto que contiene el contenido de la nota y una casilla de verificación que se comprueba si la nota está configurada para <code>completar</code>.</p> <p>Pero mira la tercera línea. Misterioso. Para explicar esto, tenemos que desviarnos un poco..</p> <h3>Servicios de restauración</h3> <p>Has oído hablar de los dos términos GET y POST..</p> <ul> <li> <strong>OBTENER: </strong>Los más comunes. Por lo general, es para solicitar una página y se puede marcar como favorito..</li> <li> <strong>ENVIAR: </strong> Se utiliza para enviar datos y no se puede marcar como favorito.</li> </ul> <p>Pero GET y POST no son los únicos "verbos HTTP"; hay dos más que debe conocer: PUT y DELETE.</p> <p>Técnicamente, POST solo debe usarse para crear algo, como crear una nueva nota en su nueva aplicación web, por ejemplo.</p> <p>PUT es el verbo para modificar algo. Y BORRAR, lo has adivinado, es para borrar algo.</p> <p>Tener estos cuatro verbos es una excelente manera de separar una aplicación. Es logico Desafortunadamente, los navegadores web no admiten las solicitudes PUT o DELETE, por lo que es probable que nunca haya oído hablar de ellas antes..</p> <p>Entonces, volviendo al camino aquí, si queremos dividir nuestra aplicación de manera lógica (lo que Sinatra recomienda), tenemos que falsificar estas solicitudes PUT y DELETE. Verás nuestros formularios <code>acción</code> se establece en <code>enviar</code>. Lo oculto <code>_método</code> campo de entrada que hemos establecido para <code>poner</code> en la tercera línea le permite a Sinatra falsificar esta solicitud PUT, mientras que en realidad usa un POST. Los rieles, entre otros marcos, hacen las cosas de manera similar..</p> <hr> <h2>Pongámonos</h2> <p>Ahora que hemos falsificado nuestra solicitud PUT, podemos crear una ruta para ella:</p> <pre>poner '/: 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>Todo es bastante simple. Obtenemos la nota relevante utilizando el ID en el URI, configuramos los campos a los nuevos valores, guardamos y redirigimos el inicio. Observe cómo en la cuarta línea estamos usando un operador ternario para configurar <code>npleta</code> a <code>1</code> Si <code>params [: completo]</code> existe, o <code>0</code> de otra manera. Esto se debe a que el valor de una casilla de verificación solo se envía con un formulario si está marcado, por lo que simplemente estamos comprobando su existencia.</p> <hr> <h2>Borrando una nota</h2> <p>En nuestro <code>edit.erb</code> vista, agregamos un enlace 'Eliminar' a lo que es esencialmente la ruta <code>/: id / delete</code>. Agregue esto a su archivo de aplicación:</p> <pre>get '/: id / delete' do @note = Note.get params [: id] @title = "Confirmar la eliminación de la nota ## params [: id]" erb: delete end</pre> <p>En esta página, recibiremos una confirmación del usuario de que realmente desea eliminar esta nota. Crea el archivo de vista en <code>vistas / borrar.erb</code> con lo siguiente:</p> <pre><% if @note %> <p>¿Estás seguro de que deseas eliminar la siguiente nota: <em>"<%= @note.content %>"</em>?</p> <form action="/<%= @note.id %>"method =" post "> <input type="hidden" name="_method" value="delete"> <input type="submit" value="Yes, Delete It!"> "> Cancelar </form> <% else %> <p>Nota no encontrada.</p> <% end %></pre> <p>Tenga en cuenta que al igual que en la forma en que falsificamos una solicitud PUT estableciendo un oculto <code>_método</code> campo de entrada, ahora estamos falsificando una solicitud DELETE.</p> <hr> <h2>La ruta de borrado</h2> <p>Estoy seguro de que ya te has acostumbrado a esto. La ruta de borrado es:</p> <pre>delete '/: id' do n = Note.get params [: id] n.destroy redirect '/' end</pre> <p>¡Pruébalo! Ahora debería poder ver, agregar, editar y eliminar notas. Hay solo una cosa mas? </p> <hr> <h2>Marcar una nota como "Completa"</h2> <p>Ahora mismo si quieres configurar una nota como <code>completar</code> tienes que ir a la vista Editar y marcar la casilla en esa página. Vamos a hacer ese proceso un poco más simple.</p> <p>Cuando configuramos la página de inicio principal, incluimos un <code>/: id / complete</code> enlace en cada nota. Hagamos esa ruta ahora, que simplemente establecerá una nota como completa (o incompleta si ya estaba configurada para completar):</p> <pre>get '/: id / complete' do n = Note.get params [: id] n.complete = n.complete? 0: 1 # voltearlo n.updated_at = Time.now n.save redirect '/' end</pre> <hr> <h2>Conclusión</h2> <p>¡Tú y Sinatra sacáis un buen dúo! Has escrito muy rápidamente una aplicación web simple que realiza todas las operaciones de CRUD que esperas que haga una aplicación. Está escrito en código Ruby súper sexy y limpio, y está separado en sus partes lógicas.</p> <p>En la parte final de Singing with Sinatra, el Encore, mejoraremos el manejo de errores, aseguraremos la aplicación de XSS y crearemos una fuente RSS para las notas.</p> <p><strong>Nota:</strong> Puede navegar por los archivos de proyecto finales para este tutorial en 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">Código</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">Cantando con 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">Cantando con 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="/">es.accentsconagua.com</a><div class="h-block-a"></div></div> <div class="footer-text"> Información interesante y consejos útiles sobre programación. Desarrollo de sitios web, diseño web y desarrollo web. Tutoriales de Photoshop. Creación de juegos de ordenador y aplicaciones móviles. Conviértete en un programador profesional desde cero. </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="Buscar..."> <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>