├── .java-version ├── .gitattributes ├── src ├── main │ ├── resources │ │ └── version.txt │ └── java │ │ ├── webserver │ │ ├── templating │ │ │ ├── Layout.java │ │ │ ├── ContentWithVariables.java │ │ │ ├── YamlFrontMatter.java │ │ │ └── Template.java │ │ └── compilers │ │ │ ├── LessCompiler.java │ │ │ └── CoffeeScriptCompiler.java │ │ ├── geeks │ │ ├── Geek.java │ │ └── Geeks.java │ │ ├── resources │ │ ├── MainResource.java │ │ ├── SearchResource.java │ │ ├── StaticResource.java │ │ └── AbstractResource.java │ │ ├── twitter │ │ ├── TwitterCommands.java │ │ ├── GeektickHashTagListener.java │ │ └── sample │ │ │ └── GetAccessToken.java │ │ └── main │ │ └── MainGeekticServer.java └── test │ └── java │ ├── webserver │ └── templating │ │ ├── LayoutTest.java │ │ ├── TemplateTest.java │ │ └── YamlFrontMatterTest.java │ ├── twitter │ ├── GeektickHashTagListenerTest.java │ └── TwitterCommandsTest.java │ ├── resources │ └── SearchResourceTest.java │ ├── AbstractPageTest.java │ ├── HomePageTest.java │ ├── misc │ ├── PhantomJsTest.java │ └── PhantomJsDownloader.java │ └── geeks │ └── GeeksTest.java ├── Procfile ├── web ├── favicon.ico ├── static │ ├── img │ │ ├── favicon.png │ │ ├── geek0.jpg │ │ ├── geek1.jpg │ │ ├── geek2.jpg │ │ ├── geek3.jpg │ │ ├── geek4.jpg │ │ ├── geek5.jpg │ │ ├── geek6.jpg │ │ ├── geek7.jpg │ │ └── noise.png │ └── js │ │ ├── underscore-min.js │ │ └── angular.min.js ├── controllers.coffee ├── layouts │ └── default.html ├── index.html ├── style.less └── geeks.json ├── .gitignore ├── Readme.md └── pom.xml /.java-version: -------------------------------------------------------------------------------- 1 | jdk1.7 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | version.txt export-subst 2 | -------------------------------------------------------------------------------- /src/main/resources/version.txt: -------------------------------------------------------------------------------- 1 | a087112ee45e00596da778b868305af40697d9d8 -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java -Xmx512M -cp target/classes:target/dependency/* main.MainGeekticServer 2 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/favicon.ico -------------------------------------------------------------------------------- /web/static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/favicon.png -------------------------------------------------------------------------------- /web/static/img/geek0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/geek0.jpg -------------------------------------------------------------------------------- /web/static/img/geek1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/geek1.jpg -------------------------------------------------------------------------------- /web/static/img/geek2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/geek2.jpg -------------------------------------------------------------------------------- /web/static/img/geek3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/geek3.jpg -------------------------------------------------------------------------------- /web/static/img/geek4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/geek4.jpg -------------------------------------------------------------------------------- /web/static/img/geek5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/geek5.jpg -------------------------------------------------------------------------------- /web/static/img/geek6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/geek6.jpg -------------------------------------------------------------------------------- /web/static/img/geek7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/geek7.jpg -------------------------------------------------------------------------------- /web/static/img/noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeStory/code-story-geektic/master/web/static/img/noise.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .DS_Store 3 | .idea/ 4 | target/ 5 | snapshots/ 6 | /votes 7 | infinitest.filters 8 | twitter4j-bak.properties 9 | local-geeks.json 10 | -------------------------------------------------------------------------------- /web/controllers.coffee: -------------------------------------------------------------------------------- 1 | homeController = ($scope, $http, $location) -> 2 | $scope.findGeeks = -> 3 | parameters = keywords: $scope.keywords 4 | 5 | $http.get('/search', params: parameters).success (data) -> 6 | $scope.geeks = data 7 | $location.url "?keyword=#{$scope.keywords}" 8 | 9 | -------------------------------------------------------------------------------- /src/main/java/webserver/templating/Layout.java: -------------------------------------------------------------------------------- 1 | package webserver.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/geeks/Geek.java: -------------------------------------------------------------------------------- 1 | package geeks; 2 | 3 | public class Geek { 4 | public final String nom; 5 | public final String[] likes; 6 | public String imageUrl; 7 | 8 | public Geek(String nom, String... likes) { 9 | this.nom = nom; 10 | this.likes = likes; 11 | } 12 | 13 | public String getNom() { 14 | return nom; 15 | } 16 | 17 | public String getImageUrl() { 18 | return imageUrl; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/webserver/templating/LayoutTest.java: -------------------------------------------------------------------------------- 1 | package webserver.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/resources/MainResource.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.Produces; 6 | import javax.ws.rs.core.Response; 7 | 8 | import java.io.File; 9 | 10 | @Path("/") 11 | public class MainResource extends AbstractResource { 12 | @GET 13 | @Produces("text/html;charset=UTF-8") 14 | public Response index() { 15 | File file = file("index.html"); 16 | return ok(templatize(read(file)), file.lastModified()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/webserver/templating/ContentWithVariables.java: -------------------------------------------------------------------------------- 1 | package webserver.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 | -------------------------------------------------------------------------------- /web/layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [[title]] 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | [[body]] 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Application geektic pour DevoxxFR 2013 2 | 3 | Pourquoi un framework web maison ? 4 | ================================== 5 | 6 | + Pour essayer 7 | + Parceque ce framework demarre tres vite et les tests en sont d'autant plus rapides 8 | 9 | TODO : 10 | ====== 11 | 12 | + Preparer le deploiement 13 | + Enlever le bouton Search 14 | + Améliorer le rendu avec par exemple une mosaique 15 | + Rechercher plusieurs termes 16 | + Cibler sur une ville 17 | + Cibler sur un perimetre geographique 18 | + S'ajouter a la liste (saisir son email, ses gouts et valider a reception d'un email) 19 | + Sauvegarde des geeks 20 | + Donner ses coordonnees actuelle (checkin) 21 | + Partager le site sur twitter 22 | + Changer l'url en live 23 | -------------------------------------------------------------------------------- /src/main/java/resources/SearchResource.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import geeks.Geek; 4 | import geeks.Geeks; 5 | 6 | import javax.inject.Inject; 7 | import javax.ws.rs.GET; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.QueryParam; 11 | 12 | import java.util.Collection; 13 | 14 | @Path("/search") 15 | public class SearchResource extends AbstractResource { 16 | private final Geeks geeks; 17 | 18 | @Inject 19 | public SearchResource(Geeks geeks) { 20 | this.geeks = geeks; 21 | } 22 | 23 | @GET 24 | @Produces("application/json;encoding=utf-8") 25 | public Collection searchGeeks(@QueryParam("keywords") String keywords) { 26 | return geeks.search(keywords); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/twitter/GeektickHashTagListenerTest.java: -------------------------------------------------------------------------------- 1 | package twitter; 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.mockito.runners.MockitoJUnitRunner; 8 | import twitter4j.Status; 9 | 10 | import static org.mockito.Mockito.verify; 11 | 12 | @RunWith(MockitoJUnitRunner.class) 13 | public class GeektickHashTagListenerTest { 14 | 15 | @InjectMocks 16 | private GeektickHashTagListener listener; 17 | 18 | @Mock 19 | private Status status; 20 | 21 | @Mock 22 | private TwitterCommands twitterCommands; 23 | 24 | @Test 25 | public void testOnStatus() throws Exception { 26 | listener.onStatus(status); 27 | 28 | verify(twitterCommands).onTweet(status); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/webserver/compilers/LessCompiler.java: -------------------------------------------------------------------------------- 1 | package webserver.compilers; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.io.Files; 5 | import org.lesscss.LessException; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | 10 | public class LessCompiler { 11 | private final org.lesscss.LessCompiler compiler; 12 | 13 | public LessCompiler() { 14 | this.compiler = new org.lesscss.LessCompiler(); 15 | } 16 | 17 | public synchronized String compile(File file) { 18 | try { 19 | return compiler.compile(Files.toString(file, Charsets.UTF_8)); 20 | } catch (IOException e) { 21 | throw new IllegalStateException("Unable to read less file", e); 22 | } catch (LessException e) { 23 | throw new IllegalStateException("Invalid less file", e); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/resources/SearchResourceTest.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import geeks.Geek; 4 | import geeks.Geeks; 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 java.util.List; 12 | 13 | import static java.util.Arrays.asList; 14 | import static org.fest.assertions.Assertions.assertThat; 15 | import static org.mockito.Mockito.when; 16 | 17 | @RunWith(MockitoJUnitRunner.class) 18 | public class SearchResourceTest { 19 | @Mock 20 | Geeks geeks; 21 | 22 | @InjectMocks 23 | SearchResource searchResource; 24 | 25 | @Test 26 | public void should_search() { 27 | List geeks = asList(new Geek("David")); 28 | when(this.geeks.search("java")).thenReturn(geeks); 29 | 30 | assertThat(searchResource.searchGeeks("java")).isSameAs(geeks); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: layouts/default.html 3 | title: CodeStory - Geektic 4 | script: controllers.coffee 5 | --- 6 | 7 |
8 | 18 |
19 |
20 | 21 | 22 |
23 |

{{geek.nom}}

24 | 25 | 28 |
29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /src/main/java/webserver/compilers/CoffeeScriptCompiler.java: -------------------------------------------------------------------------------- 1 | package webserver.compilers; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.io.Files; 5 | import org.jcoffeescript.JCoffeeScriptCompileException; 6 | import org.jcoffeescript.JCoffeeScriptCompiler; 7 | import org.jcoffeescript.Option; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | import static java.util.Arrays.asList; 13 | 14 | public class CoffeeScriptCompiler { 15 | private final JCoffeeScriptCompiler compiler; 16 | 17 | public CoffeeScriptCompiler() { 18 | compiler = new JCoffeeScriptCompiler(asList(Option.BARE)); 19 | } 20 | 21 | public synchronized String compile(File file) { 22 | try { 23 | return compiler.compile(Files.toString(file, Charsets.UTF_8)); 24 | } catch (IOException e) { 25 | throw new IllegalStateException("Unable to read coffeescript file", e); 26 | } catch (JCoffeeScriptCompileException e) { 27 | throw new IllegalStateException("Invalid coffeescript file", e); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/AbstractPageTest.java: -------------------------------------------------------------------------------- 1 | import main.MainGeekticServer; 2 | import misc.PhantomJsTest; 3 | import org.junit.Before; 4 | 5 | import java.util.Random; 6 | 7 | public abstract class AbstractPageTest extends PhantomJsTest { 8 | private static final int TRY_COUNT = 10; 9 | private static final int DEFAULT_PORT = 8183; 10 | private static final Random RANDOM_PORT = new Random(); 11 | 12 | private static int port; 13 | private static MainGeekticServer server; 14 | 15 | @Before 16 | public void startServerOnce() { 17 | if (server != null) { 18 | return; 19 | } 20 | 21 | for (int i = 0; i < TRY_COUNT; i++) { 22 | try { 23 | port = randomPort(); 24 | server = new MainGeekticServer(); 25 | server.start(port); 26 | return; 27 | } catch (Exception e) { 28 | System.err.println("Unable to bind server: " + e); 29 | } 30 | } 31 | throw new IllegalStateException("Unable to start server"); 32 | } 33 | 34 | private synchronized int randomPort() { 35 | return DEFAULT_PORT + RANDOM_PORT.nextInt(1000); 36 | } 37 | 38 | @Override 39 | protected String defaultUrl() { 40 | return "http://localhost:" + port; 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/twitter/TwitterCommands.java: -------------------------------------------------------------------------------- 1 | package twitter; 2 | 3 | import com.google.inject.Inject; 4 | import geeks.Geek; 5 | import geeks.Geeks; 6 | import twitter4j.Status; 7 | 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | import static com.google.common.base.Splitter.on; 12 | import static com.google.common.collect.Iterables.toArray; 13 | 14 | public class TwitterCommands { 15 | public static final Pattern TWEET_PATTERN = Pattern.compile(".*#geektic (.+)"); 16 | 17 | private final Geeks geeks; 18 | 19 | @Inject 20 | public TwitterCommands(Geeks geeks) { 21 | this.geeks = geeks; 22 | } 23 | 24 | public void onTweet(Status status) { 25 | String text = status.getText(); 26 | 27 | Matcher matcher = TWEET_PATTERN.matcher(text); 28 | if (!matcher.matches()) { 29 | return; 30 | } 31 | 32 | String[] likes = toArray(on(' ').split(matcher.group(1)), String.class); 33 | 34 | Geek geek = new Geek(status.getUser().getName(), likes); 35 | String profileImageURL = status.getUser().getBiggerProfileImageURL(); 36 | if (profileImageURL != null) { 37 | geek.imageUrl = profileImageURL; 38 | } 39 | geeks.addGeek(geek); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/webserver/templating/YamlFrontMatter.java: -------------------------------------------------------------------------------- 1 | package webserver.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/webserver/templating/Template.java: -------------------------------------------------------------------------------- 1 | package webserver.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 | public 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/main/java/twitter/GeektickHashTagListener.java: -------------------------------------------------------------------------------- 1 | package twitter; 2 | 3 | import com.google.inject.Inject; 4 | import twitter4j.FilterQuery; 5 | import twitter4j.Status; 6 | import twitter4j.StatusAdapter; 7 | import twitter4j.TwitterStream; 8 | import twitter4j.TwitterStreamFactory; 9 | import twitter4j.conf.ConfigurationBuilder; 10 | 11 | public class GeektickHashTagListener extends StatusAdapter { 12 | private final TwitterCommands twitterCommands; 13 | 14 | @Inject 15 | public GeektickHashTagListener(TwitterCommands twitterCommands) { 16 | this.twitterCommands = twitterCommands; 17 | } 18 | 19 | public void start() { 20 | String accessToken = System.getenv("ACCESSTOKEN"); 21 | String accessTokenSecret = System.getenv("ACCESSTOKENSECRET"); 22 | String consumerKey = System.getenv("CONSUMERKEY"); 23 | String consumerSecret = System.getenv("CONSUMERSECRET"); 24 | 25 | TwitterStream twitterStream = new TwitterStreamFactory( 26 | new ConfigurationBuilder() 27 | .setOAuthAccessToken(accessToken) 28 | .setOAuthAccessTokenSecret(accessTokenSecret) 29 | .setOAuthConsumerKey(consumerKey) 30 | .setOAuthConsumerSecret(consumerSecret) 31 | .build() 32 | ).getInstance(); 33 | 34 | twitterStream.addListener(this); 35 | twitterStream.filter(new FilterQuery().track(new String[]{"#geektic"})); 36 | } 37 | 38 | @Override 39 | public void onStatus(Status status) { 40 | twitterCommands.onTweet(status); 41 | } 42 | 43 | @Override 44 | public void onException(Exception ex) { 45 | ex.printStackTrace(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/HomePageTest.java: -------------------------------------------------------------------------------- 1 | import com.google.common.base.Predicate; 2 | import org.junit.Before; 3 | import org.junit.Test; 4 | 5 | import static org.fest.assertions.Assertions.assertThat; 6 | 7 | public class HomePageTest extends AbstractPageTest { 8 | @Before 9 | public void go_to_homepage() { 10 | goTo("/"); 11 | } 12 | 13 | @Test 14 | public void should_display_homepage() { 15 | assertThat(title()).isEqualTo("CodeStory - Geektic"); 16 | assertThat(find("#geeks").getText()).isEmpty(); 17 | } 18 | 19 | @Test 20 | public void should_search_geeks_who_love_cats() { 21 | fill("#searchInput").with("chats"); 22 | 23 | await().until("#geeks .square .caption").withText().contains("Gageot"); 24 | await().until("#geeks .square .caption").withText().contains("Java Tests Chats"); 25 | } 26 | 27 | @Test 28 | public void should_search_scala_geeks() { 29 | fill("#searchInput").with("scala"); 30 | 31 | await().until("#geeks").withText().contains("Odersky"); 32 | } 33 | 34 | @Test 35 | public void should_clear_search_field() { 36 | fill("#searchInput").with("java"); 37 | await().until("#geeks").withText().contains("David"); 38 | 39 | clear("#searchInput"); 40 | await().until("#geeks").withText().equalTo(""); 41 | } 42 | 43 | @Test 44 | public void should_have_an_url_with_the_tags() { 45 | fill("#searchInput").with("java"); 46 | 47 | await().until(new Predicate() { 48 | @Override 49 | public boolean apply(Object input) { 50 | return url().contains("?keyword=java"); 51 | } 52 | }); 53 | } 54 | } -------------------------------------------------------------------------------- /src/test/java/webserver/templating/TemplateTest.java: -------------------------------------------------------------------------------- 1 | package webserver.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/test/java/twitter/TwitterCommandsTest.java: -------------------------------------------------------------------------------- 1 | package twitter; 2 | 3 | import geeks.Geek; 4 | import geeks.Geeks; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.ArgumentCaptor; 8 | import org.mockito.Captor; 9 | import org.mockito.InjectMocks; 10 | import org.mockito.Mock; 11 | import org.mockito.runners.MockitoJUnitRunner; 12 | import twitter4j.Status; 13 | import twitter4j.User; 14 | 15 | import java.net.MalformedURLException; 16 | 17 | import static org.fest.assertions.Assertions.assertThat; 18 | import static org.mockito.Mockito.verify; 19 | import static org.mockito.Mockito.when; 20 | 21 | @RunWith(MockitoJUnitRunner.class) 22 | public class TwitterCommandsTest { 23 | @Mock 24 | Status status; 25 | @Mock 26 | User user; 27 | @Mock 28 | private Geeks geeks; 29 | @Captor 30 | private ArgumentCaptor geekCaptor; 31 | 32 | @InjectMocks 33 | private TwitterCommands twitterCommands; 34 | 35 | @Test 36 | public void should_create_geek_on_tweet() throws MalformedURLException { 37 | when(status.getUser()).thenReturn(user); 38 | when(user.getName()).thenReturn("Xavier Hanin"); 39 | when(user.getBiggerProfileImageURL()).thenReturn("http://exemple.org/foo.jpg"); 40 | when(status.getText()).thenReturn("#geektic LIKE1 LIKE2 LIKE3"); 41 | 42 | twitterCommands.onTweet(status); 43 | 44 | verify(geeks).addGeek(geekCaptor.capture()); 45 | 46 | Geek newGeek = geekCaptor.getValue(); 47 | assertThat(newGeek.nom).isEqualTo("Xavier Hanin"); 48 | assertThat(newGeek.likes).contains("LIKE1", "LIKE2", "LIKE3"); 49 | assertThat(newGeek.imageUrl).contains("http://exemple.org/foo.jpg"); 50 | } 51 | 52 | @Test 53 | public void should_recognize_only_3_likes_after_geektic_hashtag() { 54 | when(status.getUser()).thenReturn(user); 55 | when(status.getText()).thenReturn("Hello World #geektic LIKE1 LIKE2 LIKE3 LIKE4"); 56 | 57 | twitterCommands.onTweet(status); 58 | 59 | verify(geeks).addGeek(geekCaptor.capture()); 60 | 61 | Geek newGeek = geekCaptor.getValue(); 62 | assertThat(newGeek.likes).contains("LIKE1", "LIKE2", "LIKE3"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/resources/StaticResource.java: -------------------------------------------------------------------------------- 1 | package resources; 2 | 3 | import webserver.compilers.CoffeeScriptCompiler; 4 | import webserver.compilers.LessCompiler; 5 | 6 | import javax.inject.Inject; 7 | import javax.ws.rs.GET; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.PathParam; 10 | import javax.ws.rs.Produces; 11 | import javax.ws.rs.core.Response; 12 | 13 | import java.io.File; 14 | 15 | @Path("/static/{version}") 16 | public class StaticResource extends AbstractResource { 17 | private final LessCompiler lessCompiler; 18 | private final CoffeeScriptCompiler coffeeScriptCompiler; 19 | 20 | @Inject 21 | public StaticResource(CoffeeScriptCompiler coffeeScriptCompiler, LessCompiler lessCompiler) { 22 | this.coffeeScriptCompiler = coffeeScriptCompiler; 23 | this.lessCompiler = lessCompiler; 24 | } 25 | 26 | @GET 27 | @Path("{path : .*\\.css}") 28 | @Produces("text/css;charset=UTF-8") 29 | public File css(@PathParam("path") String path) { 30 | return file("static", path); 31 | } 32 | 33 | @GET 34 | @Path("{path : .*\\.js}") 35 | @Produces("application/javascript;charset=UTF-8") 36 | public File js(@PathParam("path") String path) { 37 | return file("static", path); 38 | } 39 | 40 | @GET 41 | @Path("{path : .*\\.less}") 42 | @Produces("text/css;charset=UTF-8") 43 | public Response less(@PathParam("path") String path) { 44 | File less = file(path); 45 | return ok(templatize(lessCompiler.compile(less)), less.lastModified()); 46 | } 47 | 48 | @GET 49 | @Path("{path : .*\\.coffee}") 50 | @Produces("application/javascript;charset=UTF-8") 51 | public Response coffee(@PathParam("path") String path) { 52 | File coffee = file(path); 53 | return ok(coffeeScriptCompiler.compile(coffee), coffee.lastModified()); 54 | } 55 | 56 | @GET 57 | @Path("{path : .*\\.png}") 58 | @Produces("image/png") 59 | public File png(@PathParam("path") String path) { 60 | return file("static", path); 61 | } 62 | 63 | @GET 64 | @Path("{path : .*\\.jpg}") 65 | @Produces("image/jpeg") 66 | public File jpg(@PathParam("path") String path) { 67 | return file("static", path); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/webserver/templating/YamlFrontMatterTest.java: -------------------------------------------------------------------------------- 1 | package webserver.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/geeks/Geeks.java: -------------------------------------------------------------------------------- 1 | package geeks; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.base.Splitter; 5 | import com.google.common.base.Strings; 6 | import com.google.common.collect.Sets; 7 | import com.google.common.io.Files; 8 | import com.google.gson.Gson; 9 | import com.google.inject.Singleton; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.Collection; 14 | import java.util.Random; 15 | import java.util.Set; 16 | 17 | import static webserver.templating.Template.readGitHash; 18 | 19 | @Singleton 20 | public class Geeks { 21 | private static final Random RANDOM = new Random(); 22 | 23 | private final Set geekSet; 24 | private File localDataLocation = new File("local-geeks.json"); 25 | 26 | public Geeks() { 27 | this.geekSet = Sets.newCopyOnWriteArraySet(); 28 | } 29 | 30 | public void addGeek(Geek geek) { 31 | geekSet.add(geek); 32 | try { 33 | Files.write(new Gson().toJson(geekSet), localDataLocation, Charsets.UTF_8); 34 | } catch (IOException e) { 35 | // FIXME 36 | } 37 | } 38 | 39 | public Collection search(String keywords) { 40 | Set friends = Sets.newHashSet(); 41 | 42 | if (Strings.isNullOrEmpty(keywords)) { 43 | return friends; 44 | } 45 | 46 | for (String keyword : Splitter.on(" ").trimResults().omitEmptyStrings().split(keywords)) { 47 | for (Geek geek : geekSet) { 48 | for (String like : geek.likes) { 49 | if (like.equalsIgnoreCase(keyword)) { 50 | friends.add(geek); 51 | break; 52 | } 53 | } 54 | } 55 | } 56 | 57 | return friends; 58 | } 59 | 60 | public void load() throws IOException { 61 | File geeksFile; 62 | String json = null; 63 | if (localDataLocation.exists()) { 64 | geeksFile = localDataLocation; 65 | json = Files.toString(geeksFile, Charsets.UTF_8); 66 | } 67 | if (Strings.isNullOrEmpty(json)) { 68 | geeksFile = new File("web/geeks.json"); 69 | json = Files.toString(geeksFile, Charsets.UTF_8); 70 | } 71 | 72 | geekSet.clear(); 73 | for (Geek geek : new Gson().fromJson(json, Geek[].class)) { 74 | geek.imageUrl = String.format("static/%s/img/geek%s.jpg", readGitHash(), RANDOM.nextInt(8)); 75 | 76 | addGeek(geek); 77 | } 78 | } 79 | 80 | public void setLocalDataLocation(File localDataLocation) { 81 | this.localDataLocation = localDataLocation; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /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.sun.jersey.api.NotFoundException; 8 | import webserver.templating.ContentWithVariables; 9 | import webserver.templating.Layout; 10 | import webserver.templating.Template; 11 | import webserver.templating.YamlFrontMatter; 12 | 13 | import javax.ws.rs.core.Response; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | import java.util.Date; 18 | import java.util.Map; 19 | 20 | public abstract class AbstractResource { 21 | private static final long ONE_YEAR = 1000L * 3600 * 24 * 365; 22 | 23 | protected Response ok(Object entity, long modified) { 24 | return Response.ok(entity).lastModified(new Date(modified)).expires(new Date(modified + ONE_YEAR)).header("Cache-Control", "public").build(); 25 | } 26 | 27 | protected String templatize(String body) { 28 | return templatize(body, ImmutableMap.of()); 29 | } 30 | 31 | protected String templatize(String body, Map variables) { 32 | ContentWithVariables yaml = new YamlFrontMatter().parse(body); 33 | 34 | Map yamlVariables = yaml.getVariables(); 35 | String content = yaml.getContent(); 36 | 37 | String layout = yamlVariables.get("layout"); 38 | if (layout != null) { 39 | content = new Layout(read(file(layout))).apply(content); 40 | } 41 | 42 | return new Template().apply(content, ImmutableMap.builder().putAll(variables).putAll(yamlVariables).build()); 43 | } 44 | 45 | protected String read(File file) { 46 | try { 47 | return Files.toString(file, Charsets.UTF_8); 48 | } catch (IOException e) { 49 | throw Throwables.propagate(e); 50 | } 51 | } 52 | 53 | protected File file(String parent, String path) { 54 | return file(new File(parent, path).getPath()); 55 | } 56 | 57 | protected File file(String path) { 58 | if (!exists(path)) { 59 | throw new NotFoundException(); 60 | } 61 | return new File("web", path); 62 | } 63 | 64 | protected boolean exists(String path) { 65 | if (path.endsWith("/")) { 66 | return false; 67 | } 68 | 69 | try { 70 | File root = new File("web"); 71 | File file = new File(root, path); 72 | if (!file.exists() || !file.getCanonicalPath().startsWith(root.getCanonicalPath())) { 73 | return false; 74 | } 75 | 76 | return true; 77 | } catch (IOException e) { 78 | return false; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/main/MainGeekticServer.java: -------------------------------------------------------------------------------- 1 | package main; 2 | 3 | import com.google.inject.Guice; 4 | import com.google.inject.Injector; 5 | import com.sun.jersey.api.container.filter.GZIPContentEncodingFilter; 6 | import com.sun.jersey.api.core.DefaultResourceConfig; 7 | import com.sun.jersey.api.core.ResourceConfig; 8 | import com.sun.jersey.simple.container.SimpleServerFactory; 9 | import geeks.Geeks; 10 | import org.codehaus.jackson.jaxrs.JacksonJsonProvider; 11 | import resources.MainResource; 12 | import resources.SearchResource; 13 | import resources.StaticResource; 14 | import twitter.GeektickHashTagListener; 15 | 16 | import java.io.IOException; 17 | 18 | import static com.google.common.base.Objects.firstNonNull; 19 | import static com.sun.jersey.api.core.ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS; 20 | import static com.sun.jersey.api.core.ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS; 21 | import static java.lang.Integer.parseInt; 22 | 23 | public class MainGeekticServer { 24 | private final Injector injector; 25 | 26 | public MainGeekticServer() { 27 | injector = Guice.createInjector(); 28 | } 29 | 30 | public static void main(String[] args) throws IOException { 31 | int port = parseInt(firstNonNull(System.getenv("PORT"), "8080")); 32 | 33 | MainGeekticServer geekticServer = new MainGeekticServer(); 34 | geekticServer.start(port); 35 | geekticServer.startTwitterListener(); 36 | } 37 | 38 | public void start(int port) throws IOException { 39 | System.out.println("Starting server on port: " + port); 40 | 41 | loadGeeks(); 42 | SimpleServerFactory.create("http://localhost:" + port, configuration()); 43 | } 44 | 45 | private ResourceConfig configuration() throws IOException { 46 | DefaultResourceConfig config = new DefaultResourceConfig(); 47 | 48 | config.getClasses().add(JacksonJsonProvider.class); 49 | 50 | config.getSingletons().add(injector.getInstance(MainResource.class)); 51 | config.getSingletons().add(injector.getInstance(StaticResource.class)); 52 | config.getSingletons().add(injector.getInstance(SearchResource.class)); 53 | 54 | config.getProperties().put(PROPERTY_CONTAINER_REQUEST_FILTERS, GZIPContentEncodingFilter.class); 55 | config.getProperties().put(PROPERTY_CONTAINER_RESPONSE_FILTERS, GZIPContentEncodingFilter.class); 56 | 57 | return config; 58 | } 59 | 60 | private void loadGeeks() throws IOException { 61 | Geeks geeks = injector.getInstance(Geeks.class); 62 | geeks.load(); 63 | } 64 | 65 | private void startTwitterListener() { 66 | GeektickHashTagListener listener = injector.getInstance(GeektickHashTagListener.class); 67 | listener.start(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /web/style.less: -------------------------------------------------------------------------------- 1 | @white: #FFFFFF; 2 | @black: #000000; 3 | @red: rgb(242, 50, 48); 4 | @pink: rgb(217, 141, 166); 5 | @blue: rgb(0, 163, 219); 6 | @square_width: 250px; 7 | @square_height: 250px; 8 | 9 | [ng-cloak] { 10 | display: none !important; 11 | } 12 | 13 | @font-face { 14 | font-family: 'Kranky'; 15 | font-style: normal; 16 | font-weight: 400; 17 | src: local('Kranky'), url(http://themes.googleusercontent.com/static/fonts/kranky/v3/wv_FP3njkHr-QOxKLpPxYevvDin1pK8aKteLpeZ5c0A.woff) format('woff'); 18 | } 19 | 20 | html { 21 | width: 100%; 22 | background-image: url('/static/[[version]]/img/noise.png'), -webkit-gradient(radial, 50% 0%, 0, 50% 0%, 750, color-stop(0%, @pink), color-stop(100%, @red)); 23 | background-image: url('/static/[[version]]/img/noise.png'), -webkit-radial-gradient(top center, @pink, @red 750px); 24 | background-image: url('/static/[[version]]/img/noise.png'), -moz-radial-gradient(top center, @pink, @red 750px); 25 | background-image: url('/static/[[version]]/img/noise.png'), -ms-radial-gradient(top center, @pink, @red 750px); 26 | background-image: url('/static/[[version]]/img/noise.png'), radial-gradient(top center, @pink, @red 750px); 27 | padding-top: 20px; 28 | min-height: 1024px; 29 | } 30 | 31 | body { 32 | background-color: transparent; 33 | font-family: 'Kranky', cursive; 34 | 35 | #main { 36 | margin: 0 auto; 37 | width: 762px; 38 | height: 762px; 39 | 40 | #search { 41 | background-color: @blue; 42 | text-align: center; 43 | .transform(rotateZ(-5deg) translateX(-20px) translateY(-20px)); 44 | 45 | h1 { 46 | margin-top: 50px; 47 | } 48 | 49 | h4 { 50 | padding: 5px; 51 | } 52 | } 53 | 54 | .image, .result, .square { 55 | width: @square_width; 56 | height: @square_height; 57 | } 58 | 59 | .image { 60 | .transition(-webkit-transform 1s); 61 | .backface-visibility(hidden); 62 | } 63 | 64 | .result { 65 | position: relative; 66 | bottom: @square_height; 67 | } 68 | 69 | .square { 70 | float: left; 71 | margin: 0; 72 | border-top: 1px solid #954d5a; 73 | border-left: 1px solid #954d5a; 74 | } 75 | 76 | #intro { 77 | .back('/static/[[version]]/img/geek7.jpg', @square_width, @square_height); 78 | 79 | .full-caption { 80 | background-color: rgba(0, 0, 0, .8); 81 | color: @white; 82 | width: 100%; 83 | height: 100%; 84 | 85 | h3, p { 86 | margin: 0; 87 | padding: 10px 10px 0 10px; 88 | } 89 | } 90 | } 91 | 92 | .caption { 93 | background-color: rgba(0, 0, 0, .8); 94 | color: @white; 95 | height: 60px; 96 | width: @square_width; 97 | position: relative; 98 | bottom: 60px; 99 | overflow: hidden; 100 | vertical-align: middle; 101 | text-align: center; 102 | 103 | p { 104 | padding-left: 10px; 105 | margin: 0; 106 | line-height: 40px; 107 | font-size: 20px; 108 | } 109 | 110 | p.likes { 111 | line-height: 20px; 112 | font-size: 15px; 113 | } 114 | } 115 | 116 | .shown { 117 | .transform(rotateY(0deg)) !important; 118 | } 119 | 120 | .hidden { 121 | .transform(rotateY(180deg)) !important; 122 | } 123 | 124 | .even { 125 | .shown { 126 | .transform(rotateX(0deg)) !important; 127 | } 128 | 129 | .hidden { 130 | .transform(rotateX(180deg)) !important; 131 | } 132 | } 133 | } 134 | } 135 | 136 | .back(@url, @width, @height) { 137 | width: @width; 138 | height: @height; 139 | background-image: url(@url); 140 | background-size: @width @height; 141 | } 142 | 143 | .transition(@arg1) { 144 | -webkit-transition: @arg1; 145 | -moz-transition: @arg1; 146 | transition: @arg1; 147 | } 148 | 149 | .transform(@arg1) { 150 | -webkit-transform: @arg1; 151 | -moz-transform: @arg1; 152 | transform: @arg1; 153 | } 154 | 155 | .backface-visibility(@type) { 156 | -webkit-backface-visibility: @type; 157 | -moz-backface-visibility: @type; 158 | backface-visibility: @type; 159 | } 160 | -------------------------------------------------------------------------------- /src/test/java/geeks/GeeksTest.java: -------------------------------------------------------------------------------- 1 | package geeks; 2 | 3 | import com.google.common.base.Charsets; 4 | import com.google.common.io.Files; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.util.Collection; 11 | 12 | import static org.fest.assertions.Assertions.assertThat; 13 | 14 | public class GeeksTest { 15 | private final Geeks geeks = new Geeks(); 16 | private File geeksFile; 17 | 18 | @Before 19 | public void setUp() throws Exception { 20 | geeksFile = File.createTempFile("geektic", ".json"); 21 | geeksFile.deleteOnExit(); 22 | geeks.setLocalDataLocation(geeksFile); 23 | } 24 | 25 | @Test 26 | public void no_geek_matches_empty_keywords() { 27 | geeks.addGeek(geek("Xavier", "image", "java")); 28 | geeks.addGeek(geek("Martin", "image", "scala")); 29 | 30 | assertThat(geeks.search("")).isEmpty(); 31 | } 32 | 33 | @Test 34 | public void should_search_java_geek() { 35 | geeks.addGeek(geek("Xavier", "http://exemple.org/sample.jpg", "java")); 36 | 37 | Collection javaGeeks = geeks.search("java"); 38 | assertThat(javaGeeks).onProperty("nom").containsOnly("Xavier"); 39 | assertThat(javaGeeks).onProperty("imageUrl").containsOnly("http://exemple.org/sample.jpg"); 40 | } 41 | 42 | @Test 43 | public void should_search_one_java_geek() { 44 | geeks.addGeek(geek("Xavier", "image", "java")); 45 | geeks.addGeek(geek("Martin", "image", "scala")); 46 | 47 | assertThat(geeks.search("scala")).onProperty("nom").containsOnly("Martin"); 48 | } 49 | 50 | @Test 51 | public void should_search_case_insensitive() { 52 | geeks.addGeek(geek("Xavier", "image", "java")); 53 | geeks.addGeek(geek("Martin", "image", "scala")); 54 | 55 | assertThat(geeks.search("SCaLa")).onProperty("nom").containsOnly("Martin"); 56 | } 57 | 58 | @Test 59 | public void should_search_on_any_keyword() { 60 | geeks.addGeek(geek("Xavier", "image", "java", "coffee")); 61 | 62 | assertThat(geeks.search("coffee")).onProperty("nom").containsOnly("Xavier"); 63 | } 64 | 65 | @Test 66 | public void should_search_on_two_keywords() { 67 | geeks.addGeek(geek("Xavier", "image", "java", "coffee")); 68 | geeks.addGeek(geek("Christophe", "image", "java", "linux")); 69 | 70 | assertThat(geeks.search("coffee linux")).onProperty("nom").containsOnly("Xavier", "Christophe"); 71 | } 72 | 73 | @Test 74 | public void should_search_on_two_keywords_avoid_duplaicates() { 75 | geeks.addGeek(geek("Xavier", "image", "java", "coffee")); 76 | geeks.addGeek(geek("Christophe", "image", "java", "linux")); 77 | 78 | assertThat(geeks.search("coffee java")).hasSize(2); 79 | } 80 | 81 | @Test 82 | public void should_store_geeks() throws Exception { 83 | geeks.load(); 84 | geeks.addGeek(geek("Azerty", "image", "rien")); 85 | geeks.load(); 86 | assertThat(geeks.search("rien")).onProperty("nom").containsOnly("Azerty"); 87 | } 88 | 89 | @Test 90 | public void should_load_geeks() throws IOException { 91 | geeks.load(); 92 | assertThat(geeks.search("java")).onProperty("nom").containsOnly("Ardhuin", "Baligand", "Biville", "Bonvillain", "Cheype", "Dhordain", "Gageot", "Guérin", "Hanin", "Labouisse", "Le Merdy", "Le Merdy", "Leclaire", "Renou", "Tremblay", "Voisin", "Wauquier", "Gosling"); 93 | } 94 | 95 | @Test 96 | public void should_load_geeks_local_geeks() throws IOException { 97 | Files.write("[ {\n" + 98 | " \"nom\": \"Hanin\",\n" + 99 | " \"prenom\": \"Xavier\",\n" + 100 | " \"email\": \"xavier.hanin@gmail.com\",\n" + 101 | " \"ville\": \"Bordeaux\",\n" + 102 | " \"likes\": [\"Developpement\",\n" + 103 | " \"Java\",\n" + 104 | " \"Web\"],\n" + 105 | " \"hate1\": \"Perdre du temps\",\n" + 106 | " \"hate2\": \"Le closed source\"\n" + 107 | " }]", geeksFile, Charsets.UTF_8); 108 | geeks.load(); 109 | assertThat(geeks.search("java")).onProperty("nom").containsOnly("Hanin"); 110 | } 111 | 112 | static Geek geek(String name, String imageUrl, String... likes) { 113 | Geek geek = new Geek(name, likes); 114 | geek.imageUrl = imageUrl; 115 | return geek; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | codestory 8 | geektic 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 1.7 13 | 1.7 14 | UTF-8 15 | 1.17.1 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 | junit 43 | junit 44 | 4.11 45 | test 46 | 47 | 48 | org.fluentlenium 49 | fluentlenium-core 50 | 0.7.5 51 | test 52 | 53 | 54 | junit 55 | junit 56 | 57 | 58 | 59 | 60 | com.github.detro.ghostdriver 61 | phantomjsdriver 62 | 1.0.3-dev 63 | test 64 | 65 | 66 | org.easytesting 67 | fest-assert 68 | 1.4 69 | test 70 | 71 | 72 | org.mockito 73 | mockito-all 74 | 1.9.5 75 | test 76 | 77 | 78 | 79 | 80 | com.sun.jersey 81 | jersey-server 82 | ${jersey.version} 83 | 84 | 85 | com.sun.jersey 86 | jersey-json 87 | ${jersey.version} 88 | 89 | 90 | com.sun.jersey.contribs 91 | jersey-simple-server 92 | ${jersey.version} 93 | 94 | 95 | com.google.code.maven-play-plugin.com.github.yeungda.jcoffeescript 96 | jcoffeescript 97 | 1.0 98 | 99 | 100 | com.google.guava 101 | guava 102 | 14.0.1 103 | 104 | 105 | com.google.inject 106 | guice 107 | 3.0 108 | 109 | 110 | com.github.spullara.mustache.java 111 | compiler 112 | 0.8.10 113 | 114 | 115 | org.lesscss 116 | lesscss 117 | 1.3.1 118 | 119 | 120 | com.google.code.gson 121 | gson 122 | 2.2.2 123 | 124 | 125 | org.twitter4j 126 | twitter4j-stream 127 | 3.0.3 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /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 | private final boolean isLinux64; 22 | 23 | PhantomJsDownloader() { 24 | String osName = System.getProperty("os.name"); 25 | isWindows = osName.startsWith("Windows"); 26 | isMac = osName.startsWith("Mac OS X") || osName.startsWith("Darwin"); 27 | isLinux64 = System.getProperty("sun.arch.data.model").equals("64"); 28 | } 29 | 30 | public File downloadAndExtract() { 31 | File installDir = new File(new File(System.getProperty("user.home")), ".phantomjstest"); 32 | 33 | String url; 34 | File phantomJsExe; 35 | if (isWindows) { 36 | url = "http://phantomjs.googlecode.com/files/phantomjs-1.8.1-windows.zip"; 37 | phantomJsExe = new File(installDir, "phantomjs-1.8.1-windows/phantomjs.exe"); 38 | } else if (isMac) { 39 | url = "http://phantomjs.googlecode.com/files/phantomjs-1.8.1-macosx.zip"; 40 | phantomJsExe = new File(installDir, "phantomjs-1.8.1-macosx/bin/phantomjs"); 41 | } else if (isLinux64) { 42 | url = "http://phantomjs.googlecode.com/files/phantomjs-1.8.1-linux-x86_64.tar.bz2"; 43 | phantomJsExe = new File(installDir, "phantomjs-1.8.1-linux-x86_64/bin/phantomjs"); 44 | } else { 45 | url = "http://phantomjs.googlecode.com/files/phantomjs-1.8.1-linux-i686.tar.bz2"; 46 | phantomJsExe = new File(installDir, "phantomjs-1.8.1-linux-i686/bin/phantomjs"); 47 | } 48 | 49 | extractExe(url, installDir, phantomJsExe); 50 | 51 | return phantomJsExe; 52 | } 53 | 54 | private void extractExe(String url, File phantomInstallDir, File phantomJsExe) { 55 | if (phantomJsExe.exists()) { 56 | return; 57 | } 58 | 59 | File targetZip = new File(phantomInstallDir, "phantomjs.zip"); 60 | downloadZip(url, targetZip); 61 | 62 | System.out.println("Extracting phantomjs"); 63 | try { 64 | if (isWindows) { 65 | unzip(targetZip, phantomInstallDir); 66 | } else if (isMac) { 67 | new ProcessBuilder().command("/usr/bin/unzip", "-qo", "phantomjs.zip").directory(phantomInstallDir).start().waitFor(); 68 | } else { 69 | new ProcessBuilder().command("tar", "-xjvf", "phantomjs.zip").directory(phantomInstallDir).start().waitFor(); 70 | } 71 | } catch (Exception e) { 72 | throw new IllegalStateException("Unable to unzip phantomjs from " + targetZip.getAbsolutePath()); 73 | } 74 | } 75 | 76 | private void downloadZip(String url, File targetZip) { 77 | if (targetZip.exists()) { 78 | return; 79 | } 80 | 81 | System.out.println("Downloading phantomjs from " + url + "..."); 82 | 83 | File zipTemp = new File(targetZip.getAbsolutePath() + ".temp"); 84 | try { 85 | zipTemp.getParentFile().mkdirs(); 86 | 87 | InputSupplier input = Resources.newInputStreamSupplier(URI.create(url).toURL()); 88 | OutputSupplier ouput = Files.newOutputStreamSupplier(zipTemp); 89 | 90 | ByteStreams.copy(input, ouput); 91 | } catch (IOException e) { 92 | throw new IllegalStateException("Unable to download phantomjs from " + url); 93 | } 94 | 95 | zipTemp.renameTo(targetZip); 96 | } 97 | 98 | private static void unzip(File zip, File toDir) throws IOException { 99 | final ZipFile zipFile = new ZipFile(zip); 100 | try { 101 | Enumeration entries = zipFile.entries(); 102 | while (entries.hasMoreElements()) { 103 | final ZipEntry entry = entries.nextElement(); 104 | if (entry.isDirectory()) { 105 | continue; 106 | } 107 | 108 | File to = new File(toDir, entry.getName()); 109 | to.getParentFile().mkdirs(); 110 | 111 | Files.copy(new InputSupplier() { 112 | @Override 113 | public InputStream getInput() throws IOException { 114 | return zipFile.getInputStream(entry); 115 | } 116 | }, to); 117 | } 118 | } finally { 119 | zipFile.close(); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/twitter/sample/GetAccessToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007 Yusuke Yamamoto 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package twitter.sample; 18 | 19 | import twitter4j.Twitter; 20 | import twitter4j.TwitterException; 21 | import twitter4j.TwitterFactory; 22 | import twitter4j.auth.AccessToken; 23 | import twitter4j.auth.RequestToken; 24 | 25 | import java.awt.*; 26 | import java.io.*; 27 | import java.net.URI; 28 | import java.net.URISyntaxException; 29 | import java.util.Properties; 30 | 31 | /** 32 | * @author Yusuke Yamamoto - yusuke at mac.com 33 | * @since Twitter4J 2.1.7 34 | */ 35 | public class GetAccessToken { 36 | /** 37 | * Usage: java twitter4j.examples.oauth.GetAccessToken [consumer key] [consumer secret] 38 | * 39 | * @param args message 40 | */ 41 | public static void main(String[] args) { 42 | File file = new File("twitter4j.properties"); 43 | Properties prop = new Properties(); 44 | InputStream is = null; 45 | OutputStream os = null; 46 | try { 47 | if (file.exists()) { 48 | is = new FileInputStream(file); 49 | prop.load(is); 50 | } 51 | if (args.length < 2) { 52 | if (null == prop.getProperty("oauth.consumerKey") 53 | && null == prop.getProperty("oauth.consumerSecret")) { 54 | // consumer key/secret are not set in twitter4j-bak.properties 55 | System.out.println( 56 | "Usage: java twitter4j.examples.oauth.GetAccessToken [consumer key] [consumer secret]"); 57 | System.exit(-1); 58 | } 59 | } else { 60 | prop.setProperty("oauth.consumerKey", args[0]); 61 | prop.setProperty("oauth.consumerSecret", args[1]); 62 | os = new FileOutputStream("twitter4j.properties"); 63 | prop.store(os, "twitter4j.properties"); 64 | } 65 | } catch (IOException ioe) { 66 | ioe.printStackTrace(); 67 | System.exit(-1); 68 | } finally { 69 | if (is != null) { 70 | try { 71 | is.close(); 72 | } catch (IOException ignore) { 73 | } 74 | } 75 | if (os != null) { 76 | try { 77 | os.close(); 78 | } catch (IOException ignore) { 79 | } 80 | } 81 | } 82 | try { 83 | Twitter twitter = new TwitterFactory().getInstance(); 84 | RequestToken requestToken = twitter.getOAuthRequestToken(); 85 | System.out.println("Got request token."); 86 | System.out.println("Request token: " + requestToken.getToken()); 87 | System.out.println("Request token secret: " + requestToken.getTokenSecret()); 88 | AccessToken accessToken = null; 89 | 90 | BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 91 | while (null == accessToken) { 92 | System.out.println("Open the following URL and grant access to your account:"); 93 | System.out.println(requestToken.getAuthorizationURL()); 94 | try { 95 | Desktop.getDesktop().browse(new URI(requestToken.getAuthorizationURL())); 96 | } catch (UnsupportedOperationException ignore) { 97 | } catch (IOException ignore) { 98 | } catch (URISyntaxException e) { 99 | throw new AssertionError(e); 100 | } 101 | System.out.print("Enter the PIN(if available) and hit enter after you granted access.[PIN]:"); 102 | String pin = br.readLine(); 103 | try { 104 | if (pin.length() > 0) { 105 | accessToken = twitter.getOAuthAccessToken(requestToken, pin); 106 | } else { 107 | accessToken = twitter.getOAuthAccessToken(requestToken); 108 | } 109 | } catch (TwitterException te) { 110 | if (401 == te.getStatusCode()) { 111 | System.out.println("Unable to get the access token."); 112 | } else { 113 | te.printStackTrace(); 114 | } 115 | } 116 | } 117 | System.out.println("Got access token."); 118 | System.out.println("Access token: " + accessToken.getToken()); 119 | System.out.println("Access token secret: " + accessToken.getTokenSecret()); 120 | 121 | try { 122 | prop.setProperty("oauth.accessToken", accessToken.getToken()); 123 | prop.setProperty("oauth.accessTokenSecret", accessToken.getTokenSecret()); 124 | os = new FileOutputStream(file); 125 | prop.store(os, "twitter4j.properties"); 126 | os.close(); 127 | } catch (IOException ioe) { 128 | ioe.printStackTrace(); 129 | System.exit(-1); 130 | } finally { 131 | if (os != null) { 132 | try { 133 | os.close(); 134 | } catch (IOException ignore) { 135 | } 136 | } 137 | } 138 | System.out.println("Successfully stored access token to " + file.getAbsolutePath() + "."); 139 | System.exit(0); 140 | } catch (TwitterException te) { 141 | te.printStackTrace(); 142 | System.out.println("Failed to get accessToken: " + te.getMessage()); 143 | System.exit(-1); 144 | } catch (IOException ioe) { 145 | ioe.printStackTrace(); 146 | System.out.println("Failed to read the system input."); 147 | System.exit(-1); 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /web/static/js/underscore-min.js: -------------------------------------------------------------------------------- 1 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); -------------------------------------------------------------------------------- /web/geeks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "nom": "Antoine", 4 | "prenom": "Philippe", 5 | "email": "phil.antoine@gmail.com", 6 | "ville": "Paris", 7 | "likes": ["HTML5", 8 | "JavaScript", 9 | "Barcamps"], 10 | "hate1": "Passwords", 11 | "hate2": "Configs xml" 12 | }, 13 | { 14 | "nom": "Ardhuin", 15 | "prenom": "Alexandre", 16 | "email": "alexandre.ardhuin@gmail.com", 17 | "ville": "Nancy", 18 | "likes": ["Coder", 19 | "java", 20 | "web"], 21 | "hate1": "IE", 22 | "hate2": "le code pourri" 23 | }, 24 | { 25 | "nom": "Baligand", 26 | "prenom": "Fabien", 27 | "email": "fbaligand@gmail.com", 28 | "ville": "Palaiseau", 29 | "likes": ["java", 30 | "web", 31 | "jQuery"], 32 | "hate1": "le code illisible et inmaintenable", 33 | "hate2": "Liferay" 34 | }, 35 | { 36 | "nom": "Bernard", 37 | "prenom": "Emmanuel", 38 | "email": "emmanuel@lescastcodeurs.com", 39 | "ville": "", 40 | "likes": ["", 41 | "", 42 | ""], 43 | "hate1": "", 44 | "hate2": "" 45 | }, 46 | { 47 | "nom": "Biville", 48 | "prenom": "Florent", 49 | "email": "florent.biville@gmail.com", 50 | "ville": "Paris", 51 | "likes": ["Apprendre", 52 | "Java", 53 | "JS"], 54 | "hate1": "deprecated stuff", 55 | "hate2": "so-called opinionated people" 56 | }, 57 | { 58 | "nom": "Bonnel", 59 | "prenom": "Yan", 60 | "email": "ybonnel@gmail.com", 61 | "ville": "Rouen", 62 | "likes": ["Java (sous IntelliJ)", 63 | "Android", 64 | "Coder avec d'autres languages (actuellement Scala)"], 65 | "hate1": "La complexité pour rien", 66 | "hate2": "Le temps normand (je veux retourner dans le sud...)" 67 | }, 68 | { 69 | "nom": "Bonvillain", 70 | "prenom": "Damien", 71 | "email": "night.kame@gmail.com", 72 | "ville": "Montoire sur le Loir", 73 | "likes": ["Coder", 74 | "Prendre du recul", 75 | "Java"], 76 | "hate1": "Les amnésiques", 77 | "hate2": "Maven" 78 | }, 79 | { 80 | "nom": "Bourdon", 81 | "prenom": "Ugo", 82 | "email": "bourdon.ugo@gmail.com", 83 | "ville": "Paris", 84 | "likes": ["Scala", 85 | "extreme programming", 86 | "git"], 87 | "hate1": "le code compliqué (différent de complexe)", 88 | "hate2": "faire toujours la même chose" 89 | }, 90 | { 91 | "nom": "Broca", 92 | "prenom": "Didier", 93 | "email": "didier.broca@libertysurf.fr", 94 | "ville": "", 95 | "likes": ["", 96 | "", 97 | ""], 98 | "hate1": "", 99 | "hate2": "" 100 | }, 101 | { 102 | "nom": "Cazaux", 103 | "prenom": "Jean-Baptiste", 104 | "email": "jbcazaux@gmail.com", 105 | "ville": "Ermont", 106 | "likes": ["play tennis", 107 | "eat", 108 | "video games"], 109 | "hate1": "no time for...", 110 | "hate2": "to lose" 111 | }, 112 | { 113 | "nom": "Chammah", 114 | "prenom": "Tarek", 115 | "email": "tchammah@google.com", 116 | "ville": "Paris", 117 | "likes": ["OCAML", 118 | "dependent types", 119 | "Hindley-Milner"], 120 | "hate1": "", 121 | "hate2": "" 122 | }, 123 | { 124 | "nom": "Cheype", 125 | "prenom": "Julien", 126 | "email": "cheype@gmail.com", 127 | "ville": "Paris", 128 | "likes": ["java", 129 | "les lapins", 130 | "scala"], 131 | "hate1": "eclipse RCP", 132 | "hate2": "perdre mon temps" 133 | }, 134 | { 135 | "nom": "Courtine", 136 | "prenom": "Benoît", 137 | "email": "benoit.courtine@gmail.com", 138 | "ville": "Paris", 139 | "likes": ["Java (avec son indispensable IntelliJ)", 140 | "Linux (et plus généralement l'OSS)", 141 | "Git"], 142 | "hate1": "les réunions inutiles (et chronophages)", 143 | "hate2": "les évolutions \"\"urgentes\"\" et importantes à faire sur un code sans tests" 144 | }, 145 | { 146 | "nom": "David", 147 | "prenom": "Michel", 148 | "email": "mimah35@gmail.com", 149 | "ville": "", 150 | "likes": ["", 151 | "", 152 | ""], 153 | "hate1": "", 154 | "hate2": "" 155 | }, 156 | { 157 | "nom": "De Loof", 158 | "prenom": "Nicolas", 159 | "email": "nicolas.deloof@gmail.com", 160 | "ville": "Rennes", 161 | "likes": ["Coder en Java (sous Idea) avec mon Chat sur les genoux et une bonne bière", 162 | "Git", 163 | "Maven (si si)"], 164 | "hate1": "Devoir assurer la compatibilité ascendant sur du code legacy (ah, jenkins...)", 165 | "hate2": "Maven (si si)" 166 | }, 167 | { 168 | "nom": "de Morlhon", 169 | "prenom": "Jean-Laurent", 170 | "email": "jeanlaurent@morlhon.net", 171 | "ville": "Houilles", 172 | "likes": ["coffeescript", 173 | "clean coding", 174 | "hacking stuff"], 175 | "hate1": "Meetings", 176 | "hate2": "scala" 177 | }, 178 | { 179 | "nom": "Detante", 180 | "prenom": "Antoine", 181 | "email": "antoine.detante@gmail.com", 182 | "ville": "Metz", 183 | "likes": ["Scala", 184 | "Mac OS X", 185 | "Bière"], 186 | "hate1": "Grippe", 187 | "hate2": "WebSphere" 188 | }, 189 | { 190 | "nom": "Dhordain", 191 | "prenom": "Gautier", 192 | "email": "g.dhordain@gmail.com", 193 | "ville": "Lille", 194 | "likes": ["Java", 195 | "Apprendre des trucs utiles", 196 | "Apprendre des trucs inutiles"], 197 | "hate1": "Grippe", 198 | "hate2": "Code non indenté" 199 | }, 200 | { 201 | "nom": "Dumont", 202 | "prenom": "Antoine", 203 | "email": "eniotna.t@gmail.com", 204 | "ville": "Livry Gargan", 205 | "likes": ["Clojure/Haskell (functional prog)", 206 | "Linux/Unix", 207 | "Simplicity"], 208 | "hate1": "Transports en commun", 209 | "hate2": "closed source" 210 | }, 211 | { 212 | "nom": "Finance", 213 | "prenom": "Cédric", 214 | "email": "cedric.finance@soat.fr", 215 | "ville": "", 216 | "likes": ["", 217 | "", 218 | ""], 219 | "hate1": "", 220 | "hate2": "" 221 | }, 222 | { 223 | "nom": "Gageot", 224 | "prenom": "David", 225 | "email": "david@gageot.net", 226 | "ville": "Paris", 227 | "likes": ["Java", 228 | "Tests", 229 | "Chats"], 230 | "hate1": "Duplication", 231 | "hate2": "Usines à gaz" 232 | }, 233 | { 234 | "nom": "Guérin", 235 | "prenom": "Thomas", 236 | "email": "tguerin@xebia.fr", 237 | "ville": "Paris", 238 | "likes": ["Java", 239 | "Android", 240 | "Scala"], 241 | "hate1": "RER A", 242 | "hate2": "TU de 4000 lignes" 243 | }, 244 | { 245 | "nom": "Hanin", 246 | "prenom": "Xavier", 247 | "email": "xavier.hanin@gmail.com", 248 | "ville": "Bordeaux", 249 | "likes": ["Developpement", 250 | "Java", 251 | "Web"], 252 | "hate1": "Perdre du temps", 253 | "hate2": "Le closed source" 254 | }, 255 | { 256 | "nom": "Labaye", 257 | "prenom": "Denis", 258 | "email": "denis.labaye@gmail.com", 259 | "ville": "Paris", 260 | "likes": ["Simplicity", 261 | "Clojure, Emacs", 262 | "Mindfulness meditation"], 263 | "hate1": "Complexity", 264 | "hate2": "To hate" 265 | }, 266 | { 267 | "nom": "Labouisse", 268 | "prenom": "Christophe", 269 | "email": "consulting@labouisse.com", 270 | "ville": "Bordeaux", 271 | "likes": ["Java", 272 | "Linux/Unix", 273 | "Git"], 274 | "hate1": "Qui veut épouser mon fils", 275 | "hate2": "Récupérer du code qui \"\"marche presque\"\"" 276 | }, 277 | { 278 | "nom": "Lacôte", 279 | "prenom": "Cyril", 280 | "email": "cyril@ninja-squad.com", 281 | "ville": "", 282 | "likes": ["", 283 | "", 284 | ""], 285 | "hate1": "", 286 | "hate2": "" 287 | }, 288 | { 289 | "nom": "Lagarde", 290 | "prenom": "Jérémie", 291 | "email": "jeremie.codestory@gmail.com", 292 | "ville": "Paris", 293 | "likes": ["meet people", 294 | "get feedback", 295 | "open discussion"], 296 | "hate1": "lose patience", 297 | "hate2": "rain" 298 | }, 299 | { 300 | "nom": "Le Merdy", 301 | "prenom": "Eric", 302 | "email": "eric.lemerdy@gmail.com", 303 | "ville": "Suresnes", 304 | "likes": ["Java", 305 | "Code", 306 | "Raspberrypi"], 307 | "hate1": "haters", 308 | "hate2": "untested code" 309 | }, 310 | { 311 | "nom": "Le Merdy", 312 | "prenom": "Sebastian", 313 | "email": "sebastian.lemerdy@gmail.com", 314 | "ville": "Colombes", 315 | "likes": ["Java", 316 | "Software Craftsmanship", 317 | "Smart UI"], 318 | "hate1": "Architecture", 319 | "hate2": "" 320 | }, 321 | { 322 | "nom": "Leclaire", 323 | "prenom": "Mathieu", 324 | "email": "mat.leclaire@gmail.com", 325 | "ville": "Metz", 326 | "likes": ["Chat", 327 | "Linux", 328 | "java"], 329 | "hate1": "le poussin piou", 330 | "hate2": "flex" 331 | }, 332 | { 333 | "nom": "Lemée", 334 | "prenom": "Mathilde", 335 | "email": "mathilde.lemee.pmu@gmail.com", 336 | "ville": "", 337 | "likes": ["", 338 | "", 339 | ""], 340 | "hate1": "", 341 | "hate2": "" 342 | }, 343 | { 344 | "nom": "Lemée", 345 | "prenom": "Jean-Baptiste", 346 | "email": "jblemee@gmail.com", 347 | "ville": "Pontarmé", 348 | "likes": ["Clean code", 349 | "Big Data", 350 | "Demain"], 351 | "hate1": "Les embouteillages", 352 | "hate2": "Attendre" 353 | }, 354 | { 355 | "nom": "Lerman", 356 | "prenom": "Benjamin", 357 | "email": "Benjamin.Lerman@google.com", 358 | "ville": "Paris", 359 | "likes": ["scala", 360 | "Un defi interessant", 361 | "vi"], 362 | "hate1": "XCode", 363 | "hate2": "Perdre mon temps" 364 | }, 365 | { 366 | "nom": "Lussiez", 367 | "prenom": "Jeremie", 368 | "email": "jeremie.lussiez@gmail.com", 369 | "ville": "Seclin", 370 | "likes": ["Inventer des trucs", 371 | "Les robots", 372 | "Les chats"], 373 | "hate1": "Le code \"\"legacy driven\"\"", 374 | "hate2": "Le manque de curiosité" 375 | }, 376 | { 377 | "nom": "Mandrikov", 378 | "prenom": "Evgeny", 379 | "email": "mandrikov@gmail.com", 380 | "ville": "La Roche-sur-Foron", 381 | "likes": ["CodeStory", 382 | "SonarSource", 383 | "Linux/Java/Git/Maven"], 384 | "hate1": "7 deadly sins of developer", 385 | "hate2": "slow builds" 386 | }, 387 | { 388 | "nom": "Martignole", 389 | "prenom": "Nicolas", 390 | "email": "nicolas@martignole.net", 391 | "ville": "", 392 | "likes": ["", 393 | "", 394 | ""], 395 | "hate1": "", 396 | "hate2": "" 397 | }, 398 | { 399 | "nom": "Moulou", 400 | "prenom": "Mouhcine", 401 | "email": "mouhcine.moulou@soat.fr", 402 | "ville": "Vitry/Seine", 403 | "likes": ["Java & scala", 404 | "video Games", 405 | "Intellij"], 406 | "hate1": "Microsoft Products", 407 | "hate2": "Java (jdk 8)" 408 | }, 409 | { 410 | "nom": "Muller", 411 | "prenom": "Alain", 412 | "email": "alain.muller@gmail.com", 413 | "ville": "", 414 | "likes": ["", 415 | "", 416 | ""], 417 | "hate1": "", 418 | "hate2": "" 419 | }, 420 | { 421 | "nom": "Neveu", 422 | "prenom": "Audrey", 423 | "email": "neveu.audrey@gmail.com", 424 | "ville": "Paris", 425 | "likes": ["Java & JS", 426 | "Linux", 427 | "Git"], 428 | "hate1": "Me battre contre des moulins à vents", 429 | "hate2": "Ceux qui savent tout mieux que tout le monde" 430 | }, 431 | { 432 | "nom": "Nizet", 433 | "prenom": "Jean-Baptiste", 434 | "email": "jb+codestory@ninja-squad.com", 435 | "ville": "", 436 | "likes": ["", 437 | "", 438 | ""], 439 | "hate1": "", 440 | "hate2": "" 441 | }, 442 | { 443 | "nom": "Nopre", 444 | "prenom": "Xavier", 445 | "email": "xnopre@gmail.com", 446 | "ville": "Gap", 447 | "likes": ["Java, JS, WEB2", 448 | "Clean Code", 449 | "Découvrir"], 450 | "hate1": "Legacy", 451 | "hate2": "code pourri" 452 | }, 453 | { 454 | "nom": "Peru", 455 | "prenom": "Nicolas", 456 | "email": "nicolas.peru@gmail.com", 457 | "ville": "Genève", 458 | "likes": ["Clean Code", 459 | "Jouer", 460 | "Neige"], 461 | "hate1": "Joe les bonnes idées", 462 | "hate2": "overkill" 463 | }, 464 | { 465 | "nom": "Philippart", 466 | "prenom": "Gilles", 467 | "email": "gilles.philippart@sgcib.com", 468 | "ville": "", 469 | "likes": ["", 470 | "", 471 | ""], 472 | "hate1": "", 473 | "hate2": "" 474 | }, 475 | { 476 | "nom": "Renou", 477 | "prenom": "Morgan", 478 | "email": "mrenou@gmail.com", 479 | "ville": "Versailles", 480 | "likes": ["Clean Code", 481 | "Java", 482 | "Idea"], 483 | "hate1": "les epinards", 484 | "hate2": "les méthodes de 50 lignes" 485 | }, 486 | { 487 | "nom": "Rose", 488 | "prenom": "Guillaume", 489 | "email": "guillaume.rose@gmail.com", 490 | "ville": "Issy", 491 | "likes": ["Robotique", 492 | "Logiciels libres", 493 | "Agile"], 494 | "hate1": "Legacy", 495 | "hate2": "Complexité inutile" 496 | }, 497 | { 498 | "nom": "Roux", 499 | "prenom": "Julien", 500 | "email": "roujul@gmail.com", 501 | "ville": "Genève", 502 | "likes": ["Moto", 503 | "Football", 504 | "Bière"], 505 | "hate1": "Mes voisins", 506 | "hate2": "Paris" 507 | }, 508 | { 509 | "nom": "Sirot", 510 | "prenom": "Jean-Christophe", 511 | "email": "jcsirot@gmail.com", 512 | "ville": "Ivry-sur-Seine", 513 | "likes": ["coder (en java mais pas que)", 514 | "l'Irlande", 515 | "les jeux video"], 516 | "hate1": "la RATP", 517 | "hate2": "ceux qui pense que c'était mieux avant" 518 | }, 519 | { 520 | "nom": "Tremblay", 521 | "prenom": "Henri", 522 | "email": "henri.tremblay@gmail.com", 523 | "ville": "Paris", 524 | "likes": ["Java", 525 | "Performance", 526 | "EasyMock"], 527 | "hate1": "Le manque de pragmatisme", 528 | "hate2": "Tout de qui est lent" 529 | }, 530 | { 531 | "nom": "Vilbé", 532 | "prenom": "Yves", 533 | "email": "uv0.xtr@gmail.com", 534 | "ville": "", 535 | "likes": ["", 536 | "", 537 | ""], 538 | "hate1": "", 539 | "hate2": "" 540 | }, 541 | { 542 | "nom": "Voisin", 543 | "prenom": "Benoît", 544 | "email": "bvo123@123mail.org", 545 | "ville": "Paris", 546 | "likes": ["Java", 547 | "Agile", 548 | "Scala"], 549 | "hate1": "le rouge", 550 | "hate2": "ne pas s'améliorer" 551 | }, 552 | { 553 | "nom": "Wauquier", 554 | "prenom": "Francois", 555 | "email": "wokier@gmail.com", 556 | "ville": "Paris", 557 | "likes": ["Java", 558 | "TDD", 559 | "Agile"], 560 | "hate1": "duplication", 561 | "hate2": "le rouge" 562 | }, 563 | { 564 | "nom": "Wursteisen", 565 | "prenom": "David", 566 | "email": "david.wursteisen@gmail.com", 567 | "ville": "Boulogne", 568 | "likes": ["le vide", 569 | "photo", 570 | "Starcraft II"], 571 | "hate1": "Starcraft II", 572 | "hate2": "la complexité inutile" 573 | }, 574 | { 575 | "nom": "Zoritchak", 576 | "prenom": "Gaetan", 577 | "email": "g.zoritchak@gmail.com", 578 | "ville": "Genève", 579 | "likes": ["Clean Code", 580 | "kotlin", 581 | "Intellij"], 582 | "hate1": "xml", 583 | "hate2": "le code perdu dans la doc" 584 | }, 585 | { 586 | "nom": "Mitnick", 587 | "prenom": "Kevin", 588 | "email": "kevin@condor.net", 589 | "ville": "Equateur", 590 | "likes": ["hacking", 591 | "vms", 592 | "tcp"], 593 | "hate1": "fbi", 594 | "hate2": "Shimomura" 595 | }, 596 | { 597 | "nom": "Torvald", 598 | "prenom": "Linus", 599 | "email": "torvalds@osdl.org", 600 | "ville": "Helsinki", 601 | "likes": ["linux", 602 | "git", 603 | "plongée"], 604 | "hate1": "fsf", 605 | "hate2": "rms" 606 | }, 607 | { 608 | "nom": "Gates", 609 | "prenom": "Bill", 610 | "email": "bill@gates.org", 611 | "ville": "Seattle", 612 | "likes": ["Windows", 613 | "ms-dos", 614 | "visual studio"], 615 | "hate1": "Blue Screen", 616 | "hate2": "Cash" 617 | }, 618 | { 619 | "nom": "Laforge", 620 | "prenom": "Guillaume", 621 | "email": "glaforge@gmail.com", 622 | "ville": "Villiers sur Marne", 623 | "likes": ["Groovy", 624 | "Grails", 625 | "Blagues"], 626 | "hate1": "Devoxx FR", 627 | "hate2": "Erlang" 628 | }, 629 | { 630 | "nom": "Carmack", 631 | "prenom": "John", 632 | "email": "john@doom.com", 633 | "ville": "Roeland Park", 634 | "likes": ["Doom", 635 | "Quake", 636 | "3D"], 637 | "hate1": "sprites", 638 | "hate2": "unreal tournament" 639 | }, 640 | { 641 | "nom": "Gosling", 642 | "prenom": "James", 643 | "email": "james@javawillneverdie.com", 644 | "ville": "MoutainView", 645 | "likes": ["Java", 646 | "JVM", 647 | "Robots"], 648 | "hate1": "surcharge d'opérateur", 649 | "hate2": "oracle" 650 | }, 651 | { 652 | "nom": "Zuckerberg", 653 | "prenom": "Mark", 654 | "email": "imceobitch@facebook.com", 655 | "ville": "Palo Alto", 656 | "likes": ["Facebook", 657 | "Tongues", 658 | "PHP"], 659 | "hate1": "Tyler Winklevoss", 660 | "hate2": "Cameron Winklevoss" 661 | }, 662 | { 663 | "nom": "Odersky", 664 | "prenom": "Martin", 665 | "email": "martin@scalaftw.net", 666 | "ville": "Lausanne", 667 | "likes": ["Scala", 668 | "Java Generics", 669 | "javac"], 670 | "hate1": "Untyped Language", 671 | "hate2": "scripting" 672 | }, 673 | { 674 | "nom": "Beck", 675 | "prenom": "Kent", 676 | "email": "kent@tdd.org", 677 | "ville": "Medford", 678 | "likes": ["Smalltalk", 679 | "JUnit", 680 | "TDD"], 681 | "hate1": "Waterfall", 682 | "hate2": "Big Design UpFront" 683 | } 684 | ] 685 | -------------------------------------------------------------------------------- /web/static/js/angular.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.5 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(X,Y,q){'use strict';function n(b,a,c){var d;if(b)if(H(b))for(d in b)d!="prototype"&&d!="length"&&d!="name"&&b.hasOwnProperty(d)&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==n)b.forEach(a,c);else if(!b||typeof b.length!=="number"?0:typeof b.hasOwnProperty!="function"&&typeof b.constructor!="function"||b instanceof L||ca&&b instanceof ca||xa.call(b)!=="[object Object]"||typeof b.callee==="function")for(d=0;d=0&&b.splice(c,1);return a}function U(b,a){if(pa(b)|| 10 | b&&b.$evalAsync&&b.$watch)throw Error("Can't copy Window or Scope");if(a){if(b===a)throw Error("Can't copy equivalent objects or arrays");if(B(b))for(var c=a.length=0;c2?ha.call(arguments,2):[];return H(a)&&!(a instanceof RegExp)?c.length?function(){return arguments.length?a.apply(b,c.concat(ha.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}:a}function ic(b,a){var c=a;/^\$+/.test(b)?c=q:pa(a)?c="$WINDOW":a&&Y===a?c="$DOCUMENT":a&&a.$evalAsync&&a.$watch&&(c="$SCOPE");return c}function da(b,a){return JSON.stringify(b,ic,a?" ":null)}function ob(b){return A(b)?JSON.parse(b):b}function Va(b){b&&b.length!== 13 | 0?(b=y(""+b),b=!(b=="f"||b=="0"||b=="false"||b=="no"||b=="n"||b=="[]")):b=!1;return b}function qa(b){b=u(b).clone();try{b.html("")}catch(a){}var c=u("
").append(b).html();try{return b[0].nodeType===3?y(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+y(b)})}catch(d){return y(c)}}function Wa(b){var a={},c,d;n((b||"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]),a[d]=x(c[1])?decodeURIComponent(c[1]):!0)});return a}function pb(b){var a=[];n(b,function(b, 14 | d){a.push(Xa(d,!0)+(b===!0?"":"="+Xa(b,!0)))});return a.length?a.join("&"):""}function Ya(b){return Xa(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Xa(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(a?null:/%20/g,"+")}function jc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,h=["ng:app","ng-app","x-ng-app","data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;n(h,function(a){h[a]=!0;c(Y.getElementById(a)); 15 | a=a.replace(":","\\:");b.querySelectorAll&&(n(b.querySelectorAll("."+a),c),n(b.querySelectorAll("."+a+"\\:"),c),n(b.querySelectorAll("["+a+"]"),c))});n(d,function(a){if(!e){var b=f.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):n(a.attributes,function(b){if(!e&&h[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}function qb(b,a){b=u(b);a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");var c=rb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector", 16 | function(a,b,c,h){a.$apply(function(){b.data("$injector",h);c(b)(a)})}]);return c}function Za(b,a){a=a||"_";return b.replace(kc,function(b,d){return(d?a:"")+b.toLowerCase()})}function $a(b,a,c){if(!b)throw Error("Argument '"+(a||"?")+"' is "+(c||"required"));return b}function ra(b,a,c){c&&B(b)&&(b=b[b.length-1]);$a(H(b),a,"not a function, got "+(b&&typeof b=="object"?b.constructor.name||"Object":typeof b));return b}function lc(b){function a(a,b,e){return a[b]||(a[b]=e())}return a(a(b,"angular",Object), 17 | "module",function(){var b={};return function(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);return a(b,d,function(){function a(c,d,e){return function(){b[e||"push"]([c,d,arguments]);return k}}if(!e)throw Error("No module: "+d);var b=[],c=[],i=a("$injector","invoke"),k={_invokeQueue:b,_runBlocks:c,requires:e,name:d,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider", 18 | "register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:i,run:function(a){c.push(a);return this}};g&&i(g);return k})}})}function sb(b){return b.replace(mc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(nc,"Moz$1")}function ab(b,a){function c(){var e;for(var b=[this],c=a,h,f,j,i,k,m;b.length;){h=b.shift();f=0;for(j=h.length;f 
"+b;a.removeChild(a.firstChild);bb(this,a.childNodes);this.remove()}else bb(this,b)}function cb(b){return b.cloneNode(!0)}function sa(b){tb(b);for(var a=0,b=b.childNodes||[];a-1}function wb(b,a){a&&n(a.split(" "),function(a){b.className=O((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+O(a)+" "," "))})}function xb(b,a){a&&n(a.split(" "),function(a){if(!Da(b,a))b.className=O(b.className+" "+O(a))})}function bb(b,a){if(a)for(var a=!a.nodeName&&x(a.length)&&!pa(a)?a:[a],c=0;c4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!")}else{if(j.cookie!== 33 | $){$=j.cookie;d=$.split("; ");r={};for(f=0;f0&&(r[unescape(e.substring(0,i))]=unescape(e.substring(i+1)))}return r}};f.defer=function(a,b){var c;o++;c=m(function(){delete t[c];e(a)},b||0);t[c]=!0;return c};f.defer.cancel=function(a){return t[a]?(delete t[a],l(a),e(C),!0):!1}}function wc(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new vc(b,d,a,c)}]}function xc(){this.$get=function(){function b(b,d){function e(a){if(a!=m){if(l){if(l== 34 | a)l=a.n}else l=a;g(a.n,a.p);g(a,m);m=a;m.n=null}}function g(a,b){if(a!=b){if(a)a.p=b;if(b)b.n=a}}if(b in a)throw Error("cacheId "+b+" taken");var h=0,f=v({},d,{id:b}),j={},i=d&&d.capacity||Number.MAX_VALUE,k={},m=null,l=null;return a[b]={put:function(a,b){var c=k[a]||(k[a]={key:a});e(c);w(b)||(a in j||h++,j[a]=b,h>i&&this.remove(l.key))},get:function(a){var b=k[a];if(b)return e(b),j[a]},remove:function(a){var b=k[a];if(b){if(b==m)m=b.p;if(b==l)l=b.n;g(b.n,b.p);delete k[a];delete j[a];h--}},removeAll:function(){j= 35 | {};h=0;k={};m=l=null},destroy:function(){k=f=j=null;delete a[b]},info:function(){return v({},f,{size:h})}}}var a={};b.info=function(){var b={};n(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function yc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Cb(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: ",h=/^\s*(https?|ftp|mailto):/; 36 | this.directive=function j(d,e){A(d)?($a(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];n(a[d],function(a){try{var g=b.invoke(a);if(H(g))g={compile:I(g)};else if(!g.compile&&g.link)g.compile=I(g.link);g.priority=g.priority||0;g.name=g.name||d;g.require=g.require||g.controller&&g.name;g.restrict=g.restrict||"A";e.push(g)}catch(h){c(h)}});return e}])),a[d].push(e)):n(d,nb(j));return this};this.urlSanitizationWhitelist=function(a){return x(a)? 37 | (h=a,this):h};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document",function(b,i,k,m,l,t,o,p,s){function J(a,b,c){a instanceof u||(a=u(a));n(a,function(b,c){b.nodeType==3&&b.nodeValue.match(/\S+/)&&(a[c]=u(b).wrap("").parent()[0])});var d=z(a,b,a,c);return function(b,c){$a(b,"scope");for(var e=c?va.clone.call(a):a,g=0,i=e.length;gr.priority)break;if(W=r.scope)ua("isolated scope",K,r,D),M(W)&&(F(D,"ng-isolate-scope"),K=r),F(D,"ng-scope"),p=p||r;G=r.name;if(W=r.controller)x=x||{}, 44 | ua("'"+G+"' controller",x[G],r,D),x[G]=r;if(W=r.transclude)ua("transclusion",ka,r,D),ka=r,l=r.priority,W=="element"?(S=u(b),D=c.$$element=u(Y.createComment(" "+G+": "+c[G]+" ")),b=D[0],C(e,u(S[0]),b),R=J(S,d,l)):(S=u(cb(b)).contents(),D.html(""),R=J(S,d));if(W=r.template)if(ua("template",z,r,D),z=r,W=Eb(W),r.replace){S=u("
"+O(W)+"
").contents();b=S[0];if(S.length!=1||b.nodeType!==1)throw Error(g+W);C(e,D,b);G={$attr:{}};a=a.concat(V(b,a.splice(v+1,a.length-(v+1)),G));$(c,G);y=a.length}else D.html(W); 45 | if(r.templateUrl)ua("template",z,r,D),z=r,j=P(a.splice(v,a.length-v),j,D,c,e,r.replace,R),y=a.length;else if(r.compile)try{w=r.compile(D,c,R),H(w)?i(null,w):w&&i(w.pre,w.post)}catch(E){k(E,qa(D))}if(r.terminal)j.terminal=!0,l=Math.max(l,r.priority)}j.scope=p&&p.scope;j.transclude=ka&&R;return j}function r(d,e,i,g){var h=!1;if(a.hasOwnProperty(e))for(var l,e=b.get(e+c),o=0,m=e.length;ol.priority)&&l.restrict.indexOf(i)!=-1)d.push(l),h=!0}catch(t){k(t)}return h}function $(a, 46 | b){var c=b.$attr,d=a.$attr,e=a.$$element;n(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});n(b,function(b,i){i=="class"?(F(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):i=="style"?e.attr("style",e.attr("style")+";"+b):i.charAt(0)!="$"&&!a.hasOwnProperty(i)&&(a[i]=b,d[i]=c[i])})}function P(a,b,c,d,e,i,h){var j=[],k,o,t=c[0],s=a.shift(),p=v({},s,{controller:null,templateUrl:null,transclude:null,scope:null});c.html("");m.get(s.templateUrl,{cache:l}).success(function(l){var m, 47 | s,l=Eb(l);if(i){s=u("
"+O(l)+"
").contents();m=s[0];if(s.length!=1||m.nodeType!==1)throw Error(g+l);l={$attr:{}};C(e,c,m);V(m,a,l);$(d,l)}else m=t,c.html(l);a.unshift(p);k=K(a,m,d,h);for(o=z(c.contents(),h);j.length;){var ia=j.pop(),l=j.pop();s=j.pop();var r=j.pop(),D=m;s!==t&&(D=cb(m),C(l,u(s),D));k(function(){b(o,r,D,e,ia)},r,D,e,ia)}j=null}).error(function(a,b,c,d){throw Error("Failed to load template: "+d.url);});return function(a,c,d,e,i){j?(j.push(c),j.push(d),j.push(e),j.push(i)): 48 | k(function(){b(o,c,d,e,i)},c,d,e,i)}}function G(a,b){return b.priority-a.priority}function ua(a,b,c,d){if(b)throw Error("Multiple directives ["+b.name+", "+c.name+"] asking for "+a+" on: "+qa(d));}function x(a,b){var c=i(b,!0);c&&a.push({priority:0,compile:I(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);F(d.data("$binding",e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=a})})})}function R(a,b,c,d){var e=i(c,!0);e&&b.push({priority:100,compile:I(function(a,b,c){b=c.$$observers|| 49 | (c.$$observers={});d==="class"&&(e=i(c[d],!0));c[d]=q;(b[d]||(b[d]=[])).$$inter=!0;(c.$$observers&&c.$$observers[d].$$scope||a).$watch(e,function(a){c.$set(d,a)})})})}function C(a,b,c){var d=b[0],e=d.parentNode,i,g;if(a){i=0;for(g=a.length;i 67 | 0){var e=P[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return!1}function f(b,c,d,f){return(b=h(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),P.shift(),b):!1}function j(a){f(a)||e("is unexpected, expecting ["+a+"]",h())}function i(a,b){return function(c,d){return a(c,d,b)}}function k(a,b,c){return function(d,e){return b(d,e,a,c)}}function m(){for(var a=[];;)if(P.length>0&&!h("}",")",";","]")&&a.push(w()),!f(";"))return a.length==1?a[0]:function(b,c){for(var d,e=0;e","<=",">="))a=k(a,b.fn,s());return a}function J(){for(var a=n(),b;b=f("*","/","%");)a=k(a,b.fn,n());return a}function n(){var a;return f("+")?z():(a=f("-"))?k(r,a.fn,n()):(a=f("!"))?i(a.fn,n()):z()}function z(){var a;if(f("("))a=w(),j(")");else if(f("["))a=V();else if(f("{"))a=K();else{var b=f();(a=b.fn)||e("not a primary expression",b)}for(var c;b=f("(","[",".");)b.text==="("?(a=x(a,c),c=null):b.text==="["?(c=a,a=R(a)):b.text==="."?(c=a,a=u(a)):e("IMPOSSIBLE");return a}function V(){var a= 70 | [];if(g().text!="]"){do a.push(G());while(f(","))}j("]");return function(b,c){for(var d=[],e=0;e1;d++){var e=a.shift(),g=b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]= 73 | c}function gb(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,h=0;h7),hasEvent:function(c){if(c=="input"&&Z==9)return!1;if(w(a[c])){var e=b.document.createElement("div");a[c]="on"+c in e}return a[c]},csp:!1}}]}function Uc(){this.$get=I(X)}function Nb(b){var a={},c,d,e;if(!b)return a;n(b.split("\n"),function(b){e=b.indexOf(":");c=y(O(b.substr(0, 91 | e)));d=O(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function Ob(b){var a=M(b)?b:q;return function(c){a||(a=Nb(b));return c?a[y(c)]||null:a}}function Pb(b,a,c){if(H(c))return c(b,a);n(c,function(c){b=c(b,a)});return b}function Vc(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d=this.defaults={transformResponse:[function(d){A(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=ob(d,!0)));return d}],transformRequest:[function(a){return M(a)&&xa.apply(a)!=="[object File]"?da(a):a}], 92 | headers:{common:{Accept:"application/json, text/plain, */*","X-Requested-With":"XMLHttpRequest"},post:{"Content-Type":"application/json;charset=utf-8"},put:{"Content-Type":"application/json;charset=utf-8"}}},e=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,j,i,k){function m(a){function c(a){var b=v({},a,{data:Pb(a.data,a.headers,f)});return 200<=a.status&&a.status<300?b:i.reject(b)}a.method=ma(a.method);var e=a.transformRequest|| 93 | d.transformRequest,f=a.transformResponse||d.transformResponse,g=d.headers,g=v({"X-XSRF-TOKEN":b.cookies()["XSRF-TOKEN"]},g.common,g[y(a.method)],a.headers),e=Pb(a.data,Ob(g),e),j;w(a.data)&&delete g["Content-Type"];j=l(a,e,g);j=j.then(c,c);n(p,function(a){j=a(j)});j.success=function(b){j.then(function(c){b(c.data,c.status,c.headers,a)});return j};j.error=function(b){j.then(null,function(c){b(c.data,c.status,c.headers,a)});return j};return j}function l(b,c,d){function e(a,b,c){n&&(200<=a&&a<300?n.put(q, 94 | [a,b,Nb(c)]):n.remove(q));f(b,a,c);j.$apply()}function f(a,c,d){c=Math.max(c,0);(200<=c&&c<300?k.resolve:k.reject)({data:a,status:c,headers:Ob(d),config:b})}function h(){var a=Aa(m.pendingRequests,b);a!==-1&&m.pendingRequests.splice(a,1)}var k=i.defer(),l=k.promise,n,p,q=t(b.url,b.params);m.pendingRequests.push(b);l.then(h,h);b.cache&&b.method=="GET"&&(n=M(b.cache)?b.cache:o);if(n)if(p=n.get(q))if(p.then)return p.then(h,h),p;else B(p)?f(p[1],p[0],U(p[2])):f(p,200,{});else n.put(q,l);p||a(b.method, 95 | q,c,e,d,b.timeout,b.withCredentials);return l}function t(a,b){if(!b)return a;var c=[];fc(b,function(a,b){a==null||a==q||(M(a)&&(a=da(a)),c.push(encodeURIComponent(b)+"="+encodeURIComponent(a)))});return a+(a.indexOf("?")==-1?"?":"&")+c.join("&")}var o=c("$http"),p=[];n(e,function(a){p.push(A(a)?k.get(a):k.invoke(a))});m.pendingRequests=[];(function(a){n(arguments,function(a){m[a]=function(b,c){return m(v(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){n(arguments,function(a){m[a]= 96 | function(b,c,d){return m(v(d||{},{method:a,url:b,data:c}))}})})("post","put");m.defaults=d;return m}]}function Wc(){this.$get=["$browser","$window","$document",function(b,a,c){return Xc(b,Yc,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}function Xc(b,a,c,d,e,g){function h(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;Z?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror= 97 | d;e.body.appendChild(c)}return function(e,j,i,k,m,l,t){function o(a,c,d,e){c=(j.match(Gb)||["",g])[1]=="file"?d?200:404:c;a(c==1223?204:c,d,e);b.$$completeOutstandingRequest(C)}b.$$incOutstandingRequestCount();j=j||b.url();if(y(e)=="jsonp"){var p="_"+(d.counter++).toString(36);d[p]=function(a){d[p].data=a};h(j.replace("JSON_CALLBACK","angular.callbacks."+p),function(){d[p].data?o(k,200,d[p].data):o(k,-2);delete d[p]})}else{var s=new a;s.open(e,j,!0);n(m,function(a,b){a&&s.setRequestHeader(b,a)}); 98 | var q;s.onreadystatechange=function(){if(s.readyState==4){var a=s.getAllResponseHeaders(),b=["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified","Pragma"];a||(a="",n(b,function(b){var c=s.getResponseHeader(b);c&&(a+=b+": "+c+"\n")}));o(k,q||s.status,s.responseText,a)}};if(t)s.withCredentials=!0;s.send(i||"");l>0&&c(function(){q=-1;s.abort()},l)}}}function Zc(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0, 99 | maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),SHORTMONTH:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),DAY:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),SHORTDAY:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","), 100 | AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return b===1?"one":"other"}}}}function $c(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,f,j){var i=c.defer(),k=i.promise,m=x(j)&&!j,f=a.defer(function(){try{i.resolve(e())}catch(a){i.reject(a),d(a)}m||b.$apply()},f),j=function(){delete g[k.$$timeoutId]}; 101 | k.$$timeoutId=f;g[f]=i;k.then(j,j);return k}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),a.defer.cancel(b.$$timeoutId)):!1};return e}]}function Qb(b){function a(a,e){return b.factory(a+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Rb);a("date",Sb);a("filter",ad);a("json",bd);a("limitTo",cd);a("lowercase",dd);a("number",Tb);a("orderBy",Ub);a("uppercase",ed)}function ad(){return function(b, 102 | a){if(!B(b))return b;var c=[];c.check=function(a){for(var b=0;b-1;case "object":for(var c in a)if(c.charAt(0)!=="$"&&d(a[c],b))return!0;return!1;case "array":for(c=0;ce+1?h="0":(f=h,i=!0)}if(!i){h=(h.split(Wb)[1]||"").length;w(e)&&(e=Math.min(Math.max(a.minFrac,h),a.maxFrac));var h=Math.pow(10,e),b=Math.round(b*h)/h,b=(""+b).split(Wb),h=b[0],b=b[1]||"",i=0,k=a.lgSize, 105 | m=a.gSize;if(h.length>=k+m)for(var i=h.length-k,l=0;l0||e>-c)e+= 106 | c;e===0&&c==-12&&(e=12);return jb(e,a,d)}}function Ka(b,a){return function(c,d){var e=c["get"+b](),g=ma(a?"SHORT"+b:b);return d[g][e]}}function Sb(b){function a(a){var b;if(b=a.match(c)){var a=new Date(0),g=0,h=0;b[9]&&(g=E(b[9]+b[10]),h=E(b[9]+b[11]));a.setUTCFullYear(E(b[1]),E(b[2])-1,E(b[3]));a.setUTCHours(E(b[4]||0)-g,E(b[5]||0)-h,E(b[6]||0),E(b[7]||0))}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var g= 107 | "",h=[],f,j,e=e||"mediumDate",e=b.DATETIME_FORMATS[e]||e;A(c)&&(c=fd.test(c)?E(c):a(c));Ra(c)&&(c=new Date(c));if(!oa(c))return c;for(;e;)(j=gd.exec(e))?(h=h.concat(ha.call(j,1)),e=h.pop()):(h.push(e),e=null);n(h,function(a){f=hd[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function bd(){return function(b){return da(b,!0)}}function cd(){return function(b,a){if(!(b instanceof Array))return b;var a=E(a),c=[],d,e;if(!b||!(b instanceof Array))return c;a>b.length? 108 | a=b.length:a<-b.length&&(a=-b.length);a>0?(d=0,e=a):(d=b.length+a,e=b.length);for(;dm?(d.$setValidity("maxlength",!1),q):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(c);d.$formatters.push(c)}}function kb(b,a){b="ngClass"+b;return Q(function(c,d,e){function g(b){if(a===!0||c.$index%2===a)j&&b!==j&&h(j),f(b);j=b}function h(a){M(a)&&!B(a)&&(a=Sa(a,function(a,b){if(a)return b}));d.removeClass(B(a)? 114 | a.join(" "):a)}function f(a){M(a)&&!B(a)&&(a=Sa(a,function(a,b){if(a)return b}));a&&d.addClass(B(a)?a.join(" "):a)}var j=q;c.$watch(e[b],g,!0);e.$observe("class",function(){var a=c.$eval(e[b]);g(a,a)});b!=="ngClass"&&c.$watch("$index",function(d,g){var j=d%2;j!==g%2&&(j==a?f(c.$eval(e[b])):h(c.$eval(e[b])))})})}var y=function(b){return A(b)?b.toLowerCase():b},ma=function(b){return A(b)?b.toUpperCase():b},Z=E((/msie (\d+)/.exec(y(navigator.userAgent))||[])[1]),u,ca,ha=[].slice,Qa=[].push,xa=Object.prototype.toString, 115 | Zb=X.angular||(X.angular={}),ta,fb,aa=["0","0","0"];C.$inject=[];na.$inject=[];fb=Z<9?function(b){b=b.nodeName?b:b[0];return b.scopeName&&b.scopeName!="HTML"?ma(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var kc=/[A-Z]/g,id={full:"1.0.5",major:1,minor:0,dot:5,codeName:"flatulent-propulsion"},Ca=L.cache={},Ba=L.expando="ng-"+(new Date).getTime(),oc=1,$b=X.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+ 116 | a,c)},db=X.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},mc=/([\:\-\_]+(.))/g,nc=/^moz([A-Z])/,va=L.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;this.bind("DOMContentLoaded",a);L(X).bind("load",a)},toString:function(){var b=[];n(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return b>=0?u(this[b]):u(this[this.length+b])},length:0,push:Qa,sort:[].sort,splice:[].splice},Fa={};n("multiple,selected,checked,disabled,readOnly,required".split(","), 117 | function(b){Fa[y(b)]=b});var Ab={};n("input,select,option,textarea,button,form".split(","),function(b){Ab[ma(b)]=!0});n({data:vb,inheritedData:Ea,scope:function(b){return Ea(b,"$scope")},controller:yb,injector:function(b){return Ea(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Da,css:function(b,a,c){a=sb(a);if(x(c))b.style[a]=c;else{var d;Z<=8&&(d=b.currentStyle&&b.currentStyle[a],d===""&&(d="auto"));d=d||b.style[a];Z<=8&&(d=d===""?q:d);return d}},attr:function(b,a,c){var d= 118 | y(a);if(Fa[d])if(x(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||C).specified?d:q;else if(x(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?q:b},prop:function(b,a,c){if(x(c))b[a]=c;else return b[a]},text:v(Z<9?function(b,a){if(b.nodeType==1){if(w(a))return b.innerText;b.innerText=a}else{if(w(a))return b.nodeValue;b.nodeValue=a}}:function(b,a){if(w(a))return b.textContent;b.textContent=a},{$dv:""}), 119 | val:function(b,a){if(w(a))return b.value;b.value=a},html:function(b,a){if(w(a))return b.innerHTML;for(var c=0,d=b.childNodes;c":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Lc= 128 | {n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},ib={},Yc=X.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw Error("This browser does not support XMLHttpRequest.");};Qb.$inject=["$provide"];Rb.$inject=["$locale"];Tb.$inject=["$locale"];var Wb=".",hd={yyyy:N("FullYear",4),yy:N("FullYear",2,0,!0),y:N("FullYear",1),MMMM:Ka("Month"), 129 | MMM:Ka("Month",!0),MM:N("Month",2,1),M:N("Month",1,1),dd:N("Date",2),d:N("Date",1),HH:N("Hours",2),H:N("Hours",1),hh:N("Hours",2,-12),h:N("Hours",1,-12),mm:N("Minutes",2),m:N("Minutes",1),ss:N("Seconds",2),s:N("Seconds",1),EEEE:Ka("Day"),EEE:Ka("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){var a=-1*a.getTimezoneOffset(),c=a>=0?"+":"";c+=jb(a/60,2)+jb(Math.abs(a%60),2);return c}},gd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, 130 | fd=/^\d+$/;Sb.$inject=["$locale"];var dd=I(y),ed=I(ma);Ub.$inject=["$parse"];var jd=I({restrict:"E",compile:function(a,c){Z<=8&&(!c.href&&!c.name&&c.$set("href",""),a.append(Y.createComment("IE fix")));return function(a,c){c.bind("click",function(a){c.attr("href")||a.preventDefault()})}}}),lb={};n(Fa,function(a,c){var d=ea("ng-"+c);lb[d]=function(){return{priority:100,compile:function(){return function(a,g,h){a.$watch(h[d],function(a){h.$set(c,!!a)})}}}}});n(["src","href"],function(a){var c=ea("ng-"+ 131 | a);lb[c]=function(){return{priority:99,link:function(d,e,g){g.$observe(c,function(c){c&&(g.$set(a,c),Z&&e.prop(a,g[a]))})}}}});var Na={$addControl:C,$removeControl:C,$setValidity:C,$setDirty:C};Xb.$inject=["$element","$attrs","$scope"];var Qa=function(a){return["$timeout",function(c){var d={name:"form",restrict:"E",controller:Xb,compile:function(){return{pre:function(a,d,h,f){if(!h.action){var j=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};$b(d[0],"submit",j);d.bind("$destroy", 132 | function(){c(function(){db(d[0],"submit",j)},0,!1)})}var i=d.parent().controller("form"),k=h.name||h.ngForm;k&&(a[k]=f);i&&d.bind("$destroy",function(){i.$removeControl(f);k&&(a[k]=q);v(f,Na)})}}}};return a?v(U(d),{restrict:"EAC"}):d}]},kd=Qa(),ld=Qa(!0),md=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,nd=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,od=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,bc={text:Pa,number:function(a,c,d,e,g,h){Pa(a,c,d,e,g,h);e.$parsers.push(function(a){var c= 133 | T(a);return c||od.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",!1),q)});e.$formatters.push(function(a){return T(a)?"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!T(a)&&aj?(e.$setValidity("max",!1),q):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return T(a)|| 134 | Ra(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),q)})},url:function(a,c,d,e,g,h){Pa(a,c,d,e,g,h);a=function(a){return T(a)||md.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url",!1),q)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,h){Pa(a,c,d,e,g,h);a=function(a){return T(a)||nd.test(a)?(e.$setValidity("email",!0),a):(e.$setValidity("email",!1),q)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){w(d.name)&&c.attr("name",ya());c.bind("click", 135 | function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,h=d.ngFalseValue;A(g)||(g=!0);A(h)||(h=!1);c.bind("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:h})},hidden:C,button:C,submit:C,reset:C}, 136 | cc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,h){h&&(bc[y(g.type)]||bc.text)(d,e,g,h,c,a)}}}],Ma="ng-valid",La="ng-invalid",Oa="ng-pristine",Yb="ng-dirty",pd=["$scope","$exceptionHandler","$attrs","$element","$parse",function(a,c,d,e,g){function h(a,c){c=c?"-"+Za(c,"-"):"";e.removeClass((a?La:Ma)+c).addClass((a?Ma:La)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine= 137 | !0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var f=g(d.ngModel),j=f.assign;if(!j)throw Error(Db+d.ngModel+" ("+qa(e)+")");this.$render=C;var i=e.inheritedData("$formController")||Na,k=0,m=this.$error={};e.addClass(Oa);h(!0);this.$setValidity=function(a,c){if(m[a]!==!c){if(c){if(m[a]&&k--,!k)h(!0),this.$valid=!0,this.$invalid=!1}else h(!1),this.$invalid=!0,this.$valid=!1,k++;m[a]=!c;h(c,a);i.$setValidity(a,c,this)}};this.$setViewValue=function(d){this.$viewValue=d;if(this.$pristine)this.$dirty= 138 | !0,this.$pristine=!1,e.removeClass(Oa).addClass(Yb),i.$setDirty();n(this.$parsers,function(a){d=a(d)});if(this.$modelValue!==d)this.$modelValue=d,j(a,d),n(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};var l=this;a.$watch(function(){var c=f(a);if(l.$modelValue!==c){var d=l.$formatters,e=d.length;for(l.$modelValue=c;e--;)c=d[e](c);if(l.$viewValue!==c)l.$viewValue=c,l.$render()}})}],qd=function(){return{require:["ngModel","^?form"],controller:pd,link:function(a,c,d,e){var g=e[0],h= 139 | e[1]||Na;h.$addControl(g);c.bind("$destroy",function(){h.$removeControl(g)})}}},rd=I({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),dc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&(T(a)||a===!1))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},sd=function(){return{require:"ngModel", 140 | link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){var c=[];a&&n(a.split(g),function(a){a&&c.push(O(a))});return c});e.$formatters.push(function(a){return B(a)?a.join(", "):q})}}},td=/^(true|false|\d+)$/,ud=function(){return{priority:100,compile:function(a,c){return td.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a,!1)})}}}},vd=Q(function(a,c,d){c.addClass("ng-binding").data("$binding", 141 | d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==q?"":a)})}),wd=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],xd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe,function(a){c.html(a||"")})}}],yd=kb("",!0),zd=kb("Odd",0),Ad=kb("Even",1),Bd=Q({compile:function(a,c){c.$set("ngCloak",q); 142 | a.removeClass("ng-cloak")}}),Cd=[function(){return{scope:!0,controller:"@"}}],Dd=["$sniffer",function(a){return{priority:1E3,compile:function(){a.csp=!0}}}],ec={};n("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave".split(" "),function(a){var c=ea("ng-"+a);ec[c]=["$parse",function(d){return function(e,g,h){var f=d(h[c]);g.bind(y(a),function(a){e.$apply(function(){f(e,{$event:a})})})}}]});var Ed=Q(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}), 143 | Fd=["$http","$templateCache","$anchorScroll","$compile",function(a,c,d,e){return{restrict:"ECA",terminal:!0,compile:function(g,h){var f=h.ngInclude||h.src,j=h.onload||"",i=h.autoscroll;return function(g,h){var l=0,n,o=function(){n&&(n.$destroy(),n=null);h.html("")};g.$watch(f,function(f){var s=++l;f?a.get(f,{cache:c}).success(function(a){s===l&&(n&&n.$destroy(),n=g.$new(),h.html(a),e(h.contents())(n),x(i)&&(!i||g.$eval(i))&&d(),n.$emit("$includeContentLoaded"),g.$eval(j))}).error(function(){s===l&& 144 | o()}):o()})}}}}],Gd=Q({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Hd=Q({terminal:!0,priority:1E3}),Id=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,h){var f=h.count,j=g.attr(h.$attr.when),i=h.offset||0,k=e.$eval(j),m={},l=c.startSymbol(),t=c.endSymbol();n(k,function(a,e){m[e]=c(a.replace(d,l+f+"-"+i+t))});e.$watch(function(){var c=parseFloat(e.$eval(f));return isNaN(c)?"":(k[c]||(c=a.pluralCat(c-i)),m[c](e,g,!0))},function(a){g.text(a)})}}}], 145 | Jd=Q({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){return function(a,c,h){var f=h.ngRepeat,h=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/),j,i,k;if(!h)throw Error("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f=h[1];j=h[2];h=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!h)throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '"+f+"'.");i=h[3]||h[1];k=h[2];var m=new eb;a.$watch(function(a){var e,f,h=a.$eval(j), 146 | n=c,q=new eb,x,z,u,w,r,v;if(B(h))r=h||[];else{r=[];for(u in h)h.hasOwnProperty(u)&&u.charAt(0)!="$"&&r.push(u);r.sort()}x=r.length;e=0;for(f=r.length;eA;)u.pop().element.remove()}for(;r.length>y;)r.pop()[0].element.remove()}var i;if(!(i=p.match(d)))throw Error("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+p+"'.");var j=c(i[2]||i[1]),k=i[4]||i[6],l=i[5],m=c(i[3]||""), 156 | n=c(i[2]?i[1]:k),t=c(i[7]),r=[[{element:f,label:""}]];s&&(a(s)(e),s.removeClass("ng-scope"),s.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a,c=t(e)||[],d={},h,i,j,m,p,s;if(o){i=[];m=0;for(s=r.length;m@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}'); 162 | --------------------------------------------------------------------------------