├── .gitattributes ├── src ├── main │ ├── resources │ │ └── version.txt │ └── java │ │ ├── auth │ │ ├── AuthenticationException.java │ │ ├── Authenticator.java │ │ ├── FakeAuthenticator.java │ │ ├── User.java │ │ └── TwitterAuthenticator.java │ │ ├── templating │ │ ├── Layout.java │ │ ├── ContentWithVariables.java │ │ ├── YamlFrontMatter.java │ │ └── Template.java │ │ ├── PlanningServerModule.java │ │ ├── resources │ │ ├── AuthenticationResource.java │ │ ├── PlanningResource.java │ │ ├── StaticResource.java │ │ └── AbstractResource.java │ │ ├── PlanningServer.java │ │ └── planning │ │ └── Planning.java └── test │ └── java │ ├── templating │ ├── LayoutTest.java │ ├── TemplateTest.java │ └── YamlFrontMatterTest.java │ ├── resources │ ├── PlanningResourceTest.java │ └── AuthenticationResourceTest.java │ ├── AbstractPageTest.java │ ├── misc │ ├── PhantomJsTest.java │ └── PhantomJsDownloader.java │ ├── auth │ └── TwitterAuthenticatorTest.java │ ├── HomePageTest.java │ └── planning │ └── PlanningTest.java ├── Procfile ├── web ├── favicon.ico ├── img │ ├── arrow.png │ ├── david.png │ ├── eric.png │ ├── fight.jpg │ ├── fight2.jpg │ ├── iphone.png │ ├── laptop.png │ ├── favicon.png │ ├── sprites.png │ ├── devoxx-logo.png │ ├── sebastian.png │ └── jean-laurent.png ├── layouts │ └── default.html ├── js │ ├── jquery.cookie.js │ ├── jquery.scrollTo.min.js │ ├── planning.js │ ├── hogan.js │ ├── purl.js │ ├── underscore.js │ └── jquery.js ├── planning.html └── css │ └── planning.less ├── .gitignore ├── README.md ├── etc ├── install_node_module.sh └── buildjson.coffee └── pom.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | version.txt export-subst 2 | -------------------------------------------------------------------------------- /src/main/resources/version.txt: -------------------------------------------------------------------------------- 1 | 2febb630616989dc8b226e94ba7a0eadf8419abb -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java -Xmx512M -cp target/classes:target/dependency/* PlanningServer 2 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/favicon.ico -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | codestory.iml 2 | target/ 3 | .idea/ 4 | .DS_Store 5 | prod.log 6 | data/ 7 | snapshot/ 8 | -------------------------------------------------------------------------------- /web/img/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/arrow.png -------------------------------------------------------------------------------- /web/img/david.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/david.png -------------------------------------------------------------------------------- /web/img/eric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/eric.png -------------------------------------------------------------------------------- /web/img/fight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/fight.jpg -------------------------------------------------------------------------------- /web/img/fight2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/fight2.jpg -------------------------------------------------------------------------------- /web/img/iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/iphone.png -------------------------------------------------------------------------------- /web/img/laptop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/laptop.png -------------------------------------------------------------------------------- /web/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/favicon.png -------------------------------------------------------------------------------- /web/img/sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/sprites.png -------------------------------------------------------------------------------- /web/img/devoxx-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/devoxx-logo.png -------------------------------------------------------------------------------- /web/img/sebastian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/sebastian.png -------------------------------------------------------------------------------- /web/img/jean-laurent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-planning/master/web/img/jean-laurent.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is the preparation project for CodeStory session that will take place at Devoxx World on November 14th&15th. -------------------------------------------------------------------------------- /etc/install_node_module.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | npm uninstall -g zombie mocha chai underscore promised-io expect.js coffee-script 3 | npm install -g zombie@1.4.1 mocha chai underscore promised-io expect.js coffee-script 4 | -------------------------------------------------------------------------------- /src/main/java/auth/AuthenticationException.java: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | public class AuthenticationException extends RuntimeException { 4 | public AuthenticationException(Throwable cause) { 5 | super(cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/auth/Authenticator.java: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | import java.net.URI; 4 | 5 | public interface Authenticator { 6 | URI getAuthenticateURI() throws AuthenticationException; 7 | 8 | User authenticate(String aouthToken, String oauthVerifier) throws AuthenticationException; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/templating/Layout.java: -------------------------------------------------------------------------------- 1 | package templating; 2 | 3 | public class Layout { 4 | private final String layout; 5 | 6 | public Layout(String layout) { 7 | this.layout = layout; 8 | } 9 | 10 | public String apply(String body) { 11 | return layout.replace("[[body]]", body); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/auth/FakeAuthenticator.java: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | import java.net.URI; 4 | 5 | public class FakeAuthenticator implements Authenticator { 6 | @Override 7 | public URI getAuthenticateURI() { 8 | return URI.create("/user/fakeauthenticate"); 9 | } 10 | 11 | @Override 12 | public User authenticate(String oauthToken, String oauthVerifier) { 13 | return new User(42L, "arnold", "ring", "girl"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/templating/LayoutTest.java: -------------------------------------------------------------------------------- 1 | package templating; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.fest.assertions.Assertions.assertThat; 6 | 7 | public class LayoutTest { 8 | @Test 9 | public void should_apply_layout() { 10 | Layout layout = new Layout("header/[[body]]/footer"); 11 | 12 | String content = layout.apply("Hello"); 13 | 14 | assertThat(content).isEqualTo("header/Hello/footer"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/templating/ContentWithVariables.java: -------------------------------------------------------------------------------- 1 | package templating; 2 | 3 | import java.util.Map; 4 | 5 | public class ContentWithVariables { 6 | private final String content; 7 | private final Map variables; 8 | 9 | public ContentWithVariables(String content, Map variables) { 10 | this.content = content; 11 | this.variables = variables; 12 | } 13 | 14 | public String getContent() { 15 | return content; 16 | } 17 | 18 | public Map getVariables() { 19 | return variables; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/auth/User.java: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | public class User { 4 | private final Long id; 5 | private final String screenName; 6 | private final String token; 7 | private final String secret; 8 | 9 | public User(Long id, String screenName, String token, String secret) { 10 | this.id = id; 11 | this.screenName = screenName; 12 | this.token = token; 13 | this.secret = secret; 14 | } 15 | 16 | public Long getId() { 17 | return id; 18 | } 19 | 20 | public String getScreenName() { 21 | return screenName; 22 | } 23 | 24 | public String getToken() { 25 | return token; 26 | } 27 | 28 | public String getSecret() { 29 | return secret; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/resources/PlanningResourceTest.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import com.google.common.collect.ImmutableSet; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.InjectMocks; 7 | import org.mockito.Mock; 8 | import org.mockito.runners.MockitoJUnitRunner; 9 | import planning.Planning; 10 | 11 | import static org.fest.assertions.Assertions.assertThat; 12 | import static org.mockito.Mockito.verify; 13 | import static org.mockito.Mockito.when; 14 | 15 | @RunWith(MockitoJUnitRunner.class) 16 | public class PlanningResourceTest { 17 | @Mock 18 | private Planning planning; 19 | 20 | @InjectMocks 21 | private PlanningResource resource; 22 | 23 | @Test 24 | public void should_register_user_for_talk() { 25 | resource.star("user", "talkId"); 26 | 27 | verify(planning).star("user", "talkId"); 28 | } 29 | 30 | @Test 31 | public void should_unregister_user_for_talk() { 32 | resource.unstar("user", "talkId"); 33 | 34 | verify(planning).unstar("user", "talkId"); 35 | } 36 | 37 | @Test 38 | public void should_get_my_registrations() { 39 | when(planning.stars("user")).thenReturn(ImmutableSet.of("talkId1", "talkId2")); 40 | 41 | String talkIds = resource.stars("user", ""); 42 | 43 | assertThat(talkIds).contains("talkId1").contains("talkId2"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /web/layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [[title]] 5 | 6 | 7 | 8 | 9 | 10 | [[body]] 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 34 | -------------------------------------------------------------------------------- /src/main/java/templating/YamlFrontMatter.java: -------------------------------------------------------------------------------- 1 | package templating; 2 | 3 | import com.google.common.base.Splitter; 4 | import com.google.common.collect.Maps; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | import java.util.Map; 8 | 9 | public class YamlFrontMatter { 10 | private static final String SEPARATOR = "---\n"; 11 | 12 | public ContentWithVariables parse(String content) { 13 | if (StringUtils.countMatches(content, SEPARATOR) < 2) { 14 | return new ContentWithVariables(content, Maps. newHashMap()); 15 | } 16 | return new ContentWithVariables(stripHeader(content), parseVariables(content)); 17 | } 18 | 19 | private static String stripHeader(String content) { 20 | return StringUtils.substringAfter(StringUtils.substringAfter(content, SEPARATOR), SEPARATOR); 21 | } 22 | 23 | private static Map parseVariables(String content) { 24 | Map variables = Maps.newHashMap(); 25 | 26 | String header = StringUtils.substringBetween(content, SEPARATOR, SEPARATOR); 27 | for (String line : Splitter.on('\n').split(header)) { 28 | String key = StringUtils.substringBefore(line, ":").trim(); 29 | String value = StringUtils.substringAfter(line, ":").trim(); 30 | 31 | if (!key.startsWith("#")) { 32 | variables.put(key, value); 33 | } 34 | } 35 | 36 | return variables; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/templating/Template.java: -------------------------------------------------------------------------------- 1 | package templating; 2 | 3 | import com.github.mustachejava.DefaultMustacheFactory; 4 | import com.github.mustachejava.Mustache; 5 | import com.google.common.base.Charsets; 6 | import com.google.common.base.Throwables; 7 | import com.google.common.collect.ImmutableMap; 8 | import com.google.common.io.Resources; 9 | 10 | import java.io.IOException; 11 | import java.io.StringReader; 12 | import java.io.StringWriter; 13 | import java.util.Map; 14 | 15 | public class Template { 16 | private final DefaultMustacheFactory mustacheFactory = new DefaultMustacheFactory(); 17 | 18 | public String apply(String content, Map variables) { 19 | Mustache mustache = mustacheFactory.compile(new StringReader(content), content, "[[", "]]"); 20 | 21 | Map additional = ImmutableMap.of("body", "[[body]]", "version", readGitHash()); 22 | try { 23 | StringWriter output = new StringWriter(); 24 | mustache.execute(output, new Object[]{variables, additional}).flush(); 25 | return output.toString(); 26 | } catch (IOException e) { 27 | throw Throwables.propagate(e); 28 | } 29 | } 30 | 31 | private static String readGitHash() { 32 | try { 33 | String hash = Resources.toString(Resources.getResource("version.txt"), Charsets.UTF_8); 34 | return hash.replace("$Format:%H$", "GIT_HASH"); 35 | } catch (IOException e) { 36 | throw new IllegalStateException("Unable to read version.txt in the classpath"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/templating/TemplateTest.java: -------------------------------------------------------------------------------- 1 | package templating; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import org.junit.Test; 5 | 6 | import static org.fest.assertions.Assertions.assertThat; 7 | 8 | public class TemplateTest { 9 | Template template = new Template(); 10 | 11 | @Test 12 | public void should_get_original_content() { 13 | String content = ""; 14 | String result = template.apply(content, ImmutableMap.of()); 15 | 16 | assertThat(result).isEqualTo(content); 17 | } 18 | 19 | @Test 20 | public void should_replace_variable() { 21 | String content = "BEFORE-[[KEY]]-AFTER"; 22 | String result = template.apply(content, ImmutableMap.of("KEY", "VALUE")); 23 | 24 | assertThat(result).isEqualTo("BEFORE-VALUE-AFTER"); 25 | } 26 | 27 | @Test 28 | public void should_ignore_standard_mustaches() { 29 | String content = "BEFORE-[[KEY]]-{{IGNORED}}-AFTER"; 30 | String result = template.apply(content, ImmutableMap.of("KEY", "VALUE")); 31 | 32 | assertThat(result).isEqualTo("BEFORE-VALUE-{{IGNORED}}-AFTER"); 33 | } 34 | 35 | @Test 36 | public void should_ignore_body() { 37 | String content = "BEFORE-[[body]]-AFTER"; 38 | String result = template.apply(content, ImmutableMap.of()); 39 | 40 | assertThat(result).isEqualTo("BEFORE-[[body]]-AFTER"); 41 | } 42 | 43 | @Test 44 | public void should_include_version() { 45 | String content = "[[version]]"; 46 | String result = template.apply(content, ImmutableMap.of()); 47 | 48 | assertThat(result).isEqualTo("GIT_HASH"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/PlanningServerModule.java: -------------------------------------------------------------------------------- 1 | import auth.Authenticator; 2 | import auth.TwitterAuthenticator; 3 | import com.google.inject.AbstractModule; 4 | import com.google.inject.Provides; 5 | import com.google.inject.name.Named; 6 | import twitter4j.TwitterFactory; 7 | import twitter4j.conf.Configuration; 8 | import twitter4j.conf.ConfigurationBuilder; 9 | 10 | import java.io.File; 11 | 12 | import static com.google.inject.name.Names.named; 13 | 14 | public class PlanningServerModule extends AbstractModule { 15 | private final File workingDir; 16 | 17 | public PlanningServerModule(File workingDir) { 18 | this.workingDir = workingDir; 19 | } 20 | 21 | @Override 22 | protected void configure() { 23 | bindConstant().annotatedWith(named("oAuth.callback")).to(env("OAUTH_CALLBACK", "")); 24 | bindConstant().annotatedWith(named("oAuth.key")).to(env("OAUTH_CONSUMER_KEY", "")); 25 | bindConstant().annotatedWith(named("oAuth.secret")).to(env("OAUTH_CONSUMER_SECRET", "")); 26 | bind(File.class).annotatedWith(named("planning.root")).toInstance(new File(env("PLANNING_ROOT", "data"))); 27 | 28 | bind(Authenticator.class).to(TwitterAuthenticator.class); 29 | } 30 | 31 | @Provides 32 | public Configuration createConfiguration(@Named("oAuth.key") String key, @Named("oAuth.secret") String secret) { 33 | return new ConfigurationBuilder().setOAuthConsumerKey(key).setOAuthConsumerSecret(secret).build(); 34 | } 35 | 36 | @Provides 37 | public TwitterFactory createTwitterFactory(Configuration config) { 38 | return new TwitterFactory(config); 39 | } 40 | 41 | private static String env(String name, String defaultValue) { 42 | String value = System.getenv(name); 43 | return (null != value) ? value : defaultValue; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /etc/buildjson.coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | 3 | _ = require('underscore') 4 | http = require('http') 5 | Promise = require("promised-io/promise").Promise 6 | all = require("promised-io/promise").all 7 | 8 | promises = [] 9 | 10 | retrieve = (url) -> 11 | promise = new Promise() 12 | http.get url, (response) -> 13 | scheduleData = "" 14 | response.on 'data', (data) -> scheduleData += data 15 | response.on 'end', () -> 16 | try 17 | promise.resolve JSON.parse(scheduleData) 18 | catch e 19 | promise.resolve {} 20 | promise 21 | 22 | transformTalks = (data) -> 23 | _.chain(data) 24 | .filter((talk) -> talk.speaker?) 25 | .sortBy((talk) -> talk.fromTime) 26 | .map((talk) -> 27 | id: talk.id 28 | title: talk.title 29 | uri: talk.presentationUri 30 | summary: '' 31 | room: talk.room 32 | speaker: talk.speaker 33 | day: talk.fromTime[0..9] 34 | from: talk.fromTime[11..15] 35 | to: talk.toTime[11..15] 36 | type: talk.type 37 | speakers: _.pluck(talk.speakers, 'speaker') 38 | ).map((talk) -> 39 | promises.push retrieve(talk.uri).then (presentation) -> 40 | talk.summary = presentation.summary 41 | talk.tags = _.pluck(presentation.tags, 'name') 42 | talk 43 | ).groupBy((talk) -> talk.day) 44 | .map((talks, day) -> 45 | day: day, 46 | slots: _.chain(talks).groupBy((talk) -> talk.from).map((talks, slot) -> 47 | slot: slot, 48 | talks: talks 49 | ).value() 50 | ).value() 51 | 52 | retrieve('http://cfp.devoxx.com/rest/v1/events/8/schedule').then (talks) -> 53 | schedule = {days: transformTalks(talks)} 54 | all(promises).then () -> 55 | #console.log JSON.stringify(schedule, null, " ") 56 | console.log JSON.stringify(schedule) 57 | 58 | -------------------------------------------------------------------------------- /src/test/java/AbstractPageTest.java: -------------------------------------------------------------------------------- 1 | import auth.Authenticator; 2 | import auth.FakeAuthenticator; 3 | import com.google.inject.AbstractModule; 4 | import com.google.inject.util.Modules; 5 | import misc.PhantomJsTest; 6 | import org.junit.Before; 7 | import org.junit.ClassRule; 8 | import org.junit.rules.TemporaryFolder; 9 | 10 | import java.util.Random; 11 | 12 | public abstract class AbstractPageTest extends PhantomJsTest { 13 | private static final int TRY_COUNT = 10; 14 | private static final int DEFAULT_PORT = 8183; 15 | private static Random RANDOM_PORT = new Random(); 16 | 17 | private static int port; 18 | private static PlanningServer server; 19 | 20 | @ClassRule 21 | public static TemporaryFolder temporaryFolder = new TemporaryFolder(); 22 | 23 | @Before 24 | public void startServerOnce() { 25 | if (server != null) { 26 | return; 27 | } 28 | 29 | for (int i = 0; i < TRY_COUNT; i++) { 30 | try { 31 | port = randomPort(); 32 | server = new PlanningServer(Modules.override(new PlanningServerModule(temporaryFolder.getRoot())).with(testConfiguration())); 33 | server.start(port); 34 | return; 35 | } catch (Exception e) { 36 | System.err.println("Unable to bind server: " + e); 37 | } 38 | } 39 | throw new IllegalStateException("Unable to start server"); 40 | } 41 | 42 | private static AbstractModule testConfiguration() { 43 | return new AbstractModule() { 44 | @Override 45 | protected void configure() { 46 | bind(Authenticator.class).to(FakeAuthenticator.class); 47 | } 48 | }; 49 | } 50 | 51 | private synchronized int randomPort() { 52 | return DEFAULT_PORT + RANDOM_PORT.nextInt(1000); 53 | } 54 | 55 | @Override 56 | protected String defaultUrl() { 57 | return "http://localhost:" + port; 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/auth/TwitterAuthenticator.java: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | import com.google.common.base.Strings; 4 | import com.google.inject.Inject; 5 | import com.google.inject.Singleton; 6 | import com.google.inject.name.Named; 7 | import twitter4j.Twitter; 8 | import twitter4j.TwitterException; 9 | import twitter4j.TwitterFactory; 10 | import twitter4j.auth.AccessToken; 11 | import twitter4j.auth.RequestToken; 12 | 13 | import java.net.URI; 14 | import java.util.Map; 15 | 16 | import static com.google.common.collect.Maps.newHashMap; 17 | 18 | @Singleton 19 | public class TwitterAuthenticator implements Authenticator { 20 | private final TwitterFactory twitterFactory; 21 | private final String callback; 22 | private final Map oauthRequestByToken; 23 | 24 | @Inject 25 | public TwitterAuthenticator(TwitterFactory twitterFactory, @Named("oAuth.callback") String callback) { 26 | this.twitterFactory = twitterFactory; 27 | this.oauthRequestByToken = newHashMap(); 28 | this.callback = Strings.emptyToNull(callback); 29 | } 30 | 31 | @Override 32 | public URI getAuthenticateURI() { 33 | Twitter twitter = twitterFactory.getInstance(); 34 | try { 35 | RequestToken requestToken = twitter.getOAuthRequestToken(callback); 36 | oauthRequestByToken.put(requestToken.getToken(), requestToken); 37 | return URI.create(requestToken.getAuthenticationURL()); 38 | } catch (TwitterException e) { 39 | throw new AuthenticationException(e); 40 | } 41 | } 42 | 43 | @Override 44 | public User authenticate(String oauthToken, String oauthVerifier) { 45 | Twitter twitter = twitterFactory.getInstance(); 46 | try { 47 | AccessToken accessToken = twitter.getOAuthAccessToken(oauthRequestByToken.remove(oauthToken), oauthVerifier); 48 | return new User(accessToken.getUserId(), accessToken.getScreenName(), accessToken.getToken(), accessToken.getTokenSecret()); 49 | } catch (TwitterException e) { 50 | throw new AuthenticationException(e); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /web/js/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.3 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2011, Klaus Hartl 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.opensource.org/licenses/GPL-2.0 9 | */ 10 | (function ($, document, undefined) { 11 | 12 | var pluses = /\+/g; 13 | 14 | function raw(s) { 15 | return s; 16 | } 17 | 18 | function decoded(s) { 19 | return decodeURIComponent(s.replace(pluses, ' ')); 20 | } 21 | 22 | var config = $.cookie = function (key, value, options) { 23 | 24 | // write 25 | if (value !== undefined) { 26 | options = $.extend({}, config.defaults, options); 27 | 28 | if (value === null) { 29 | options.expires = -1; 30 | } 31 | 32 | if (typeof options.expires === 'number') { 33 | var days = options.expires, t = options.expires = new Date(); 34 | t.setDate(t.getDate() + days); 35 | } 36 | 37 | value = config.json ? JSON.stringify(value) : String(value); 38 | 39 | return (document.cookie = [ 40 | encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), 41 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 42 | options.path ? '; path=' + options.path : '', 43 | options.domain ? '; domain=' + options.domain : '', 44 | options.secure ? '; secure' : '' 45 | ].join('')); 46 | } 47 | 48 | // read 49 | var decode = config.raw ? raw : decoded; 50 | var cookies = document.cookie.split('; '); 51 | for (var i = 0, l = cookies.length; i < l; i++) { 52 | var parts = cookies[i].split('='); 53 | if (decode(parts.shift()) === key) { 54 | var cookie = decode(parts.join('=')); 55 | return config.json ? JSON.parse(cookie) : cookie; 56 | } 57 | } 58 | 59 | return null; 60 | }; 61 | 62 | config.defaults = {}; 63 | 64 | $.removeCookie = function (key, options) { 65 | if ($.cookie(key) !== null) { 66 | $.cookie(key, null, options); 67 | return true; 68 | } 69 | return false; 70 | }; 71 | 72 | })(jQuery, document); 73 | -------------------------------------------------------------------------------- /src/test/java/templating/YamlFrontMatterTest.java: -------------------------------------------------------------------------------- 1 | package templating; 2 | 3 | import com.google.common.base.Joiner; 4 | import org.junit.Test; 5 | 6 | import static org.fest.assertions.Assertions.assertThat; 7 | import static org.fest.assertions.MapAssert.entry; 8 | 9 | public class YamlFrontMatterTest { 10 | YamlFrontMatter yamlFrontMatter = new YamlFrontMatter(); 11 | 12 | @Test 13 | public void should_read_empty_file() { 14 | String content = fileContent(""); 15 | 16 | ContentWithVariables parsed = yamlFrontMatter.parse(content); 17 | 18 | assertThat(parsed.getVariables()).isEmpty(); 19 | assertThat(parsed.getContent()).isEmpty(); 20 | } 21 | 22 | @Test 23 | public void should_read_file_without_headers() { 24 | String content = fileContent("CONTENT"); 25 | 26 | ContentWithVariables parsed = yamlFrontMatter.parse(content); 27 | 28 | assertThat(parsed.getVariables()).isEmpty(); 29 | assertThat(parsed.getContent()).isEqualTo("CONTENT"); 30 | } 31 | 32 | @Test 33 | public void should_read_header_variables() { 34 | String content = fileContent( 35 | "---", 36 | "layout: standard", 37 | "title: CodeStory - Devoxx Fight", 38 | "---", 39 | "CONTENT"); 40 | 41 | ContentWithVariables parsed = yamlFrontMatter.parse(content); 42 | 43 | assertThat(parsed.getVariables()).includes( 44 | entry("layout", "standard"), 45 | entry("title", "CodeStory - Devoxx Fight")); 46 | assertThat(parsed.getContent()).isEqualTo("CONTENT"); 47 | } 48 | 49 | @Test 50 | public void should_ignore_commented_variable() { 51 | String content = fileContent( 52 | "---", 53 | "#layout: standard", 54 | "title: CodeStory - Devoxx Fight", 55 | "---", 56 | "CONTENT"); 57 | 58 | ContentWithVariables parsed = yamlFrontMatter.parse(content); 59 | 60 | assertThat(parsed.getVariables()) 61 | .excludes(entry("layout", "standard")) 62 | .excludes(entry("#layout", "standard")) 63 | .includes(entry("title", "CodeStory - Devoxx Fight")); 64 | } 65 | 66 | static String fileContent(String... lines) { 67 | return Joiner.on("\n").join(lines); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/resources/AuthenticationResource.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import auth.Authenticator; 4 | import auth.User; 5 | import com.google.inject.Inject; 6 | import com.google.inject.Singleton; 7 | 8 | import javax.ws.rs.CookieParam; 9 | import javax.ws.rs.GET; 10 | import javax.ws.rs.Path; 11 | import javax.ws.rs.QueryParam; 12 | import javax.ws.rs.core.NewCookie; 13 | import javax.ws.rs.core.Response; 14 | 15 | import java.net.URI; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | @Singleton 19 | @Path("/user") 20 | public class AuthenticationResource extends AbstractResource { 21 | private static final int MAX_AGE = (int) TimeUnit.DAYS.toSeconds(7); 22 | 23 | private final Authenticator authenticator; 24 | 25 | @Inject 26 | public AuthenticationResource(Authenticator authenticator) { 27 | this.authenticator = authenticator; 28 | } 29 | 30 | @GET 31 | @Path("authenticate") 32 | public Response authenticate() { 33 | return seeOther(authenticator.getAuthenticateURI()); 34 | } 35 | 36 | @GET 37 | @Path("authenticated") 38 | public Response authenticated(@QueryParam("oauth_token") String token, @QueryParam("oauth_verifier") String verifier) { 39 | try { 40 | User user = authenticator.authenticate(token, verifier); 41 | 42 | return redirectToPlanning().cookie( 43 | new NewCookie("userId", user.getId().toString(), "/", null, null, MAX_AGE, false), 44 | new NewCookie("screenName", user.getScreenName(), "/", null, null, MAX_AGE, false)) 45 | .build(); 46 | } catch (Exception e) { 47 | return redirectToPlanning().build(); 48 | } 49 | } 50 | 51 | @GET 52 | @Path("logout") 53 | public Response logout(@CookieParam("userId") String userId) { 54 | return redirectToPlanning().cookie( 55 | new NewCookie("userId", null, "/", null, null, 0, false), 56 | new NewCookie("screenName", null, "/", null, null, 0, false)) 57 | .build(); 58 | } 59 | 60 | // For tests 61 | @GET 62 | @Path("fakeauthenticate") 63 | public Response fakeauthenticate() { 64 | return seeOther("/user/authenticated?oauthToken=foo&oauthVerifier=bar"); 65 | } 66 | 67 | private static Response.ResponseBuilder redirectToPlanning() { 68 | return Response.seeOther(URI.create("/")); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/resources/PlanningResource.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import com.google.inject.Inject; 4 | import com.google.inject.Singleton; 5 | import planning.Planning; 6 | 7 | import javax.ws.rs.CookieParam; 8 | import javax.ws.rs.FormParam; 9 | import javax.ws.rs.GET; 10 | import javax.ws.rs.POST; 11 | import javax.ws.rs.Path; 12 | import javax.ws.rs.Produces; 13 | import javax.ws.rs.QueryParam; 14 | import javax.ws.rs.WebApplicationException; 15 | import javax.ws.rs.core.Response; 16 | 17 | import static javax.ws.rs.core.Response.Status.FORBIDDEN; 18 | 19 | @Path("/") 20 | @Singleton 21 | public class PlanningResource extends AbstractResource { 22 | private final Planning planning; 23 | 24 | @Inject 25 | public PlanningResource(Planning planning) { 26 | this.planning = planning; 27 | } 28 | 29 | @GET 30 | @Produces("text/html;charset=UTF-8") 31 | public Response index() { 32 | return okTemplatize(file("planning.html")); 33 | } 34 | 35 | @POST 36 | @Path("star") 37 | public void star(@CookieParam("userId") String userId, @FormParam("talkId") String talkId) { 38 | planning.star(assertAuthenticated(userId), talkId); 39 | } 40 | 41 | @POST 42 | @Path("unstar") 43 | public void unstar(@CookieParam("userId") String userId, @FormParam("talkId") String talkId) { 44 | planning.unstar(assertAuthenticated(userId), talkId); 45 | } 46 | 47 | @GET 48 | @Path("planning.json") 49 | @Produces("application/javascript;charset=UTF-8") 50 | public String planning(@QueryParam("callback") String callback) { 51 | return jsonp(read("planning.json"), callback); // TODO add small cache duration or an etag 52 | } 53 | 54 | @GET 55 | @Path("stars") 56 | @Produces("application/javascript;charset=UTF-8") 57 | public String stars(@CookieParam("userId") String userId, @QueryParam("callback") String callback) { 58 | return jsonp(planning.stars(assertAuthenticated(userId)), callback); 59 | } 60 | 61 | @GET 62 | @Path("starsPerTalk") 63 | @Produces("application/javascript;charset=UTF-8") 64 | public String starsPerTalk(@QueryParam("callback") String callback) { 65 | return jsonp(planning.countPerTalk(), callback); 66 | } 67 | 68 | private static String assertAuthenticated(String userId) { 69 | if (userId == null) { 70 | throw new WebApplicationException(FORBIDDEN); 71 | } 72 | return userId; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/PlanningServer.java: -------------------------------------------------------------------------------- 1 | import com.google.inject.Guice; 2 | import com.google.inject.Injector; 3 | import com.google.inject.Module; 4 | import com.sun.jersey.api.container.filter.GZIPContentEncodingFilter; 5 | import com.sun.jersey.api.container.httpserver.HttpServerFactory; 6 | import com.sun.jersey.api.core.DefaultResourceConfig; 7 | import com.sun.jersey.api.core.ResourceConfig; 8 | import com.sun.net.httpserver.HttpServer; 9 | import resources.AuthenticationResource; 10 | import resources.PlanningResource; 11 | import resources.StaticResource; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | 16 | import static com.google.common.base.Objects.firstNonNull; 17 | import static com.sun.jersey.api.core.ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS; 18 | import static com.sun.jersey.api.core.ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS; 19 | import static java.lang.Integer.parseInt; 20 | import static java.lang.String.format; 21 | 22 | public class PlanningServer { 23 | private final Injector injector; 24 | private HttpServer server; 25 | 26 | public PlanningServer(Module module) { 27 | this.injector = Guice.createInjector(module); 28 | } 29 | 30 | public static void main(String[] args) throws IOException { 31 | int port = parseInt(firstNonNull(System.getenv("PORT"), "8080")); 32 | String workingDir = firstNonNull(System.getenv("PLANNING_ROOT"), "data"); 33 | 34 | new PlanningServer(new PlanningServerModule(new File(workingDir))).start(port); 35 | } 36 | 37 | public void start(int port) throws IOException { 38 | System.out.println("Starting server on port: " + port); 39 | 40 | server = HttpServerFactory.create(format("http://localhost:%d/", port), configuration()); 41 | server.start(); 42 | } 43 | 44 | private ResourceConfig configuration() { 45 | DefaultResourceConfig config = new DefaultResourceConfig(); 46 | config.getSingletons().add(injector.getInstance(AuthenticationResource.class)); 47 | config.getSingletons().add(injector.getInstance(StaticResource.class)); 48 | config.getSingletons().add(injector.getInstance(PlanningResource.class)); 49 | 50 | config.getProperties().put(PROPERTY_CONTAINER_REQUEST_FILTERS, GZIPContentEncodingFilter.class); 51 | config.getProperties().put(PROPERTY_CONTAINER_RESPONSE_FILTERS, GZIPContentEncodingFilter.class); 52 | 53 | return config; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /web/js/jquery.scrollTo.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2012 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com 3 | * Dual licensed under MIT and GPL. 4 | * @author Ariel Flesler 5 | * @version 1.4.3.1 6 | */ 7 | ;(function($){var h=$.scrollTo=function(a,b,c){$(window).scrollTo(a,b,c)};h.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1,limit:true};h.window=function(a){return $(window)._scrollable()};$.fn._scrollable=function(){return this.map(function(){var a=this,isWin=!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!isWin)return a;var b=(a.contentWindow||a).document||a.ownerDocument||a;return/webkit/i.test(navigator.userAgent)||b.compatMode=='BackCompat'?b.body:b.documentElement})};$.fn.scrollTo=function(e,f,g){if(typeof f=='object'){g=f;f=0}if(typeof g=='function')g={onAfter:g};if(e=='max')e=9e9;g=$.extend({},h.defaults,g);f=f||g.duration;g.queue=g.queue&&g.axis.length>1;if(g.queue)f/=2;g.offset=both(g.offset);g.over=both(g.over);return this._scrollable().each(function(){if(e==null)return;var d=this,$elem=$(d),targ=e,toff,attr={},win=$elem.is('html,body');switch(typeof targ){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=$(targ,this);if(!targ.length)return;case'object':if(targ.is||targ.style)toff=(targ=$(targ)).offset()}$.each(g.axis.split(''),function(i,a){var b=a=='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,old=d[key],max=h.max(d,a);if(toff){attr[key]=toff[pos]+(win?0:old-$elem.offset()[pos]);if(g.margin){attr[key]-=parseInt(targ.css('margin'+b))||0;attr[key]-=parseInt(targ.css('border'+b+'Width'))||0}attr[key]+=g.offset[pos]||0;if(g.over[pos])attr[key]+=targ[a=='x'?'width':'height']()*g.over[pos]}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)=='%'?parseFloat(c)/100*max:c}if(g.limit&&/^\d+$/.test(attr[key]))attr[key]=attr[key]<=0?0:Math.min(attr[key],max);if(!i&&g.queue){if(old!=attr[key])animate(g.onAfterFirst);delete attr[key]}});animate(g.onAfter);function animate(a){$elem.animate(attr,f,g.easing,a&&function(){a.call(this,e,g)})}}).end()};h.max=function(a,b){var c=b=='x'?'Width':'Height',scroll='scroll'+c;if(!$(a).is('html,body'))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,html=a.ownerDocument.documentElement,body=a.ownerDocument.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery); -------------------------------------------------------------------------------- /src/main/java/resources/StaticResource.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import com.google.inject.Singleton; 4 | import org.jcoffeescript.JCoffeeScriptCompileException; 5 | import org.jcoffeescript.JCoffeeScriptCompiler; 6 | import org.jcoffeescript.Option; 7 | import org.lesscss.LessCompiler; 8 | import org.lesscss.LessException; 9 | 10 | import javax.ws.rs.GET; 11 | import javax.ws.rs.Path; 12 | import javax.ws.rs.PathParam; 13 | import javax.ws.rs.Produces; 14 | import javax.ws.rs.core.Response; 15 | 16 | import java.io.File; 17 | import java.io.IOException; 18 | import java.util.Arrays; 19 | 20 | @Path("/static/{version : [^/]*}/") 21 | @Singleton 22 | public class StaticResource extends AbstractResource { 23 | private final LessCompiler lessCompiler; 24 | private final JCoffeeScriptCompiler coffeeScriptCompiler; 25 | 26 | public StaticResource() { 27 | lessCompiler = new LessCompiler(); 28 | coffeeScriptCompiler = new JCoffeeScriptCompiler(Arrays.asList(Option.BARE)); 29 | } 30 | 31 | @GET 32 | @Path("{path : .*}.css") 33 | @Produces("text/css;charset=UTF-8") 34 | public Response cssOrLess(@PathParam("path") String path) throws IOException, LessException { 35 | // Css 36 | if (exists(path + ".css")) { 37 | return okTemplatize(file(path + ".css")); 38 | } 39 | 40 | // Less 41 | File output = new File("target", path + ".css"); 42 | synchronized (lessCompiler) { 43 | lessCompiler.compile(file(path + ".less"), output, false); 44 | } 45 | return okTemplatize(output); 46 | } 47 | 48 | @GET 49 | @Path("{path : .*}.js") 50 | @Produces("application/javascript;charset=UTF-8") 51 | public Response jsOrCoffee(@PathParam("path") String path) throws JCoffeeScriptCompileException { 52 | // Js 53 | if (exists(path + ".js")) { 54 | File js = file(path + ".js"); 55 | return ok(js); 56 | } 57 | 58 | // Coffee 59 | File coffee = file(path + ".coffee"); 60 | String js; 61 | synchronized (coffeeScriptCompiler) { 62 | js = coffeeScriptCompiler.compile(read(coffee)); 63 | } 64 | return ok(js, coffee.lastModified()); 65 | } 66 | 67 | @GET 68 | @Path("{path : .*\\.png}") 69 | @Produces("image/png") 70 | public Response png(@PathParam("path") String path) { 71 | return ok(file(path)); 72 | } 73 | 74 | @GET 75 | @Path("{path : .*\\.jpg}") 76 | @Produces("image/jpeg") 77 | public Response jpeg(@PathParam("path") String path) { 78 | return ok(file(path)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /web/planning.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layouts/default.html 3 | title: CodeStory - Devoxx Planning 4 | style: css/planning.css 5 | script: planning.js 6 | --- 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 | 15 | 16 |
by Code-Story
17 |
18 | 25 |
26 | 27 |
28 |
Chargement des présentations...
29 |
30 | 31 | 32 | 42 | 43 | -------------------------------------------------------------------------------- /src/test/java/resources/AuthenticationResourceTest.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import auth.Authenticator; 4 | import auth.User; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.InjectMocks; 8 | import org.mockito.Mock; 9 | import org.mockito.runners.MockitoJUnitRunner; 10 | 11 | import javax.ws.rs.core.Cookie; 12 | import javax.ws.rs.core.Response; 13 | 14 | import java.net.URI; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | import static com.google.common.collect.Lists.newArrayList; 19 | import static org.fest.assertions.Assertions.assertThat; 20 | import static org.fest.assertions.MapAssert.entry; 21 | import static org.mockito.Mockito.when; 22 | 23 | @RunWith(MockitoJUnitRunner.class) 24 | public class AuthenticationResourceTest { 25 | @Mock 26 | private Authenticator authenticator; 27 | 28 | @InjectMocks 29 | private AuthenticationResource resource; 30 | 31 | @Test 32 | public void should_redirect_to_twitter() throws Exception { 33 | when(authenticator.getAuthenticateURI()).thenReturn(URI.create("http://exemple.com/")); 34 | 35 | Response response = resource.authenticate(); 36 | 37 | assertRedirected(response, "http://exemple.com/"); 38 | } 39 | 40 | private Map> assertRedirected(Response response, String location) { 41 | assertThat(response.getStatus()).isEqualTo(Response.Status.SEE_OTHER.getStatusCode()); 42 | Map> metadata = response.getMetadata(); 43 | assertThat(metadata).includes(entry("Location", newArrayList(URI.create(location)))); 44 | return metadata; 45 | } 46 | 47 | @Test 48 | public void with_granted_user_should_authenticate_on_twitter_callback() throws Exception { 49 | User user = new User(42L, "arnold", "token", "secret"); 50 | when(authenticator.authenticate("oauthToken", "oauthVerifier")).thenReturn(user); 51 | 52 | Response response = resource.authenticated("oauthToken", "oauthVerifier"); 53 | 54 | Map> metadata = assertRedirected(response, "/"); 55 | List cookies = metadata.get("Set-Cookie"); 56 | assertThat(cookies).hasSize(2); 57 | Cookie userIdCookie = (Cookie) cookies.get(0); 58 | assertThat(userIdCookie.getName()).isEqualTo("userId"); 59 | assertThat(userIdCookie.getValue()).isEqualTo("42"); 60 | Cookie screenNameCookie = (Cookie) cookies.get(1); 61 | assertThat(screenNameCookie.getName()).isEqualTo("screenName"); 62 | assertThat(screenNameCookie.getValue()).isEqualTo("arnold"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/misc/PhantomJsTest.java: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import org.fluentlenium.core.Fluent; 4 | import org.fluentlenium.core.FluentAdapter; 5 | import org.junit.Rule; 6 | import org.junit.rules.TestWatcher; 7 | import org.junit.runner.Description; 8 | import org.openqa.selenium.Dimension; 9 | import org.openqa.selenium.WebDriver; 10 | import org.openqa.selenium.phantomjs.PhantomJSDriver; 11 | import org.openqa.selenium.phantomjs.PhantomJSDriverService; 12 | import org.openqa.selenium.remote.DesiredCapabilities; 13 | import org.openqa.selenium.remote.service.DriverService; 14 | 15 | import java.io.File; 16 | 17 | import static org.openqa.selenium.phantomjs.PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY; 18 | 19 | public abstract class PhantomJsTest extends FluentAdapter { 20 | private static final Dimension DEFAULT_WINDOW_SIZE = new Dimension(1024, 768); 21 | 22 | private static WebDriver driver; 23 | 24 | @Rule 25 | public LifeCycle lifecycle = new LifeCycle(); 26 | 27 | protected abstract String defaultUrl(); 28 | 29 | public Fluent goTo(String url) { 30 | withDefaultUrl(defaultUrl()); 31 | return super.goTo(url); 32 | } 33 | 34 | class LifeCycle extends TestWatcher { 35 | @Override 36 | protected void starting(Description description) { 37 | if (null == driver) { 38 | Runtime.getRuntime().addShutdownHook(new Thread() { 39 | @Override 40 | public void run() { 41 | if (driver != null) { 42 | driver.quit(); 43 | } 44 | } 45 | }); 46 | 47 | driver = createDriver(); 48 | } 49 | 50 | driver.manage().deleteAllCookies(); 51 | driver.manage().window().setSize(DEFAULT_WINDOW_SIZE); 52 | initFluent(driver); 53 | } 54 | 55 | @Override 56 | protected void succeeded(Description description) { 57 | snapshotFile(description).delete(); 58 | } 59 | 60 | @Override 61 | protected void failed(Throwable e, Description description) { 62 | takeScreenShot(snapshotFile(description).getAbsolutePath()); 63 | } 64 | 65 | private File snapshotFile(Description description) { 66 | return new File("snapshots", description.getMethodName() + ".png"); 67 | } 68 | 69 | private WebDriver createDriver() { 70 | File phantomJsExe = new PhantomJsDownloader().downloadAndExtract(); 71 | 72 | DesiredCapabilities capabilities = new DesiredCapabilities(); 73 | capabilities.setCapability(PHANTOMJS_EXECUTABLE_PATH_PROPERTY, phantomJsExe.getAbsolutePath()); 74 | 75 | DriverService service = PhantomJSDriverService.createDefaultService(capabilities); 76 | 77 | return new PhantomJSDriver(service, capabilities); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/auth/TwitterAuthenticatorTest.java: -------------------------------------------------------------------------------- 1 | package auth; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.mockito.InjectMocks; 6 | import org.mockito.Mock; 7 | import org.powermock.core.classloader.annotations.PrepareForTest; 8 | import org.powermock.modules.junit4.PowerMockRunner; 9 | import twitter4j.Twitter; 10 | import twitter4j.TwitterException; 11 | import twitter4j.TwitterFactory; 12 | import twitter4j.auth.AccessToken; 13 | import twitter4j.auth.RequestToken; 14 | 15 | import java.net.MalformedURLException; 16 | import java.net.URI; 17 | 18 | import static org.fest.assertions.Assertions.assertThat; 19 | import static org.mockito.Matchers.anyString; 20 | import static org.mockito.Mockito.any; 21 | import static org.mockito.Mockito.eq; 22 | import static org.mockito.Mockito.mock; 23 | import static org.mockito.Mockito.verify; 24 | import static org.mockito.Mockito.when; 25 | 26 | @RunWith(PowerMockRunner.class) 27 | @PrepareForTest({TwitterFactory.class, RequestToken.class}) 28 | public class TwitterAuthenticatorTest { 29 | @Mock 30 | TwitterFactory twitterFactory; 31 | 32 | @Mock 33 | Twitter twitter; 34 | 35 | @InjectMocks 36 | TwitterAuthenticator authenticator; 37 | 38 | @Test 39 | public void should_generate_an_authentication_URL() throws MalformedURLException, AuthenticationException, TwitterException { 40 | RequestToken requestToken = mock(RequestToken.class); 41 | when(twitterFactory.getInstance()).thenReturn(twitter); 42 | when(twitter.getOAuthRequestToken(anyString())).thenReturn(requestToken); 43 | when(requestToken.getAuthenticationURL()).thenReturn("http://api.twitter.com/oauth/authenticate?oauth_token=fake"); 44 | 45 | URI authenticateURI = authenticator.getAuthenticateURI(); 46 | 47 | assertThat(authenticateURI).isEqualTo(URI.create("http://api.twitter.com/oauth/authenticate?oauth_token=fake")); 48 | } 49 | 50 | @Test(expected = AuthenticationException.class) 51 | public void should_raise_exception_when_authentication_fails() throws MalformedURLException, TwitterException { 52 | when(twitterFactory.getInstance()).thenReturn(twitter); 53 | when(twitter.getOAuthAccessToken(any(RequestToken.class), eq("bar"))).thenThrow(new TwitterException("", null, 401)); 54 | 55 | authenticator.authenticate("", "bar"); 56 | } 57 | 58 | @Test 59 | public void should_authenticate() throws MalformedURLException, TwitterException { 60 | AccessToken accessToken = mock(AccessToken.class); 61 | when(twitterFactory.getInstance()).thenReturn(twitter); 62 | when(twitter.getOAuthAccessToken(any(RequestToken.class), eq("foo"))).thenReturn(accessToken); 63 | 64 | User user = authenticator.authenticate("", "foo"); 65 | 66 | assertThat(user).isNotNull(); 67 | verify(accessToken).getUserId(); 68 | verify(accessToken).getScreenName(); 69 | verify(accessToken).getToken(); 70 | verify(accessToken).getTokenSecret(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/resources/AbstractResource.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.base.Throwables; 5 | import com.google.common.collect.ImmutableMap; 6 | import com.google.common.io.Files; 7 | import com.google.gson.Gson; 8 | import com.sun.jersey.api.NotFoundException; 9 | import templating.ContentWithVariables; 10 | import templating.Layout; 11 | import templating.Template; 12 | import templating.YamlFrontMatter; 13 | 14 | import javax.ws.rs.core.Response; 15 | 16 | import java.io.File; 17 | import java.io.IOException; 18 | import java.net.URI; 19 | import java.util.Date; 20 | import java.util.Map; 21 | 22 | abstract class AbstractResource { 23 | private static final long ONE_YEAR = 1000L * 3600 * 24 * 365; 24 | 25 | protected Response seeOther(URI uri) { 26 | return Response.seeOther(uri).build(); 27 | } 28 | 29 | protected Response seeOther(String url) { 30 | return seeOther(URI.create(url)); 31 | } 32 | 33 | protected Response ok(Object entity, long modified) { 34 | return Response.ok(entity).lastModified(new Date(modified)).expires(new Date(modified + ONE_YEAR)).header("Cache-Control", "public").build(); 35 | } 36 | 37 | protected Response ok(File file) { 38 | return ok(file, file.lastModified()); 39 | } 40 | 41 | protected Response okTemplatize(File file) { 42 | return ok(templatize(file, ImmutableMap.of()), file.lastModified()); 43 | } 44 | 45 | protected String jsonp(Object entity, String callback) { 46 | String json = (entity instanceof String) ? (String) entity : new Gson().toJson(entity); 47 | 48 | if (callback != null) { 49 | return callback + "(" + json + ")"; 50 | } 51 | 52 | return json; 53 | } 54 | 55 | protected String templatize(File file, Map variables) { 56 | ContentWithVariables yaml = new YamlFrontMatter().parse(read(file)); 57 | 58 | Map yamlVariables = yaml.getVariables(); 59 | String content = yaml.getContent(); 60 | 61 | String layout = yamlVariables.get("layout"); 62 | if (layout != null) { 63 | content = new Layout(read(file(layout))).apply(content); 64 | } 65 | 66 | return new Template().apply(content, ImmutableMap.builder().putAll(variables).putAll(yamlVariables).build()); 67 | } 68 | 69 | protected String read(String path) { 70 | return read(file(path)); 71 | } 72 | 73 | protected String read(File file) { 74 | try { 75 | return Files.toString(file, Charsets.UTF_8); 76 | } catch (IOException e) { 77 | throw Throwables.propagate(e); 78 | } 79 | } 80 | 81 | protected File file(String path) { 82 | if (!exists(path)) { 83 | throw new NotFoundException(); 84 | } 85 | return new File("web", path); 86 | } 87 | 88 | protected boolean exists(String path) { 89 | if (path.endsWith("/")) { 90 | return false; 91 | } 92 | 93 | try { 94 | File root = new File("web"); 95 | File file = new File(root, path); 96 | if (!file.exists() || !file.getCanonicalPath().startsWith(root.getCanonicalPath())) { 97 | return false; 98 | } 99 | 100 | return true; 101 | } catch (IOException e) { 102 | return false; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/HomePageTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.fest.assertions.Assertions.assertThat; 4 | 5 | public class HomePageTest extends AbstractPageTest { 6 | @Test 7 | public void should_display_homepage() { 8 | goTo("/"); 9 | 10 | assertThat(title()).isEqualTo("CodeStory - Devoxx Planning"); 11 | } 12 | 13 | @Test 14 | public void should_display_toc() { 15 | goTo("/"); 16 | 17 | assertThat(text(".toc a")).contains("09:30"); 18 | } 19 | 20 | @Test 21 | public void should_display_sessions() { 22 | goTo("/"); 23 | 24 | assertThat(text("#talk-1253 h2")).contains("Android Bad Practices : comment foirer son app (Conference)"); 25 | assertThat(text("#talk-1253 .speaker")).contains("Pierre-yves Ricau @Auditorium Vendredi de 17:00 à 17:50"); 26 | assertThat(text("#talk-1253 p")).contains("Comment écrire du code incompréhensible ? Comment garantir l'incompatibilité avec les différentes versions d'Android ? Comment faire planter une appli en cas de rotation, d'interaction avec la backstack ou de retour à la Home ? Comment construire un build non reproductible ? Venez découvrir de nombreuses erreurs, comment les expliquer et comment les éviter à l'avenir. Nous couvrirons des problématiques allant du build à l'archi, avec au milieu beaucoup de code. J'en profiterais pour vous présenter la stack technique de Square.)"); 27 | } 28 | 29 | @Test 30 | public void should_show_no_star_if_not_logged_in() { 31 | goTo("/"); 32 | 33 | assertThat(find("#talk-1253 .star")).isNotEmpty(); 34 | assertThat(find("#talk-1242 .star")).isNotEmpty(); 35 | assertThat(find("#talk-1253 .starred")).isEmpty(); 36 | assertThat(find("#talk-1242 .starred")).isEmpty(); 37 | } 38 | 39 | @Test 40 | public void should_redirect_to_authenticate_when_user_stars() { 41 | goTo("/"); 42 | 43 | assertThat(text("#auth a")).contains("Se connecter"); 44 | assertThat(text("#screenName")).isEmpty(); 45 | assertThat(getCookie("userId")).isNull(); 46 | assertThat(getCookie("screenName")).isNull(); 47 | 48 | click("#talk-1253 .star"); 49 | 50 | assertThat(text("#auth a")).contains("Déconnexion"); 51 | assertThat(url()).endsWith("/"); 52 | } 53 | 54 | @Test 55 | public void should_log_in() { 56 | goTo("/"); 57 | 58 | click("#login"); 59 | 60 | assertThat(text("#auth a")).contains("Déconnexion"); 61 | assertThat(text("#screenName")).contains("@arnold"); 62 | assertThat(getCookie("userId").getValue()).isEqualTo("42"); 63 | assertThat(getCookie("screenName").getValue()).isEqualTo("arnold"); 64 | } 65 | 66 | @Test 67 | public void should_log_out() { 68 | goTo("/"); 69 | 70 | click("#login"); 71 | click("#logout"); 72 | 73 | assertThat(text("#auth a")).contains("Se connecter"); 74 | assertThat(text("#screenName")).isEmpty(); 75 | assertThat(getCookie("userId")).isNull(); 76 | assertThat(getCookie("#screenName")).isNull(); 77 | } 78 | 79 | @Test 80 | public void should_star() { 81 | goTo("/"); 82 | 83 | click("#login"); 84 | assertThat(find("#talk-1242 .starred")).isEmpty(); 85 | 86 | click("#talk-1242 .star"); 87 | assertThat(find("#talk-1242 .starred")).isNotEmpty(); 88 | 89 | click("#talk-1242 .starred"); 90 | assertThat(find("#talk-1242 .starred")).isEmpty(); 91 | } 92 | 93 | @Test 94 | public void should_filter_with_url() { 95 | goTo("/?q=foo"); 96 | 97 | assertThat(find("#search_box").getValue()).isEqualTo("foo"); 98 | } 99 | } -------------------------------------------------------------------------------- /src/test/java/planning/PlanningTest.java: -------------------------------------------------------------------------------- 1 | package planning; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.base.Joiner; 5 | import com.google.common.io.Files; 6 | import org.junit.Before; 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.rules.TemporaryFolder; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.Map; 14 | 15 | import static org.fest.assertions.Assertions.assertThat; 16 | import static org.fest.assertions.MapAssert.entry; 17 | 18 | public class PlanningTest { 19 | Planning planning; 20 | File root; 21 | 22 | @Rule 23 | public TemporaryFolder temporaryFolder = new TemporaryFolder(); 24 | 25 | @Before 26 | public void setUp() throws IOException { 27 | root = temporaryFolder.newFolder(); 28 | planning = new Planning(root); 29 | } 30 | 31 | @Test 32 | public void should_read_stars_for_empty_planning() { 33 | assertThat(planning.stars("dgageot")).isEmpty(); 34 | } 35 | 36 | @Test 37 | public void should_star() { 38 | planning.star("dgageot", "talk01"); 39 | 40 | assertThat(planning.stars("dgageot")).containsOnly("talk01"); 41 | } 42 | 43 | @Test 44 | public void should_star_multiple_users() { 45 | planning.star("dgageot", "talk01"); 46 | planning.star("dgageot", "talk02"); 47 | planning.star("elemerdy", "talk01"); 48 | 49 | assertThat(planning.stars("dgageot")).containsOnly("talk01", "talk02"); 50 | assertThat(planning.stars("elemerdy")).containsOnly("talk01"); 51 | } 52 | 53 | @Test 54 | public void should_unstar() { 55 | planning.star("dgageot", "talk01"); 56 | planning.unstar("dgageot", "talk01"); 57 | 58 | assertThat(planning.stars("dgageot")).isEmpty(); 59 | } 60 | 61 | @Test 62 | public void should_load_from_files() throws IOException { 63 | file(root, "dgageot", "+talk01", "+talk02", "+talk03", "-talk03"); 64 | file(root, "elemerdy", "+talk05"); 65 | 66 | planning = new Planning(root); 67 | 68 | assertThat(planning.stars("dgageot")).containsOnly("talk01", "talk02"); 69 | assertThat(planning.stars("elemerdy")).containsOnly("talk05"); 70 | } 71 | 72 | @Test 73 | public void should_save_stars_to_disk() throws IOException { 74 | planning.star("dgageot", "talk01"); 75 | planning.star("dgageot", "talk02"); 76 | planning.star("elemerdy", "talk01"); 77 | 78 | Planning otherPlanning = new Planning(root); 79 | 80 | assertThat(otherPlanning.stars("dgageot")).containsOnly("talk01", "talk02"); 81 | assertThat(otherPlanning.stars("elemerdy")).containsOnly("talk01"); 82 | } 83 | 84 | @Test 85 | public void should_count_stars_per_talk() { 86 | planning.star("dgageot", "talk01"); 87 | planning.star("dgageot", "talk02"); 88 | planning.star("elemerdy", "talk01"); 89 | 90 | Map countPerTalk = planning.countPerTalk(); 91 | 92 | assertThat(countPerTalk).includes(entry("talk01", 2L), entry("talk02", 1L)); 93 | } 94 | 95 | @Test 96 | public void should_count_stars_loaded_from_file() throws IOException { 97 | file(root, "dgageot", "+talk01", "+talk02", "+talk03", "-talk03"); 98 | 99 | planning = new Planning(root); 100 | Map countPerTalk = planning.countPerTalk(); 101 | 102 | assertThat(countPerTalk).includes(entry("talk01", 1L), entry("talk02", 1L)); 103 | } 104 | 105 | @Test 106 | public void should_count_stars_for_empty_planning() { 107 | Map countPerTalk = planning.countPerTalk(); 108 | 109 | assertThat(countPerTalk).isEmpty(); 110 | } 111 | 112 | static void file(File root, String login, String... content) throws IOException { 113 | File userFile = new File(root, login); 114 | Files.write(Joiner.on("\n").join(content), userFile, Charsets.UTF_8); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/planning/Planning.java: -------------------------------------------------------------------------------- 1 | package planning; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.base.Throwables; 5 | import com.google.common.cache.CacheBuilder; 6 | import com.google.common.cache.CacheLoader; 7 | import com.google.common.cache.LoadingCache; 8 | import com.google.common.collect.ImmutableSet; 9 | import com.google.common.collect.Sets; 10 | import com.google.common.io.Files; 11 | import com.google.common.io.LineProcessor; 12 | import com.google.common.util.concurrent.AtomicLongMap; 13 | import com.google.inject.Inject; 14 | import com.google.inject.Singleton; 15 | import com.google.inject.name.Named; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.util.Map; 20 | import java.util.Set; 21 | 22 | @Singleton 23 | public class Planning { 24 | private final File root; 25 | private final LoadingCache> talksPerUser = CacheBuilder.newBuilder().build(new CacheLoader>() { 26 | @Override 27 | public Set load(String key) { 28 | return Sets.newHashSet(); 29 | } 30 | }); 31 | private final AtomicLongMap starsPerTalk = AtomicLongMap.create(); 32 | 33 | @Inject 34 | public Planning(@Named("planning.root") File root) throws IOException { 35 | this.root = root; 36 | load(root); 37 | } 38 | 39 | public Set stars(String login) { 40 | Set userTalks = userTalks(login); 41 | synchronized (userTalks) { 42 | return ImmutableSet.copyOf(userTalks); 43 | } 44 | } 45 | 46 | public void star(String login, String talkId) { 47 | Set userTalks = userTalks(login); 48 | synchronized (userTalks) { 49 | userTalks.add(talkId); 50 | 51 | writeToFile(login, "+", talkId); 52 | } 53 | starsPerTalk.incrementAndGet(talkId); 54 | } 55 | 56 | public void unstar(String login, String talkId) { 57 | Set userTalks = userTalks(login); 58 | synchronized (userTalks) { 59 | userTalks.remove(talkId); 60 | 61 | writeToFile(login, "-", talkId); 62 | } 63 | starsPerTalk.decrementAndGet(talkId); 64 | } 65 | 66 | private void writeToFile(String login, String action, String talkId) { 67 | try { 68 | File file = new File(root, login); 69 | Files.append(action + talkId + "\n", file, Charsets.UTF_8); 70 | } catch (IOException e) { 71 | throw Throwables.propagate(e); 72 | } 73 | } 74 | 75 | private Set userTalks(String login) { 76 | return talksPerUser.getUnchecked(login); 77 | } 78 | 79 | private void load(File root) throws IOException { 80 | root.mkdirs(); 81 | 82 | File[] files = root.listFiles(); 83 | if (null == files) { 84 | return; 85 | } 86 | 87 | for (File file : files) { 88 | String login = file.getName(); 89 | Set talkIds = loadUserTalks(file); 90 | 91 | talksPerUser.put(login, talkIds); 92 | 93 | for (String talkId : talkIds) { 94 | starsPerTalk.incrementAndGet(talkId); 95 | } 96 | } 97 | } 98 | 99 | private Set loadUserTalks(File file) throws IOException { 100 | return Files.readLines(file, Charsets.UTF_8, new LineProcessor>() { 101 | private final Set talkIds = Sets.newHashSet(); 102 | 103 | @Override 104 | public boolean processLine(String line) { 105 | String talkId = line.substring(1); 106 | 107 | if (line.startsWith("-")) { 108 | talkIds.remove(talkId); 109 | } else { 110 | talkIds.add(talkId); 111 | } 112 | 113 | return true; 114 | } 115 | 116 | @Override 117 | public Set getResult() { 118 | return talkIds; 119 | } 120 | }); 121 | } 122 | 123 | public Map countPerTalk() { 124 | return starsPerTalk.asMap(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/test/java/misc/PhantomJsDownloader.java: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import com.google.common.io.ByteStreams; 4 | import com.google.common.io.Files; 5 | import com.google.common.io.InputSupplier; 6 | import com.google.common.io.OutputSupplier; 7 | import com.google.common.io.Resources; 8 | 9 | import java.io.File; 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.net.URI; 14 | import java.util.Enumeration; 15 | import java.util.zip.ZipEntry; 16 | import java.util.zip.ZipFile; 17 | 18 | class PhantomJsDownloader { 19 | private final boolean isWindows; 20 | private final boolean isMac; 21 | 22 | PhantomJsDownloader() { 23 | isWindows = System.getProperty("os.name").startsWith("Windows"); 24 | isMac = System.getProperty("os.name").startsWith("Mac OS X"); 25 | } 26 | 27 | public File downloadAndExtract() { 28 | File installDir = new File(new File(System.getProperty("user.home")), ".phantomjstest"); 29 | 30 | String url; 31 | File phantomJsExe; 32 | if (isWindows) { 33 | url = "http://phantomjs.googlecode.com/files/phantomjs-1.8.1-windows.zip"; 34 | phantomJsExe = new File(installDir, "phantomjs-1.8.1-windows/phantomjs.exe"); 35 | } else if (isMac) { 36 | url = "http://phantomjs.googlecode.com/files/phantomjs-1.8.1-macosx.zip"; 37 | phantomJsExe = new File(installDir, "phantomjs-1.8.1-macosx/bin/phantomjs"); 38 | } else { 39 | url = "http://phantomjs.googlecode.com/files/phantomjs-1.8.1-linux-x86_64.tar.bz2"; 40 | phantomJsExe = new File(installDir, "phantomjs-1.8.1-linux-x86_64/bin/phantomjs"); 41 | } 42 | 43 | extractExe(url, installDir, phantomJsExe); 44 | 45 | return phantomJsExe; 46 | } 47 | 48 | private void extractExe(String url, File phantomInstallDir, File phantomJsExe) { 49 | if (phantomJsExe.exists()) { 50 | return; 51 | } 52 | 53 | File targetZip = new File(phantomInstallDir, "phantomjs.zip"); 54 | downloadZip(url, targetZip); 55 | 56 | System.out.println("Extracting phantomjs"); 57 | try { 58 | if (isWindows) { 59 | unzip(targetZip, phantomInstallDir); 60 | } else if (isMac) { 61 | new ProcessBuilder().command("/usr/bin/unzip", "-qo", "phantomjs.zip").directory(phantomInstallDir).start().waitFor(); 62 | } else { 63 | new ProcessBuilder().command("/usr/bin/tar", "-xjvf", "phantomjs.zip").directory(phantomInstallDir).start().waitFor(); 64 | } 65 | } catch (Exception e) { 66 | throw new IllegalStateException("Unable to unzip phantomjs from " + targetZip.getAbsolutePath()); 67 | } 68 | } 69 | 70 | private void downloadZip(String url, File targetZip) { 71 | if (targetZip.exists()) { 72 | return; 73 | } 74 | 75 | System.out.println("Downloading phantomjs from " + url + "..."); 76 | 77 | File zipTemp = new File(targetZip.getAbsolutePath() + ".temp"); 78 | try { 79 | zipTemp.getParentFile().mkdirs(); 80 | 81 | InputSupplier input = Resources.newInputStreamSupplier(URI.create(url).toURL()); 82 | OutputSupplier ouput = Files.newOutputStreamSupplier(zipTemp); 83 | 84 | ByteStreams.copy(input, ouput); 85 | } catch (IOException e) { 86 | throw new IllegalStateException("Unable to download phantomjs from " + url); 87 | } 88 | 89 | zipTemp.renameTo(targetZip); 90 | } 91 | 92 | private static void unzip(File zip, File toDir) throws IOException { 93 | final ZipFile zipFile = new ZipFile(zip); 94 | try { 95 | Enumeration entries = zipFile.entries(); 96 | while (entries.hasMoreElements()) { 97 | final ZipEntry entry = entries.nextElement(); 98 | if (entry.isDirectory()) { 99 | continue; 100 | } 101 | 102 | File to = new File(toDir, entry.getName()); 103 | to.getParentFile().mkdirs(); 104 | 105 | Files.copy(new InputSupplier() { 106 | @Override 107 | public InputStream getInput() throws IOException { 108 | return zipFile.getInputStream(entry); 109 | } 110 | }, to); 111 | } 112 | } finally { 113 | zipFile.close(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | codestory 8 | planning 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 1.6 13 | 1.6 14 | UTF-8 15 | 1.17 16 | 17 | 18 | 19 | 20 | 21 | maven-dependency-plugin 22 | 23 | 24 | copy-dependencies 25 | install 26 | 27 | copy-dependencies 28 | 29 | 30 | compile 31 | runtime 32 | test 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | junit 44 | junit 45 | 4.11 46 | test 47 | 48 | 49 | org.fluentlenium 50 | fluentlenium-core 51 | 0.7.5 52 | test 53 | 54 | 55 | junit 56 | junit 57 | 58 | 59 | 60 | 61 | com.github.detro.ghostdriver 62 | phantomjsdriver 63 | 1.0.3-dev 64 | test 65 | 66 | 67 | org.easytesting 68 | fest-assert 69 | 1.4 70 | test 71 | 72 | 73 | org.mockito 74 | mockito-core 75 | 1.9.5 76 | test 77 | 78 | 79 | org.powermock 80 | powermock-module-junit4 81 | 1.5 82 | test 83 | 84 | 85 | org.powermock 86 | powermock-api-mockito 87 | 1.5 88 | test 89 | 90 | 91 | org.powermock 92 | powermock-core 93 | 1.5 94 | test 95 | 96 | 97 | 98 | 99 | com.google.code.maven-play-plugin.com.github.yeungda.jcoffeescript 100 | jcoffeescript 101 | 1.0 102 | 103 | 104 | com.sun.jersey 105 | jersey-server 106 | ${jersey.version} 107 | 108 | 109 | com.sun.jersey 110 | jersey-core 111 | ${jersey.version} 112 | 113 | 114 | com.github.spullara.mustache.java 115 | compiler 116 | 0.8.10 117 | 118 | 119 | org.lesscss 120 | lesscss 121 | 1.3.1 122 | 123 | 124 | com.google.guava 125 | guava 126 | 14.0 127 | 128 | 129 | com.google.inject 130 | guice 131 | 3.0 132 | 133 | 134 | com.google.code.gson 135 | gson 136 | 2.2.2 137 | 138 | 139 | org.twitter4j 140 | twitter4j-core 141 | 2.2.6 142 | 143 | 144 | org.apache.commons 145 | commons-lang3 146 | 3.1 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /web/js/planning.js: -------------------------------------------------------------------------------- 1 | var dayLabels = { 2 | '2013-03-27': 'Mercredi 27 mars 2013', 3 | '2013-03-28': 'Jeudi 28 mars 2013', 4 | '2013-03-29': 'Vendredi 29 mars 2013' 5 | }; 6 | 7 | var dayNames = { 8 | '2013-03-27': 'Mercredi', 9 | '2013-03-28': 'Jeudi', 10 | '2013-03-29': 'Vendredi' 11 | }; 12 | 13 | function set_content(selector, template, data) { 14 | $(selector).html(Hogan.compile($(template).html()).render(data)); 15 | } 16 | 17 | function initAuthenticationState() { 18 | var authenticated = ($.cookie('userId') != null); 19 | var headerTemplateParameters = { 20 | 'authenticated': authenticated 21 | }; 22 | if (authenticated) { 23 | headerTemplateParameters.screenName = $.cookie('screenName'); 24 | } 25 | set_content('#auth', '#header-template', headerTemplateParameters); 26 | } 27 | 28 | function refreshStars() { 29 | var authenticated = ($.cookie('userId') != null); 30 | if (!authenticated) { 31 | return $.when(); 32 | } 33 | 34 | return $.getJSON('stars', function (json) { 35 | $('.star').removeClass('starred').html(''); 36 | 37 | _.each(json, function (talkId) { 38 | $('#talk-' + talkId + ' .star').addClass('starred').html('starred'); 39 | }); 40 | }); 41 | } 42 | 43 | function enrichPlanning(planning) { 44 | _.each(planning.days, function (day) { 45 | day.dayLabel = dayLabels[day.day]; 46 | day.dayName = dayNames[day.day]; 47 | 48 | _.each(day.slots, function (slot) { 49 | slot.slot_slug = slot.slot.replace(':', ''); 50 | _.each(slot.talks, function (talk) { 51 | talk.speakers_string = talk.speakers.join(', '); 52 | }); 53 | }); 54 | }); 55 | } 56 | 57 | function refreshPlanning() { 58 | return $.getJSON('planning.json', function (json) { 59 | enrichPlanning(json); 60 | set_content('#content', '#talks-template', json); 61 | refreshStars().done(filterTalks); 62 | }); 63 | } 64 | 65 | function listenStarClicks() { 66 | $(document).on('click', '.star', function () { 67 | var talkId = $(this).attr('data-talk'); 68 | 69 | if ($(this).hasClass('starred')) { 70 | $.post('unstar', { talkId: talkId }, refreshStars).error(redirectToAuthentication); 71 | } else { 72 | $.post('star', { talkId: talkId }, refreshStars).error(redirectToAuthentication); 73 | } 74 | 75 | return false; 76 | }); 77 | } 78 | 79 | function redirectToAuthentication() { 80 | window.location = "/user/authenticate"; 81 | } 82 | 83 | var filter = undefined; 84 | 85 | function filterTalks() { 86 | var text = $('#search_box').val(); 87 | if (text == filter) { 88 | return; 89 | } 90 | filter = text; 91 | 92 | $('.day_wrapper, .toc-link, .slot, .talk').show(); 93 | 94 | if (text.length < 3) { 95 | return; 96 | } 97 | 98 | var words = _.filter(text.toLowerCase().split(' '), function (word) { 99 | return word.length > 0; 100 | }); 101 | 102 | $('.talk').filter(function () { 103 | var fulltext = $(this).text().toLowerCase(); 104 | return !_.all(words, function (word) { 105 | return fulltext.indexOf(word) > 0; 106 | }); 107 | }).hide(); 108 | 109 | $('.slot:not(:has(.talk:visible))').hide(); 110 | $('.day_wrapper:not(:has(.slot:visible))').hide(); 111 | $('.hour:hidden').each(function () { 112 | $('.toc-link[href="#' + this.id + '"]').hide(); 113 | }); 114 | } 115 | 116 | function supports_history_api() { 117 | return !!(window.history && history.pushState); 118 | } 119 | 120 | function updateUrlWithSearchQuery() { 121 | if (!supports_history_api()) { 122 | return; 123 | } 124 | 125 | var searchQuery = $('#search_box').val(); 126 | var url = $.url(); 127 | var newUrl = url.attr('protocol') + '://' + url.attr('host'); 128 | if (url.attr('port') != '80') { 129 | newUrl += ':' + url.attr('port'); 130 | } 131 | newUrl += url.attr('path'); 132 | if (searchQuery != '') { 133 | newUrl += '?q=' + searchQuery; 134 | } 135 | 136 | if (url.attr('source') != newUrl) { 137 | history.pushState(null, null, newUrl); 138 | } 139 | } 140 | 141 | function listenSearch() { 142 | var searchTimer = null; 143 | 144 | $(document).on('keyup', '#search_box', function () { 145 | if (searchTimer == null) { 146 | searchTimer = window.setTimeout(function () { 147 | searchTimer = null; 148 | updateUrlWithSearchQuery(); 149 | filterTalks(); 150 | }, 700); 151 | } 152 | }); 153 | } 154 | 155 | function setFilterFromQueryString() { 156 | $('#search_box').val($.url().param('q')); 157 | } 158 | 159 | function listenBackButton() { 160 | window.addEventListener("popstate", function () { 161 | setFilterFromQueryString(); 162 | filterTalks(); 163 | }); 164 | } 165 | 166 | function ignoreEnterKey() { 167 | $(document).on('keypress', '#search_box', function (e) { 168 | if (e.which == 13) { 169 | return false; 170 | } 171 | }); 172 | } 173 | 174 | function listenGoto() { 175 | $(document).on('click', 'a[href^="#"]', function () { 176 | console.log(this.hash); 177 | $.scrollTo(this.hash, 500, {}); 178 | return false; 179 | }); 180 | } 181 | 182 | $(document).ready(function () { 183 | initAuthenticationState(); 184 | ignoreEnterKey(); 185 | setFilterFromQueryString(); 186 | refreshPlanning().done(function () { 187 | listenStarClicks(); 188 | listenSearch(); 189 | listenBackButton(); 190 | listenGoto(); 191 | }); 192 | }); -------------------------------------------------------------------------------- /web/js/hogan.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */var HoganTemplate=function(){function a(a){this.text=a}function h(a){var h=String(a===null?"":a);return g.test(h)?h.replace(b,"&").replace(c,"<").replace(d,">").replace(e,"'").replace(f,"""):h}a.prototype={r:function(a,b){return""},v:h,render:function(a,b){return this.r(a,b)},rp:function(a,b,c,d){var e=c[a];return e?e.render(b,c):""},rs:function(a,b,c){var d="",e=a[a.length-1];if(!i(e))return d=c(a,b),d;for(var f=0;f=0;h--){f=b[h];if(f&&typeof f=="object"&&a in f){e=f[a],g=!0;break}}return g?(!d&&typeof e=="function"&&(e=this.lv(e,b,c)),e):d?!1:""},ho:function(a,b,c,d){var e=a.call(b,d,function(a){return Hogan.compile(a).render(b)}),f=Hogan.compile(e.toString()).render(b,c);return this.b=f,!1},b:"",ls:function(a,b,c,d,e){var f=b[b.length-1];if(a.length>0)return this.ho(a,f,c,this.text.substring(d,e));var g=a.call(f);return typeof g=="function"?this.ho(g,f,c,this.text.substring(d,e)):g},lv:function(a,b,c){var d=b[b.length-1];return Hogan.compile(a.call(d).toString()).render(d,c)}};var b=/&/g,c=//g,e=/\'/g,f=/\"/g,g=/[&<>\"\']/,i=Array.isArray||function(a){return Object.prototype.toString.call(a)==="[object Array]"};return a}(),Hogan=function(){function a(a){function s(){l.length>0&&(m.push(new String(l)),l="")}function t(){var a=!0;for(var b=p;b0){j=a.shift();if(j.tag=="#"||j.tag=="^"||g(j,d))c.push(j),j.nodes=f(a,j.tag,c,d),e.push(j);else{if(j.tag=="/"){if(c.length==0)throw new Error("Closing tag without opener: /"+j.n);i=c.pop();if(j.n!=i.n&&!h(j.n,i.n,d))throw new Error("Nesting error: "+i.n+" vs. "+j.n);return i.end=j.i,e}e.push(j)}}if(c.length>0)throw new Error("missing closing tag: "+c.pop().n);return e}function g(a,b){for(var c=0,d=b.length;c"?b+=s(a[c].n):e=="{"||e=="&"?b+=t(a[c].n,o(a[c].n)):e=="\n"?b+=v("\n"):e=="_v"?b+=u(a[c].n,o(a[c].n)):e===undefined&&(b+=v(a[c]))}return b}function q(a,b,c,d,e){var f="if(_.s(_."+c+'("'+n(b)+'",c,p,1),';return f+="c,p,0,"+d+","+e+")){",f+="b += _.rs(c,p,",f+='function(c,p){ var b = "";',f+=p(a),f+="return b;});c.pop();}",f+='else{b += _.b; _.b = ""};',f}function r(a,b,c){var d="if (!_.s(_."+c+'("'+n(b)+'",c,p,1),c,p,1,0,0)){';return d+=p(a),d+="};",d}function s(a){return'b += _.rp("'+n(a)+'",c[c.length - 1],p);'}function t(a,b){return"b += (_."+b+'("'+n(a)+'",c,p,0));'}function u(a,b){return"b += (_.v(_."+b+'("'+n(a)+'",c,p,0)));'}function v(a){return'b += "'+n(a)+'";'}var c=/\S/,d={"#":1,"^":2,"/":3,"!":4,">":5,"<":6,"=":7,_v:8,"{":9,"&":10},j=/\"/g,k=/\n/g,l=/\r/g,m=/\\/g;return{scan:a,parse:function(a,b){return b=b||{},f(a,"",[],b.sectionTags||[])},cache:{},compile:function(b,c){c=c||{};var d=this.cache[b];return d?d:(d=i(this.parse(a(b),c),b,c),this.cache[b]=d)}}}();typeof module!="undefined"&&module.exports?(module.exports=Hogan,module.exports.Template=HoganTemplate):typeof exports!="undefined"&&(exports.Hogan=Hogan,exports.HoganTemplate=HoganTemplate); -------------------------------------------------------------------------------- /web/css/planning.less: -------------------------------------------------------------------------------- 1 | @white: #FFFFFF; 2 | @black: #000000; 3 | @gray: #EEEEEE; 4 | @darkgray: #A9A9A9; 5 | @red: #FF0000; 6 | @blue: #0000FF; 7 | @green: #00FF00; 8 | @light_gray: #d3d3d3; 9 | @codestory_background: #375b63; 10 | @blue_devoxx: #009FE6; 11 | 12 | @font-face { 13 | font-family: 'Oxygen'; 14 | font-style: normal; 15 | font-weight: 400; 16 | src: local('Oxygen'), url(http://themes.googleusercontent.com/static/fonts/oxygen/v1/eAWT4YudG0otf3rlsJD6zOvvDin1pK8aKteLpeZ5c0A.woff) format('woff'); 17 | } 18 | 19 | @font-face { 20 | font-family: 'Sirin Stencil'; 21 | font-style: normal; 22 | font-weight: 400; 23 | src: local('SirinStencil'), local('SirinStencil-Regular'), url(http://themes.googleusercontent.com/static/fonts/sirinstencil/v1/pRpLdo0SawzO7MoBpvowsE-MuKPVvYIo0wJmbsvCQvE.woff) format('woff'); 24 | } 25 | 26 | ul { 27 | padding-left: 0; 28 | } 29 | 30 | li { 31 | list-style-type: none; 32 | } 33 | 34 | h1 { 35 | display: block; 36 | margin: 0 auto; 37 | width: 800px; 38 | text-align: center; 39 | margin-top: 30px; 40 | } 41 | 42 | body { 43 | background-color: @gray; 44 | font-family: 'Ubuntu', sans-serif; 45 | margin: 0; 46 | padding: 0; 47 | 48 | .anchor { 49 | margin-top: -255px; 50 | padding-top: 255px; 51 | visibility: hidden; 52 | height: 0; 53 | } 54 | 55 | header { 56 | background-color: @black; 57 | text-align: center; 58 | #styles > .title; 59 | position: fixed; 60 | top: 0px; 61 | width: 100%; 62 | z-index: 1000; 63 | -moz-box-shadow: 0 2px 2px @black; 64 | -webkit-box-shadow: 0 2px 2px @black; 65 | box-shadow: 0px 2px 2px @black; 66 | 67 | #logo-section { 68 | color: darken(@blue_devoxx, 10%); 69 | font-family: 'Sirin Stencil', cursive; 70 | font-weight: lighter; 71 | padding-top: 15px; 72 | padding-bottom: 15px; 73 | height: 128px; 74 | 75 | #logo { 76 | height: 80px; 77 | overflow: hidden; 78 | letter-spacing: 1em; 79 | padding-left: 1em; 80 | text-transform: uppercase; 81 | background-image: url('/static/[[version]]/img/devoxx-logo.png'); 82 | background-repeat: no-repeat; 83 | background-position: top; 84 | height: 100px; 85 | display: block; 86 | margin: 0 auto; 87 | color: darken(@blue_devoxx, 10%); 88 | text-decoration: none; 89 | line-height: 150px; 90 | vertical-align: bottom; 91 | } 92 | 93 | #attribution { 94 | text-align: right; 95 | font-size: .8em; 96 | padding-right: .5em; 97 | } 98 | } 99 | 100 | nav { 101 | #styles > .horizontal-section; 102 | background-color: @gray; 103 | font-size: .8em; 104 | overflow: hidden; 105 | white-space: nowrap; 106 | 107 | a { 108 | display: block; 109 | height: 50px; 110 | font-weight: bold; 111 | padding-left: 29px; 112 | text-decoration: none; 113 | color: @black; 114 | } 115 | 116 | #search_box { 117 | height: 20px; 118 | line-height: 20px; 119 | font-size: .8em; 120 | } 121 | 122 | #auth { 123 | height: 50px; 124 | float: right; 125 | 126 | #screenName { 127 | float: right; 128 | color: @black; 129 | margin-right: 12px; 130 | } 131 | 132 | #login, #logout { 133 | background: url('/static/[[version]]/img/sprites.png') 0 -73px no-repeat !important; 134 | float: right; 135 | height: 23px; 136 | } 137 | } 138 | 139 | #search_box { 140 | width: 320px; 141 | } 142 | } 143 | } 144 | 145 | .wait { 146 | display: block; 147 | padding-top: 30px; 148 | } 149 | } 150 | 151 | #content { 152 | margin-top: 208px; 153 | 154 | .day_wrapper { 155 | .goto { 156 | float: right; 157 | background: url('/static/[[version]]/img/sprites.png') 0 -96px no-repeat; 158 | height: 23px; 159 | width: 23px; 160 | } 161 | } 162 | 163 | #loading { 164 | text-align: center; 165 | margin: 0 auto; 166 | margin-top: 20px; 167 | } 168 | 169 | .day { 170 | #styles > .title; 171 | #styles > .horizontal-section; 172 | background-color: @black; 173 | } 174 | 175 | .toc { 176 | #styles > .horizontal-section; 177 | background-color: @black; 178 | text-align: center; 179 | 180 | a { 181 | margin: 0 10px; 182 | display: inline; 183 | color: white; 184 | } 185 | } 186 | 187 | .slot { 188 | #styles > .gradient(lighten(@blue_devoxx, 35%), @gray); 189 | 190 | .hour { 191 | font-size: 1.5em; 192 | #styles > .horizontal-section; 193 | } 194 | 195 | .talk { 196 | position: relative; 197 | background-color: @white; 198 | margin: 0 28px 34px; 199 | border: 18px solid @white; 200 | overflow: hidden; 201 | 202 | .star { 203 | position: absolute; 204 | top: 0; 205 | left: 0; 206 | width: 25px; 207 | height: 25px; 208 | background: url('/static/[[version]]/img/sprites.png') 0 0 no-repeat; 209 | color: transparent; 210 | } 211 | 212 | .starred { 213 | background: url('/static/[[version]]/img/sprites.png') 0 -25px no-repeat !important; 214 | } 215 | 216 | h2 { 217 | position: absolute; 218 | top: 3px; 219 | left: 30px; 220 | font-weight: bold; 221 | font-size: 125%; 222 | white-space: nowrap; 223 | text-align: left; 224 | margin: 0; 225 | width: 90%; 226 | overflow: hidden; 227 | } 228 | 229 | .speaker { 230 | position: absolute; 231 | top: 30px; 232 | color: slategray; 233 | font-size: 85%; 234 | white-space: nowrap; 235 | width: 100%; 236 | border-top: 1px solid @gray; 237 | padding-top: 10px; 238 | } 239 | 240 | .tag { 241 | float: right; 242 | color: @white; 243 | font-size: .8em; 244 | padding: 2px 10px; 245 | margin-top: 38px; 246 | margin-left: 5px; 247 | #styles > .rounded-corners(5px); 248 | #styles > .colored(@blue_devoxx); 249 | } 250 | 251 | p { 252 | margin: 80px 0 0 0; 253 | text-align: justify; 254 | } 255 | } 256 | } 257 | } 258 | 259 | #styles { 260 | .title { 261 | color: @white; 262 | font-size: 1.5em; 263 | text-align: center; 264 | } 265 | 266 | .colored(@color: @red) { 267 | background-color: @color; 268 | border: 1px solid darken(@color, 5%); 269 | } 270 | 271 | .rounded-corners(@radius: 5px) { 272 | border-radius: @radius; 273 | } 274 | 275 | .horizontal-section { 276 | height: 37px; 277 | padding: 13px 1em 0 1em; 278 | } 279 | 280 | .gradient(@startColor, @endColor) { 281 | background: @endColor; /* Old browsers */ 282 | background: -moz-linear-gradient(top, @startColor 0%, @endColor 25%); /* FF3.6+ */ 283 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,@startColor), color-stop(25%,@endColor)); /* Chrome,Safari4+ */ 284 | background: -webkit-linear-gradient(top, @startColor 0%,@endColor 25%); /* Chrome10+,Safari5.1+ */ 285 | background: -o-linear-gradient(top, @startColor 0%,@endColor 25%); /* Opera 11.10+ */ 286 | background: -ms-linear-gradient(top, @startColor 0%,@endColor 25%); /* IE10+ */ 287 | background: linear-gradient(to bottom, @startColor 0%,@endColor 25%); /* W3C */ 288 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='@startColor', endColorstr='@endColor', GradientType=0); /* IE6-9 */ 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /web/js/purl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JQuery URL Parser plugin, v2.2.1 3 | * Developed and maintanined by Mark Perkins, mark@allmarkedup.com 4 | * Source repository: https://github.com/allmarkedup/jQuery-URL-Parser 5 | * Licensed under an MIT-style license. See https://github.com/allmarkedup/jQuery-URL-Parser/blob/master/LICENSE for details. 6 | */ 7 | 8 | ;(function(factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | // AMD available; use anonymous module 11 | if ( typeof jQuery !== 'undefined' ) { 12 | define(['jquery'], factory); 13 | } else { 14 | define([], factory); 15 | } 16 | } else { 17 | // No AMD available; mutate global vars 18 | if ( typeof jQuery !== 'undefined' ) { 19 | factory(jQuery); 20 | } else { 21 | factory(); 22 | } 23 | } 24 | })(function($, undefined) { 25 | 26 | var tag2attr = { 27 | a : 'href', 28 | img : 'src', 29 | form : 'action', 30 | base : 'href', 31 | script : 'src', 32 | iframe : 'src', 33 | link : 'href' 34 | }, 35 | 36 | key = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment'], // keys available to query 37 | 38 | aliases = { 'anchor' : 'fragment' }, // aliases for backwards compatability 39 | 40 | parser = { 41 | strict : /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, //less intuitive, more accurate to the specs 42 | loose : /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs 43 | }, 44 | 45 | toString = Object.prototype.toString, 46 | 47 | isint = /^[0-9]+$/; 48 | 49 | function parseUri( url, strictMode ) { 50 | var str = decodeURI( url ), 51 | res = parser[ strictMode || false ? 'strict' : 'loose' ].exec( str ), 52 | uri = { attr : {}, param : {}, seg : {} }, 53 | i = 14; 54 | 55 | while ( i-- ) { 56 | uri.attr[ key[i] ] = res[i] || ''; 57 | } 58 | 59 | // build query and fragment parameters 60 | uri.param['query'] = parseString(uri.attr['query']); 61 | uri.param['fragment'] = parseString(uri.attr['fragment']); 62 | 63 | // split path and fragement into segments 64 | uri.seg['path'] = uri.attr.path.replace(/^\/+|\/+$/g,'').split('/'); 65 | uri.seg['fragment'] = uri.attr.fragment.replace(/^\/+|\/+$/g,'').split('/'); 66 | 67 | // compile a 'base' domain attribute 68 | uri.attr['base'] = uri.attr.host ? (uri.attr.protocol ? uri.attr.protocol+'://'+uri.attr.host : uri.attr.host) + (uri.attr.port ? ':'+uri.attr.port : '') : ''; 69 | 70 | return uri; 71 | }; 72 | 73 | function getAttrName( elm ) { 74 | var tn = elm.tagName; 75 | if ( typeof tn !== 'undefined' ) return tag2attr[tn.toLowerCase()]; 76 | return tn; 77 | } 78 | 79 | function promote(parent, key) { 80 | if (parent[key].length == 0) return parent[key] = {}; 81 | var t = {}; 82 | for (var i in parent[key]) t[i] = parent[key][i]; 83 | parent[key] = t; 84 | return t; 85 | } 86 | 87 | function parse(parts, parent, key, val) { 88 | var part = parts.shift(); 89 | if (!part) { 90 | if (isArray(parent[key])) { 91 | parent[key].push(val); 92 | } else if ('object' == typeof parent[key]) { 93 | parent[key] = val; 94 | } else if ('undefined' == typeof parent[key]) { 95 | parent[key] = val; 96 | } else { 97 | parent[key] = [parent[key], val]; 98 | } 99 | } else { 100 | var obj = parent[key] = parent[key] || []; 101 | if (']' == part) { 102 | if (isArray(obj)) { 103 | if ('' != val) obj.push(val); 104 | } else if ('object' == typeof obj) { 105 | obj[keys(obj).length] = val; 106 | } else { 107 | obj = parent[key] = [parent[key], val]; 108 | } 109 | } else if (~part.indexOf(']')) { 110 | part = part.substr(0, part.length - 1); 111 | if (!isint.test(part) && isArray(obj)) obj = promote(parent, key); 112 | parse(parts, obj, part, val); 113 | // key 114 | } else { 115 | if (!isint.test(part) && isArray(obj)) obj = promote(parent, key); 116 | parse(parts, obj, part, val); 117 | } 118 | } 119 | } 120 | 121 | function merge(parent, key, val) { 122 | if (~key.indexOf(']')) { 123 | var parts = key.split('['), 124 | len = parts.length, 125 | last = len - 1; 126 | parse(parts, parent, 'base', val); 127 | } else { 128 | if (!isint.test(key) && isArray(parent.base)) { 129 | var t = {}; 130 | for (var k in parent.base) t[k] = parent.base[k]; 131 | parent.base = t; 132 | } 133 | set(parent.base, key, val); 134 | } 135 | return parent; 136 | } 137 | 138 | function parseString(str) { 139 | return reduce(String(str).split(/&|;/), function(ret, pair) { 140 | try { 141 | pair = decodeURIComponent(pair.replace(/\+/g, ' ')); 142 | } catch(e) { 143 | // ignore 144 | } 145 | var eql = pair.indexOf('='), 146 | brace = lastBraceInKey(pair), 147 | key = pair.substr(0, brace || eql), 148 | val = pair.substr(brace || eql, pair.length), 149 | val = val.substr(val.indexOf('=') + 1, val.length); 150 | 151 | if ('' == key) key = pair, val = ''; 152 | 153 | return merge(ret, key, val); 154 | }, { base: {} }).base; 155 | } 156 | 157 | function set(obj, key, val) { 158 | var v = obj[key]; 159 | if (undefined === v) { 160 | obj[key] = val; 161 | } else if (isArray(v)) { 162 | v.push(val); 163 | } else { 164 | obj[key] = [v, val]; 165 | } 166 | } 167 | 168 | function lastBraceInKey(str) { 169 | var len = str.length, 170 | brace, c; 171 | for (var i = 0; i < len; ++i) { 172 | c = str[i]; 173 | if (']' == c) brace = false; 174 | if ('[' == c) brace = true; 175 | if ('=' == c && !brace) return i; 176 | } 177 | } 178 | 179 | function reduce(obj, accumulator){ 180 | var i = 0, 181 | l = obj.length >> 0, 182 | curr = arguments[2]; 183 | while (i < l) { 184 | if (i in obj) curr = accumulator.call(undefined, curr, obj[i], i, obj); 185 | ++i; 186 | } 187 | return curr; 188 | } 189 | 190 | function isArray(vArg) { 191 | return Object.prototype.toString.call(vArg) === "[object Array]"; 192 | } 193 | 194 | function keys(obj) { 195 | var keys = []; 196 | for ( prop in obj ) { 197 | if ( obj.hasOwnProperty(prop) ) keys.push(prop); 198 | } 199 | return keys; 200 | } 201 | 202 | function purl( url, strictMode ) { 203 | if ( arguments.length === 1 && url === true ) { 204 | strictMode = true; 205 | url = undefined; 206 | } 207 | strictMode = strictMode || false; 208 | url = url || window.location.toString(); 209 | 210 | return { 211 | 212 | data : parseUri(url, strictMode), 213 | 214 | // get various attributes from the URI 215 | attr : function( attr ) { 216 | attr = aliases[attr] || attr; 217 | return typeof attr !== 'undefined' ? this.data.attr[attr] : this.data.attr; 218 | }, 219 | 220 | // return query string parameters 221 | param : function( param ) { 222 | return typeof param !== 'undefined' ? this.data.param.query[param] : this.data.param.query; 223 | }, 224 | 225 | // return fragment parameters 226 | fparam : function( param ) { 227 | return typeof param !== 'undefined' ? this.data.param.fragment[param] : this.data.param.fragment; 228 | }, 229 | 230 | // return path segments 231 | segment : function( seg ) { 232 | if ( typeof seg === 'undefined' ) { 233 | return this.data.seg.path; 234 | } else { 235 | seg = seg < 0 ? this.data.seg.path.length + seg : seg - 1; // negative segments count from the end 236 | return this.data.seg.path[seg]; 237 | } 238 | }, 239 | 240 | // return fragment segments 241 | fsegment : function( seg ) { 242 | if ( typeof seg === 'undefined' ) { 243 | return this.data.seg.fragment; 244 | } else { 245 | seg = seg < 0 ? this.data.seg.fragment.length + seg : seg - 1; // negative segments count from the end 246 | return this.data.seg.fragment[seg]; 247 | } 248 | } 249 | 250 | }; 251 | 252 | }; 253 | 254 | if ( typeof $ !== 'undefined' ) { 255 | 256 | $.fn.url = function( strictMode ) { 257 | var url = ''; 258 | if ( this.length ) { 259 | url = $(this).attr( getAttrName(this[0]) ) || ''; 260 | } 261 | return purl( url, strictMode ); 262 | }; 263 | 264 | $.url = purl; 265 | 266 | } else { 267 | window.purl = purl; 268 | } 269 | 270 | }); 271 | 272 | -------------------------------------------------------------------------------- /web/js/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.4.2 2 | // http://underscorejs.org 3 | // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. 4 | // Underscore may be freely distributed under the MIT license. 5 | (function(){var e=this,t=e._,n={},r=Array.prototype,i=Object.prototype,s=Function.prototype,o=r.push,u=r.slice,a=r.concat,f=r.unshift,l=i.toString,c=i.hasOwnProperty,h=r.forEach,p=r.map,d=r.reduce,v=r.reduceRight,m=r.filter,g=r.every,y=r.some,b=r.indexOf,w=r.lastIndexOf,E=Array.isArray,S=Object.keys,x=s.bind,T=function(e){if(e instanceof T)return e;if(!(this instanceof T))return new T(e);this._wrapped=e};typeof exports!="undefined"?(typeof module!="undefined"&&module.exports&&(exports=module.exports=T),exports._=T):e._=T,T.VERSION="1.4.2";var N=T.each=T.forEach=function(e,t,r){if(e==null)return;if(h&&e.forEach===h)e.forEach(t,r);else if(e.length===+e.length){for(var i=0,s=e.length;i2;e==null&&(e=[]);if(d&&e.reduce===d)return r&&(t=T.bind(t,r)),i?e.reduce(t,n):e.reduce(t);N(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError("Reduce of empty array with no initial value");return n},T.reduceRight=T.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(v&&e.reduceRight===v)return r&&(t=T.bind(t,r)),arguments.length>2?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=T.keys(e);s=o.length}N(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError("Reduce of empty array with no initial value");return n},T.find=T.detect=function(e,t,n){var r;return C(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},T.filter=T.select=function(e,t,n){var r=[];return e==null?r:m&&e.filter===m?e.filter(t,n):(N(e,function(e,i,s){t.call(n,e,i,s)&&(r[r.length]=e)}),r)},T.reject=function(e,t,n){var r=[];return e==null?r:(N(e,function(e,i,s){t.call(n,e,i,s)||(r[r.length]=e)}),r)},T.every=T.all=function(e,t,r){t||(t=T.identity);var i=!0;return e==null?i:g&&e.every===g?e.every(t,r):(N(e,function(e,s,o){if(!(i=i&&t.call(r,e,s,o)))return n}),!!i)};var C=T.some=T.any=function(e,t,r){t||(t=T.identity);var i=!1;return e==null?i:y&&e.some===y?e.some(t,r):(N(e,function(e,s,o){if(i||(i=t.call(r,e,s,o)))return n}),!!i)};T.contains=T.include=function(e,t){var n=!1;return e==null?n:b&&e.indexOf===b?e.indexOf(t)!=-1:(n=C(e,function(e){return e===t}),n)},T.invoke=function(e,t){var n=u.call(arguments,2);return T.map(e,function(e){return(T.isFunction(t)?t:e[t]).apply(e,n)})},T.pluck=function(e,t){return T.map(e,function(e){return e[t]})},T.where=function(e,t){return T.isEmpty(t)?[]:T.filter(e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},T.max=function(e,t,n){if(!t&&T.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);if(!t&&T.isEmpty(e))return-Infinity;var r={computed:-Infinity};return N(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},T.min=function(e,t,n){if(!t&&T.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);if(!t&&T.isEmpty(e))return Infinity;var r={computed:Infinity};return N(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;or||n===void 0)return 1;if(n>>1;n.call(r,e[u])=0})})},T.difference=function(e){var t=a.apply(r,u.call(arguments,1));return T.filter(e,function(e){return!T.contains(t,e)})},T.zip=function(){var e=u.call(arguments),t=T.max(T.pluck(e,"length")),n=new Array(t);for(var r=0;r=0;n--)t=[e[n].apply(this,t)];return t[0]}},T.after=function(e,t){return e<=0?t():function(){if(--e<1)return t.apply(this,arguments)}},T.keys=S||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)T.has(e,n)&&(t[t.length]=n);return t},T.values=function(e){var t=[];for(var n in e)T.has(e,n)&&t.push(e[n]);return t},T.pairs=function(e){var t=[];for(var n in e)T.has(e,n)&&t.push([n,e[n]]);return t},T.invert=function(e){var t={};for(var n in e)T.has(e,n)&&(t[e[n]]=n);return t},T.functions=T.methods=function(e){var t=[];for(var n in e)T.isFunction(e[n])&&t.push(n);return t.sort()},T.extend=function(e){return N(u.call(arguments,1),function(t){for(var n in t)e[n]=t[n]}),e},T.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return N(n,function(n){n in e&&(t[n]=e[n])}),t},T.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)T.contains(n,i)||(t[i]=e[i]);return t},T.defaults=function(e){return N(u.call(arguments,1),function(t){for(var n in t)e[n]==null&&(e[n]=t[n])}),e},T.clone=function(e){return T.isObject(e)?T.isArray(e)?e.slice():T.extend({},e):e},T.tap=function(e,t){return t(e),e};var M=function(e,t,n,r){if(e===t)return e!==0||1/e==1/t;if(e==null||t==null)return e===t;e instanceof T&&(e=e._wrapped),t instanceof T&&(t=t._wrapped);var i=l.call(e);if(i!=l.call(t))return!1;switch(i){case"[object String]":return e==String(t);case"[object Number]":return e!=+e?t!=+t:e==0?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if(typeof e!="object"||typeof t!="object")return!1;var s=n.length;while(s--)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if(i=="[object Array]"){o=e.length,u=o==t.length;if(u)while(o--)if(!(u=M(e[o],t[o],n,r)))break}else{var a=e.constructor,f=t.constructor;if(a!==f&&!(T.isFunction(a)&&a instanceof a&&T.isFunction(f)&&f instanceof f))return!1;for(var c in e)if(T.has(e,c)){o++;if(!(u=T.has(t,c)&&M(e[c],t[c],n,r)))break}if(u){for(c in t)if(T.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};T.isEqual=function(e,t){return M(e,t,[],[])},T.isEmpty=function(e){if(e==null)return!0;if(T.isArray(e)||T.isString(e))return e.length===0;for(var t in e)if(T.has(e,t))return!1;return!0},T.isElement=function(e){return!!e&&e.nodeType===1},T.isArray=E||function(e){return l.call(e)=="[object Array]"},T.isObject=function(e){return e===Object(e)},N(["Arguments","Function","String","Number","Date","RegExp"],function(e){T["is"+e]=function(t){return l.call(t)=="[object "+e+"]"}}),T.isArguments(arguments)||(T.isArguments=function(e){return!!e&&!!T.has(e,"callee")}),typeof /./!="function"&&(T.isFunction=function(e){return typeof e=="function"}),T.isFinite=function(e){return T.isNumber(e)&&isFinite(e)},T.isNaN=function(e){return T.isNumber(e)&&e!=+e},T.isBoolean=function(e){return e===!0||e===!1||l.call(e)=="[object Boolean]"},T.isNull=function(e){return e===null},T.isUndefined=function(e){return e===void 0},T.has=function(e,t){return c.call(e,t)},T.noConflict=function(){return e._=t,this},T.identity=function(e){return e},T.times=function(e,t,n){for(var r=0;r":">",'"':""","'":"'","/":"/"}};_.unescape=T.invert(_.escape);var D={escape:new RegExp("["+T.keys(_.escape).join("")+"]","g"),unescape:new RegExp("("+T.keys(_.unescape).join("|")+")","g")};T.each(["escape","unescape"],function(e){T[e]=function(t){return t==null?"":(""+t).replace(D[e],function(t){return _[e][t]})}}),T.result=function(e,t){if(e==null)return null;var n=e[t];return T.isFunction(n)?n.call(e):n},T.mixin=function(e){N(T.functions(e),function(t){var n=T[t]=e[t];T.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),F.call(this,n.apply(T,e))}})};var P=0;T.uniqueId=function(e){var t=P++;return e?e+t:t},T.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var H=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},j=/\\|'|\r|\n|\t|\u2028|\u2029/g;T.template=function(e,t,n){n=T.defaults({},n,T.templateSettings);var r=new RegExp([(n.escape||H).source,(n.interpolate||H).source,(n.evaluate||H).source].join("|")+"|$","g"),i=0,s="__p+='";e.replace(r,function(t,n,r,o,u){s+=e.slice(i,u).replace(j,function(e){return"\\"+B[e]}),s+=n?"'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'":r?"'+\n((__t=("+r+"))==null?'':__t)+\n'":o?"';\n"+o+"\n__p+='":"",i=u+t.length}),s+="';\n",n.variable||(s="with(obj||{}){\n"+s+"}\n"),s="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+s+"return __p;\n";try{var o=new Function(n.variable||"obj","_",s)}catch(u){throw u.source=s,u}if(t)return o(t,T);var a=function(e){return o.call(this,e,T)};return a.source="function("+(n.variable||"obj")+"){\n"+s+"}",a},T.chain=function(e){return T(e).chain()};var F=function(e){return this._chain?T(e).chain():e};T.mixin(T),N(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];T.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],F.call(this,n)}}),N(["concat","join","slice"],function(e){var t=r[e];T.prototype[e]=function(){return F.call(this,t.apply(this._wrapped,arguments))}}),T.extend(T.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); -------------------------------------------------------------------------------- /web/js/jquery.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v1.7.2 jquery.com | jquery.org/license */ 2 | (function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( 3 | a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f 4 | .clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); 5 | --------------------------------------------------------------------------------