Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Lots of additions/changes. Not a detailed commit (was on a roll).

  • Loading branch information...
commit ac202a8c55dca5e0fdd21b4d9196d349f919e185 1 parent 81c6ed2
@ashafa authored
Showing with 1,160 additions and 530 deletions.
  1. +48 −0 src/clj/renderer/macros.clj
  2. +0 −67 src/clj/wheresthembta/macros.clj
  3. +51 −38 src/cljs/client/wheresthembta/core.cljs
  4. +15 −38 src/cljs/server/wheresthembta/core.cljs
  5. +14 −45 src/cljs/server/wheresthembta/real_time_feed.cljs
  6. +18 −0 src/cljs/server/wheresthembta/redis_client.cljs
  7. +28 −0 src/cljs/server/wheresthembta/socket_io.cljs
  8. +59 −0 src/cljs/server/wheresthembta/twitter.cljs
  9. +61 −91 src/cljs/server/wheresthembta/views.cljs
  10. +164 −84 src/cljs/shared/mbta_data.cljs
  11. +53 −64 src/cljs/shared/templates.cljs
  12. +40 −28 src/cljs/shared/utils.cljs
  13. +239 −0 static/css/font-awesome.css
  14. +160 −51 static/css/style.css
  15. BIN  static/flashsocket/WebSocketMain.swf
  16. BIN  static/font/fontawesome-webfont.eot
  17. +175 −0 static/font/fontawesome-webfont.svg
  18. BIN  static/font/fontawesome-webfont.svgz
  19. BIN  static/font/fontawesome-webfont.ttf
  20. BIN  static/font/fontawesome-webfont.woff
  21. +2 −0  static/js/socket.io.min.js
  22. +15 −5 templates/about.html.mu
  23. +18 −15 templates/base.html.mu
  24. +0 −3  templates/privacy.html.mu
  25. +0 −1  templates/terms.html.mu
