├── public ├── stylesheets │ └── main.css ├── images │ └── favicon.png └── javascripts │ └── jquery-1.9.0.min.js ├── project ├── build.properties └── plugins.sbt ├── .gitignore ├── app ├── prismic │ ├── Action.java │ ├── ActionImpl.java │ └── Prismic.java ├── views │ ├── detail.scala.html │ ├── search.scala.html │ ├── index.scala.html │ └── main.scala.html └── controllers │ └── Application.java ├── conf ├── logback.xml ├── routes └── application.conf ├── test ├── IntegrationTest.java └── ApplicationTest.java └── README.md /public/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.9 2 | -------------------------------------------------------------------------------- /public/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prismicio/java-play-starter/master/public/images/favicon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | tmp 6 | .history 7 | dist 8 | /.idea 9 | /*.iml 10 | /out 11 | /.idea_modules 12 | /.classpath 13 | /.project 14 | /RUNNING_PID 15 | /.settings 16 | /project/*-shim.sbt 17 | *-shim.sbt 18 | 19 | -------------------------------------------------------------------------------- /app/prismic/Action.java: -------------------------------------------------------------------------------- 1 | package prismic; 2 | 3 | import play.mvc.*; 4 | import java.lang.annotation.*; 5 | 6 | @With(prismic.ActionImpl.class) 7 | @Target({ElementType.TYPE, ElementType.METHOD}) 8 | @Retention(RetentionPolicy.RUNTIME) 9 | public @interface Action {} 10 | -------------------------------------------------------------------------------- /app/views/detail.scala.html: -------------------------------------------------------------------------------- 1 | @(document: io.prismic.Document, linkResolver: io.prismic.LinkResolver) 2 | 3 | @main(s"Document detail ${document.getSlug()}") { 4 | 5 |
6 | @Html(document.asHtml(linkResolver)) 7 |
8 | 9 | } 10 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 6 | 7 | // Use the Play sbt plugin for Play projects 8 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.6") 9 | -------------------------------------------------------------------------------- /app/views/search.scala.html: -------------------------------------------------------------------------------- 1 | @(q: String, documents: java.util.List[io.prismic.Document], linkResolver: io.prismic.LinkResolver) 2 | 3 | @main("Search results") { 4 | 5 |

6 | @documents.size match { 7 | case 0 => { No results found } 8 | case 1 => { One document found } 9 | case n => { @n documents found } 10 | } 11 |

12 | 13 | 20 | 21 |

22 | Back to home 23 |

24 | 25 | } 26 | -------------------------------------------------------------------------------- /conf/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %coloredLevel - %logger - %message%n%xException 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # Home page 6 | GET / controllers.Application.index() 7 | 8 | # Document detail 9 | GET /documents/$id<[-_a-zA-Z0-9]{16}>/:slug controllers.Application.detail(id, slug) 10 | 11 | # Basic search 12 | GET /search controllers.Application.search(q: String ?= null) 13 | 14 | # Preview 15 | GET /preview controllers.Application.preview(token: String) 16 | 17 | # Map static resources from the /public folder to the /assets URL path 18 | GET /assets/*file controllers.Assets.at(path="/public", file) 19 | -------------------------------------------------------------------------------- /app/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @(someDocuments: java.util.List[io.prismic.Document]) 2 | 3 | @main("All documents") { 4 | 5 |
6 | 7 | 8 |
9 | 10 |
11 | 12 |

13 | @someDocuments.size() match { 14 | case 0 => { No documents found } 15 | case 1 => { One document found } 16 | case n => { @n documents found } 17 | } 18 |

19 | 20 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /test/IntegrationTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | import play.libs.F.Callback; 3 | import play.test.TestBrowser; 4 | 5 | import static org.hamcrest.CoreMatchers.containsString; 6 | import static org.junit.Assert.assertThat; 7 | import static play.test.Helpers.*; 8 | 9 | public class IntegrationTest { 10 | 11 | /** 12 | * add your integration test here 13 | * in this example we just check if the welcome page is being shown 14 | */ 15 | @Test 16 | public void test() { 17 | running(testServer(3333, fakeApplication(inMemoryDatabase())), HTMLUNIT, new Callback() { 18 | public void invoke(TestBrowser browser) { 19 | browser.goTo("http://localhost:3333"); 20 | assertThat(browser.pageSource(), containsString("All documents")); 21 | } 22 | }); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/views/main.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | 7 | @title 8 | 9 | 10 | 11 | 17 | @** Required for previews, edit button and experiments *@ 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 |

Your prismic.io project

26 |
27 |
28 | 29 | @content 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | import io.prismic.Document; 2 | import org.hamcrest.CoreMatchers; 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import play.twirl.api.Content; 6 | 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | import static org.hamcrest.CoreMatchers.containsString; 12 | import static org.hamcrest.CoreMatchers.equalTo; 13 | import static org.hamcrest.CoreMatchers.is; 14 | import static org.junit.Assert.assertThat; 15 | import static play.test.Helpers.contentAsString; 16 | import static play.test.Helpers.contentType; 17 | 18 | 19 | /** 20 | * 21 | * Simple (JUnit) tests that can call all parts of a play app. 22 | * If you are interested in mocking a whole application, see the wiki for more details. 23 | * 24 | */ 25 | public class ApplicationTest { 26 | 27 | @Test 28 | public void simpleCheck() { 29 | int a = 1 + 1; 30 | assertThat(a, is(2)); 31 | } 32 | 33 | @Test 34 | public void renderTemplate() { 35 | Document document = new Document("id", "uid", "StructuredText", "href", Collections.emptySet(), Arrays.asList("My super document"), Collections.emptyMap()); 36 | List someDocuments = Arrays.asList(document); 37 | Content html = views.html.index.render(someDocuments); 38 | assertThat(html.contentType(), is("text/html")); 39 | assertThat(html.body(), containsString("My super document")); 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Starter for Play with Java projects 2 | 3 | This is a blank [Play framework](http://www.playframework.com) project that will connect to any [prismic.io](https://prismic.io) repository. It uses the prismic.io Java developement kit, and provide a few helpers to integrate with the Play framework. 4 | 5 | ### How to start? 6 | 7 | Edit the `conf/application.conf` file to make the application point to the correct repository: 8 | 9 | ``` 10 | # Prismic.io 11 | # ~~~~~ 12 | 13 | # API endpoint 14 | prismic.api="https://lesbonneschoses.prismic.io/api" 15 | 16 | # If specified this token is used for all "guest" requests 17 | # prismic.token="xxx" 18 | 19 | # OAuth2 configuration 20 | # prismic.clientId="xxxxxx" 21 | # prismic.clientSecret="xxxxxx" 22 | ``` 23 | 24 | Run your play application using either the `sbt run` command or using activator and open your browser at http://localhost:9000/ 25 | 26 | ### Licence 27 | 28 | This software is licensed under the Apache 2 license, quoted below. 29 | 30 | Copyright 2013 Zengularity (http://www.zengularity.com). 31 | 32 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. 33 | 34 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 35 | -------------------------------------------------------------------------------- /conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Prismic.io 5 | # ~~~~~ 6 | 7 | # API endpoint 8 | prismic.api="https://lesbonneschoses.prismic.io/api" 9 | 10 | # If specified this token is used for all "guest" requests 11 | # prismic.token="xxx" 12 | 13 | # OAuth2 configuration 14 | # prismic.clientId="xxx" 15 | # prismic.clientSecret="xxx" 16 | 17 | # Secret key 18 | # ~~~~~ 19 | # The secret key is used to secure cryptographics functions. 20 | # 21 | # This must be changed for production, but we recommend not changing it in this file. 22 | # 23 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details. 24 | play.crypto.secret="changeMe" 25 | 26 | # The application languages 27 | # ~~~~~ 28 | play.i18n.langs=["en"] 29 | 30 | # Global object class 31 | # ~~~~~ 32 | # Define the Global object class for this application. 33 | # Default to Global in the root package. 34 | # application.global=Global 35 | 36 | # Router 37 | # ~~~~~ 38 | # Define the Router object to use for this application. 39 | # This router will be looked up first when the application is starting up, 40 | # so make sure this is the entry point. 41 | # Furthermore, it's assumed your route file is named properly. 42 | # So for an application router like `my.application.Router`, 43 | # you may need to define a router file `conf/my.application.routes`. 44 | # Default to Routes in the root package (and conf/routes) 45 | # play.http.router = my.application.Routes 46 | 47 | # Database configuration 48 | # ~~~~~ 49 | # You can declare as many datasources as you want. 50 | # By convention, the default datasource is named `default` 51 | # 52 | # db.default.driver=org.h2.Driver 53 | # db.default.url="jdbc:h2:mem:play" 54 | # db.default.user=sa 55 | # db.default.password="" 56 | 57 | # Evolutions 58 | # ~~~~~ 59 | # You can disable evolutions if needed 60 | # evolutionplugin=disabled 61 | -------------------------------------------------------------------------------- /app/prismic/ActionImpl.java: -------------------------------------------------------------------------------- 1 | package prismic; 2 | 3 | import javax.inject.Inject; 4 | 5 | import play.mvc.*; 6 | import play.Configuration; 7 | import play.libs.F; 8 | 9 | import io.prismic.Api; 10 | import io.prismic.Cache; 11 | 12 | import static io.prismic.Prismic.EXPERIMENTS_COOKIE; 13 | import static io.prismic.Prismic.PREVIEW_COOKIE; 14 | import static prismic.Prismic.ACCESS_TOKEN; 15 | import static prismic.Prismic.PRISMIC_CONTEXT; 16 | 17 | public class ActionImpl extends play.mvc.Action { 18 | 19 | @Inject 20 | private Configuration configuration; 21 | 22 | public F.Promise call(Http.Context ctx) throws Throwable { 23 | // Retrieve the accessToken from the Play session or from the configuration 24 | String accessToken = ctx.session().get(ACCESS_TOKEN); 25 | if(accessToken == null) { 26 | accessToken = configuration.getString("prismic.token"); 27 | } 28 | 29 | // Retrieve the API 30 | Api api = prismic.Prismic.getApiHome(accessToken); 31 | 32 | // Use the ref from the preview cookie, experiment cookie or master 33 | String ref = api.getMaster().getRef(); 34 | Http.Cookie previewCookie = ctx.request().cookie(io.prismic.Prismic.PREVIEW_COOKIE); 35 | Http.Cookie experimentCookie = ctx.request().cookie(io.prismic.Prismic.EXPERIMENTS_COOKIE); 36 | if (previewCookie != null) { 37 | ref = previewCookie.value(); 38 | } else if (experimentCookie != null) { 39 | ref = api.getExperiments().refFromCookie(experimentCookie.value()); 40 | } 41 | 42 | // Create the Prismic context 43 | Prismic.Context prismicContext = new Prismic.Context(api, ref, accessToken, controllers.Application.linkResolver(api, ctx.request())); 44 | 45 | // Strore it for future use 46 | ctx.args.put(PRISMIC_CONTEXT, prismicContext); 47 | 48 | // Go! 49 | try { 50 | return delegate.call(ctx); 51 | } catch (Exception e) { 52 | if ("1".equals(ctx.flash().get("clearing"))) { 53 | // Prevent infinite redirect loop if the exception is not due to the preview cookie 54 | return delegate.call(ctx); 55 | } else { 56 | ctx.response().discardCookie(io.prismic.Prismic.PREVIEW_COOKIE); 57 | ctx.flash().put("clearing", "1"); 58 | return F.Promise.pure(redirect(controllers.routes.Application.index())); 59 | } 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /app/controllers/Application.java: -------------------------------------------------------------------------------- 1 | package controllers; 2 | 3 | import play.*; 4 | import play.mvc.*; 5 | 6 | import java.util.*; 7 | 8 | import views.html.*; 9 | 10 | import io.prismic.*; 11 | import static prismic.Prismic.*; 12 | 13 | public class Application extends Controller { 14 | 15 | // -- Home page 16 | @prismic.Action 17 | public Result index() { 18 | List someDocuments = prismic().getApi().getForm("everything").ref(prismic().getRef()).submit().getResults(); 19 | return ok(views.html.index.render(someDocuments)); 20 | } 21 | 22 | // -- Document detail 23 | @prismic.Action 24 | public Result detail(String id, String slug) { 25 | Document maybeDocument = prismic().getDocument(id); 26 | String checked = prismic().checkSlug(maybeDocument, slug); 27 | if(checked == null) { 28 | return ok(views.html.detail.render(maybeDocument, prismic().getLinkResolver())); 29 | } 30 | else if(prismic.Prismic.DOCUMENT_NOT_FOUND.equals(checked)) { 31 | return pageNotFound(); 32 | } 33 | else { 34 | return redirect(routes.Application.detail(id, checked)); 35 | } 36 | } 37 | 38 | // -- Basic Search 39 | @prismic.Action 40 | public Result search(String q) { 41 | List results = new ArrayList(); 42 | if(q != null && !q.trim().isEmpty()) { 43 | results = prismic().getApi().query(Predicates.fulltext("document", q)).ref(prismic().getRef()).submit().getResults(); 44 | } 45 | return ok(views.html.search.render(q, results, prismic().getLinkResolver())); 46 | } 47 | 48 | // ---- Links 49 | 50 | // -- Resolve links to documents 51 | public static LinkResolver linkResolver(Api api, Http.Request request) { 52 | return new LinkResolver(api, request); 53 | } 54 | 55 | public static class LinkResolver extends SimpleLinkResolver { 56 | final Api api; 57 | final Http.Request request; 58 | 59 | public LinkResolver(Api api, Http.Request request) { 60 | this.api = api; 61 | this.request = request; 62 | } 63 | 64 | public String resolve(Fragment.DocumentLink link) { 65 | return routes.Application.detail(link.getId(), link.getSlug()).absoluteURL(request); 66 | } 67 | } 68 | 69 | // -- Page not found 70 | static Result pageNotFound() { 71 | return notFound("Page not found"); 72 | } 73 | 74 | // -- 75 | // -- Previews 76 | // -- 77 | @prismic.Action 78 | public Result preview(String token) { 79 | String indexUrl = controllers.routes.Application.index().url(); 80 | String url = prismic().getApi().previewSession(token, prismic().getLinkResolver(), indexUrl); 81 | response().setCookie(io.prismic.Prismic.PREVIEW_COOKIE, token, 1800); 82 | return redirect(url); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /app/prismic/Prismic.java: -------------------------------------------------------------------------------- 1 | package prismic; 2 | 3 | import play.*; 4 | import play.mvc.*; 5 | import play.libs.*; 6 | import play.libs.ws.*; 7 | 8 | import java.lang.Exception; 9 | import java.net.*; 10 | import java.util.*; 11 | import java.lang.annotation.*; 12 | 13 | import io.prismic.*; 14 | import scala.util.control.*; 15 | 16 | public class Prismic extends Controller { 17 | 18 | // -- Define the key name to use for storing the Prismic.io access token into the Play session 19 | final static String ACCESS_TOKEN = "ACCESS_TOKEN"; 20 | 21 | // -- Define the key name to use for storing the Prismic context token into the request arguments 22 | final static String PRISMIC_CONTEXT = "PRISMIC_CONTEXT"; 23 | 24 | // -- Signal for Document not found 25 | public static final String DOCUMENT_NOT_FOUND = "DOCUMENT_NOT_FOUND".intern(); 26 | 27 | // -- Cache to use (default to keep 200 JSON responses in a LRU cache) 28 | private final static Cache CACHE = new Cache.BuiltInCache(200); 29 | 30 | // -- Write debug and error messages to the Play `prismic` logger (check the configuration in application.conf) 31 | private final static io.prismic.Logger LOGGER = new io.prismic.Logger() { 32 | public void log(String level, String message) { 33 | if("DEBUG".equals(level)) { 34 | play.Logger.of("prismic").debug(message); 35 | } 36 | else if("ERROR".equals(level)) { 37 | play.Logger.of("prismic").error(message); 38 | } 39 | else { 40 | play.Logger.of("prismic").info(message); 41 | } 42 | } 43 | }; 44 | 45 | // Helper method to read the Play application configuration 46 | private static String config(String key) { 47 | String value = Play.application().configuration().getString(key); 48 | if(value == null) { 49 | throw new RuntimeException("Missing configuration [" + key + "]"); 50 | } 51 | return value; 52 | } 53 | 54 | public static String getEndpoint() { 55 | return config("prismic.api"); 56 | } 57 | 58 | // -- Fetch the API entry document 59 | public static Api getApiHome(String accessToken) { 60 | return Api.get(getEndpoint(), accessToken, CACHE, LOGGER); 61 | } 62 | 63 | // -- A Prismic context that help to keep the reference to useful primisc.io contextual data 64 | public static class Context { 65 | final Api api; 66 | final String ref; 67 | final String accessToken; 68 | final LinkResolver linkResolver; 69 | 70 | public Context(Api api, String ref, String accessToken, LinkResolver linkResolver) { 71 | this.api = api; 72 | this.ref = ref; 73 | this.accessToken = accessToken; 74 | this.linkResolver = linkResolver; 75 | } 76 | 77 | public Api getApi() { 78 | return api; 79 | } 80 | 81 | public String getRef() { 82 | return ref; 83 | } 84 | 85 | public LinkResolver getLinkResolver() { 86 | return linkResolver; 87 | } 88 | 89 | // -- Helper: Retrieve a single document by Id 90 | public Document getDocument(String id) { 91 | List results = this.getApi().getForm("everything") 92 | .ref(this.getRef()) 93 | .query(Predicates.at("document.id", id)) 94 | .submit().getResults(); 95 | if(results.size() > 0) { 96 | return results.get(0); 97 | } 98 | return null; 99 | } 100 | 101 | // -- Helper: Retrieve several documents by Id 102 | public List getDocuments(List ids) { 103 | if(ids.isEmpty()) { 104 | return new ArrayList(); 105 | } else { 106 | return this.getApi() 107 | .getForm("everything") 108 | .query(Predicates.any("document.id", ids)) 109 | .ref(this.getRef()) 110 | .submit() 111 | .getResults(); 112 | } 113 | } 114 | 115 | // -- Helper: Retrieve a single document from its bookmark 116 | public Document getBookmark(String bookmark) { 117 | String id = this.getApi().getBookmarks().get(bookmark); 118 | if(id != null) { 119 | return getDocument(id); 120 | } else { 121 | return null; 122 | } 123 | } 124 | 125 | // -- Helper: Check if the slug is valid and return to the most recent version to redirect to if needed, or return DOCUMENT_NOT_FOUND if there is no match 126 | public String checkSlug(Document document, String slug) { 127 | if(document != null) { 128 | if(document.getSlug().equals(slug)) { 129 | return null; 130 | } 131 | if(document.getSlugs().contains(slug)) { 132 | return document.getSlug(); 133 | } 134 | } 135 | return DOCUMENT_NOT_FOUND; 136 | } 137 | 138 | } 139 | 140 | // -- Retrieve the Prismic Context from a request handled by an built using Prismic.action 141 | public static Prismic.Context prismic() { 142 | Context ctx = (Prismic.Context)Http.Context.current().args.get(PRISMIC_CONTEXT); 143 | if(ctx == null) { 144 | throw new RuntimeException("No Context API found - Is it a @Prismic.Action?"); 145 | } 146 | return ctx; 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /public/javascripts/jquery-1.9.0.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v1.9.0 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license */(function(e,t){"use strict";function n(e){var t=e.length,n=st.type(e);return st.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}function r(e){var t=Tt[e]={};return st.each(e.match(lt)||[],function(e,n){t[n]=!0}),t}function i(e,n,r,i){if(st.acceptData(e)){var o,a,s=st.expando,u="string"==typeof n,l=e.nodeType,c=l?st.cache:e,f=l?e[s]:e[s]&&s;if(f&&c[f]&&(i||c[f].data)||!u||r!==t)return f||(l?e[s]=f=K.pop()||st.guid++:f=s),c[f]||(c[f]={},l||(c[f].toJSON=st.noop)),("object"==typeof n||"function"==typeof n)&&(i?c[f]=st.extend(c[f],n):c[f].data=st.extend(c[f].data,n)),o=c[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[st.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[st.camelCase(n)])):a=o,a}}function o(e,t,n){if(st.acceptData(e)){var r,i,o,a=e.nodeType,u=a?st.cache:e,l=a?e[st.expando]:st.expando;if(u[l]){if(t&&(r=n?u[l]:u[l].data)){st.isArray(t)?t=t.concat(st.map(t,st.camelCase)):t in r?t=[t]:(t=st.camelCase(t),t=t in r?[t]:t.split(" "));for(i=0,o=t.length;o>i;i++)delete r[t[i]];if(!(n?s:st.isEmptyObject)(r))return}(n||(delete u[l].data,s(u[l])))&&(a?st.cleanData([e],!0):st.support.deleteExpando||u!=u.window?delete u[l]:u[l]=null)}}}function a(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(Nt,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:wt.test(r)?st.parseJSON(r):r}catch(o){}st.data(e,n,r)}else r=t}return r}function s(e){var t;for(t in e)if(("data"!==t||!st.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}function u(){return!0}function l(){return!1}function c(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}function f(e,t,n){if(t=t||0,st.isFunction(t))return st.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return st.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=st.grep(e,function(e){return 1===e.nodeType});if(Wt.test(t))return st.filter(t,r,!n);t=st.filter(t,r)}return st.grep(e,function(e){return st.inArray(e,t)>=0===n})}function p(e){var t=zt.split("|"),n=e.createDocumentFragment();if(n.createElement)for(;t.length;)n.createElement(t.pop());return n}function d(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function h(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function g(e){var t=nn.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function m(e,t){for(var n,r=0;null!=(n=e[r]);r++)st._data(n,"globalEval",!t||st._data(t[r],"globalEval"))}function y(e,t){if(1===t.nodeType&&st.hasData(e)){var n,r,i,o=st._data(e),a=st._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)st.event.add(t,n,s[n][r])}a.data&&(a.data=st.extend({},a.data))}}function v(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!st.support.noCloneEvent&&t[st.expando]){r=st._data(t);for(i in r.events)st.removeEvent(t,i,r.handle);t.removeAttribute(st.expando)}"script"===n&&t.text!==e.text?(h(t).text=e.text,g(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),st.support.html5Clone&&e.innerHTML&&!st.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Zt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}function b(e,n){var r,i,o=0,a=e.getElementsByTagName!==t?e.getElementsByTagName(n||"*"):e.querySelectorAll!==t?e.querySelectorAll(n||"*"):t;if(!a)for(a=[],r=e.childNodes||e;null!=(i=r[o]);o++)!n||st.nodeName(i,n)?a.push(i):st.merge(a,b(i,n));return n===t||n&&st.nodeName(e,n)?st.merge([e],a):a}function x(e){Zt.test(e.type)&&(e.defaultChecked=e.checked)}function T(e,t){if(t in e)return t;for(var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Nn.length;i--;)if(t=Nn[i]+n,t in e)return t;return r}function w(e,t){return e=t||e,"none"===st.css(e,"display")||!st.contains(e.ownerDocument,e)}function N(e,t){for(var n,r=[],i=0,o=e.length;o>i;i++)n=e[i],n.style&&(r[i]=st._data(n,"olddisplay"),t?(r[i]||"none"!==n.style.display||(n.style.display=""),""===n.style.display&&w(n)&&(r[i]=st._data(n,"olddisplay",S(n.nodeName)))):r[i]||w(n)||st._data(n,"olddisplay",st.css(n,"display")));for(i=0;o>i;i++)n=e[i],n.style&&(t&&"none"!==n.style.display&&""!==n.style.display||(n.style.display=t?r[i]||"":"none"));return e}function C(e,t,n){var r=mn.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function k(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;4>o;o+=2)"margin"===n&&(a+=st.css(e,n+wn[o],!0,i)),r?("content"===n&&(a-=st.css(e,"padding"+wn[o],!0,i)),"margin"!==n&&(a-=st.css(e,"border"+wn[o]+"Width",!0,i))):(a+=st.css(e,"padding"+wn[o],!0,i),"padding"!==n&&(a+=st.css(e,"border"+wn[o]+"Width",!0,i)));return a}function E(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=ln(e),a=st.support.boxSizing&&"border-box"===st.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=un(e,t,o),(0>i||null==i)&&(i=e.style[t]),yn.test(i))return i;r=a&&(st.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+k(e,t,n||(a?"border":"content"),r,o)+"px"}function S(e){var t=V,n=bn[e];return n||(n=A(e,t),"none"!==n&&n||(cn=(cn||st("