Spring Framework
¿Qué es Spring?
- Es un framework que integra los mejores frameworks o tecnologías permitiendo desarrollar aplicaciones robustas sobre la máquina virtual de java, evitando las muchas configuraciones.
¿Qué es Spring?
-
El core de Spring Framework provee las siguientes características:
– Inyección de dependencias (DI)
– Programación orientada a aspectos
– Soporte para aplicaciones MVC y servicios RESTful.
– Soporte para JDBC, JPA y JMS
– Soporte para automatización de pruebas
Módulos Spring
Controlador IoC (DI)
Anotaciones para DI
@Autowired | Se puede aplicar a métodos y propiedades. Automáticamente se asignan los objetos de los cuales la clase depende. Se puede indicar que una dependencia es opcional (required=false) |
@Qualifier("name") | Cuando hay varios candidatos para satisfacer la dependencia se especifica el nombre del bean mediante esta anotación |
@PostConstruct | Anota un método que se ejecuta después de construir los beans |
@Component("name") | Define un bean manejado por Spring |
Anotaciones para DI
@Service | Define un bean de la capa de servicio |
@Controller | Define un bean de la capa de control |
@Repository | Define un bean de la capa de datos |
@Scope("prototype") | Define el mecanismo para la creación del bean. Por defecto es el mecanismo es singleton. Puede ser prototype, request, session, global-session |
@Configuration | Define una clase que permite la creación de beans (clases) implementadas fuera de nuestro proyecto |
@Bean | Define un bean implementado fuera de nuestro proyecto |
Anotaciones para DI
@Import(ConfigA.class) | Importa los beans definidos en otra clase anotada con @Configuration |
@Value | Carga una propiedad con un valor definido en una archivo .properties o por una expresion SpEL. Ej: @Value("$ {spring.datasource.password}"), @Value(“#{ T(java.lang.Math).random() * 100.0}”), @Value(“#{ @bean.property}”) |
Manejo de Transacciones
- Spring Framework provee mecanismos para el manejo automático de transacciones.
- Soporta automáticamente el manejo de transacciones para el acceso a base de datos.
- Podemos usar la anotación @Transactional para hacer que una de nuestras clases sea manejado por el gestor de transacciones de Spring.
- Un atributo muy útil de esta anotación es el timeout, que define el máximo tiempo de espera de una operación.
Capa de Persistencia
Spring JPA
- Spring crea repositorios automáticamente con consultas básicas y permite añadir búsquedas más complejas mediante la interfaz repository.
- Para búsquedas más eficientes de texto completo (full text search), se puede usar un sistema de indexado como Lucent.
Annotaciones de JPA
@Entity | Declara la clase como una entidad o una tabla |
@Table | Sirve para especificar el nombre de la tabla |
@Id | Especifica la clave primaria de una tabla |
@GeneratedValue | Especifica como se genera la clave primaria. Puede ser: automatica, manual o generado secuancialmente. |
@Transient | Especifica que la propiedad de la clase no se almacena en la base de datos |
@Column | Especifica que la propiedad corresponde a una columna en la base de datos |
Annotaciones de JPA
@SequenceGenerator | Crea una secuencia para generar la clave primaria. |
@JoinColumn | Usado para enlazar dos entidades o tablas. Usado para las relaciones many-to-many y one-to-many. |
@ManyToMany | Usado para definir relaciones many-to-many |
@ManyToOne | Usado para definir relaciones many-to-one |
@OneToMany | Usado para definir relaciones one-to-many |
@OneToOne | Usado para definir relaciones one-to-one |
Ejemplo JPA
package com.example.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
@Data
public class Libro {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String titulo;
private String autor;
private String precio;
public Libro()
{ }
public Libro(long id, String titulo, String autor, String precio)
{
this.id=id;
this.titulo=titulo;
this.autor=autor;
this.precio=precio;
}
}
Ejemplo JPA
package com.example.persistency;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
public interface LibroRepository extends CrudRepository<Libro, Long> {
List<Libro> findByTitulo(String titulo);
List<Libro> findByTituloLike(String tituloSegment);
}
public String cargarLibros()
{
Libro libro = new Libro("CSS3 For Web Developers", "Jeffrey Zeldman", "$ 35.000 pesos");
librosRepository.save( libro );
libro = new Libro("Diseño de páginas responsivas", "Ethan Marcotte", "$ 50.000 pesos");
librosRepository.save( libro );
libro = new Libro("You're my favorite clinet", "Mike Monteiro", "$ 25.000 pesos");
librosRepository.save( libro );
libro = new Libro("On Web Typography", "Jason Santa maria","$ 40.000 pesos");
librosRepository.save( libro );
libro = new Libro("Saas For Web Designers", "Dan Cederholm", "$ 75.000 pesos");
librosRepository.save( libro );
return "listo";
}
Ejemplo JPA
spring.datasource.url=jdbc:mysql://localhost:3306/tienda
spring.datasource.username=USUARIO
spring.datasource.password=PASSWORD
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.generate-ddl=true
application.properties
Opciones en las Consultas
public interface PersonRepository extends Repository<User, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastnname);
// Enables the distinct flag for the query
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname,String fistname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname,String fistname);
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname,String fistname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
Modificadores de Resultados
public interface PersonRepository extends Repository<User, Long> {
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
}
Paginación
public interface PersonRepository extends Repository<User, Long> {
Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);
}
Los resultados se pueden paginar de la siguiente manera:
Spring permite controlar las páginas desde el controlador de navegación asi:
//usa los parámetros page, size y sort
@RequestMapping
public String showUsers(Model model, Pageable pageable) {
model.addAttribute("users", repository.findAll(pageable));
return "users";
}
Capa Web
Web MVC
- Spring Framework soporta el patrón MVC mediante la implementación de un DispatcherServlet
- Para indicar el mapeo entre las URL y los bean correspondientes se usan anotaciones.
Web MVC
Anotaciones
@Controller | Define un bean de la capa de control de navegación |
@RequestMapping | Define la URL a la cual se debe atender. Se define también el método HTTP, tipo de contenido consumido y generado. |
@PathVariable | Se usa en los parametros para indicar una variable dentro del camino (path) de la URL. Ej: @RequestMapping(path = "/accounts/{account}" |
Anotaciones
@RequestBody | Se usa para recibir un parametro que viaja por el body HTTP |
@RequestParam | Se usa para recibir un parametro presente en la URL. |
@RequestHeader | Usado para mapear un encabezado HTTP a una variable Java. |
@CookieValue | Usado para recibir como parámetro un valor almacenado en las Cookies. |
@SessionAttributes | Usado para recibir y escribir un valor en la session. |
Ejemplo MVC
package com.example.control;
import java.util.ArrayList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class LibrosControlador {
@Autowired
private LibroRepository librosRepository;
private Iterable<Libro> resultado;
@RequestMapping({"/","/home"})
public String home(@RequestParam(value="busqueda", required=false) String busqueda, Model model) {
if(busqueda!=null&&!busqueda.equals("Escriba el libro a buscar"))
{
resultado=librosRepository.findByTituloLike("%"+busqueda+"%");
}
else
{
resultado=librosRepository.findAll();
}
model.addAttribute("resultado",resultado);
return "index";
}
}
Tecnologías para Vistas
- JSP y JSLT
- Thymeleaf
- Groovy Markup Templates
- Velocity
- FreeMarker
- XML (Marshalling o Mapping)
- XSLT
- Documents (PDF/Excel)
- JasperReports (CSV, Excel, HTML y PDF)
- Feeds (RSS, Atom)
- JSON
Thymeleaf
- Permite hacer vistas que son documentos XML y HTML válidos aún sin ser procesadas por el servidor.
- Usa un mecanismo para generar el DOM muy eficiente.
- Define un lenguaje de expresiones propio, pero tiene soporte para el SpEL.
- Usa los mismos nombres de los parámetros definidos por HTML y es completamente compatible con HTML5.
Thymeleaf
- Define las siguientes variables para manejar contexto:
– param: Accede a los parámetros enviados por HTTP.
– session: Accede a las variables en la sesión.
– application: Accede a los atributos del ServletContext.
Ejemplos Thymeleaf
<!-- ${} para obtener variables u objetos; || permite concatenar texto sin usar '' -->
<!-- el ejemplo genera <span> conevar twovar, threevar-->
<span th:text="${conevar} + ' ' + |${twovar}, ${threevar}|">
<!-- @{} resuelve URLs; para los parametros se usan () -->
<!-- el ejemplo genera <a href="/details/user.login?orderId=o.id">view</a> -->
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>
<!-- th:object obtiene un objeto para usarlo en un conjunto de etiquetas, las propiedades se obtienen mediante *{} -->
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
<!-- Thymeleaf incluye unos objetos de utilidad como #calendars, #numbers, #lists -->
<!-- https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#appendix-b-expression-utility-objects -->
<p>
Today is: <span th:text="${#calendars.format(today,'dd MMMM
yyyy')}">13 May 2011</span>
</p>
<!-- Se pueden mostrar etiquetas opcionales -->
<div th:if="${user.isAdmin()} == false">
<!-- Condicionales con el carácter ? luego el valor si se cumple : y el valor si no-->
<tr th:class="${row.even}? 'even' : 'odd'">
...
</tr>
<div th:object="${session.user}">
...
<p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p>
</div>
<div th:text="${@authService.getUserName()}">...</div>
<p>Age: <span th:text="*{age != null}? *{age} : '(no age
specified)'">27</span>.</p>
<!-- Repetición de etiquetas para cada elemento de una lista o iterable con th:each-->
<tr th:each="prod : ${prods}" class="row" th:classappend="$
{prodStat.odd}? 'odd'">
<!-- Generación de etiquetas según el caso con th:switch y th:case-->
<!-- Obtención de mensajes o propiedades de la aplicación mediante #{} -->
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>
Ejemplo Thymeleaf - Tienda
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="shortcut icon" href="images/sign.ico" type="image/x-icon" />
<link href="estilos/index.css" rel="stylesheet" type="text/css"
media="all" />
<title>Una tienda en linea responsiva</title>
<script
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js'
type='text/javascript'></script>
<script type="text/javascript">
$(document).ready(function(){
$("input").focus(function(){
if($(this).attr("class")=="emptyInput")
$(this).val("");
$(this).removeClass("emptyInput");
});
$("input").blur(function(){
if($(this).val()=="")
{
$(this).val("Escriba el libro a buscar");
$(this).addClass("emptyInput");
}
});
});
</script>
</head>
<body id="index" class="">
<div id='main'>
<header>
<h1>
<a class='logo icon-aba-logo' href='/'>Tienda de Libros</a> <span
class='logo-tagline'>Libros para diseñadores de páginas</span>
</h1>
<nav class='primary-nav'>
<ul>
<li id='nav-home'><a href='/'>Home</a></li>
<li id='nav-help'><a href='/paginas/ayuda/'>Ayuda</a></li>
<li>
<form method="post" action="/tienda/buscar" th:action="@{/}">
<input class="emptyInput" name="busqueda" value="Escriba el libro a buscar"></input>
<input type="submit" value="Buscar"></input>
</form>
</li>
</ul>
</nav> <a id='nav-cart' class='callout shopping-cart' href='/cart'
title='View Cart'> <span class='shopping-cart-count'>0</span> <span
class='shopping-cart-icon icon-cart-empty'></span>
</a> </header>
<div class='content' id='home'>
<section class='feature'> <a
href='/products/css3-for-web-designers'> <span
class='feature-edition'>Second Edition</span>
<div class='feature-big-cover'></div>
<div class='feature-small-cover'>
<div class="book-thumb icon-aba-cover-2"></div>
</div>
<div class='feature-info'>
<h2 class='feature-header'>
<span class='feature-author'>Dan Cederholm</span> <span
class='feature-title'>CSS3 for Web Designers</span>
</h2>
<h3 class='foreword-byline'>
<span class='byline-prefix'>foreword by</span> <span
class='byline-name'>Jeffrey Zeldman</span>
</h3>
<p class='foreword-text'>Rediscover a universe of creative
possibilities with CSS3. Dan Cederholm updates his essential guide
with new examples, polished code, and a new chapter on micro
layouts.</p>
<p class='feature-format-note'>Available in paperback, ePub,
PDF, and mobi</p>
</div>
</a> <a class='feature-buy-button button button--large'
href='/products/css3-for-web-designers' title='Buy Now'>Buy Now</a>
</section>
<section class='other-books subsection divider-top-thick divider-bottom-thick'>
<h3>Also from A Book Apart</h3>
<ul class='books'>
<li th:each="libro : ${resultado}" class='book'><a
href='/products/titulo' th:href="@{'/products/'+${libro.titulo}}">
<div class="book-thumb"><img src="images/Books.png" class="book-image"></img></div>
<h4 class='book-title' th:text="${libro.titulo}">Título</h4>
<p class='book-byline' >
<span class='byline-prefix'>por</span> <span class='byline-name' th:text="${libro.autor}">Autor</span>
<br></br>
<br th:text="${libro.precio}">precio</br>
</p>
<span class='button book-buy-button' title='Comprar'>Comprar</span>
</a>
</li>
</ul>
</section>
</div>
<footer>
<ul class='footer-links links'>
<li>© Copyright 2015, Gustavo Uribe</li>
</ul>
</footer>
</div>
</body>
</html>
/templates/index.html
Ejemplo Thymeleaf - Tienda
@
-ms-viewport {
width: device-width
}
*, *:before, *:after {
box-sizing: border-box
}
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p,
blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn,
em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var,
b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas,
details, embed, figure, figcaption, footer, header, hgroup, menu, nav,
output, ruby, section, summary, time, mark, audio, video {
border: 0;
font: inherit;
font-size: 100%;
margin: 0;
padding: 0;
vertical-align: baseline
}
html {
line-height: 1
}
ol, ul {
list-style: none
}
table {
border-collapse: collapse;
border-spacing: 0
}
caption, th, td {
font-weight: normal;
text-align: left;
vertical-align: middle
}
q, blockquote {
quotes: none
}
q:before, q:after, blockquote:before, blockquote:after {
content: "";
content: none
}
a img {
border: none
}
article, aside, details, figcaption, figure, footer, header, hgroup,
menu, nav, section, summary {
display: block
}
.wf-loading thead th, .wf-loading .button, .wf-loading .callout h3,
.wf-loading .primary-nav, .wf-loading h2.title, .wf-loading .subsection h3,
.wf-loading .book-byline .byline-name, .wf-loading .feature-header,
.wf-loading .feature-title, .wf-loading .feature-author, .wf-loading .press-date,
.wf-loading #product .title .callout, .wf-loading #product .title .callout p,
.wf-loading .product-byline, .wf-loading .product-option-info h4,
.wf-loading .alert__title, .wf-loading .product-option-note,
.wf-loading .offer-message {
visibility: hidden
}
body {
font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans",
Verdana, Arial, sans-serif
}
h4, .logo-tagline, .byline-prefix, .foreword-text, .book-summary,
.feature-author, .feature-format-note, .press p, .press ul, .press ol,
.alert--fancy p, .product-byline .byline-name i, .books .byline-name i,
.product-foreword p, .product-desc-gift p, .product-bundle-summary p,
.product-review-quote, .product-review-byline .byline-title,
.product-author-bio p, .product-bundle-author-bio p, .book-desc,
.newsletter-message, #faqs h4, #faqs p, .about-person-name,
.about-person-title, .about-text p, .basic-text p {
font-family: Georgia, "Times New Roman", Times, serif
}
thead th, .button, .callout h3, .primary-nav, .subsection h3,
.byline-name, .book-byline .byline-name, .press-article h3, .press-date,
#product .title .callout p, .product-option-info h4,
.product-out-of-stock strong, .alert__title, .offer-message,
.savings-qty, .savings-discount, .savings-msg, #cart-main .callout:before
{
font-family: "titling-gothic-condensed", "Helvetica Neue", Helvetica,
Arial, sans-serif
}
h2, .button--large, .feature-title {
font-family: "titling-gothic-skyline", "Helvetica Neue", Helvetica,
Arial, sans-serif
}
body {
border-top: solid 5px #82bc00;
color: #333;
background: #fff;
font-size: 1em;
width: 100%;
zoom: 1
}
body:before, body:after {
content: "";
display: table
}
body:after {
clear: both
}
a, .feature-title {
-webkit-transition: color .25s;
transition: color .25s;
color: #82bc00
}
a:hover, .feature-title:hover {
color: #997e11
}
em {
font-style: italic
}
strong {
font-weight: bold
}
h2 {
font-weight: 400;
font-style: normal;
font-size: 5em;
text-transform: uppercase
}
h2.title {
margin-bottom: 24px
}
h2.title em {
font-style: normal
}
h4 {
margin-bottom: 4px;
font-size: 1.125em;
line-height: 1.25em
}
h4 a {
text-decoration: none
}
th, tr, td {
vertical-align: baseline
}
thead th {
font-weight: 500;
font-style: normal;
margin-bottom: 12px;
font-size: .675em;
line-height: 1.5em;
text-transform: uppercase;
letter-spacing: .1em
}
.wf-active thead th {
font-size: .75em
}
.bulleted-list li {
margin-bottom: 12px;
padding: 0px 0px 0px 12px;
background-position: 0px 6px;
font-size: .75em;
line-height: 1.25em
}
.bulleted-list li a {
font-style: italic
}
.bulleted-list li:last-child {
margin-bottom: 0px
}
.callout {
border-radius: 4px;
padding: 8px;
background-color: #f6f5ea
}
.callout h3 {
font-weight: 500;
font-style: normal;
font-size: .625em;
text-transform: uppercase;
letter-spacing: .1em;
color: #997e11;
margin-bottom: 7px
}
.callout p {
font-size: .75em;
line-height: 1.5em
}
.callout-for-touch {
clear: both;
margin-bottom: 10px;
text-align: center;
color: #999;
background-color: #f7f7f7
}
.wf-active .callout h3 {
font-size: .75em
}
#main {
width: 100%;
margin: 0px
}
header {
zoom: 1;
position: relative;
margin-bottom: 30px
}
header:before, header:after {
content: "";
display: table
}
header:after {
clear: both
}
h1 {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.primary-nav {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%;
font-weight: 400;
font-style: normal;
margin-top: 20px;
font-size: .9375em;
line-height: 1.1em;
letter-spacing: .1em;
text-transform: uppercase
}
.primary-nav li {
display: inline;
float: left;
margin-top: 10px;
margin-right: 3px
}
.primary-nav li:last-child {
margin-right: 0px
}
.primary-nav li a,form {
border-radius: 3px;
display: block;
padding: 6px 8px 6px 8px;
text-decoration: none;
background-color: #fff;
border: 1px solid #fff;
color: #333;
-webkit-transition: color .25s, background-color .25s, border-color .25s;
transition: color .25s, background-color .25s, border-color .25s
}
.primary-nav li a.selected, .primary-nav li a:hover {
color: #1e90de;
background-color: #f6f5ea;
border-color: #bcb79c
}
body#index #nav-home a, body#collection #nav-store a, body#product #nav-store a,
body#product-tk #nav-store a, body#blog #nav-press a, body#article #nav-press a,
body.about #nav-about a, body.help #nav-help a {
color: #1e90de;
background-color: #f6f5ea;
border-color: #bcb79c
}
body#index #nav-home a:hover, body#collection #nav-store a:hover, body#product #nav-store a:hover,
body#product-tk #nav-store a:hover, body#blog #nav-press a:hover, body#article #nav-press a:hover,
body.about #nav-about a:hover, body.help #nav-help a:hover {
border-color: #997e11
}
body#cart #nav-cart {
color: #1e90de;
background-color: #f6f5ea;
border-color: #bcb79c
}
body#cart #nav-cart:hover {
border-color: #997e11
}
.wf-active .primary-nav {
font-size: 1.1em
}
.logo {
display: block;
text-indent: -9999px;
width: 244px;
height: 32px;
margin: 26px 0px 3px 0px
}
.logo-tagline {
font-size: .8em;
color: #82bc00;
font-style: italic
}
.shopping-cart {
position: absolute;
top: 24px;
right: 3.125%;
padding: 15px;
text-decoration: none;
-webkit-transition: border-color .25s;
transition: border-color .25s;
border: #fff solid 1px
}
.shopping-cart:hover {
border-color: #bcb79c
}
.shopping-cart-count {
display: inline;
float: left;
border-radius: 20px;
padding: 4px 4px 3px 4px;
min-width: 20px;
font-size: .825em;
text-align: center;
background-color: #c1bfb0;
color: #fff
}
.shopping-cart-icon {
display: inline;
float: left;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70);
opacity: .7;
width: 25px;
height: 20px;
margin-left: 4px
}
.is-full .shopping-cart-count {
background-color: #7db72f
}
.is-full .shopping-cart-icon {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
opacity: 1
}
aside {
display: none;
border-left: solid 1px #ccc;
padding-left: 20px
}
aside .subsection {
padding-top: 20px;
margin-top: 20px;
border-top-color: #ccc
}
aside #question-callout a {
padding-left: 19px;
background-position: 0px 2px
}
aside h4 {
margin-bottom: 10px
}
aside .subsection a {
text-decoration: none
}
aside .book-thumb {
margin: 0px
}
h2.title {
font-size: 1.875em
}
.content h2.title {
display: inline;
float: left;
width: 93.75%;
font-size: 1.875em;
margin-left: 3.125%;
margin-right: 3.125%
}
.wf-active h2.title {
font-size: 5em;
letter-spacing: 1px
}
.subsection h3, th {
color: #997e11
}
.content-with-sidebar {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
margin-left: 0px !important;
margin-right: 0px !important
}
.content-with-sidebar:before, .content-with-sidebar:after {
content: "";
display: table
}
.content-with-sidebar:after {
clear: both
}
.content-with-sidebar h2.title {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.subsection h3 {
font-weight: 500;
font-style: normal;
margin-bottom: 12px;
font-size: .75em;
line-height: 1.5em;
text-transform: uppercase;
letter-spacing: .1em
}
.wf-active .subsection h3 {
font-size: .9375em
}
.divider-top, .divider-top-thick {
padding-top: 30px;
margin-top: 30px;
border-top-style: solid;
border-top-width: 1px
}
.divider-top-thick {
border-top-width: 5px
}
.divider-bottom, .divider-bottom-thick {
padding-bottom: 30px;
margin-bottom: 30px;
border-bottom-style: solid;
border-bottom-width: 1px
}
.divider-bottom-thick {
border-bottom-width: 5px
}
.divider-top, .divider-bottom {
border-color: #ccc
}
.divider-top-thick, .divider-bottom-thick {
border-color: #f2f1e2
}
.byline-name {
font-weight: 500;
font-style: normal;
text-transform: uppercase;
letter-spacing: .1em;
text-decoration: none
}
.byline-prefix {
font-style: italic;
text-transform: none;
letter-spacing: 0px
}
.book-thumb {
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box;
display: inline;
float: left;
margin-right: 10px;
height: 154px;
width: 100px;
background-size: cover;
-webkit-transition: border-color .25s;
transition: border-color .25s;
border: solid 1px #eee
}
.gift-card-thumb {
display: inline;
float: left;
margin-top: 5px;
margin-right: 10px;
height: 100px;
width: 100px;
background-size: cover
}
.bundle-thumb {
width: 150px
}
.book-thumb:hover {
border-color: #ccc
}
.book-byline {
display: block;
margin: 4px 0px 20px 0px;
color: #998f52
}
.book-byline .byline-prefix {
font-size: .75em
}
.book-byline .byline-name {
font-weight: 500;
font-style: normal;
font-size: .675em;
text-transform: uppercase;
letter-spacing: .1em
}
.product-bundle-offer .book-byline {
margin-bottom: 10px
}
.wf-active .book-byline .byline-name {
font-size: .75em
}
.book-summary {
margin-top: 16px;
font-size: .825em;
line-height: 1.5em
}
.books {
margin-bottom: -30px
}
.books li {
margin-bottom: 30px
}
#collection .books li {
margin-bottom: 60px
}
.books a {
text-decoration: none
}
.feature {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%;
text-decoration: none;
box-shadow: inset 0px 0px 80px rgba(0, 0, 0, 0.05);
border-radius: 5px;
position: relative;
background: #f6f5ea;
min-height: 300px;
margin-bottom: 10px
}
.feature a {
text-decoration: none
}
.feature .byline-prefix {
font-size: .875em
}
.feature .byline-name {
font-size: .75em
}
.feature .foreword-text {
color: #333;
margin: 16px 0px;
font-size: .9375em;
line-height: 1.45em
}
.feature-edition {
color: #fff;
background: #c1bfb0;
padding: 10px 10px 5px 5px;
font-size: .75em;
font-family: titling-gothic-condensed, "Helvetica Neue", Helvetica,
Arial, sans-serif;
letter-spacing: 1px;
text-transform: uppercase;
position: absolute;
top: 0;
right: 0;
border-radius: 0 5px 0 3px
}
.feature-image img {
display: block;
width: 100%;
height: auto
}
.feature-big-cover {
border-radius: 5px;
-webkit-transition: background-position 1s, width 1s;
transition: background-position 1s, width 1s;
position: absolute;
left: 0px;
top: 0px;
width: 0px;
height: 100%;
z-index: 1;
background: transparent
url(//cdn.shopify.com/s/files/1/0051/7692/t/2/assets/aba-home-2-2e-feat.jpg?8276597854392778624)
no-repeat;
background-size: auto 327px;
background-position: -430px 0px
}
.feature-small-cover .book-thumb {
display: block;
float: none;
margin: 25px
}
.feature-info {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
position: relative;
margin-right: 0px !important;
padding: 25px
}
.feature-info:before, .feature-info:after {
content: "";
display: table
}
.feature-info:after {
clear: both
}
.feature-header {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
margin-bottom: 0px;
font-size: 1em
}
.feature-header:before, .feature-header:after {
content: "";
display: table
}
.feature-header:after {
clear: both
}
.feature-title {
display: block;
font-size: 2.5em;
font-style: normal;
text-decoration: none;
color: #82bc00
}
.feature-author {
display: block;
margin-bottom: 4px;
font-size: 1.5em;
text-transform: none;
color: #333
}
.feature-format-note {
margin: 13px 0px 26px 0px;
font-style: italic;
font-size: .8125em;
line-height: 1.5em;
color: #b1a55f
}
.feature-buy-button {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
position: absolute;
right: 10px;
bottom: -20px
}
.wf-active .feature-title {
font-weight: 400;
letter-spacing: 1px;
font-size: 5.5em
}
.press-room {
position: relative
}
.press-room .links {
position: absolute;
top: 25px;
right: 0px;
text-align: right
}
.press {
position: relative
}
.press p {
margin-bottom: 16px;
font-size: 1em;
line-height: 1.5em
}
.press ul, .press ol {
margin-left: 130px;
margin-bottom: 16px;
font-size: 1em;
line-height: 1.5em
}
.press ul {
list-style: disc
}
.press ol {
list-style: decimal
}
.press-thumb {
float: left;
margin: 5px 10px 10px 0
}
#blog .press-listing p {
margin-bottom: 16px;
font-size: 1em;
line-height: 1.5em
}
.press-listing p {
margin-bottom: 6px;
font-size: .825em;
line-height: 1.5em
}
.press-article .press-thumb {
width: 90px !important;
height: auto
}
.press-article h3 {
margin-bottom: 8px;
font-weight: 500;
font-style: normal;
font-size: .9375em;
line-height: 1.5em;
text-transform: uppercase;
letter-spacing: .1em
}
.press-date {
margin-bottom: 8px;
font-weight: 500;
font-style: normal;
font-size: .625em;
text-transform: uppercase;
letter-spacing: .1em;
color: #888
}
.press-article img {
width: 100%
}
.wf-active .press-date {
font-size: .75em
}
.press-more {
font-size: .75em;
line-height: 1.5em
}
#pagination {
margin-bottom: 10px;
font-size: .75em;
line-height: 1.5em
}
#pagination .current, #pagination a {
border-radius: 2px;
padding: 2px 6px;
margin: 0 2px
}
#pagination .current {
background: #997e11;
color: #fff
}
#pagination a {
background: #f6f5ea;
color: #997e11;
text-decoration: none
}
#pagination a:hover {
background: #997e11;
color: #fff
}
.links {
font-size: .75em;
line-height: 1.5em;
color: #888
}
.links li {
display: inline-block;
margin-right: 7px
}
.links li:before {
content: "\2022";
margin-right: 8px
}
.links li:first-child:before {
display: none
}
.links li:last-child {
margin-right: 0px
}
.links-rss {
padding-left: 18px;
background-position: 0px
}
.links-twitter {
padding-left: 19px
}
#product .title {
margin-bottom: 6px
}
#product .title .callout {
display: inline;
float: left;
padding: 4px 8px;
margin: 6px 0px 0px 0px;
color: #fff;
background-color: #7db72f
}
#product .title .second-edition {
background-color: #c1bfb0
}
#product .title .callout p {
font-style: normal;
text-transform: uppercase;
font-size: .4em;
letter-spacing: .1em
}
#product .other-books, #cart .other-books {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#product .other-books .books, #cart .other-books .books {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px
}
#product .other-books .books:before, #product .other-books .books:after,
#cart .other-books .books:before, #cart .other-books .books:after {
content: "";
display: table
}
#product .other-books .books:after, #cart .other-books .books:after {
clear: both
}
#product .other-books .books li, #cart .other-books .books li {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px
}
#product .other-books .books li:before, #product .other-books .books li:after,
#cart .other-books .books li:before, #cart .other-books .books li:after
{
content: "";
display: table
}
#product .other-books .books li:after, #cart .other-books .books li:after
{
clear: both
}
.wf-active #product .title .callout {
padding: 5px 10px 4px 10px;
margin: 14px 0px
}
.wf-active #product .title .callout p {
font-weight: 300;
font-size: .875rem
}
.wf-active .product-byline {
font-size: 100%
}
.wf-active .product-option-info h4 {
font-size: .875em;
line-height: 1.5em
}
.wf-active .product-option-note {
margin: 4px 0px 0px 4px
}
.wf-active .offer-message {
font-size: 1.5em;
letter-spacing: 1px
}
.product-title-text {
display: inline;
float: left;
margin-right: 18px
}
.product-byline {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%;
margin-bottom: 24px;
color: #998f52;
font-size: .75em
}
.product-byline .byline-prefix {
font-size: 1.125em
}
.product-byline .byline-name {
font-size: 1.25em;
line-height: 1.2em
}
.product-byline .byline-name i, .books .byline-name i {
font-size: .875em;
margin-right: .1em;
font-style: italic;
letter-spacing: 0;
font-weight: normal;
text-transform: none
}
.hero-image {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%;
height: auto;
margin-bottom: 30px
}
.product-options {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.gift-card-options {
margin-top: 50px;
margin-bottom: 30px
}
.product-options-list li {
border-radius: 5px;
position: relative;
background-color: #f6f5ea;
padding: 12px 10px 14px 10px
}
.product-options-list li:first-child {
padding-left: 10px
}
.product-options-list li:last-child {
border-right: none
}
.product-option-icon {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);
opacity: .5;
position: absolute;
top: 12px;
left: 10px;
height: 36px
}
.product-option-paperback .product-option-icon {
width: 36px
}
.product-option-paperback .product-option-info {
padding-left: 40px
}
.product-option-ebook .product-option-icon {
width: 36px
}
.product-option-ebook .product-option-info {
padding-left: 40px
}
.product-option-gift .product-option-icon {
width: 36px
}
.product-option-gift .product-option-info {
padding-left: 40px
}
.product-option-bundle .product-option-icon {
width: 64px
}
.product-option-bundle .product-option-info {
padding-left: 68px
}
.product-option-info h4 {
display: inline;
margin-bottom: 2px;
float: left;
font-style: normal;
font-weight: 500;
font-size: .75em;
line-height: 1.5em;
text-transform: uppercase
}
.product-option-note {
display: inline;
float: left;
position: relative;
margin: 2px 0px 0px 4px
}
.product-option-note:hover>.product-option-callout {
display: block
}
.product-option-question-icon {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=75);
opacity: .75;
position: absolute;
top: 0px;
left: 0px;
width: 12px;
height: 12px;
text-indent: -9999px
}
.product-option-question-icon:hover {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
opacity: 1
}
.product-option-callout p {
line-height: 1.5em;
color: #ccc
}
.product-option-callout {
position: absolute;
background: rgba(0, 0, 0, 0.8);
left: -84px;
bottom: 12px;
padding: 8px 12px;
z-index: 999;
display: none;
width: 180px
}
.product-option-callout:after {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none
}
.product-option-callout:after {
border-color: rgba(0, 0, 0, 0);
border-top-color: rgba(0, 0, 0, 0.8);
border-width: 8px;
left: 50%;
margin-left: -8px
}
.product-option-price {
display: block;
clear: both;
font-size: .6875em;
font-weight: bold;
color: #998f52
}
.product-out-of-stock {
background: #efeddc;
border-radius: 0 5px 5px 0;
bottom: 0;
margin: 0;
padding: 12px 10px 0;
position: absolute;
right: 0;
text-align: center;
top: 0;
width: auto;
color: #bcb79c
}
.product-out-of-stock strong {
display: block;
font-size: .875em;
font-weight: normal;
line-height: 1.5em;
letter-spacing: 1px;
margin-bottom: 2px;
text-transform: uppercase
}
.product-out-of-stock em {
display: block;
font-style: normal;
font-size: .6875em
}
.product-option-add-button {
position: absolute;
top: 18px;
right: 16px;
padding-left: 16px;
padding-right: 16px;
font-size: 1em
}
.product-content, .product-bundle-offer {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.product-bundle-offer a {
text-decoration: none
}
h3.offer-message {
margin: 20px 0 10px
}
.wf-active h3.offer-message {
font-size: 1.25em
}
.product-bundle-offer-books {
zoom: 1;
position: relative;
width: 100%;
width: 620px;
min-height: 130px;
padding-top: 20px
}
.product-bundle-offer-books:before, .product-bundle-offer-books:after {
content: "";
display: table
}
.product-bundle-offer-books:after {
clear: both
}
.product-bundle-offer-book {
display: inline;
float: left;
position: relative;
height: 123px
}
.ieold .product-bundle-offer-book {
width: 250px
}
.product-bundle-offer-book h4 {
font-size: 1em;
font-style: italic;
margin-bottom: 0px
}
.product-bundle-offer-book.left-book {
width: 80px
}
.ieold .product-bundle-offer-book.left-book {
padding-right: 110px
}
.left-book h4, .left-book .book-byline {
display: none
}
.product-bundle-offer-book.right-book {
width: 80px
}
.ieold .product-bundle-offer-book.right-book {
padding-left: 110px
}
.product-bundle-offer-book.right-book .product-bundle-offer-book-thumb {
left: 0px
}
.product-bundle-offer-book .book-byline {
margin-top: 0px
}
.product-bundle-offer-book-thumb {
position: absolute;
top: 0px;
width: 80px;
height: 123px;
margin: 0px
}
.ieold .product-bundle-offer-book-thumb {
width: 100px;
height: 154px
}
.product-bundle-offer-book-divider {
display: inline;
float: left;
background-position: center center;
width: 80px;
height: 123px
}
.offer {
margin-top: 20px;
border-left: none;
padding: 0px
}
.ieold .offer {
margin-top: 80px
}
.offer-message {
zoom: 1;
margin-top: 6px;
font-weight: 300;
font-style: normal;
line-height: 1.25em;
color: #7db72f;
font-size: 1.125em;
letter-spacing: 0em;
text-transform: uppercase;
text-align: center
}
.offer-message:before, .offer-message:after {
content: "";
display: table
}
.offer-message:after {
clear: both
}
.offer-form {
margin-top: 15px
}
.product-foreword {
display: inline;
float: left;
width: 100%
}
.product-desc-gift {
display: inline;
float: left;
width: 100%
}
.product-bundle-summary {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.product-foreword p, .product-desc-gift p, .product-bundle-summary p {
margin-bottom: 24px;
font-size: 1.25em;
line-height: 1.5em
}
.product-foreword p:last-child, .product-desc-gift p:last-child,
.product-bundle-summary p:last-child {
margin-bottom: 0px
}
.product-foreword-byline {
font-size: .75em
}
.product-contents, .product-tk-thumb, .product-gift-thumb {
display: inline;
float: left;
width: 100%;
border-color: #ccc
}
.product-contents h4 {
font-size: .9375em;
line-height: 1.5em
}
.product-contents .bulleted-list {
margin-bottom: 16px
}
.product-contents .bulleted-list:last-child {
margin-bottom: 0px
}
.product-additional-info h4, .product-contents h4 {
color: #666
}
.product-tk-thumb .book-thumb-tk {
width: 220px;
height: 339px;
display: block;
border: 1px solid #eee;
background-size: cover
}
.product-gift-thumb .gift-thumb {
width: 220px;
height: 131px;
display: block;
background-size: cover
}
.ieold .product-tk-thumb .book-thumb-tk {
width: 100px;
height: 154px
}
.product-reviews {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%;
padding-bottom: 0px
}
.product-reviews blockquote {
padding-left: 40px
}
.product-review-list li {
margin-bottom: 30px
}
.product-review-quote {
font-size: .9375em;
font-style: italic;
line-height: 1.5em;
margin-bottom: 15px
}
.product-review-byline {
font-size: .8125em;
text-indent: -1.1em
}
.product-review-byline .byline-title {
display: block;
margin: 6px 0px 0px 0px;
font-style: italic;
line-height: 1.25em;
color: #998f52;
text-indent: 0
}
.product-bundle-author-list {
zoom: 1;
margin-bottom: -30px
}
.product-bundle-author-list:before, .product-bundle-author-list:after {
content: "";
display: table
}
.product-bundle-author-list:after {
clear: both
}
.product-bundle-author-list li {
margin-bottom: 30px
}
.product-author-info, #product-tk .product-author-info {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.product-author-headshot {
margin-left: 0px !important;
margin-bottom: 20px;
height: auto;
display: inline;
float: left;
width: 30%;
margin-right: 5%
}
.product-author-bio p, .book-desc {
margin-bottom: 15px;
font-size: 1em;
line-height: 1.5em
}
.product-author-bio p:last-child {
margin-bottom: 0px
}
.product-bundle-authors-info {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.product-bundle-author-headshot {
margin-bottom: 20px;
display: inline;
float: left;
width: 30%;
margin-right: 5%
}
.product-bundle-author-bio p {
margin-bottom: 15px;
font-size: 1em;
line-height: 1.5em
}
.product-bundle-author-bio p:last-child {
margin-bottom: 0px
}
.product-additional-info {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%;
border-color: #ccc
}
.foreword-byline {
color: #998f52
}
.q {
display: inline;
float: left;
width: 200px;
margin: 0px 6px 0px 0px;
border-radius: 2px;
border: solid 1px #ccc;
padding: 5px 4px;
font-size: .8em;
color: #888
}
#q-search-form {
display: inline-block;
margin-bottom: 30px
}
#newsletter {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%;
padding: 20px 0px 16px 0px;
margin-bottom: 30px
}
#newsletter h3 {
margin-bottom: 6px
}
.newsletter-note {
margin-left: 0px !important
}
.newsletter-message {
font-size: .825em;
line-height: 1.5em
}
#mc_embed_signup {
margin-right: 0px !important
}
#mc-embedded-subscribe-form {
margin-top: 15px
}
#mc-embedded-subscribe, #q-search {
display: inline;
float: right;
margin-top: 0px
}
.mc-field-group {
display: inline;
float: right
}
#mce-EMAIL, .q {
display: inline;
float: left;
width: 200px;
margin: 0px 6px 0px 0px;
border-radius: 2px;
border: solid 1px #ccc;
padding: 5px 4px;
font-size: .8em;
color: #888
}
#mce-responses .response {
display: inline;
float: right;
border-radius: 2px;
width: 96%;
padding: 8px;
margin-top: 10px !important;
font-style: italic;
font-size: .75em;
line-height: 1.25em;
color: #fff
}
#mce-responses #mce-error-response {
background-color: #dd4b39
}
#mce-responses #mce-success-response {
background-color: #7db72f
}
#mce-responses a:link, #mce-responses a:visited {
color: #fff
}
#mce-responses a:hover, #mce-responses a:active {
opacity: .8
}
footer {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%;
padding-bottom: 50px;
color: #888
}
.footer-links {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
margin-left: 0px !important;
font-size: .6875em;
line-height: 2em
}
.footer-links:before, .footer-links:after {
content: "";
display: table
}
.footer-links:after {
clear: both
}
.footer-links li {
display: block
}
.footer-links li:before {
display: none
}
.footer-partner {
display: inline;
float: right;
margin-top: 5px
}
.footer-partner-arcustech {
clear: right;
margin-top: 20px
}
.footer-partner-byline {
display: inline;
float: left;
display: block;
margin-top: 2px;
font-size: .6875em
}
.footer-partner-logo {
-webkit-transition: opacity .25s;
transition: opacity .25s;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70);
opacity: .7;
display: inline;
float: left;
display: block;
width: 38px;
height: 38px;
margin: -10px 0px 0px 5px;
text-indent: -9999px
}
.footer-partner-logo:hover {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
opacity: 1
}
#home .other-books {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#home .other-books .books {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px
}
#home .other-books .books:before, #home .other-books .books:after {
content: "";
display: table
}
#home .other-books .books:after {
clear: both
}
#home .other-books .books li {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
margin-bottom: 30px
}
#home .other-books .books li:before, #home .other-books .books li:after
{
content: "";
display: table
}
#home .other-books .books li:after {
clear: both
}
#home .press-room {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#home .press-room .press {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
margin-bottom: -30px
}
#home .press-room .press:before, #home .press-room .press:after {
content: "";
display: table
}
#home .press-room .press:after {
clear: both
}
#home .press-room .press li {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
margin-bottom: 30px
}
#home .press-room .press li:before, #home .press-room .press li:after {
content: "";
display: table
}
#home .press-room .press li:after {
clear: both
}
#products {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#products .books {
margin-bottom: 30px
}
#products .books:last-child {
margin-bottom: 0px
}
#products .book-summary {
padding-left: 112px
}
#products .books .bundle {
width: 450px;
margin-left: 10px;
margin-right: 10px
}
#products .books .bundle .book-thumb {
width: 150px
}
#products .books .single {
width: 300px;
margin-left: 10px;
margin-right: 10px
}
#cart-main {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#press-room .press-list li, #press-room #pagination {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#press {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#help {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#help .callout {
position: relative
}
#help .callout p {
background-position: 0px 2px;
padding-left: 24px
}
#help .callout a {
font-weight: bold
}
#faqs {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#faqs h4 {
margin-bottom: 4px;
font-weight: bold;
font-size: 1em;
line-height: 1.25em
}
#faqs p {
margin-bottom: 1.5em;
font-size: 1em;
line-height: 1.5em
}
#about, #basic {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.about-people {
margin-top: 20px
}
.about-person-info {
max-height: 48px
}
.about-person-signature {
margin-bottom: 10px;
height: 72px
}
.about-person-avatar {
display: inline;
float: left;
margin-right: 10px;
width: 48px
}
.about-person-name {
display: block;
font-size: .875em;
line-height: 1.25em
}
.about-person-title {
font-size: .75em;
font-style: italic;
color: #998f52
}
.about-main-people, .about-additional-people {
zoom: 1;
margin-bottom: -30px
}
.about-main-people:before, .about-main-people:after,
.about-additional-people:before, .about-additional-people:after {
content: "";
display: table
}
.about-main-people:after, .about-additional-people:after {
clear: both
}
.about-main-people li, .about-additional-people li {
margin-bottom: 30px
}
.about-text p, .basic-text p {
margin-bottom: 16px;
font-size: 1.125em;
line-height: 1.5em
}
#cart-form {
margin-top: 30px
}
#cart-head #head-options span {
display: none
}
#cart-table {
width: 100%
}
#cart-table th, #cart-table tr {
padding: 10px 0px
}
#cart-table td {
padding: 0px 30px 0px 0px
}
#cart-table td.cart-item-options {
padding: 0px
}
#cart-body th {
padding-right: 30px
}
#cart-body th, #cart-body td {
font-size: .75em;
line-height: 1.5em
}
.cart-item-title {
font-weight: bold
}
.cart-item-quantity {
text-align: right
}
.cart-item-remove-option {
display: block;
width: 14px;
height: 16px;
text-indent: -9999px
}
.cart-item-remove-option span {
display: none
}
.cart-total, .cart-options {
text-align: right
}
.cart-total-label {
color: #666
}
.cart-total-price {
font-weight: bold;
color: #997e11
}
.cart-checkout-button {
margin-left: 5px
}
button[disabled], button[disabled]:hover, button[disabled]:focus {
cursor: progress;
opacity: .5
}
button[disabled]:before {
background:
url(//cdn.shopify.com/s/files/1/0051/7692/t/2/assets/loading_12x12_green.gif?8276597854392778624)
no-repeat 50% 50%;
content: '';
display: inline-block;
height: 12px;
margin-right: 6px;
position: relative;
width: 12px
}
button[disabled].is-disabled, button[disabled].is-disabled:hover, button[disabled].is-disabled:focus
{
cursor: not-allowed
}
button[disabled].is-disabled:before {
content: none
}
.cart-update-button {
display: none
}
.cart-update-button.is-next-action {
display: inline-block
}
#cart .other-books h3 {
font-size: 1.5em;
letter-spacing: 1px;
font-weight: 300;
margin-bottom: 20px
}
#cart .books li {
margin-bottom: 40px
}
.savings {
background: #f6f5ea;
border-radius: 4px;
padding: 15px;
min-height: 154px;
min-width: 300px
}
.savings-table {
float: left;
width: 125px;
margin-right: 10px
}
.savings-table tr {
border-top: 1px #ccc solid
}
.savings-table .savings-qty-hed {
padding: 0 14px 4px 8px
}
.savings-table .savings-discount-hed {
padding: 0 8px 4px 8px
}
.savings-qty {
padding: 8px 14px 8px 8px
}
.savings-discount {
padding: 8px
}
.savings-table thead tr {
border-top: none
}
.savings-qty, .savings-discount {
color: #666;
font-size: 16px;
letter-spacing: 1px
}
.savings-discount span {
color: #999;
letter-spacing: 0
}
.savings-msg {
float: right;
width: 110px;
height: 110px;
text-align: center;
text-transform: uppercase;
letter-spacing: 1px;
color: #fff;
margin-top: 5px;
margin-right: 8px
}
.savings-msg span {
width: 88px;
margin: 23px auto 0;
display: block;
line-height: 1.3em
}
@media only screen and (max-width: 480px) {
.logo {
width: 100%;
height: 24px;
background-size: contain
}
.logo-tagline {
font-size: .6em
}
#mce-EMAIL, .q {
width: 100%
}
.mc-field-group {
float: none;
display: block
}
#mc-embedded-subscribe, #q-search {
margin-top: 8px
}
#nav-home {
display: none
}
aside .book-thumb {
margin: 0px;
float: none
}
#cart-table table, #cart-table thead, #cart-table tbody, #cart-table tfoot,
caption, #cart-table th, #cart-table td, #cart-table tr {
display: block;
-webkit-text-size-adjust: none
}
#cart-table thead {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px
}
#cart-table tr {
overflow: hidden
}
#cart-table td {
text-align: right;
margin: 0px 0px 4px 0px;
padding: 0px
}
#cart-table td:nth-child(2):before {
content: "Price: ";
font-weight: bold
}
#cart-table td:nth-child(3):before {
content: "Qty: ";
font-weight: bold
}
#cart-table td:nth-child(4):before {
content: "Total: ";
font-weight: bold
}
#cart-body tr {
position: relative;
padding: 0px 0px 20px 0px
}
#cart-body th {
position: absolute;
top: 0px;
left: 0px;
width: 70%;
padding: 0px
}
.cart-item-remove-option {
display: inline;
float: right
}
.wf-active h2.title {
font-size: 3em
}
.wf-active .product-byline .byline-prefix, .wf-active .product-byline .byline-name,
.wf-active .product-byline .byline-name i {
font-size: 1em
}
.product-option-icon {
display: none
}
.product-option-question-icon {
display: none
}
.product-option-paperback .product-option-info, .product-option-ebook .product-option-info,
.product-option-gift .product-option-info, .product-option-bundle .product-option-info
{
padding-left: 0px
}
.offer-form {
zoom: 1
}
.offer-form:before, .offer-form:after {
content: "";
display: table
}
.offer-form:after {
clear: both
}
.offer-dropdown {
width: 100%
}
.offer-add-button {
float: right;
margin-top: 10px
}
#home .feature-small-cover {
display: block;
width: 100%
}
.feature-small-cover .book-thumb {
margin: 25px auto 0
}
#home .feature-header {
text-align: center
}
#home .foreword-byline {
text-align: center
}
#home .press-room .links li:before {
display: none
}
#home .press-room .links-rss {
display: none
}
#products .books .single, #products .books .bundle {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
margin-bottom: 30px
}
#products .books li:before, #products .books li:after {
content: "";
display: table
}
#products .books li:after {
clear: both
}
#products .books li:last-child {
margin-bottom: 0px
}
.about-people li {
display: inline;
float: left;
width: 93.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.footer-links {
text-align: center
}
.footer-partner {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
margin-top: 24px;
float: none;
padding-left: 33%
}
.footer-partner:before, .footer-partner:after {
content: "";
display: table
}
.footer-partner:after {
clear: both
}
}
@media only screen and (max-width: 768px) {
.product-options-list {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
float: none
}
.product-options-list:before, .product-options-list:after {
content: "";
display: table
}
.product-options-list:after {
clear: both
}
.product-option-question-icon {
display: none
}
.product-options-list li {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
margin-right: 0px;
margin-bottom: 10px;
float: none
}
.product-options-list li:before, .product-options-list li:after {
content: "";
display: table
}
.product-options-list li:after {
clear: both
}
.product-options-list li button {
border-radius: 0 5px 5px 0;
position: absolute;
top: 0px !important;
right: 0px !important;
height: 100% !important;
margin: 0px !important;
padding: 17px !important;
font-size: 1.25em !important;
border: none
}
.product-bundle-offer-books {
width: 100%;
text-align: center
}
.product-foreword p, .product-desc-gift p, .product-bundle-summary p {
margin-bottom: 16px;
font-size: 1.125em;
line-height: 1.5em
}
.product-contents, .product-tk-thumb, .product-gift-thumb {
padding-top: 30px;
margin-top: 30px;
border-top-style: solid;
border-top-width: 1px
}
.product-additional-info {
padding-top: 30px;
margin-top: 30px;
border-top-style: solid;
border-top-width: 1px
}
.center-books {
width: 240px;
height: 123px;
margin: 0 auto 20px
}
.product-bundle-offer-book {
min-height: 123px
}
.product-bundle-offer-book.left-book {
padding-right: 80px
}
.right-book-info {
display: block;
clear: both;
padding: 0 30px
}
}
@media only screen and (min-width: 481px) and (max-width: 768px) {
.newsletter-note {
zoom: 1;
display: block;
width: auto;
margin-right: 0px;
float: none
}
.newsletter-note:before, .newsletter-note:after {
content: "";
display: table
}
.newsletter-note:after {
clear: both
}
#mc_embed_signup {
zoom: 1;
display: block;
width: auto;
margin-left: 0px;
float: none
}
#mc_embed_signup:before, #mc_embed_signup:after {
content: "";
display: table
}
#mc_embed_signup:after {
clear: both
}
#mc-embedded-subscribe-form {
margin-top: 15px
}
#mce-responses .response {
width: 100%
}
#product .other-books .books, #cart .other-books .books {
width: 106.25%;
margin-left: -3.125%;
margin-right: -3.125%;
zoom: 1
}
#product .other-books .books li, #cart .other-books .books li {
display: inline;
float: left;
width: 43.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#product .other-books .books li:last-child {
display: none
}
.product-options-list li {
position: relative
}
.product-option-question-icon {
display: none
}
.offer-form {
display: inline;
float: left;
position: relative;
left: 50%
}
.offer-dropdown, .offer-add-button {
display: inline;
float: left;
position: relative;
right: 50%
}
.product-author-headshot {
display: inline;
float: left;
width: 18.75%;
margin-left: 3.125%;
margin-right: 0px
}
.product-author-bio {
display: inline;
float: left;
width: 68.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.product-bundle-author-list li {
zoom: 1
}
.product-bundle-author-list li:before, .product-bundle-author-list li:after
{
content: "";
display: table
}
.product-bundle-author-list li:after {
clear: both
}
.product-bundle-author-headshot {
display: inline;
float: left;
width: 18.75%;
margin-left: 0px;
margin-right: 0px
}
.product-bundle-author-bio {
display: inline;
float: left;
width: 68.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#home .feature-small-cover {
display: inline;
position: absolute
}
#home .feature-info {
margin-left: 125px
}
#home .feature-header {
display: inline
}
#home .other-books .books {
width: 106.25%;
margin-left: -3.125%;
margin-right: -3.125%
}
#home .other-books .books li {
display: inline;
float: left;
width: 43.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#products .books {
display: block;
width: 106.25%;
margin-left: -3.125%;
margin-right: -3.125%;
zoom: 1
}
#products .books:before, #products .books:after {
content: "";
display: table
}
#products .books:after {
clear: both
}
#products .books .single {
display: inline;
float: left;
width: 43.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
#products .books .bundle {
display: inline;
float: left;
width: 87.5%;
margin-left: 3.125%;
margin-right: 3.125%
}
.about-people li {
display: inline;
float: left;
width: 43.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
.footer-links {
display: inline;
float: left;
width: 68.75%;
margin-left: 3.125%;
margin-right: 3.125%
}
}
@media only screen and (min-width: 769px) and (max-width: 1024px) {
#newsletter {
width: 620px
}
.newsletter-note {
width: 300px
}
.primary-nav {
width: 620px;
margin-left: 10px;
margin-right: 10px
}
#main {
width: 640px;
margin: 0px auto
}
#cart-table table, #cart-table thead, #cart-table tbody, #cart-table tfoot,
caption, #cart-table th, #cart-table td, #cart-table tr {
display: block;
-webkit-text-size-adjust: none
}
#cart-table thead {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px
}
#cart-table tr {
overflow: hidden
}
#cart-table td {
text-align: right;
margin: 0px 0px 4px 0px;
padding: 0px
}
#cart-table td:nth-child(2):before {
content: "Price: ";
font-weight: bold
}
#cart-table td:nth-child(3):before {
content: "Qty: ";
font-weight: bold
}
#cart-table td:nth-child(4):before {
content: "Total: ";
font-weight: bold
}
#cart-body tr {
position: relative;
padding: 0px 0px 20px 0px
}
#cart-body th {
position: absolute;
top: 0px;
left: 0px;
width: 280px;
padding: 0px
}
.cart-item-remove-option {
display: inline;
float: right
}
#product .other-books, #cart .other-books {
width: 620px
}
#product .other-books .books {
width: 660px
}
#product .other-books .books li:last-child {
display: none
}
.product-byline {
width: 620px;
font-size: .875em
}
.hero-image {
width: 620px
}
.product-options {
width: 620px
}
.product-options-list {
width: 660px
}
.product-options-list li {
margin-bottom: 20px
}
.product-content, .product-bundle-offer {
width: 620px
}
.product-bundle-offer-book {
width: 260px
}
.center-books {
margin-left: 60px
}
.offer-form {
display: inline;
float: left;
position: relative;
left: 50%
}
.offer-dropdown, .offer-add-button {
display: inline;
float: left;
position: relative;
right: 50%
}
.product-foreword, .product-bundle-summary {
width: 380px
}
.product-desc-gift {
width: 380px
}
.product-foreword-byline {
font-size: .875em
}
.product-contents, .product-tk-thumb, .product-gift-thumb {
width: 220px
}
.product-reviews {
width: 620px
}
.product-review-list {
width: 660px
}
.product-review-list-2 li, .product-review-list-3 li,
.product-review-list-4 li {
width: 300px
}
.product-bundle-author-list li {
zoom: 1
}
.product-bundle-author-list li:before, .product-bundle-author-list li:after
{
content: "";
display: table
}
.product-bundle-author-list li:after {
clear: both
}
.product-bundle-authors-info {
width: 620px
}
.product-bundle-author-bio {
width: 460px
}
.product-additional-info {
width: 620px;
padding-top: 30px;
margin-top: 30px;
border-top-style: solid;
border-top-width: 1px
}
#home .feature {
width: 620px
}
#home .feature-small-cover {
display: inline;
position: absolute
}
#home .feature-info {
margin-left: 125px
}
#home .feature-header {
display: inline
}
#home .other-books {
width: 620px
}
#home .other-books .books {
width: 660px
}
#home .press-room {
width: 620px
}
#home .press-room .press {
width: 660px
}
#home .press-room .press li {
width: 300px
}
.content h2.title {
width: 620px
}
.content-with-sidebar, .content-with-sidebar h2.title {
width: 380px
}
#products {
width: 620px
}
#products .books .bundle {
width: 620px
}
#cart-main, #cart .other-books {
width: 380px
}
#press-room .press-list li, #press-room #pagination {
width: 380px
}
#press {
width: 380px
}
#about, #basic {
width: 380px
}
.about-people li {
width: 180px
}
#help, #faqs {
width: 380px
}
footer {
width: 620px
}
.footer-links {
width: 380px
}
.footer-links li:nth-child(3):before {
display: none
}
}
@media only screen and (min-width: 769px) {
.newsletter-note {
display: inline;
float: left
}
#mc_embed_signup {
display: inline;
float: left;
width: 300px
}
#mc-embedded-subscribe-form {
margin-top: 23px
}
h1 {
width: 244px
}
.shopping-cart {
right: 10px
}
.callout-for-touch {
display: none
}
.right-book-info {
padding-left: 320px
}
.product-option-add-button {
top: 16px;
padding: 6px;
font-size: .625em
}
#product .other-books .books li {
display: inline;
float: left;
width: 300px
}
#cart .other-books .books li {
display: inline;
float: left;
width: 380px
}
.product-options-list {
display: block;
zoom: 1
}
.product-options-list:before, .product-options-list:after {
content: "";
display: table
}
.product-options-list:after {
clear: both
}
.product-options-list li {
display: inline;
float: left;
width: 300px
}
.product-bundle-contents .product-contents:not (:first-child ){
padding-top: 30px;
margin-top: 30px;
border-top-style: solid;
border-top-width: 1px
}
.product-review-list {
display: block;
zoom: 1
}
.product-review-list:before, .product-review-list:after {
content: "";
display: table
}
.product-review-list:after {
clear: both
}
.product-review-list-2 li, .product-review-list-3 li,
.product-review-list-4 li {
display: inline;
float: left
}
.product-author-info, #product-tk .product-author-info {
width: 620px
}
.product-author-headshot {
display: inline;
float: left;
width: 140px
}
.product-author-bio {
display: inline;
float: left;
width: 460px;
margin-left: 10px;
margin-right: 0px
}
.product-bundle-author-headshot {
display: inline;
float: left;
width: 140px;
margin-right: 10px;
margin-left: 0px
}
.product-bundle-author-bio {
display: inline;
float: left;
margin-right: -1px;
margin-left: 10px
}
#home .feature {
display: inline;
float: left
}
#home .other-books .books li {
display: inline;
float: left;
width: 300px
}
#home .press-room .press li {
display: inline;
float: left
}
.content-with-sidebar {
display: inline;
float: left
}
aside {
display: inline;
width: 220px;
float: right
}
aside .book-summary {
clear: both;
padding-top: 10px
}
#products .books {
display: block;
zoom: 1
}
#products .books:before, #products .books:after {
content: "";
display: table
}
#products .books:after {
clear: both
}
#products .books li {
display: inline;
float: left
}
.about-people li {
display: inline;
float: left
}
.about-text p, .basic-text p {
font-size: 1em
}
.footer-links {
display: inline;
float: left
}
.footer-links li, .footer-links li:before {
display: inline
}
#newsletter, .newsletter-note, #mc_embed_signup, h1, #product .other-books,
#product .other-books .books li, #cart .other-books, #cart .other-books .books li,
.product-byline, .hero-image, .product-options, .product-options-list li,
.product-bundle-offer, .product-content, .product-bundle-summary,
.product-reviews, .product-review-list-2 li, .product-review-list-3 li,
.product-review-list-4 li, .product-author-info, #product-tk .product-author-info,
.product-author-headshot, .product-bundle-authors-info,
.product-additional-info, #home .feature, #home .other-books, #home .other-books .books li,
#home .press-room, #home .press-room .press li, .content h2.title,
.content-with-sidebar, .content-with-sidebar h2.title, aside,
#products, #products .books li, #cart-main, #press-room .press-list li,
#press-room #pagination, #press, #about, #basic, .about-people li,
#help, #faqs, footer, .footer-links {
margin-left: 10px;
margin-right: 10px
}
.product-foreword, .product-desc-gift {
margin-left: 0;
margin-right: 10px
}
.product-contents, .product-tk-thumb, .product-gift-thumb {
margin-left: 10px;
margin-right: 0
}
#product .other-books .books, #cart .other-books .books,
.product-options-list, .product-review-list, #home .other-books .books,
#home .press-room .press, #products .books {
margin-left: -10px;
margin-right: -10px
}
}
@media only screen and (min-width: 1025px) {
#newsletter {
width: 940px
}
.newsletter-note {
width: 620px
}
.primary-nav {
width: 460px;
margin-left: 34px;
margin-right: 10px;
margin-top: 0px
}
.primary-nav li {
margin-top: 0px;
margin-right: 10px
}
.primary-nav li a,form {
border-radius: 0px;
padding: 34px 8px 3px 8px;
border-width: 0 0 1px
}
#main {
width: 960px;
margin: 0px auto
}
#product .other-books {
width: 940px
}
#cart .other-books {
width: 700px
}
#product .other-books .books {
width: 980px
}
#cart .other-books .books {
width: 720px
}
#cart .other-books .books li {
width: 340px
}
.product-byline {
width: 940px
}
.hero-image {
width: 940px
}
.product-options {
width: 940px
}
h2.offer-message {
width: 620px
}
h3.offer-message {
width: 300px;
float: right;
margin-top: -26px
}
.right-book-info {
padding-left: 255px
}
.product-content, .product-bundle-offer {
width: 940px
}
.product-bundle-offer-books {
display: inline;
float: left
}
.offer {
display: inline;
float: right;
border-left: solid 5px #eee;
padding: 0px 0px 15px 30px;
margin-top: 0px
}
.offer-message {
text-align: left
}
.offer-form {
margin-top: 10px
}
.product-foreword, .product-bundle-summary {
width: 620px
}
.product-desc-gift {
width: 500px
}
.product-foreword p, .product-desc-gift p, .product-bundle-summary p {
font-size: 1.5em
}
.product-foreword-byline {
font-size: 1em
}
#product-tk .product-author-info {
width: 940px;
border-right: none
}
.product-contents, .product-tk-thumb {
width: 300px
}
.product-gift-thumb {
width: 420px
}
.product-tk-thumb .book-thumb-tk {
width: 300px;
height: 463px
}
.product-gift-thumb .gift-thumb {
width: 420px;
height: 250px
}
.ieold .product-tk-thumb .book-thumb-tk {
width: 100px;
height: 154px
}
.product-reviews {
width: 940px
}
.product-review-list {
width: 980px
}
.product-review-list-2 li {
width: 460px
}
.product-review-list-3 li {
width: 300px
}
.product-review-list-4 li {
width: 220px
}
.product-author-info {
border-right: solid 1px #ccc
}
.product-author-bio {
padding-right: 20px;
margin-right: -1px
}
.product-bundle-authors-info {
width: 940px
}
.product-bundle-author-list {
display: block;
width: 980px;
margin-left: -10px;
margin-right: -10px
}
.product-bundle-author-list li {
display: inline;
float: left;
width: 460px;
margin-left: 10px;
margin-right: 10px
}
.product-bundle-author-bio {
width: 300px;
margin-right: 0px;
margin-bottom: 0px;
padding-right: 20px
}
.product-additional-info {
width: 300px
}
.product-bundle-author-list-library {
width: 620px
}
.product-bundle-author-list-library li {
width: 620px
}
.product-bundle-author-list-library .product-bundle-author-bio {
width: 460px
}
.product-bundle-offer .product-options-list li {
background-color: transparent;
border-radius: 0;
margin-left: 0
}
.product-bundle-offer .product-options-list {
width: 300px;
float: right;
margin-left: 0;
margin-right: 0;
background-color: #f6f5ea;
border-radius: 5px
}
#home .subsection {
border-top-width: 5px
}
#home .feature {
width: 940px
}
#home .feature-big-cover {
width: 430px;
background-position: 0px 0px
}
.feature-author {
padding-top: 25px
}
#home .feature-small-cover {
display: none
}
#home .feature-info {
display: inline;
width: 480px;
margin-left: 10px;
margin-right: 10px;
float: right;
padding: 0px 25px 0px 0px
}
#home .other-books {
width: 940px
}
#home .other-books .books {
width: 980px
}
#home .press-room {
width: 940px
}
#home .press-room .press {
width: 980px
}
#home .press-room .press li {
width: 460px
}
.content h2.title {
width: 940px
}
.content-with-sidebar, .content-with-sidebar h2.title {
width: 700px
}
#products {
width: 940px
}
#products .books {
width: 980px
}
#products .books li {
width: 300px
}
#cart-main {
width: 700px
}
.savings {
margin-right: 50px !important;
width: 300px !important
}
#press-room .press-list li, #press-room #pagination {
width: 700px
}
#press {
width: 700px
}
#about, #basic {
width: 700px
}
.about-people {
margin-left: -10px !important;
margin-right: -10px !important
}
.about-people li {
width: 220px
}
#help, #faqs {
width: 700px
}
footer {
width: 940px
}
.footer-links {
width: 580px;
line-height: 1.85em
}
.footer-partner {
margin-top: 4px
}
.footer-partner-arcustech {
margin-right: 40px;
clear: none
}
.press-title {
margin-left: 100px
}
.press-thumb {
position: absolute;
left: 0px;
top: 0px;
margin: 0;
height: auto
}
.press-article h3, .press-date, .press p {
margin-left: 100px
}
}
#announcement {
width: 100%;
background: #291700;
padding: .8em 0 .8em;
font-size: .9em;
color: #fff;
text-align: center;
border-bottom: solid 5px #82bc00
}
#announcement a {
color: #fff
}
#announcement a:hover {
opacity: .7
}
@media only screen and (max-width: 480px) {
#announcement {
font-size: .75em
}
}
.library-collection .hero-image {
margin-left: 0;
margin-right: 0;
width: 100%
}
.library-bar {
float: right;
width: 100%;
background: #f6f5ea;
box-shadow: inset 0px 0px 80px rgba(0, 0, 0, 0.05);
border-radius: 5px;
margin-top: -46px;
padding: 12px 10px 10px;
margin-bottom: 20px;
position: relative
}
.library-collection:before, .library-collection:after {
content: "";
display: table
}
.library-collection:after {
clear: both
}
.subsection .library-bar h3 {
font-weight: 400;
letter-spacing: 1px;
line-height: 1;
color: #333;
margin-bottom: 10px;
margin-right: 0;
font-size: 1em
}
.subsection .library-bar p {
margin-bottom: 45px;
color: #333;
font-size: .9375em;
line-height: 1.45em;
margin-right: 0
}
@media only screen and (min-width: 480px) {
.library-bar {
margin-bottom: 0
}
.subsection .library-bar h3 {
font-size: 1.2em;
margin-right: 150px
}
.subsection .library-bar p {
margin-bottom: 10px;
margin-right: 150px
}
.library-bar .feature-buy-button {
bottom: initial;
top: 10px
}
}
@media only screen and (min-width: 1024px) {
.library-collection .hero-image {
width: 620px
}
.library-bar {
position: relative;
margin-top: 5px;
padding: 20px;
width: 300px;
min-height: 258px
}
.subsection .library-bar h3 {
font-size: 1.8em;
line-height: 1;
padding-top: 0;
float: left;
margin-right: 0;
margin-bottom: 20px
}
.subsection .library-bar p {
margin-right: 0
}
.library-bar .feature-buy-button {
bottom: 14px;
top: initial
}
}
@media only screen and (max-width: 1024px) {
.press ul, .press ol {
margin-left: 30px
}
}
.button {
background-color: #7db72f;
background-image: -webkit-linear-gradient(#7db72f, #4e7d0e);
background-image: linear-gradient(#7db72f, #4e7d0e);
border: solid 1px #64991e;
border-radius: 3px;
color: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
cursor: pointer;
font-size: .625em;
font-style: normal;
font-weight: 500;
letter-spacing: .1em;
outline: none;
margin: 0px;
padding: 6px;
text-align: center;
text-decoration: none;
text-transform: uppercase;
vertical-align: baseline;
zoom: 1
}
.button:hover {
color: #fff;
background: #538018
}
.wf-active .button {
padding: 5px 8px;
font-size: .75em
}
.button--large {
border: solid 2px #fff;
display: block;
font-size: 1.5em;
letter-spacing: .05em;
padding: 8px 10px 5px
}
.wf-active .button--large {
font-weight: 400;
font-size: 3em;
padding: 8px 10px 5px
}
.button--alt {
background-color: #fbfbf6;
background-image: -webkit-linear-gradient(#fbfbf6, #e8e5d4);
background-image: linear-gradient(#fbfbf6, #e8e5d4);
border: solid 1px #d1cdba;
color: #1e90de
}
.button--alt:hover {
color: #1e90de;
background: #e8e5d4
}
.alert {
background-color: #f7f7f7;
color: #666;
margin-bottom: 10px
}
.alert>* {
display: inline
}
.alert__title {
color: #333;
font-size: .85em;
font-style: normal;
font-weight: 500;
line-height: 1.5;
margin-right: 5px;
text-transform: uppercase
}
.alert__title--block {
display: block
}
.icon-audiobook {
display: inline-block;
height: 19px;
margin-right: 5px;
margin-bottom: -2px;
width: 20px
}
#product .alert {
display: inline;
float: left;
margin-right: 3.125%;
margin-left: 3.125%;
text-align: center;
width: 93.75%
}
@media only screen and (min-width: 769px) {
#product .alert {
margin-right: 10px;
margin-left: 10px;
width: 620px
}
}
@media only screen and (min-width: 1025px) {
#product .alert {
width: 940px
}
}
.product-options+.alert {
margin-top: 10px;
margin-bottom: 0
}
.divider-bottom+.alert {
margin-top: 0;
margin-bottom: 10px
}
.alert:last-child {
margin-bottom: 0
}
#product .product-content .alert {
margin-right: 0;
margin-left: 0;
width: 100%
}
#product .book .alert {
margin-bottom: 0
}
#cart-main .alert {
margin-top: 10px
}
.alert--info {
background-color: #cae6fa;
color: #333
}
.alert--warning {
background-color: #ffffa5;
color: #8b730e
}
.alert--warning .alert__title {
color: #8b730e
}
.alert--success {
background-color: #7db72f
}
.alert--error {
background-color: #dd4b39
}
.alert--success, .alert--error {
color: #fff
}
.alert--success a, .alert--error a {
color: inherit;
-webkit-transition: all .25s;
transition: all .25s
}
.alert--success a:hover, .alert--success a:focus, .alert--error a:hover,
.alert--error a:focus {
opacity: .75
}
.alert--success .alert__title, .alert--error .alert__title {
color: #fff
}
.alert--em {
font-style: italic
}
.alert--em em {
font-style: normal
}
.alert--block>* {
display: block
}
.alert--small {
padding: 5px 8px 8px
}
.alert--large {
padding: 10px
}
.alert--large p {
font-size: .85em
}
.alert--left {
text-align: left
}
.book-image{
width: 100%
}
#product .extra, #collection .extra {
margin-bottom: 30px
}
.emptyInput{
color: #babdb6;
}
/static/estilos/index.css
Ejemplo 2
/static/images/Books.png
/static/images/sign.ico
Aspectos de Seguridad
Estándares Soportados
- OpenID
- HTTP BASIC
- HTTP Digest
- LDAP
- Form-based authentication
- Java Authentication and Authorization Service (JAAS)
- ...
Valores por Defecto
- Spring-boot-starter-security por defecto maneja la autenticación en memoria y crea un usuario “user”, con una contraseña aleatoria y role USER.
- Para fijar una contraseña para este usuario podemos usar la propiedad security.user.password.
- El mecanismo de autenticación por defecto es HTTP Basic.
Configuración
- La configuración se realiza por medio de la clase WebSecurityConfigurerAdapter
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/estilos/**").permitAll()
.antMatchers("/images/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.httpBasic()
.and()
.logout()
.permitAll();
}
}
Usuarios desde la Base de Datos
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/estilos/**").permitAll()
.antMatchers("/images/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.httpBasic()
.and()
.logout()
.permitAll();
//En la base de datos los roles deben almacenarse como ROLE_ADMIN
//.antMatchers("/admin/**").hasRole("ADMIN")
//.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
}
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select username,password, enabled from users where username=?")
.authoritiesByUsernameQuery(
"select username, authority from authorities where username=?");
}
}
Usuarios desde la Base de Datos
package co.edu.unicomfacauca.tienda.model;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
@Entity
@Data
public class Users {
@Id
private String username;
private String password;
private Boolean enabled;
@OneToMany(cascade=CascadeType.ALL)
private List<Authorities> authorities;
}
package co.edu.unicomfacauca.tienda.repository;
import org.springframework.data.repository.CrudRepository;
public interface UsersRepository extends CrudRepository<Users, Long> {
}
Usuarios desde la Base de Datos
package com.example.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@Entity
@Data
public class Authorities {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne
@JoinColumn(name="username")
private Users users;
private String authority;
}
package com.example.persistency;
import org.springframework.data.repository.CrudRepository;
public interface AuthoritiesRepository extends CrudRepository<Authorities, Long> {
}
Login Page
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<!-- En caso de error o logout se envía como parámetro un booleano -->
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="@{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<!-- csf es incluido automáticamente e impide que un usuario (casi siempre malvado) repita la petición -->
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>
Internacionalización
Internacionalización con Spring Framework
- Spring implementa una clase llamada MessageSource la cual carga los mensajes definidos en diferentes idiomas
- Los mensajes deben estar definidos en archivos .properties que definan la localidad. Ej: errors_en.properties, errors_es_CO.properties.
- Usando spring-boot los mensajes se buscan en los archivos llamados messages, concatenados con la localidad.
resources/messages_es.properties
ready = listo
usersLoaded = Tabla usuarios inicializada
buy = comprar
resources/messages.properties
ready = ready
usersLoaded = Users table initialized
buy = buy
Ejemplos
String getMessage(String code, Object[] args, String default, Locale loc)
String message = resources.getMessage("message", null, "Default", null);
# in exceptions_en_GB.properties
argument.required=The '{0}' argument is required.
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
Ejemplos
@RequestMapping("/numLibros")
@ResponseBody
public String numeroDeLibros(Locale locale)
{
long numLibros=librosRepository.count();
return messages.getMessage("numLibros", new Object [] {""+numLibros}, "my null", locale );
}
@Configuration
public class WebConfigurer extends WebMvcConfigurerAdapter {
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.forLanguageTag("es-co"));
return slr;
}
//http://localhost:8080/home?lang=en
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
REST en Spring
Generación de Recursos
- @ResponseBody: Indica que la respuesta no es una vista sino un recurso serializado
- @RequestBody: Indica que se recibe en el cuerpo HTTP un recurso serializado
- @RestController: Evita que se tenga que anotar cada método del controlador con @ResponseBody
Generación de Recursos
package com.example.control;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.model.Libro;
import com.example.persistency.LibroRepository;
@RestController
public class ControladorRestLibros {
@Autowired
private LibroRepository libroRepository;
@RequestMapping(value="/rest/libros",method=RequestMethod.GET, produces="application/json")
public List<Libro> getLibros()
{
return (List<Libro>) libroRepository.findAll();
}
@RequestMapping(value="/rest/libro/{id}",method=RequestMethod.DELETE)
public List<Libro> deleteLibros(@PathVariable (value="id") Long id)
{
Libro libro=new Libro();
libro.setId(id);
libroRepository.delete(libro);
return (List<Libro>) libroRepository.findAll();
}
@RequestMapping(value="/rest/libro/{id}",method=RequestMethod.GET)
public Libro getLibro(@PathVariable (value="id") Long id)
{
return libroRepository.findOne(id);
}
@RequestMapping(value="/rest/libro",method=RequestMethod.PUT)
public Libro crearLibro(@RequestBody Libro libro)
{
return libroRepository.save(libro);
}
}
Consumo de Recursos
Para el consumo de recuro se hace uso de la clase RestTemplate
Operaciones RestTemplate
Método | Descripción |
---|---|
delete() | Ejecuta una solicitud DELETE sobre un recurso |
exchange() | Ejecuta el método HTTP especificado sobre la URL. Devuelve un response entity. |
execute() | Ejecuta el método HTTP especificado sobre la URL. Devuelve un objeto. |
getForObject() | Ejecuta una solicitud GET sobre un recurso |
optionsForAllow() | Ejecuta una solicitud OPTIONS sobre una URL |
Operaciones RestTemplate
Método | Descripción |
---|---|
postForEntity() | Publica un recurso. Devuelve un response entity. |
postForLocation() | Publica un recurso. Devuelve la URL del nuevo recurso. |
postForObject() | Publica un recurso. Devuelve el objeto creado. |
put() | Incluye datos de un recurso mediante la función put |
Operaciones RestTemplate
package com.example;
import java.util.Base64;
import java.util.Random;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.example.Libro;
@Component
public class ConsumirRest {
@Autowired
private RestTemplate restTemplate;
public void ConsumirRestSinSeguridad()
{
new Thread("consumirRest" ){
public void run(){
try {
Thread.sleep(6000);
} catch (InterruptedException e) {}
Libro libro=restTemplate.getForObject("http://localhost:8080/rest/libro/3", Libro.class);
System.out.println(libro);
restTemplate.delete("http://localhost:8080/rest/libro/6");
libro.setAutor("Gustavo123");
libro.setId(0);
restTemplate.putForObject("http://localhost:8080/rest/libro", libro, Libro.class);
}
}.start();
}
@PostConstruct
public void ConsumirRestConSeguridad()
{
new Thread("consumirRestSeguro" ){
public void run(){
try {
Thread.sleep(6000);
} catch (InterruptedException e) {}
//Definimos las cabeceras HTTP
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Content-Type", "text/plain");
//Se fijan las credenciales por medio de las cabeceras HTTP
String basicCredentials = Base64.getEncoder().encodeToString (("gaurgo"+":"+"123").getBytes());
requestHeaders.set("Authorization", "Basic " + basicCredentials);
HttpEntity<String> httpEntity = new HttpEntity<String>("Body", requestHeaders);
ResponseEntity<Libro> libroResponse = restTemplate.exchange("http://localhost:8080/rest/libro/3", HttpMethod.GET, httpEntity,Libro.class);
System.out.println(libroResponse.getBody());
//NUEVO POST
Libro libro=libroResponse.getBody();
libro.setAutor("Gustavo12052007");
libro.setId(0);
requestHeaders.set("Content-Type", "application/json");
HttpEntity<Libro> httpEntity2 = new HttpEntity<Libro>(libro, requestHeaders);
restTemplate.exchange("http://localhost:8080/rest/libro", HttpMethod.PUT,httpEntity2, Libro.class);
}
}.start();
}
}
Spring
By Gustavo Andrés Uribe Gómez
Spring
- 638