View
48 src/clj/renderer/macros.clj
@@ -0,0 +1,48 @@
+(ns renderer.macros)
+
+(defmacro render
+ [& body]
+ (let [output (last body)
+ body (butlast body)
+ args (if (vector? (first body)) (first body) ['req 'res])
+ handler-args (if (= (count args) 1) (conj args 'res) args)
+ res-code (or (first (filter integer? body)) 200)
+ default-type {:Content-Type "text/html; charset=UTF-8"}
+ headers (merge default-type (or (first (filter map? body)) {}))]
+ (cond (= (and (list? output) (first output)) '>>)
+ `(fn [~@handler-args]
+ (let [req# (first ~handler-args)
+ res# (second ~handler-args)
+ result# [~@output]
+ file# (nth result# 1)
+ context# (last result#)]
+ (cond (fn? context#)
+ (context# req# res#)
+ (map? context#)
+ (.render (node/require "mu") file# (utils/clj->js context#) (utils/clj->js {})
+ (fn [error# output#]
+ (if error#
+ (do (.writeHeader res# 500 (utils/clj->js ~default-type))
+ (.end res# (str error#)))
+ (do (.writeHeader res# ~res-code (utils/clj->js ~headers))
+ (doto output#
+ (.addListener "data" #(.write res# %))
+ (.addListener "end" #(.end res#)))))))
+ :else
+ (do (.writeHeader res# 500 (utils/clj->js ~default-type))
+ (.end res# "'map' to render template OR 'fn' required.")))))
+ (list? output)
+ `(fn [~@handler-args]
+ (let [req# (first ~handler-args)
+ res# (second ~handler-args)
+ context# ~output]
+ (if (fn? context#)
+ (context# req# res#)
+ (doto (second ~handler-args)
+ (.writeHeader ~res-code (utils/clj->js ~headers))
+ (.end context#)))))
+ :else
+ `(fn [~@handler-args]
+ (doto (second ~handler-args)
+ (.writeHeader ~res-code (utils/clj->js ~headers))
+ (.end (str ~output)))))))
View
67 src/clj/wheresthembta/macros.clj
@@ -1,67 +0,0 @@
-;; Copyright (c) 2012 Tunde Ashafa
-;; All rights reserved.
-
-;; Redistribution and use in source and binary forms, with or without
-;; modification, are permitted provided that the following conditions
-;; are met:
-;; 1. Redistributions of source code must retain the above copyright
-;; notice, this list of conditions and the following disclaimer.
-;; 2. Redistributions in binary form must reproduce the above copyright
-;; notice, this list of conditions and the following disclaimer in the
-;; documentation and/or other materials provided with the distribution.
-;; 3. The name of the author may not be used to endorse or promote products
-;; derived from this software without specific prior written permission.
-
-;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-(ns wheresthembta.macros)
-
-(defmacro render
- [& body]
- (let [output (last body)
- body (butlast body)
- args (if (vector? (first body)) (first body) ['req 'res])
- handler-args (if (= (count args) 1) (conj args 'res) args)
- res-code (or (first (filter integer? body)) 200)
- default-type {:Content-Type "text/html; charset=UTF-8"}
- headers (merge default-type (or (first (filter map? body)) {}))]
- (cond (= (and (list? output) (first output)) '>>)
- `(fn [~@handler-args]
- (let [req# (first ~handler-args)
- res# (second ~handler-args)
- file# (nth [~@output] 1)
- context# (nth [~@output] 2)]
- (cond (fn? context#)
- (context# req# res#)
- (map? context#)
- (.render (node/require "mu") file# (utils/clj->js context#) (utils/clj->js {})
- (fn [error# output#]
- (if error#
- (do (.writeHeader res# 500 (utils/clj->js ~default-type))
- (.end res# (str error#)))
- (do (.writeHeader res# ~res-code (utils/clj->js ~headers))
- (doto output#
- (.addListener "data" #(.write res# %))
- (.addListener "end" #(.end res#)))))))
- :else
- (do (.writeHeader res# 500 (utils/clj->js ~default-type))
- (.end res# "'map' to render template OR 'fn' required.")))))
- (list? output)
- `(fn [~@handler-args]
- (doto (second ~handler-args)
- (.writeHeader ~res-code (utils/clj->js ~headers))
- (.end ~output)))
- :else
- `(fn [~@handler-args]
- (doto (second ~handler-args)
- (.writeHeader ~res-code (utils/clj->js ~headers))
- (.end (str ~output)))))))
View
89 src/cljs/client/wheresthembta/core.cljs
@@ -1,28 +1,3 @@
-;; Copyright (c) 2012 Tunde Ashafa
-;; All rights reserved.
-
-;; Redistribution and use in source and binary forms, with or without
-;; modification, are permitted provided that the following conditions
-;; are met:
-;; 1. Redistributions of source code must retain the above copyright
-;; notice, this list of conditions and the following disclaimer.
-;; 2. Redistributions in binary form must reproduce the above copyright
-;; notice, this list of conditions and the following disclaimer in the
-;; documentation and/or other materials provided with the distribution.
-;; 3. The name of the author may not be used to endorse or promote products
-;; derived from this software without specific prior written permission.
-
-;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
(ns wheresthembta.core
(:require [wheresthembta.client-utils :as client-utils]
[wheresthembta.shared.utils :as utils]
@@ -42,9 +17,15 @@
[]
(client-utils/get-json-with-post current-url {}
{:success (fn [data]
- (.off ($ "#main") "click" "#predictions" get-predictions)
- (-> ($ "#status-good") (.stop true true) (.animate (utils/clj->js {:opacity 1}) 500))
- (-> ($ "div.tool-tip") (.stop true true) (.fadeOut 150 #(.remove ($ "div.tool-tip"))))
+ (-> ($ "#status-good")
+ (.stop true true)
+ (.fadeIn 500))
+ (-> ($ "#main")
+ (.stop true true)
+ (.animate (utils/clj->js {:opacity 1}) 500))
+ (-> ($ "div.tool-tip")
+ (.stop true true)
+ (.fadeOut 500 #(.remove ($ "div.tool-tip"))))
(indicate-freshness 60)
(set! js/PREDICTIONS data))}))
@@ -55,12 +36,17 @@
(js/clearTimeout @fresh-indicator)
(reset! fresh-indicator
(js/setTimeout (fn []
- (-> ($ "#status-good") (.stop true true) (.animate (utils/clj->js {:opacity 0}) 500))
- (.prepend ($ "#main") (templates/status-bar-tool-tip "top:12px;right:5px;"))
- (-> ($ "div.tool-tip") (.stop true true) (.fadeIn 150))
- (.click ($ "input#supress-status-bar-tip")
- (fn [] (-> ($ "div.tool-tip") (.stop true true) (.fadeOut 150 #(.remove ($ "div.tool-tip"))))))
- (.on ($ "#main") "click" "#predictions" get-predictions))
+ (-> ($ "#status-good")
+ (.stop true true)
+ (.fadeOut 500))
+ (-> ($ "#main")
+ (.prepend (templates/status-bar-tool-tip ""))
+ (.stop true true)
+ (.animate (utils/clj->js {:opacity 0.5}) 500))
+ (-> ($ "div.tool-tip")
+ (.stop true true)
+ (.fadeIn 500)
+ (.click get-predictions)))
(* age 1000))))
@@ -78,7 +64,7 @@
[]
(-> js/navigator .-geolocation
(.getCurrentPosition
- #(let [stations (take 3 (sort-by :distance
+ #(let [stations (take 6 (sort-by :distance
(for [station mbta-data/all-stations]
(assoc station :distance
(utils/calculate-distance (first (station :location))
@@ -87,13 +73,40 @@
(.. % -coords -latitude))))))]
(.html ($ "#nearby-stations") (templates/unordered-list-of-nearest-stations stations current-url))))))
+
+(defn make-websocket
+ []
+ (let [socket (.connect js/io)]
+ (.on socket "connect"
+ (fn []
+ (doto socket
+ (.on "new-tweet" #(let [tweets-section ($ "#relevant-tweets")
+ tweet-html (templates/div-of-relevant-tweets [(js->clj % :keywordize-keys true)])]
+ (.log js/console %)
+ (if (= (.-length ($ "div" tweets-section)) 0)
+ (.html tweets-section tweet-html)
+ (.prepend ($ "ul" tweets-section) (.find ($ tweet-html) "li")))))
+ (.emit "join-room" current-url))))))
+
+
+(defn update-tweet-time
+ []
+ (js/setTimeout (fn []
+ (update-tweet-time)
+ (.each ($ "time")
+ #(this-as this
+ (let [$this ($ this)]
+ (.html $this (utils/pretty-date (.attr $this "data-time"))))))) 30000))
+
(defn main
[]
- (if (.-geolocation js/Modernizr)
+ (when (.-geolocation js/Modernizr)
(show-closest-stations))
(when js/PREDICTIONS
(.show ($ "div.status-bar"))
(indicate-freshness 60)
- (refresh-predictions)))
+ (refresh-predictions)
+ (update-tweet-time)
+ (make-websocket)))
-(main)
+($ main)
View
53 src/cljs/server/wheresthembta/core.cljs
@@ -1,47 +1,24 @@
-;; Copyright (c) 2012 Tunde Ashafa
-;; All rights reserved.
-
-;; Redistribution and use in source and binary forms, with or without
-;; modification, are permitted provided that the following conditions
-;; are met:
-;; 1. Redistributions of source code must retain the above copyright
-;; notice, this list of conditions and the following disclaimer.
-;; 2. Redistributions in binary form must reproduce the above copyright
-;; notice, this list of conditions and the following disclaimer in the
-;; documentation and/or other materials provided with the distribution.
-;; 3. The name of the author may not be used to endorse or promote products
-;; derived from this software without specific prior written permission.
-
-;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
(ns wheresthembta.core
(:require [cljs.nodejs :as node]
+ [wheresthembta.socket-io :as socket-io]
+ [wheresthembta.twitter :as twitter]
[wheresthembta.config :as config]
[wheresthembta.views :as view]))
-(def router (node/require "router"))
-
-(defn main [& args]
- (doto (.create router)
- (.get "/" view/home)
- (.get "/about" view/about)
- (.get "/terms" view/terms)
- (.get "/privacy" view/privacy)
- (.get "/{transit}" view/lines)
- (.get "/{transit}/{line}" view/stations)
- (.get "/{transit}/{line}/{station}" view/station-info)
- (.post "/{transit}/{line}/{station}" view/station-info)
- (.listen config/PORT)))
+(defn main
+ [& args]
+ (let [router (.create (node/require "router"))]
+ (doto router
+ (.get "/" view/home)
+ (.get "/about" view/about)
+ (.get "/{transit}" view/lines)
+ (.get "/{transit}/{line}" view/stations)
+ (.get "/{transit}/{line}/{station}" view/station-info)
+ (.post "/{transit}/{line}/{station}" view/station-info)
+ (.listen config/PORT))
+ (socket-io/hook router)
+ (twitter/connect)))
(set! *main-cli-fn* main)
View
59 src/cljs/server/wheresthembta/real_time_feed.cljs
@@ -1,32 +1,6 @@
-;; Copyright (c) 2012 Tunde Ashafa
-;; All rights reserved.
-
-;; Redistribution and use in source and binary forms, with or without
-;; modification, are permitted provided that the following conditions
-;; are met:
-;; 1. Redistributions of source code must retain the above copyright
-;; notice, this list of conditions and the following disclaimer.
-;; 2. Redistributions in binary form must reproduce the above copyright
-;; notice, this list of conditions and the following disclaimer in the
-;; documentation and/or other materials provided with the distribution.
-;; 3. The name of the author may not be used to endorse or promote products
-;; derived from this software without specific prior written permission.
-
-;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
(ns wheresthembta.real-time-feed
(:require [cljs.nodejs :as node]
- [wheresthembta.shared.mbta-data :as mbta-data]
- [wheresthembta.shared.utils :as utils]))
+ [wheresthembta.shared.mbta-data :as mbta-data]))
@@ -43,26 +17,21 @@
(let [transit-id (.. req -params -transit)
line-id (.. req -params -line)]
(if-let [real-time-feed-url (mbta-data/get-value transit-id :lines line-id :real-time-feed-url)]
- (let [feed (@feed-cache line-id)
- data (:data feed)
- time (:time feed)
- status (:status feed)]
+ (let [{:keys [data time status]} (@feed-cache line-id)]
(if (and (= status ::not-fetching) (< (/ (- (.now js/Date) time) 1000) 15))
(callback req res data)
(do (swap! waiters conj (partial callback req res))
(when (not= status ::fetching)
(swap! feed-cache assoc-in [line-id :status] ::fetching)
- (-> (.get restler real-time-feed-url)
- (.on "complete" #(let [data (js->clj (.parse js/JSON %) :keywordize-keys true)]
- (swap! feed-cache
- assoc line-id {:data data
- :time (.now js/Date)
- :status ::not-fetching})
- (doseq [waiter @waiters]
- (waiter data))
- (reset! waiters [])))
- (.on "error" #(do (doseq [waiter @waiters]
- (waiter (or data [])))
- (reset! waiters []))))))))
- (callback req res [])))))
-
+ (let [timeout (atom nil)
+ set-cache #(do (js/clearTimeout @timeout)
+ (swap! feed-cache assoc line-id {:data %
+ :time (.now js/Date)
+ :status ::not-fetching})
+ (doseq [waiter @waiters] (waiter %))
+ (reset! waiters []))
+ rest (-> (.get restler real-time-feed-url)
+ (.on "complete" #(set-cache (js->clj (.parse js/JSON %) :keywordize-keys true)))
+ (.on "error" #(set-cache (or data []))))]
+ (reset! timeout (js/setTimeout #(.. rest -request (abort "timeout")) 2000)))))))
+ (callback req res [])))))
View
18 src/cljs/server/wheresthembta/redis_client.cljs
@@ -0,0 +1,18 @@
+(ns wheresthembta.redis-client
+ (:require [cljs.nodejs :as node]))
+
+
+
+(def redis (.createClient (node/require "redis")))
+
+
+
+(defn save-tweet
+ [key value callback]
+ (.lpush redis key value callback))
+
+(defn get-related-tweets
+ [callback]
+ (fn [req res]
+ (.lrange redis (.-url req) 0 2 #(callback req res (map (fn [tweet]
+ (js->clj (.parse js/JSON tweet) :keywordize-keys true)) %2)))))
View
28 src/cljs/server/wheresthembta/socket_io.cljs
@@ -0,0 +1,28 @@
+(ns wheresthembta.socket-io
+ (:require [cljs.nodejs :as node])
+ (:use [wheresthembta.shared.utils :only [clj->js]]))
+
+
+
+(def io (atom nil))
+
+
+(defn send-to-room
+ [room message]
+ (.. @io -sockets (to room) (emit "new-tweet" message)))
+
+(defn hook
+ [router]
+ (reset! io (.listen (node/require "socket.io") router))
+ (doto @io
+ (.enable "browser client minification")
+ (.enable "browser client etag")
+ (.enable "browser client gzip")
+ (.set "close timeout" (* 60 1000))
+ (.set "polling duration" (* 60 1000))
+ (.set "transports" (clj->js ["websocket" "htmlfile" "flashsocket" "xhr-polling" "jsonp-polling"]))
+ (.. -sockets
+ (on "connection"
+ (fn [socket]
+ (doto socket
+ (.on "join-room" #(.join socket %))))))))
View
59 src/cljs/server/wheresthembta/twitter.cljs
@@ -0,0 +1,59 @@
+(ns wheresthembta.twitter
+ (:require [cljs.nodejs :as node]
+ [wheresthembta.socket-io :as socket-io]
+ [wheresthembta.redis-client :as redis-client]
+ [wheresthembta.shared.mbta-data :as mbta-data]
+ [wheresthembta.config :as config])
+ (:use [wheresthembta.shared.utils :only [clj->js]]))
+
+
+
+(def node-twitter (node/require "/root/projects/node-twitter/lib/twitter"))
+
+(def keys-and-tokens (clj->js {:consumer_key config/consumer-key
+ :consumer_secret config/consumer-secret
+ :access_token_key config/access-token-key
+ :access_token_secret config/access-token-secret}))
+
+(def connect-time (atom 2000))
+
+
+(defn tag-save-send-tweet
+ [tweet]
+ (let [search-for-station (fn [station]
+ (let [search-using (fn [search]
+ (map #(.test % (.-text tweet))
+ (search station)))]
+ (and (not-every? #(= false %) (search-using :search-for))
+ (not-any? #(= true %) (search-using :search-not)))))
+ tagged-stations (filter search-for-station mbta-data/all-stations)
+ tweet-stringified (.stringify js/JSON tweet)]
+ (.log js/console (.-text tweet))
+ (println tagged-stations)
+ (doseq [station tagged-stations]
+ (when (not (.-retweeted-status tweet))
+ (.log js/console "Saving...")
+ (redis-client/save-tweet (station :url) tweet-stringified #(socket-io/send-to-room (station :url) tweet))))))
+
+(defn reconnect
+ [reason]
+ (.log js/console (str "Reconnecting ('" reason "') ..."))
+ (let [time (+ @connect-time 250)
+ time (if (> time 16000) 16000 time)]
+ (reset! connect-time time)
+ (connect)))
+
+(defn connect
+ []
+ (let [twitter (node-twitter. keys-and-tokens)
+ track-params (clj->js {:track "mbta"})]
+ (js/setTimeout
+ (fn []
+ (.stream twitter "statuses/filter" track-params
+ (fn [stream]
+ (.on stream "data" #(if (.-id %) (tag-save-send-tweet %)))
+ (.on stream "error" #(.log js/console (str "Callback error: " %)))
+ (.on stream "end" reconnect)
+ (.on stream "connection-ok" #(reset! connect-time 0))
+ (.on stream "connection-error" #(println (str "Stream connection error: " (.-statusCode %)))))))
+ @connect-time)))
View
152 src/cljs/server/wheresthembta/views.cljs
@@ -1,117 +1,87 @@
-;; Copyright (c) 2012 Tunde Ashafa
-;; All rights reserved.
-
-;; Redistribution and use in source and binary forms, with or without
-;; modification, are permitted provided that the following conditions
-;; are met:
-;; 1. Redistributions of source code must retain the above copyright
-;; notice, this list of conditions and the following disclaimer.
-;; 2. Redistributions in binary form must reproduce the above copyright
-;; notice, this list of conditions and the following disclaimer in the
-;; documentation and/or other materials provided with the distribution.
-;; 3. The name of the author may not be used to endorse or promote products
-;; derived from this software without specific prior written permission.
-
-;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
(ns wheresthembta.views
(:require [cljs.nodejs :as node]
[clojure.string :as string]
[wheresthembta.config :as config]
[wheresthembta.real-time-feed :as real-time-feed]
+ [wheresthembta.redis-client :as redis-client]
[wheresthembta.shared.mbta-data :as mbta-data]
[wheresthembta.shared.utils :as utils]
[wheresthembta.shared.templates :as templates])
- (:require-macros [wheresthembta.macros :as macros]))
+ (:use-macros [renderer.macros :only [render]]))
(set! (. (node/require "mu") -templateRoot) config/TEMPLATE-ROOT)
+(defn render-json
+ [json]
+ (render {:Content-Type "application/json"} json))
+
(def resource-not-found
- (macros/render 404 "Resource not found."))
+ (render 404 (>> "base.html" {:title "404"
+ :main-content "Resource not found."})))
(def home
- (macros/render
- (>> "base.html" {:title "Where's the MBTA?"
- :home true
- :main-content (templates/unordered-list-of-transit-systems)})))
+ (render (>> "base.html" {:title "Where's the MBTA?"
+ :home true
+ :main-content (templates/unordered-list-of-transit-systems)})))
(def about
- (macros/render
- (>> "base.html" {:title "About"
- :about true
- :bread-crumbs (templates/bread-crumbs)})))
-
-(def terms
- (macros/render
- (>> "base.html" {:title "Terms of Use"
- :terms true
- :bread-crumbs (templates/bread-crumbs)})))
-
-(def privacy
- (macros/render
- (>> "base.html" {:title "Privacy Policy"
- :privacy true
- :bread-crumbs (templates/bread-crumbs)})))
+ (render (>> "base.html" {:title "About"
+ :about true
+ :bread-crumbs (templates/bread-crumbs)})))
(def lines
- (macros/render
- (>> "base.html"
- (let [transit-id (.. req -params -transit)]
- (if-let [lines (mbta-data/get-value transit-id :lines)]
- {:title (:title (mbta-data/get-value transit-id))
- :bread-crumbs (templates/bread-crumbs)
- :main-content (templates/unordered-list-of-lines transit-id lines)}
- resource-not-found)))))
+ (render (>> "base.html"
+ (let [transit-id (.. req -params -transit)]
+ (if-let [lines (mbta-data/get-value transit-id :lines)]
+ {:title (:title (mbta-data/get-value transit-id))
+ :bread-crumbs (templates/bread-crumbs)
+ :main-content (templates/unordered-list-of-lines transit-id lines)}
+ resource-not-found)))))
(def stations
- (macros/render
- (>> "base.html"
- (let [transit-id (.. req -params -transit)
- line-id (.. req -params -line)]
- (if-let [stations (mbta-data/get-value transit-id :lines line-id :stations)]
- (let [title (:title (mbta-data/get-value transit-id :lines line-id))]
- {:title title
- :bread-crumbs (templates/bread-crumbs transit-id)
- :main-content (templates/unordered-list-of-stations title transit-id line-id stations)})
- resource-not-found)))))
-
+ (render (>> "base.html"
+ (let [transit-id (.. req -params -transit)
+ line-id (.. req -params -line)]
+ (if-let [stations (mbta-data/get-value transit-id :lines line-id :stations)]
+ (let [title (:title (mbta-data/get-value transit-id :lines line-id))]
+ {:title title
+ :bread-crumbs (templates/bread-crumbs transit-id)
+ :main-content (templates/unordered-list-of-stations title transit-id line-id stations)})
+ resource-not-found)))))
+
(def station-info
(real-time-feed/get-real-time-feed-data
- (macros/render
+ (render
[req res feed-data]
- (>> "base.html"
- (let [transit-id (.. req -params -transit)
- line-id (.. req -params -line)
- station-id (.. req -params -station)]
- (if-let [platform-keys (mbta-data/get-value transit-id :lines line-id :stations station-id :platform-keys)]
- (let [prediction-data (filter #(platform-keys (:PlatformKey %)) feed-data)
- directions (mbta-data/get-value transit-id :lines line-id :directions)
- station-predictions (map (fn [direction]
- {:direction-key (:key direction)
- :direction-title (:title direction)
- :predictions (for [prediction prediction-data
- :when (and (= (:route direction) (prediction :Route))
- (= (:key direction) (last (prediction :PlatformKey))))]
- {:time (js/Date. (prediction :Time))
- :revenue (prediction :Revenue)})}) directions)
- predictions-json (.stringify js/JSON (utils/clj->js station-predictions))]
- (if (= (.-method req) "POST")
- (macros/render {:Content-Type "application/json"} predictions-json)
- {:title (first (string/split (:title (mbta-data/get-value transit-id :lines line-id :stations station-id)) #"\s-\s"))
- :bread-crumbs (templates/bread-crumbs transit-id :lines line-id)
- :main-content (templates/div-of-station-predictions station-predictions)
- :predictions true
- :predictions-json predictions-json}))
- resource-not-found))))))
+ (let [transit-id (.. req -params -transit)
+ line-id (.. req -params -line)
+ station-id (.. req -params -station)]
+ (if-let [platform-keys (mbta-data/get-value transit-id :lines line-id :stations station-id :platform-keys)]
+ (let [prediction-data (filter #(platform-keys (:PlatformKey %)) feed-data)
+ directions (mbta-data/get-value transit-id :lines line-id :directions)
+ station-predictions (map (fn [direction]
+ {:direction-key (:key direction)
+ :direction-title (:title direction)
+ :predictions (for [prediction prediction-data
+ :when (and (= (:route direction) (prediction :Route))
+ (= (:key direction) (last (prediction :PlatformKey))))]
+ {:prediction (/ (. (utils/convert-to-utc-date (js/Date. (prediction :Time))) (getTime)) 1000)
+ :revenue (prediction :Revenue)})}) directions)
+ predictions-json (.stringify js/JSON (utils/clj->js station-predictions))]
+ (if (= (.-method req) "POST")
+ (render-json predictions-json)
+ (redis-client/get-related-tweets
+ (render
+ [req res tweets]
+ (>> "base.html"
+ {:title (first (string/split (:title (mbta-data/get-value transit-id :lines line-id :stations station-id)) #"\s-\s"))
+ :bread-crumbs (templates/bread-crumbs transit-id :lines line-id)
+ :main-content (templates/div-of-station-predictions station-predictions)
+ :relevant-tweets (templates/div-of-relevant-tweets tweets)
+ :time (.getTime (js/Date.))
+ :predictions true
+ :predictions-json predictions-json})))))
+ resource-not-found)))))
View
248 src/cljs/shared/mbta_data.cljs
@@ -1,28 +1,3 @@
-;; Copyright (c) 2012 Tunde Ashafa
-;; All rights reserved.
-
-;; Redistribution and use in source and binary forms, with or without
-;; modification, are permitted provided that the following conditions
-;; are met:
-;; 1. Redistributions of source code must retain the above copyright
-;; notice, this list of conditions and the following disclaimer.
-;; 2. Redistributions in binary form must reproduce the above copyright
-;; notice, this list of conditions and the following disclaimer in the
-;; documentation and/or other materials provided with the distribution.
-;; 3. The name of the author may not be used to endorse or promote products
-;; derived from this software without specific prior written permission.
-
-;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
(ns wheresthembta.shared.mbta-data
(:require [clojure.string :as string]))
@@ -40,92 +15,135 @@
:stations [{:id "alewife"
:title "Alewife"
:platform-keys #{"RALEN" "RALES"}
- :location [-71.14137768745422,42.39475965499878]}
+ :location [-71.14137768745422 42.39475965499878]
+ :search-for [#"(?i)alewife"]
+ :search-not []}
{:id "davis"
:title "Davis"
:platform-keys #{"RDAVN" "RDAVS"}
- :location [-71.12231,42.39626]}
+ :location [-71.12231 42.39626]
+ :search-for [#"(?i)\bdavis\b"]
+ :search-not [#"(?i)gm davis" #"(?i)jon davis" #"(?i)jonathan davis" #"(?i)john davis"]}
{:id "porter"
:title "Porter"
:platform-keys #{"RPORN" "RPORS"}
- :location [-71.1192655,42.388451]}
+ :location [-71.1192655 42.388451]
+ :search-for [#"(?i)porter"]
+ :search-not []}
{:id "harvard"
:title "Harvard"
:platform-keys #{"RHARN" "RHARS"}
- :location [-71.11897587776184,42.37360239028931]}
+ :location [-71.11897587776184 42.37360239028931]
+ :search-for [#"(?i)harvard"]
+ :search-not [#"(?i)\bharvard st\b"]}
{:id "central-square"
:title "Central Square"
:platform-keys #{"RCENN" "RCENS"}
- :location [-71.10348813170638,42.36517333984375]}
+ :location [-71.10348813170638 42.36517333984375]
+ :search-for [#"(?i)central sq" #"(?i)at central" #"(?i)from central" #"(?i)to central"]
+ :search-not []}
{:id "kendall"
:title "Kendall"
:platform-keys #{"RKENN" "RKENS"}
- :location [-71.08628511428833,42.36255168914795]}
+ :location [-71.08628511428833 42.36255168914795]
+ :search-for [#"(?i)kendall" #"(?i)\bm\W?i\W?t\b"]
+ :search-not []}
{:id "charles-mgh"
:title "Charles / MGH"
:platform-keys #{"RMGHN" "RMGHS"}
- :location [-71.07221961021423,42.36124277114868]}
+ :location [-71.07221961021423 42.36124277114868]
+ :search-for [#"(?i)charles\W*mgh" #"(?i)\bmgh\b" #"(?i)\bat charles\b" #"(?i)\bto charles\b"]
+ :search-not []}
{:id "park-street"
:title "Park Street"
:platform-keys #{"RPRKN" "RPRKS"}
- :location [-71.062403,42.356372]}
+ :location [-71.062403 42.356372]
+ :search-for [#"(?i)park\W*st" #"(?i)\bat park\b" #"(?i)\bto park\b"]
+ :search-not [#"(?i)forge\W*park"]}
{:id "downtown-crossing"
:title "Downtown Crossing - Red Line"
:platform-keys #{"RDTCN" "RDTCS"}
- :location [-71.05899463560769,42.35430908203125]}
+ :location [-71.05899463560769 42.35430908203125]
+ :search-for [#"(?i)dtx" #"(?i)\bdown\W*town\b"]
+ :search-not []}
{:id "south-station"
:title "South Station"
:platform-keys #{"RSOUN" "RSOUS"}
- :location [-71.0559,42.35188]}
+ :location [-71.0559 42.35188]
+ :search-for [#"(?i)south\W*sta" #"(?i)\bs\W+sta[\w]*\b"]
+ :search-not []}
{:id "broadway"
:title "Broadway"
:platform-keys #{"RBRON" "RBROS"}
- :location [-71.05712,42.34287]}
+ :location [-71.05712 42.34287]
+ :search-for [#"(?i)broadway\W*st"]
+ :search-not []}
{:id "andrew"
:title "Andrew"
:platform-keys #{"RANDN" "RANDS"}
- :location [-71.05729,42.33002]}
+ :location [-71.05729 42.33002]
+ :search-for [#"(?i)at andrew" #"(?i)to andrew" #"(?i)from andrew" #"(?i)\bin andrew\b"]
+ :search-not []}
{:id "jfk-umass"
- :title "JFK/Umass"
+ :title "JFK / Umass"
:platform-keys #{"RJFKN" "RJFKS"}
- :location [-71.05238,42.32060]}
+ :location [-71.05238 42.32060]
+ :search-for [#"(?i)\bumass\b" #"(?i)\bjfk\b"]
+ :search-not []}
{:id "north-quincy"
:title "North Quincy"
:platform-keys #{"RNQUN" "RNQUS"}
- :location [-71.02920,42.27480]}
+ :location [-71.02920 42.27480]
+ :search-for [#"(?i)north\W*quincy"]
+ :search-not []}
{:id "wollaston"
:title "Wollaston"
:platform-keys #{"RWOLN" "RWOLS"}
- :location [-71.01955,42.26547]}
+ :location [-71.01955 42.26547]
+ :search-for [#"(?i)wollaston"]
+ :search-not []}
{:id "quincy-center"
:title "Quincy Center"
:platform-keys #{"RQUCN" "RQUCS"}
- :location [-71.00512,42.25150]}
+ :location [-71.00512 42.25150]
+ :search-for [#"(?i)quincy\W*c"]
+ :search-not []}
{:id "quincy-adams"
:title "Quincy Adams"
:platform-keys #{"RQUAN" "RQUAS"}
- :location [-71.00706,42.23317]}
+ :location [-71.00706 42.23317]
+ :search-for [#"(?i)quincy\W*a"]
+ :search-not []}
{:id "braintree"
:title "Braintree"
:platform-keys #{"RBRAN" "RBRAS"}
- :location [-71.00114107131958,42.20772385597229]}
+ :location [-71.00114107131958 42.20772385597229]
+ :search-for [#"(?i)braintree"]
+ :search-not []}
{:id "savin-hill"
:title "Savin Hill"
:platform-keys #{"RSAVN" "RSAVS"}
- :location [-71.05315,42.31133]}
+ :location [-71.05315 42.31133]
+ :search-for [#"(?i)\bsavin\b"]
+ :search-not []}
{:id "fields-corner"
:title "Fields Corner"
:platform-keys #{"RFIEN" "RFIES"}
- :location [-71.0619843006134,42.30003476142883]}
+ :location [-71.0619843006134 42.30003476142883]
+ :search-for [#"(?i)fields\W*corner"]
+ :search-not []}
{:id "shawmut"
:title "Shawmut"
:platform-keys #{"RSHAN" "RSHAS"}
- :location [-71.06575012207031,42.293264865875244]}
+ :location [-71.06575012207031 42.293264865875244]
+ :search-for [#"(?i)shawmut"]
+ :search-not []}
{:id "ashmont"
:title "Ashmont"
:platform-keys #{"RASHN" "RASHS"}
- :location [-71.06423,42.285515]}
- ]}
+ :location [-71.06423 42.285515]
+ :search-for [#"(?i)ashmont"]
+ :search-not []}]}
{:id "orange-line"
:title "Orange Line"
@@ -135,79 +153,117 @@
:stations [{:id "oak-grove"
:title "Oak Grove"
:platform-keys #{"OOAKN" "OOAKS"}
- :location [-71.07198 42.43533]}
+ :location [-71.07198 42.43533]
+ :search-for [#"(?i)(\W|^)oak\W*gr"]
+ :search-not []}
{:id "malden-center"
:title "Malden Center"
:platform-keys #{"OMALN" "OMALS"}
- :location [-71.07433 42.42723]}
+ :location [-71.07433 42.42723]
+ :search-for [#"(?i)\bmalden(\W*cen.*)?\b"]
+ :search-not []}
{:id "wellington"
:title "Wellington"
:platform-keys #{"OWELN" "OWELS"}
- :location [-71.0769502331444 42.4044189453125]}
+ :location [-71.0769502331444 42.4044189453125]
+ :search-for [#"(?i)wellignton"]
+ :search-not []}
{:id "sullivan-square"
:title "Sullivan Square"
:platform-keys #{"OSULN" "OSULS"}
- :location [-71.074715 42.384012]}
+ :location [-71.074715 42.384012]
+ :search-for [#"(?i)sullivan"]
+ :search-not []}
{:id "community-college"
:title "Community College"
:platform-keys #{"OCOMN" "OCOMS"}
- :location [-71.06793 42.37408]}
+ :location [-71.06793 42.37408]
+ :search-for [#"(?i)community\W*col"]
+ :search-not []}
{:id "north-station"
:title "North Station"
:platform-keys #{"ONSTN" "ONSTS"}
- :location [-71.061055 42.365472]}
+ :location [-71.061055 42.365472]
+ :search-for [#"(?i)north\W*st" #"(?i)\bn\W*station\b" #"(?i)bruins" #"(?i)celtics"]
+ :search-not []}
{:id "haymarket"
:title "Haymarket"
:platform-keys #{"OHAYN" "OHAYS"}
- :location [-71.05827 42.36243]}
+ :location [-71.05827 42.36243]
+ :search-for [#"(?i)haymarket"]
+ :search-not []}
{:id "state"
:title "State - Orange Line"
:platform-keys #{"OSTSN" "OSTSS"}
- :location [-71.057717 42.358675]}
+ :location [-71.057717 42.358675]
+ :search-for [#"(?i)state\W?st\W" #"(?i)\bat state\b" #"(?i)\bto state\b" #"(?i)\bin state\b" #"(?i)\bfrom state\b"]
+ :search-not []}
{:id "downtown-crossing"
:title "Downtown Crossing - Orange Line"
:platform-keys #{"ODTSN" "ODTSS"}
- :location [-71.05899463560769 42.35430908203125]}
+ :location [-71.05899463560769 42.35430908203125]
+ :search-for [#"(?i)\bdtx\b" #"(?i)\bdown\W*town\W*[cx]\w*\b"]
+ :search-not []}
{:id "chinatown"
:title "Chinatown"
:platform-keys #{"OCHSN" "OCHSS"}
- :location [-71.062503 42.35207]}
+ :location [-71.062503 42.35207]
+ :search-for [#"(?i)china\W*town"]
+ :search-not []}
{:id "tufts-medical-center"
:title "Tufts Medical Center"
:platform-keys #{"ONEMN" "ONEMS"}
- :location [-71.063262 42.350127]}
+ :location [-71.063262 42.350127]
+ :search-for [#"(?i)tufts\W*m[ed]"]
+ :search-not []}
{:id "back-bay"
:title "Back Bay"
:platform-keys #{"OBACN" "OBACS"}
- :location [-71.076007 42.347238]}
+ :location [-71.076007 42.347238]
+ :search-for [#"(?i)(\W|^)back\W*bay"]
+ :search-not []}
{:id "massachusetts-avenue"
:title "Massachusetts Avenue"
:platform-keys #{"OMASN" "OMASS"}
- :location [-71.083556 42.342047]}
+ :location [-71.083556 42.342047]
+ :search-for [#"(?i)mass\W*ave" #"(?i)massachusetts\W*ave"]
+ :search-not []}
{:id "ruggles"
:title "Ruggles"
:platform-keys #{"ORUGN" "ORUGS"}
- :location [-71.08948 42.33651]}
+ :location [-71.08948 42.33651]
+ :search-for [#"(?i)\bruggles\b"]
+ :search-not []}
{:id "roxbury-crossing"
:title "Roxbury Crossing"
:platform-keys #{"OROXN" "OROXS"}
- :location [-71.09527 42.33139]}
+ :location [-71.09527 42.33139]
+ :search-for [#"(?i)roxbury\W*[cx]" #"(?i)\brox\W*[cx]\w*\b"]
+ :search-not []}
{:id "jackson-square"
:title "Jackson Square"
:platform-keys #{"OJACN" "OJACS"}
- :location [-71.09890 42.32497]}
+ :location [-71.09890 42.32497]
+ :search-for [#"(?i)jackson\W*sq"]
+ :search-not []}
{:id "stony-brook"
:title "Stony Brook"
:platform-keys #{"OSTON" "OSTOS"}
- :location [-71.10439 42.31697]}
+ :location [-71.10439 42.31697]
+ :search-for [#"(?i)stony\W*br"]
+ :search-not []}
{:id "green-street"
:title "Green Street"
:platform-keys #{"OGREN" "OGRES"}
- :location [-71.10772 42.30999]}
+ :location [-71.10772 42.30999]
+ :search-for [#"(?i)green\W*st"]
+ :search-not []}
{:id "forest-hills"
:title "Forest Hills"
:platform-keys #{"OFORN" "OFORS"}
- :location [-71.11381 42.30067]}]}
+ :location [-71.11381 42.30067]
+ :search-for [#"(?i)forr?est\W*h" #"(?i)\bat forr?est\b" #"(?i)\bto forr?esst\b" #"(?i)\bfrom forr?est\b" #"(?i)\bin forr?est\b"]
+ :search-not []}]}
{:id "blue-line"
:title "Blue Line"
@@ -217,60 +273,84 @@
:stations [{:id "wonderland"
:title "Wonderland"
:platform-keys #{"BWONE" "BWONW"}
- :location [-70.99219635184463 42.41414416455361]}
+ :location [-70.99219635184463 42.41414416455361]
+ :search-for [#"(?i)wonder\W*land"]
+ :search-not []}
{:id "revere-beach"
:title "Revere Beach"
:platform-keys #{"BREVE" "BREVW"}
- :location [-70.992011 42.407616]}
+ :location [-70.992011 42.407616]
+ :search-for [#"(?i)revere\W*beach"]
+ :search-not []}
{:id "beachmont"
:title "Beachmont"
:platform-keys #{"BBEAE" "BBEAW"}
- :location [-70.992084 42.397836]}
+ :location [-70.992084 42.397836]
+ :search-for [#"(?i)beachmont"]
+ :search-not []}
{:id "suffolk-downs"
:title "Suffolk Downs"
:platform-keys #{"BSUFE" "BSUFW"}
- :location [-70.997195 42.390199]}
+ :location [-70.997195 42.390199]
+ :search-for [#"(?i)suffolk"]
+ :search-not []}
{:id "orient-heights"
:title "Orient Heights"
:platform-keys #{"BORHE" "BORHW"}
- :location [-71.006904 42.386734]}
+ :location [-71.006904 42.386734]
+ :search-for [#"(?i)orient\W*h"]
+ :search-not []}
{:id "wood-island"
:title "Wood Island"
:platform-keys #{"BWOOE" "BWOOW"}
- :location [-71.023104 42.381034]}
+ :location [-71.023104 42.381034]
+ :search-for [#"(?i)wood\W*isl"]
+ :search-not []}
{:id "airport"
:title "Airport"
:platform-keys #{"BAIRE" "BAIRW"}
- :location [-71.027985 42.369167]}
+ :location [-71.027985 42.369167]
+ :search-for [#"(?i)\bairport\b" #"(?i)\blogan\W*air\b"]
+ :search-not [#"(?i)\btf green\b"]}
{:id "maverick"
:title "Maverick"
:platform-keys #{"BMAVE" "BMAVW"}
- :location [-71.039212 42.368719]}
+ :location [-71.039212 42.368719]
+ :search-for [#"(?i)maverick"]
+ :search-not []}
{:id "aquarium"
:title "Aquarium"
:platform-keys #{"BAQUE" "BAQUW"}
- :location [-71.05264689683094 42.3594970703125]}
+ :location [-71.05264689683094 42.3594970703125]
+ :search-for [#"(?i)aquarium"]
+ :search-not []}
{:id "state"
:title "State - Blue Line"
:platform-keys #{"BSTAE" "BSTAW"}
- :location [-71.057708 42.358617]}
+ :location [-71.057708 42.358617]
+ :search-for [#"(?i)(\W|^)state\W*st\W" #"(?i)\bat state\b" #"(?i)\bto state\b" #"(?i)\bin state\b" #"(?i)\bfrom state\b"]
+ :search-not []}
{:id "government-center"
:title "Government Center"
:platform-keys #{"BGOVE" "BGOVW"}
- :location [-71.05939865112305 42.359161376953125]}
+ :location [-71.05939865112305 42.359161376953125]
+ :search-for [#"(?i)government\W*c[ent]" #"(?i)(\W|^)gov\W*c[ent]"]
+ :search-not []}
{:id "bowdoin"
:title "Bowdoin"
:platform-keys #{"BBOWE" "BBOWW"}
- :location [-71.06285 42.36128]}]}]}])
+ :location [-71.06285 42.36128]
+ :search-for [#"(?i)bowdoin"]
+ :search-not []}]}]}])
(defn get-value
[& path]
(reduce (fn [d id]
- (if (not (nil? d))
- (cond (keyword? id) (d id)
- (vector? d) (first (filter #(= (:id %) id) d))
- :else nil)))
+ (cond (nil? d) nil
+ (keyword? id) (d id)
+ (vector? d) (first (filter #(= (:id %) id) d))
+ :else nil))
transit-system path))
(def all-stations
View
117 src/cljs/shared/templates.cljs
@@ -1,42 +1,17 @@
-;; Copyright (c) 2012 Tunde Ashafa
-;; All rights reserved.
-
-;; Redistribution and use in source and binary forms, with or without
-;; modification, are permitted provided that the following conditions
-;; are met:
-;; 1. Redistributions of source code must retain the above copyright
-;; notice, this list of conditions and the following disclaimer.
-;; 2. Redistributions in binary form must reproduce the above copyright
-;; notice, this list of conditions and the following disclaimer in the
-;; documentation and/or other materials provided with the distribution.
-;; 3. The name of the author may not be used to endorse or promote products
-;; derived from this software without specific prior written permission.
-
-;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
(ns wheresthembta.shared.templates
(:require [clojure.string :as string]
[wheresthembta.shared.utils :as utils]
[wheresthembta.shared.mbta-data :as mbta-data]
[hiccups.runtime :as hiccups_runtime])
- (:require-macros [hiccups.core :as hiccups]))
+ (:use-macros [hiccups.core :only [defhtml]]))
-(hiccups/defhtml bread-crumbs
+(defhtml bread-crumbs
[& path]
[:div {:id "bread-crumbs"}
[:ul {}
- (butlast (interleave (for [anchor (loop [anchors [{:href "/" :text "Home"}] length 1]
+ (butlast (interleave (for [anchor (loop [anchors [{:href "/" :text "Where's the MBTA?"}] length 1]
(if (> length (count path)) anchors
(let [transit-data (apply mbta-data/get-value (take length path))]
(recur (conj anchors {:href (str (:href (last anchors)) (:id transit-data) "/")
@@ -45,61 +20,64 @@
[:li [:a {:href (:href anchor)} (:text anchor)]])
(repeat [:li.seperator "&rsaquo;"])))]])
-(hiccups/defhtml unordered-list-of-transit-systems
+(defhtml unordered-list-of-transit-systems
[]
- [:ul (for [transit mbta-data/transit-system]
- [:li [:a {:href (str "/" (transit :id))} (transit :title)]])])
+ [:h4 "Transit Systems:"]
+ [:ul.station-list
+ (for [transit mbta-data/transit-system]
+ [:li [:a {:href (str "/" (transit :id))} (transit :title)]])])
-(hiccups/defhtml unordered-list-of-lines
+(defhtml unordered-list-of-lines
[transit-id lines]
- [:p "What about the Green Line? Unfortunately, the Green Line does not have the same type of train tracking technology as other lines."]
- [:ul (for [line lines]
- (let [href (str "/" (string/join "/" [transit-id (line :id)]))]
- [:li [:a {:href href} (line :title)]]))])
+ [:h4 "Transit Lines:"]
+ [:ul.station-list
+ (for [line lines]
+ (let [href (str "/" (string/join "/" [transit-id (line :id)]))]
+ [:li [:a {:href href} (line :title)]]))])
-(hiccups/defhtml unordered-list-of-stations
+(defhtml unordered-list-of-stations
[title transit-id line-id stations]
- [:p "List of " [:em (string/lower-case title)] " stations:"]
+ [:h4 "Transit Stations:"]
[:ul.station-list
(for [station stations]
(let [href (str "/" (string/join "/" [transit-id line-id (station :id)]))]
[:li [:a {:href href} (first (string/split (station :title) #"\s-\s"))]]))])
-(hiccups/defhtml div-of-station-predictions
+(set! js/SERVER_TIME_DIFF 0)
+
+(defhtml div-of-station-predictions
[station-predictions]
[:div {:id "predictions"}
- (let [now (/ (. (utils/convert-to-utc-date (js/Date.)) (getTime)) 1000)]
- [:ul {}
+ (let [now (/ (- (. (utils/convert-to-utc-date (js/Date.)) (getTime)) js/SERVER_TIME_DIFF) 1000)]
+ [:ul
(for [station-prediction station-predictions]
- [:li [:h3 (station-prediction :direction-title)]
+ [:li.prediction [:h3 (station-prediction :direction-title)]
+ [:span.info "No predictions"]
[:ul.directions
(let [predictions (station-prediction :predictions)
predictions-count (count predictions)]
(if (> predictions-count 0)
- (for [prediction (take 3 (sort-by :time predictions))]
- (let [when (/ (. (utils/convert-to-utc-date (js/Date. (prediction :time))) (getTime)) 1000)
- revenue (if (= (prediction :revenue) "Revenue") "revenue " "non-revenue ")
+ (for [prediction (take 3 (sort-by :prediction predictions))]
+ (let [when (prediction :prediction)
+ revenue (if (= (prediction :revenue) "Revenue") "revenue" "non-revenue")
seconds (.floor js/Math (.abs js/Math (- when now)))
is-reverse? (>= now when)
time-str (utils/format-seconds seconds)
time-html (if is-reverse?
- (cond (= seconds 0) [:li {:class (str revenue "refresh")} time-str]
- (< seconds 30) [:li {:class revenue} "Approaching"]
- (= seconds 30) [:li {:class (str revenue "refresh")} "Approaching"]
- (< seconds 70) [:li {:class revenue} "Arriving"]
- (= seconds 70) [:li {:class (str revenue "refresh")} "Arriving"]
- (< seconds 130) [:li {:class revenue} "Leaving"]
- (= seconds 130) [:li {:class (str revenue "refresh")} "Leaving"]
- :else nil)
+ (cond (= seconds 0) [:li {:class (str revenue " refresh")} time-str]
+ (< seconds 40) [:li {:class revenue} "Approaching"]
+ (= seconds 40) [:li {:class (str revenue " refresh")} "Approaching"]
+ (< seconds 80) [:li {:class revenue} "Arriving"]
+ (= seconds 80) [:li {:class (str revenue " refresh")} "Arriving"]
+ (< seconds 100) [:li {:class revenue} "Departing"]
+ (= seconds 100) [:li {:class (str revenue " refresh")} "Departing"])
[:li time-str])]
- (if (and (= predictions-count 1) (not time-html))
- [:li.info "No predictions."] time-html)))
- [:li.info "No predictions."]))]])])])
+ time-html))))]])])])
-(hiccups/defhtml unordered-list-of-nearest-stations
+(defhtml unordered-list-of-nearest-stations
[stations url]
[:h4 "Nearby Stations:"]
- [:ul {}
+ [:ul.station-list
(for [station stations]
(let [href (station :url)
[title line-title] (string/split (station :title) #"\s\-\s")]
@@ -107,11 +85,22 @@
[:li [:a {:href href} title]
(if line-title [:span (str " (" line-title ")")])])))])
-(hiccups/defhtml status-bar-tool-tip
+(defhtml status-bar-tool-tip
[position]
[:div.tool-tip {:style position}
- [:div "&diams;"]
- [:p [:strong "Pro Tip: "]
- "A red status bar indicates the times shown might need to be refreshed for a more accurate prediction."]
- [:p "To refresh the times, simply click on any of the predictions."]
- [:p [:input#supress-status-bar-tip {:type "checkbox" :disabled "disabled"}] "Don't show this again."]])
+ [:span {:class "icon-refresh"}]])
+
+(defhtml div-of-relevant-tweets
+ [tweets]
+ (if (> (count tweets) 0)
+ [:div
+ [:h4 "Relevant Tweets:"]
+ [:ul.tweets
+ (for [tweet tweets]
+ (let [screen-name (-> tweet :user :screen_name)
+ created-at (tweet :created_at)]
+ [:li
+ [:strong screen-name ":"]
+ [:p (utils/linkify-tweet-text tweet)]
+ [:a {:href (str "//twitter.com/" screen-name "/status/" (tweet :id_str))}
+ [:time {:data-time created-at} (utils/pretty-date created-at)]]]))]]))
View
68 src/cljs/shared/utils.cljs
@@ -1,28 +1,3 @@
-;; Copyright (c) 2012 Tunde Ashafa
-;; All rights reserved.
-
-;; Redistribution and use in source and binary forms, with or without
-;; modification, are permitted provided that the following conditions
-;; are met:
-;; 1. Redistributions of source code must retain the above copyright
-;; notice, this list of conditions and the following disclaimer.
-;; 2. Redistributions in binary form must reproduce the above copyright
-;; notice, this list of conditions and the following disclaimer in the
-;; documentation and/or other materials provided with the distribution.
-;; 3. The name of the author may not be used to endorse or promote products
-;; derived from this software without specific prior written permission.
-
-;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-;; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-;; IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-;; NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-;; THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
(ns wheresthembta.shared.utils)
@@ -55,7 +30,7 @@
(defn convert-to-utc-date
- "Converts a date to UTC date."
+ "Converts a JavaScript date to UTC."
[date]
(js/Date. (. date (getUTCFullYear))
(. date (getUTCMonth))
@@ -73,8 +48,45 @@
(defn format-seconds
- "Formats a number in seconds to a human readable form. ex. '09:34'."
+ "Formats a number representing seconds to a more human readable string.
+ Ex. 574 => '09:34'."
[seconds]
(str (pad-number-with-zero (.floor js/Math (/ (.abs js/Math seconds) 60)))
":"
- (pad-number-with-zero (mod (.abs js/Math seconds) 60))))
+ (pad-number-with-zero (mod (.abs js/Math seconds) 60))))
+
+(defn pretty-date
+ "Takes a date string and returns a more human readable string that
+ represents how long ago the date represents if the date string is
+ less than seven days ago."
+ [date-str]
+ (let [date (convert-to-utc-date (js/Date. (or date-str "")))
+ diff (/ (- (.getTime (convert-to-utc-date (js/Date.))) (.getTime date)) 1000)
+ day-diff (.floor js/Math (/ diff 86400))]
+ (if-not (or (js/isNaN day-diff) (< day-diff 0))
+ (or (and (= day-diff 0)
+ (or (and (< diff 60) "just now")
+ (and (< diff 120) "a minute ago")
+ (and (< diff 3600) (str (.floor js/Math (/ diff 60)) " minutes ago"))
+ (and (< diff 7200) "an hour ago")
+ (and (< diff 86400) (str (.floor js/Math (/ diff 3600)) " hours ago"))))
+ (and (= day-diff 1) "yesterday")
+ (and (< day-diff 7) (str day-diff " days ago"))
+ (and (>= day-diff 7) date-str)))))
+
+(defn linkify-tweet-text
+ "Takes a tweet and returns the tweet text with hashtags, urls (not t.co), and usernames
+ converted to the appropiate html anchor links in accordance with tweets."
+ [tweet]
+ (let [text (tweet :text)
+ urls (-> tweet :entities :urls)
+ url-regex (js/RegExp. "(((https?://)|www\\.).+?)(([!?,.\\)]+)?[\\]\\)]?)([^a-z0-9_\\-\\./]|$)" "ig")
+ mention-regex (js/RegExp. "(^|[^a-z0-9_/\\\\])@([a-z0-9_]+)" "ig")
+ hashtag-regex (js/RegExp. "(^|[^a-z0-9_/\\\\])#([a-z]+[a-z0-9_]*|[0-9]+[a-z_]+)" "ig")]
+ (-> text
+ (.replace url-regex #(loop [url urls]
+ (if (or (= (:url (first url)) %2) (= 0 (count url)))
+ (str "<a href='" %2 "' class='external'>" (or (and (= 0 (count url)) %2) (:display_url (first url))) "</a>" %7)
+ (recur (next url)))))
+ (.replace mention-regex #(str %2 "@<a href='//twitter.com/" %3 "' class='external'>" %3 "</a>"))
+ (.replace hashtag-regex #(str %2 "<a href='//search.twitter.com/search?q=" %3 "' class='external'>#" %3 "</a>")))))
View
239 static/css/font-awesome.css
@@ -0,0 +1,239 @@
+/* Font Awesome
+ the iconic font designed for use with Twitter Bootstrap
+ -------------------------------------------------------
+ The full suite of pictographic icons, examples, and documentation
+ can be found at: http://fortawesome.github.com/Font-Awesome/
+
+ License
+ -------------------------------------------------------
+ The Font Awesome webfont, CSS, and LESS files are licensed under CC BY 3.0:
+ http://creativecommons.org/licenses/by/3.0/ A mention of
+ 'Font Awesome - http://fortawesome.github.com/Font-Awesome' in human-readable
+ source code is considered acceptable attribution (most common on the web).
+ If human readable source code is not available to the end user, a mention in
+ an 'About' or 'Credits' screen is considered acceptable (most common in desktop
+ or mobile software).
+
+ Contact
+ -------------------------------------------------------
+ Email: dave@davegandy.com
+ Twitter: http://twitter.com/fortaweso_me
+ Work: http://lemonwi.se co-founder
+
+ */
+
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('../font/fontawesome-webfont.eot');
+ src: url('../font/fontawesome-webfont.eot?#iefix') format('embedded-opentype'), url('../font/fontawesome-webfont.woff') format('woff'), url('../font/fontawesome-webfont.ttf') format('truetype'), url('../font/fontawesome-webfont.svgz#FontAwesomeRegular') format('svg'), url('../font/fontawesome-webfont.svg#FontAwesomeRegular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+/* sprites.less reset */
+[class^="icon-"], [class*=" icon-"] {
+ display: inline;
+ width: auto;
+ height: auto;
+ line-height: inherit;
+ vertical-align: baseline;
+ background-image: none;
+ background-position: 0% 0%;
+ background-repeat: repeat;
+}
+li[class^="icon-"], li[class*=" icon-"] {
+ display: block;
+}
+/* Font Awesome styles
+ ------------------------------------------------------- */
+[class^="icon-"]:before, [class*=" icon-"]:before {
+ font-family: FontAwesome;
+ font-weight: normal;
+ font-style: normal;
+ display: inline-block;
+ text-decoration: inherit;
+}
+a [class^="icon-"], a [class*=" icon-"] {
+ display: inline-block;
+ text-decoration: inherit;
+}
+/* makes the font 33% larger relative to the icon container */
+.icon-large:before {
+ vertical-align: top;
+ font-size: 1.3333333333333333em;
+}
+.btn [class^="icon-"], .btn [class*=" icon-"] {
+ /* keeps button heights with and without icons the same */
+ line-height: .9em;
+}
+li [class^="icon-"], li [class*=" icon-"] {
+ display: inline-block;
+ width: 1.25em;
+ text-align: center;
+}
+li .icon-large[class^="icon-"], li .icon-large[class*=" icon-"] {
+ /* 1.5 increased font size for icon-large * 1.25 width */
+ width: 1.875em;
+}
+li[class^="icon-"], li[class*=" icon-"] {
+ margin-left: 0;
+ list-style-type: none;
+}
+li[class^="icon-"]:before, li[class*=" icon-"]:before {
+ text-indent: -2em;
+ text-align: center;
+}
+li[class^="icon-"].icon-large:before, li[class*=" icon-"].icon-large:before {
+ text-indent: -1.3333333333333333em;
+}
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+ readers do not read off random characters that represent icons */
+.icon-glass:before { content: "\f000"; }
+.icon-music:before { content: "\f001"; }
+.icon-search:before { content: "\f002"; }
+.icon-envelope:before { content: "\f003"; }
+.icon-heart:before { content: "\f004"; }
+.icon-star:before { content: "\f005"; }
+.icon-star-empty:before { content: "\f006"; }
+.icon-user:before { content: "\f007"; }
+.icon-film:before { content: "\f008"; }
+.icon-th-large:before { content: "\f009"; }
+.icon-th:before { content: "\f00a"; }
+.icon-th-list:before { content: "\f00b"; }
+.icon-ok:before { content: "\f00c"; }
+.icon-remove:before { content: "\f00d"; }
+.icon-zoom-in:before { content: "\f00e"; }
+
+.icon-zoom-out:before { content: "\f010"; }
+.icon-off:before { content: "\f011"; }
+.icon-signal:before { content: "\f012"; }
+.icon-cog:before { content: "\f013"; }
+.icon-trash:before { content: "\f014"; }
+.icon-home:before { content: "\f015"; }
+.icon-file:before { content: "\f016"; }
+.icon-time:before { content: "\f017"; }
+.icon-road:before { content: "\f018"; }
+.icon-download-alt:before { content: "\f019"; }
+.icon-download:before { content: "\f01a"; }
+.icon-upload:before { content: "\f01b"; }
+.icon-inbox:before { content: "\f01c"; }
+.icon-play-circle:before { content: "\f01d"; }
+.icon-repeat:before { content: "\f01e"; }
+
+/* \f020 is not a valid unicode character. all shifted one down */
+.icon-refresh:before { content: "\f021"; }
+.icon-list-alt:before { content: "\f022"; }
+.icon-lock:before { content: "\f023"; }
+.icon-flag:before { content: "\f024"; }
+.icon-headphones:before { content: "\f025"; }
+.icon-volume-off:before { content: "\f026"; }
+.icon-volume-down:before { content: "\f027"; }
+.icon-volume-up:before { content: "\f028"; }
+.icon-qrcode:before { content: "\f029"; }
+.icon-barcode:before { content: "\f02a"; }
+.icon-tag:before { content: "\f02b"; }
+.icon-tags:before { content: "\f02c"; }
+.icon-book:before { content: "\f02d"; }
+.icon-bookmark:before { content: "\f02e"; }
+.icon-print:before { content: "\f02f"; }
+
+.icon-camera:before { content: "\f030"; }
+.icon-font:before { content: "\f031"; }
+.icon-bold:before { content: "\f032"; }
+.icon-italic:before { content: "\f033"; }
+.icon-text-height:before { content: "\f034"; }
+.icon-text-width:before { content: "\f035"; }
+.icon-align-left:before { content: "\f036"; }
+.icon-align-center:before { content: "\f037"; }
+.icon-align-right:before { content: "\f038"; }
+.icon-align-justify:before { content: "\f039"; }
+.icon-list:before { content: "\f03a"; }
+.icon-indent-left:before { content: "\f03b"; }
+.icon-indent-right:before { content: "\f03c"; }
+.icon-facetime-video:before { content: "\f03d"; }
+.icon-picture:before { content: "\f03e"; }
+
+.icon-pencil:before { content: "\f040"; }
+.icon-map-marker:before { content: "\f041"; }
+.icon-adjust:before { content: "\f042"; }
+.icon-tint:before { content: "\f043"; }
+.icon-edit:before { content: "\f044"; }
+.icon-share:before { content: "\f045"; }
+.icon-check:before { content: "\f046"; }
+.icon-move:before { content: "\f047"; }
+.icon-step-backward:before { content: "\f048"; }
+.icon-fast-backward:before { content: "\f049"; }
+.icon-backward:before { content: "\f04a"; }
+.icon-play:before { content: "\f04b"; }
+.icon-pause:before { content: "\f04c"; }
+.icon-stop:before { content: "\f04d"; }
+.icon-forward:before { content: "\f04e"; }
+
+.icon-fast-forward:before { content: "\f050"; }
+.icon-step-forward:before { content: "\f051"; }
+.icon-eject:before { content: "\f052"; }
+.icon-chevron-left:before { content: "\f053"; }
+.icon-chevron-right:before { content: "\f054"; }
+.icon-plus-sign:before { content: "\f055"; }
+.icon-minus-sign:before { content: "\f056"; }
+.icon-remove-sign:before { content: "\f057"; }
+.icon-ok-sign:before { content: "\f058"; }
+.icon-question-sign:before { content: "\f059"; }
+.icon-info-sign:before { content: "\f05a"; }
+.icon-screenshot:before { content: "\f05b"; }
+.icon-remove-circle:before { content: "\f05c"; }
+.icon-ok-circle:before { content: "\f05d"; }
+.icon-ban-circle:before { content: "\f05e"; }
+
+.icon-arrow-left:before { content: "\f060"; }
+.icon-arrow-right:before { content: "\f061"; }
+.icon-arrow-up:before { content: "\f062"; }
+.icon-arrow-down:before { content: "\f063"; }
+.icon-share-alt:before { content: "\f064"; }
+.icon-resize-full:before { content: "\f065"; }
+.icon-resize-small:before { content: "\f066"; }
+.icon-plus:before { content: "\f067"; }
+.icon-minus:before { content: "\f068"; }
+.icon-asterisk:before { content: "\f069"; }
+.icon-exclamation-sign:before { content: "\f06a"; }
+.icon-gift:before { content: "\f06b"; }
+.icon-leaf:before { content: "\f06c"; }
+.icon-fire:before { content: "\f06d"; }
+.icon-eye-open:before { content: "\f06e"; }
+
+.icon-eye-close:before { content: "\f070"; }
+.icon-warning-sign:before { content: "\f071"; }
+.icon-plane:before { content: "\f072"; }
+.icon-calendar:before { content: "\f073"; }
+.icon-random:before { content: "\f074"; }
+.icon-comment:before { content: "\f075"; }
+.icon-magnet:before { content: "\f076"; }
+.icon-chevron-up:before { content: "\f077"; }
+.icon-chevron-down:before { content: "\f078"; }
+.icon-retweet:before { content: "\f079"; }
+.icon-shopping-cart:before { content: "\f07a"; }
+.icon-folder-close:before { content: "\f07b"; }
+.icon-folder-open:before { content: "\f07c"; }
+.icon-resize-vertical:before { content: "\f07d"; }
+.icon-resize-horizontal:before { content: "\f07e"; }
+
+.icon-bar-chart:before { content: "\f080"; }
+.icon-twitter-sign:before { content: "\f081"; }
+.icon-facebook-sign:before { content: "\f082"; }
+.icon-camera-retro:before { content: "\f083"; }
+.icon-key:before { content: "\f084"; }
+.icon-cogs:before { content: "\f085"; }
+.icon-comments:before { content: "\f086"; }
+.icon-thumbs-up:before { content: "\f087"; }
+.icon-thumbs-down:before { content: "\f088"; }
+.icon-star-half:before { content: "\f089"; }
+.icon-heart-empty:before { content: "\f08a"; }
+.icon-signout:before { content: "\f08b"; }
+.icon-linkedin-sign:before { content: "\f08c"; }
+.icon-pushpin:before { content: "\f08d"; }
+.icon-external-link:before { content: "\f08e"; }
+
+.icon-signin:before { content: "\f090"; }
+.icon-trophy:before { content: "\f091"; }
+.icon-github-sign:before { content: "\f092"; }
+.icon-upload-alt:before { content: "\f093"; }
+.icon-lemon:before { content: "\f094"; }
View
211 static/css/style.css
@@ -150,7 +150,7 @@ button { width: auto; overflow: visible; }
body, select, input, textarea {
/* #444 looks better than black: twitter.com/H_FJ/statuses/11800719859 */
- color: #333;
+ color: #444;
/* Set your base font here, to apply evenly */
font: 13px/1.5 Helvetica Neue,Arial,Helvetica,'Liberation Sans',FreeSans,sans-serif;
-webkit-font-smoothing: antialiased;
@@ -191,9 +191,14 @@ textarea.contenteditable {-webkit-appearance: none;}
html {
background: #f8f8f8;
- padding: 20px;
+ padding: 0px 10px;
+}
+
+body {
+ padding: 25px 0;
}
+
#container {
text-align: center;
}
@@ -216,6 +221,7 @@ ul {
header {
padding-bottom: 20px;
+ font-size: 110%;
}
nav {
@@ -228,12 +234,12 @@ header ul {
header ul li {
display: inline-block;
- padding-right: 10px;
+ padding-right: 15px;
}
header ul li.seperator {
- padding-right: 10px;
- color: #ccc;
+ padding-right: 15px;
+ color: #aaa;
}
header a {
@@ -241,33 +247,52 @@ header a {
}
h1 {
- font-size: 200%;
+ font-size: 190%;
font-weight: normal;
}
h2 {
font-size: 110%;
+ text-align: left;
+ margin-bottom: 10px;
}
h3 {
color: #777;
- font-size: 110%;
+ font-size: 120%;
font-weight: normal;
}
h4 {
+ text-align: left;
background: #eaeaea;
color: #777;
- padding: 3px 10px;
+ font-size: 120%;
+ font-weight: normal;
+ padding: 5px 10px;
}
p {
- padding-bottom: 15px;
+ text-align: left;
+ padding-bottom: 25px;
+}
+
+ul, ol {
+ text-align: left;
+}
+
+ol.footnotes {
+ font-size: 85%;
+}
+
+#bread-crumbs {
+ font-weight: bold;
}
section#main {
position: relative;
+ text-align: center;
}
div.status-bar {
@@ -276,46 +301,89 @@ div.status-bar {
height: 3px;
width: 100%;
background: #94c468;
- display: none;
}
div#status-bad {
- background: #e72932;
+ background: #aaa;
}
section#main ul {
padding-left: 10px;
}
+
section#main ul li {
padding-bottom: 5px;
}
ul.station-list {
- padding-top: 5px;
+ padding: 10px 0 0 10px;
+ font-size: 120%;
+
+}
+
+ul.station-list li {
+ padding: 3px 0 3px 0;
+ margin: 0 0 2px 0;
+}
+
+ul.station-list li a {
+ text-decoration: none;
}
+ul.station-list li span {
+ color: #aaa;
+ font-size: 90%;
+ margin-left: 5px;
+}
+
+
div#predictions {
padding-top: 15px;
+ text-align: left;
}
+
div#predictions ul {
padding: 10px 0;
}
+
+div#predictions li.prediction {
+ position: relative;
+ margin-bottom: 25px;
+}
+
+
+div#predictions span.info {
+ position: relative;
+ left: 10px;
+ color: #aaa;
+ z-index: -1;
+}
+
div#predictions ul.directions {
padding: 0;
+ background: #f8f8f8;
+ position: absolute;
+ width: 100%;
+ top: 20px;
+
+
}
div#predictions ul.directions li {
display: inline-block;
- padding: 0 10px;
- font-size: 145%;
- height: 50px;
+ padding: 0 5px 0 10px;
+ font-size: 170%;
+ height: 45px;
}
+
section#main ul li.info {
+ margin: 0;
+ padding: 0;
width: 100%;
font-size: 100%;
color: #aaa;
@@ -323,25 +391,12 @@ section#main ul li.info {
}
section#nearby-stations {
- padding-top: 25px;
+ padding-top: 7px;
}
-section#nearby-stations ul {
- padding: 10px 0 0 10px;
-}
-
-section#nearby-stations ul li {
- padding: 0 0 5px;
-}
-
-section#nearby-stations ul li span {
- color: #aaa;
- font-size: 90%;
- margin-left: 5px;
-}
-
footer {
+ text-align: center;
color: #ccc;
margin-top: 25px;
padding-top: 5px;
@@ -357,7 +412,6 @@ footer ul li {
display: inline-block;
padding: 13px 0 0 0;
margin-right: 10px;
-
}
@@ -373,7 +427,6 @@ footer ul li:last-child {
footer a {
color: #aaa;
- text-decoration: none;
}
@@ -384,36 +437,53 @@ footer a:hover {
div.tool-tip {
- background: #444;
- color: #ddd;
- position: absolute;
border-radius: 5px;
- width: 180px;
+ margin: 0 auto;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ text-align: center;
display: none;
+ z-index: 10;
}
-div.tool-tip div {
+div.tool-tip span {
+ display: inline-block;
position: relative;
- color: #444;
- top: -14px;
- margin-bottom: -20px;
- left: 5px;
- font-size: 150%;
+ width: 40px;
+ font-size: 33px;
+ font-weight: bold;
+ margin: 40px auto;
+ color: #ddd;
+ background-color: rgba(0,0,0,0.8);
+ border-radius: 5px;
+ padding: 15px 20px 12px 20px;
+}
+
+
+ul.tweets {
+ margin-bottom: 35px;
+}
+
+ul.tweets li {
+ margin: 15px 10px 0px 10px;
}
-div.tool-tip p {
+ul.tweets li strong {
display: block;
- padding: 0 10px 10px 10px;
- line-height: 1.1em;
- font-size: 85%;
}
-div.tool-tip strong {
- margin-right: 5px;
+ul.tweets li a {
+ text-decoration: none;
+}
+
+ul.tweets li p {
+ margin: 0;
+ padding: 0;
}
-div.tool-tip input {
- margin-right: 5px;
+ul.tweets li time {
+ color: #aaa;
}
/* Adaptive Styles
@@ -431,11 +501,50 @@ div.tool-tip input {
}
-@media only screen and (max-width : 540px) {
+@media only screen and (max-width : 450px) {
+
+ header ul li {
+ padding-right: 5px;
+ }
+
+ header ul li:last-child {
+ padding-right: 0;
+ }
+
+ header ul li.seperator {
+ padding-right: 5px;
+ }
+ div#predictions ul.directions li {
+ padding: 0 8px;
+ }
+
}
+@media only screen and (max-width : 320px) {
+
+ div#predictions ul.directions li {
+ font-size: 140%;
+ padding: 0 10px 0 0;
+ }
+
+ div#predictions ul.directions li:last-child {
+ padding: 0 0 0 0;
+ }
+
+ div#predictions ul.directions li:first-child {
+ padding: 0 10px 0 15px;
+ }
+
+
+ section#main ul li.info {
+ font-size: 100%;
+ padding: 0;
+ }
+
+}
+
/* iPhone 4 and high pixel ratio devices ----------- */
@media
only screen and (-webkit-min-device-pixel-ratio : 1.5),
View
BIN  static/flashsocket/WebSocketMain.swf
Binary file not shown
View
BIN  static/font/fontawesome-webfont.eot
Binary file not shown
View
175 static/font/fontawesome-webfont.svg
@@ -0,0 +1,175 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>
+This is a custom SVG webfont generated by Font Squirrel.
+Designer : Dave Gandy
+Foundry : Fort Awesome
+</metadata>
+<defs>
+<font id="FontAwesomeRegular" horiz-adv-x="900" >
+<font-face units-per-em="1000" ascent="750" descent="-250" />
+<missing-glyph horiz-adv-x="250" />
+<glyph unicode="&#xd;" horiz-adv-x="250" />
+<glyph horiz-adv-x="0" />
+<glyph horiz-adv-x="0" />
+<glyph unicode=" " horiz-adv-x="250" />
+<glyph unicode="&#x09;" horiz-adv-x="250" />
+<glyph unicode="&#xa0;" horiz-adv-x="250" />
+<glyph unicode="&#x2000;" horiz-adv-x="375" />
+<glyph unicode="&#x2001;" horiz-adv-x="751" />
+<glyph unicode="&#x2002;" horiz-adv-x="375" />
+<glyph unicode="&#x2003;" horiz-adv-x="751" />
+<glyph unicode="&#x2004;" horiz-adv-x="250" />
+<glyph unicode="&#x2005;" horiz-adv-x="187" />
+<glyph unicode="&#x2006;" horiz-adv-x="125" />
+<glyph unicode="&#x2007;" horiz-adv-x="125" />
+<glyph unicode="&#x2008;" horiz-adv-x="93" />
+<glyph unicode="&#x2009;" horiz-adv-x="150" />
+<glyph unicode="&#x200a;" horiz-adv-x="41" />
+<glyph unicode="&#x202f;" horiz-adv-x="150" />
+<glyph unicode="&#x205f;" horiz-adv-x="187" />
+<glyph unicode="&#xe000;" horiz-adv-x="500" d="M0 0v0v0v0v0z" />
+<glyph unicode="&#xf000;" horiz-adv-x="750" d="M3 727q10 23 34 23h675q25 0 35 -23t-8 -41l-298 -298v-313h121q16 0 27 -11t11 -26q0 -16 -11 -27t-27 -11h-375q-15 0 -26 11t-11 27q0 15 11 26t26 11h122v313l-298 298q-18 18 -8 41z" />
+<glyph unicode="&#xf001;" horiz-adv-x="688" d="M0 112q0 24 11 44.5t30 35.5t45 24t55 9q13 0 24.5 -2t22.5 -5v388l500 144v-525q0 -23 -11 -43.5t-30 -36t-45 -24.5t-55 -9t-54.5 9t-44.5 24.5t-30 36t-11 43.5t11 43.5t30 35.5t44.5 24t54.5 9q24 0 47 -6v248l-312 -90v-377q0 -23 -11 -43.5t-30 -35.5t-45 -24 t-55 -9t-55 9t-45 24t-30 35.5t-11 43.5z" />
+<glyph unicode="&#xf002;" horiz-adv-x="750" d="M0 437q0 65 24.5 122t67 99.5t99.5 67t122 24.5q64 0 121 -24.5t99.5 -67t67 -99.5t24.5 -122q0 -48 -13.5 -91t-38.5 -81l168 -167q9 -10 9 -23t-9 -22l-44 -44q-9 -9 -22 -9t-22 9l-168 168q-38 -25 -81 -38.5t-91 -13.5q-65 0 -122 24.5t-99.5 67t-67 99t-24.5 121.5z M125 437q0 -39 14.5 -73t40 -59.5t60 -40t73.5 -14.5t73 14.5t59.5 40t40 59.5t14.5 73t-14.5 73t-40 59.5t-59.5 40.5t-73 15t-73.5 -15t-60 -40.5t-40 -59.5t-14.5 -73zM194 437q0 25 9.5 46.5t25.5 37.5t37.5 25.5t46.5 9.5q10 0 16.5 -7t6.5 -17t-6.5 -16.5t-16.5 -6.5 q-30 0 -51 -21t-21 -51q0 -10 -6.5 -16.5t-16.5 -6.5t-17 6.5t-7 16.5z" />
+<glyph unicode="&#xf003;" d="M0 56v587v32v19q0 23 16.5 39.5t39.5 16.5h19h750h19q23 0 39.5 -16.5t16.5 -39.5v-19v-30v-589q0 -23 -16.5 -39.5t-39.5 -16.5h-788q-23 0 -39.5 16.5t-16.5 39.5zM75 75h750v416q-15 -15 -28 -24q-29 -21 -61 -46t-64 -49q-19 -14 -36.5 -28t-32.5 -25q-3 -2 -6 -4.5 t-7 -5.5q-14 -11 -29.5 -22.5t-33.5 -22t-37.5 -17t-40.5 -6.5q-20 0 -39.5 6.5t-37 17t-33.5 22t-29 22.5q-4 3 -7 5.5t-6 4.5q-15 11 -32.5 25t-36.5 28q-32 24 -64 49t-61 46q-13 9 -28 24v-416zM75 643q0 -14 6 -30t16.5 -32t23 -30t26.5 -24q23 -17 49 -37l52 -38 q26 -20 50 -39t44 -34l22 -17q14 -11 28.5 -21t29 -17.5t27.5 -7.5h1h1q13 0 27.5 7.5t29 17.5t28.5 21l22 17q20 15 44 34t50 39l52 38q26 20 49 37q13 10 26 24t23.5 30t16.5 32t6 30v32h-750v-32z" />
+<glyph unicode="&#xf004;" horiz-adv-x="846" d="M0 519q0 64 20 108t52.5 71.5t73.5 39.5t83 12q30 0 59 -10t54 -25t45.5 -32.5t35.5 -32.5q15 15 35.5 32.5t45.5 32.5t54 25t59 10q42 0 83 -12t73.5 -39.5t52.5 -71.5t20 -108q0 -44 -16.5 -83.5t-36 -69.5t-37 -48t-18.5 -19l-289 -288q-11 -11 -26 -11t-26 11 l-290 288q-1 1 -18 19t-36.5 48t-36 69.5t-16.5 83.5z" />
+<glyph unicode="&#xf005;" horiz-adv-x="787" d="M0.5 465q4.5 13 25.5 16l238 35l106 215q10 20 23.5 20t22.5 -20l107 -215l237 -35q22 -3 26 -16t-11 -28l-172 -168l40 -236q4 -22 -7 -30t-30 3l-213 111l-212 -111q-20 -11 -31 -3t-7 30l41 236l-172 168q-16 15 -11.5 28z" />
+<glyph unicode="&#xf006;" horiz-adv-x="787" d="M0.5 465q4.5 13 25.5 16l238 34l106 216q9 19 23 19t23 -19l107 -216l237 -34q22 -3 26 -16t-11 -28l-172 -168l40 -236q3 -16 -2 -24.5t-16 -8.5q-7 0 -19 5l-213 112l-212 -112q-12 -5 -19 -5q-11 0 -16 8.5t-3 24.5l41 236l-172 168q-16 15 -11.5 28zM136 421l100 -98 l29 -27l-7 -39l-24 -139l124 66l35 18l35 -18l124 -66l-23 139l-7 39l28 27l101 98l-139 20l-39 6l-18 35l-62 126l-62 -126l-17 -35l-39 -6z" />
+<glyph unicode="&#xf007;" d="M0 34v7q11 19 19.5 40t17.5 42t19.5 40t25.5 34q7 7 15.5 13.5t19.5 10.5t23.5 5t25.5 3q37 6 77.5 12.5t78.5 12.5q4 17 7 34.5t8 33.5q-8 11 -16 21.5t-15.5 23t-13.5 28.5t-9 37q-2 11 -5 32.5t-6 44t-5.5 41t-2.5 22.5q0 25 10.5 56t33 58t58 45.5t84.5 18.5 t84.5 -18.5t58 -45.5t33 -58t10.5 -56q0 -4 -2.5 -22.5t-5.5 -41t-6 -44t-5 -32.5q-3 -21 -9 -37t-13.5 -28.5t-16 -23t-15.5 -21.5q5 -16 8 -33.5t7 -34.5q38 -6 78.5 -12.5t77.5 -12.5q13 -2 25.5 -3t23.5 -5t19.5 -10.5t15.5 -13.5q15 -15 25.5 -34t19.5 -40t17.5 -42 t19.5 -40v-7q-14 -8 -26.5 -18.5t-30.5 -15.5h-786q-18 5 -30.5 15.5t-26.5 18.5z" />
+<glyph unicode="&#xf008;" d="M0 56v638q0 23 16.5 39.5t39.5 16.5h788q23 0 39.5 -16.5t16.5 -39.5v-638q0 -23 -16.5 -39.5t-39.5 -16.5h-788q-23 0 -39.5 16.5t-16.5 39.5zM56 75q0 -8 5.5 -13.5t13.5 -5.5h75q8 0 13.5 5.5t5.5 13.5v75q0 8 -5.5 13.5t-13.5 5.5h-75q-8 0 -13.5 -5.5t-5.5 -13.5 v-75zM56 250q0 -8 5.5 -13.5t13.5 -5.5h75q8 0 13.5 5.5t5.5 13.5v75q0 8 -5.5 13.5t-13.5 5.5h-75q-8 0 -13.5 -5.5t-5.5 -13.5v-75zM56 425q0 -8 5.5 -13.5t13.5 -5.5h75q8 0 13.5 5.5t5.5 13.5v75q0 8 -5.5 13.5t-13.5 5.5h-75q-8 0 -13.5 -5.5t-5.5 -13.5v-75zM56 600 q0 -8 5.5 -13.5t13.5 -5.5h75q8 0 13.5 5.5t5.5 13.5v75q0 8 -5.5 13.5t-13.5 5.5h-75q-8 0 -13.5 -5.5t-5.5 -13.5v-75zM225 75q0 -8 5.5 -13.5t13.5 -5.5h412q8 0 13.5 5.5t5.5 13.5v250q0 8 -5.5 13.5t-13.5 5.5h-412q-8 0 -13.5 -5.5t-5.5 -13.5v-250zM225 425 q0 -8 5.5 -13.5t13.5 -5.5h412q8 0 13.5 5.5t5.5 13.5v250q0 8 -5.5 13.5t-13.5 5.5h-412q-8 0 -13.5 -5.5t-5.5 -13.5v-250zM731 75q0 -8 5.5 -13.5t13.5 -5.5h75q8 0 13.5 5.5t5.5 13.5v75q0 8 -5.5 13.5t-13.5 5.5h-75q-8 0 -13.5 -5.5t-5.5 -13.5v-75zM731 250 q0 -8 5.5 -13.5t13.5 -5.5h75q8 0 13.5 5.5t5.5 13.5v75q0 8 -5.5 13.5t-13.5 5.5h-75q-8 0 -13.5 -5.5t-5.5 -13.5v-75zM731 425q0 -8 5.5 -13.5t13.5 -5.5h75q8 0 13.5 5.5t5.5 13.5v75q0 8 -5.5 13.5t-13.5 5.5h-75q-8 0 -13.5 -5.5t-5.5 -13.5v-75zM731 600 q0 -8 5.5 -13.5t13.5 -5.5h75q8 0 13.5 5.5t5.5 13.5v75q0 8 -5.5 13.5t-13.5 5.5h-75q-8 0 -13.5 -5.5t-5.5 -13.5v-75z" />
+<glyph unicode="&#xf009;" d="M0 38v262q0 16 11 27t27 11h337q16 0 27 -11t11 -27v-262q0 -16 -11 -27t-27 -11h-337q-16 0 -27 11t-11 27zM0 450v263q0 15 11 26t27 11h337q16 0 27 -11t11 -26v-263q0 -16 -11 -26.5t-27 -10.5h-337q-16 0 -27 10.5t-11 26.5zM488 38v262q0 16 10.5 27t26.5 11h338 q15 0 26 -11t11 -27v-262q0 -16 -11 -27t-26 -11h-338q-16 0 -26.5 11t-10.5 27zM488 450v263q0 15 10.5 26t26.5 11h338q15 0 26 -11t11 -26v-263q0 -16 -11 -26.5t-26 -10.5h-338q-16 0 -26.5 10.5t-10.5 26.5z" />
+<glyph unicode="&#xf00a;" d="M0 38v132q0 16 11 26.5t27 10.5h175q15 0 26 -10.5t11 -26.5v-132q0 -16 -11 -27t-26 -11h-175q-16 0 -27 11t-11 27zM0 320v110q0 16 11 26.5t27 10.5h175q15 0 26 -10.5t11 -26.5v-110q0 -16 -11 -27t-26 -11h-175q-16 0 -27 11t-11 27zM0 580v133q0 15 11 26t27 11 h175q15 0 26 -11t11 -26v-133q0 -16 -11 -27t-26 -11h-175q-16 0 -27 11t-11 27zM325 38v132q0 16 11 26.5t27 10.5h175q15 0 26 -1