├── resources ├── public │ ├── styles │ │ ├── 600up.css │ │ ├── responsive.css │ │ ├── unresponsive.css │ │ ├── reset.css │ │ └── base.css │ ├── favicon.ico │ ├── images │ │ ├── rss.png │ │ ├── itunes.png │ │ ├── logo.png │ │ ├── splash.png │ │ ├── twitter.png │ │ └── dark-leather.png │ └── scripts │ │ └── ga.js ├── commands │ ├── repeat.html │ ├── sgml-close-tag.html │ ├── forward-list.html │ ├── ace-jump-mode.html │ ├── digit-argument.html │ ├── flush-lines.html │ ├── downcase-word.html │ ├── query-replace.html │ ├── describe-key.html │ ├── kmacro-start-macro.html │ ├── slime-js-send-defun.html │ ├── yas-expand.html │ ├── eval-last-sexp.html │ ├── iy-go-to-char.html │ ├── key-chord-define-global.html │ ├── kmacro-name-last-macro.html │ ├── sgml-delete-tag.html │ ├── transpose-words.html │ ├── dired-toggle-read-only.html │ ├── insert-kbd-macro.html │ ├── transpose-chars.html │ ├── transpose-lines.html │ ├── yank-rectangle.html │ ├── inline-string-rectangle.html │ ├── isearch-forward.html │ ├── mark-next-like-this.html │ ├── expand-region.html │ ├── zencoding-expand-line.html │ ├── rename-sgml-tag.html │ ├── string-rectangle.html │ ├── eval-and-replace.html │ ├── cleanup-buffer.html │ ├── mc-mark-all-like-this.html │ ├── mc-mark-next-like-this.html │ ├── ido-imenu.html │ ├── set-rectangular-region-anchor.html │ └── slime-js-jack-in-node.html ├── extending-episodes.edn └── episodes.edn ├── .gitignore ├── src └── emacs_rocks │ ├── server.clj │ ├── content.clj │ ├── pages.clj │ ├── system.clj │ ├── rss.clj │ ├── layout.clj │ ├── page │ ├── episodes.clj │ ├── extending.clj │ └── index.clj │ └── web.clj ├── dev └── user.clj ├── README.md └── project.clj /resources/public/styles/600up.css: -------------------------------------------------------------------------------- 1 | .episode_list a {float: left; margin: 14px;} -------------------------------------------------------------------------------- /resources/commands/repeat.html: -------------------------------------------------------------------------------- 1 |

C-x z repeat

2 |

3 | Repeat the last command. 4 |

5 | -------------------------------------------------------------------------------- /resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/emacsrocks.com/master/resources/public/favicon.ico -------------------------------------------------------------------------------- /resources/public/images/rss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/emacsrocks.com/master/resources/public/images/rss.png -------------------------------------------------------------------------------- /resources/public/images/itunes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/emacsrocks.com/master/resources/public/images/itunes.png -------------------------------------------------------------------------------- /resources/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/emacsrocks.com/master/resources/public/images/logo.png -------------------------------------------------------------------------------- /resources/public/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/emacsrocks.com/master/resources/public/images/splash.png -------------------------------------------------------------------------------- /resources/commands/sgml-close-tag.html: -------------------------------------------------------------------------------- 1 |

C-c C-e sgml-close-tag

2 |

3 | Close the current tag. 4 |

5 | -------------------------------------------------------------------------------- /resources/public/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/emacsrocks.com/master/resources/public/images/twitter.png -------------------------------------------------------------------------------- /resources/commands/forward-list.html: -------------------------------------------------------------------------------- 1 |

C-M-N forward-list

2 |

3 | Jump forward to matching paren. 4 |

5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /resources/public/images/dark-leather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/emacsrocks.com/master/resources/public/images/dark-leather.png -------------------------------------------------------------------------------- /resources/public/styles/responsive.css: -------------------------------------------------------------------------------- 1 | @import 'reset.css'; 2 | @import 'base.css'; 3 | @import "600up.css" screen and (min-width: 600px); 4 | -------------------------------------------------------------------------------- /resources/public/styles/unresponsive.css: -------------------------------------------------------------------------------- 1 | @import 'reset.css'; 2 | @import 'base.css'; 3 | @import "600up.css"; 4 | #main {width: 560px;} 5 | -------------------------------------------------------------------------------- /resources/commands/ace-jump-mode.html: -------------------------------------------------------------------------------- 1 |

C-ø ace-jump-mode

2 |

3 | Jump to a word starting with a given char. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/digit-argument.html: -------------------------------------------------------------------------------- 1 |

C-9 digit-argument

2 |

3 | Execute the following command 9 times. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/flush-lines.html: -------------------------------------------------------------------------------- 1 |

M-x flush-lines

2 |

3 | Delete lines after point matching a given regexp. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/downcase-word.html: -------------------------------------------------------------------------------- 1 |

M-l downcase-word

2 |

3 | Convert following word to lower case, moving over. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/query-replace.html: -------------------------------------------------------------------------------- 1 |

M-% query-replace

2 |

3 | Replace some occurrences of one string with another. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/describe-key.html: -------------------------------------------------------------------------------- 1 |

C-h k describe-key

2 |

3 | Describe the function bound to a given key combination. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/kmacro-start-macro.html: -------------------------------------------------------------------------------- 1 |

F3 kmacro-start-macro

2 |

3 | Record your keyboard input to play it back later. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/slime-js-send-defun.html: -------------------------------------------------------------------------------- 1 |

C-M-x slime-js-send-defun

2 |

3 | Sends the function at point to the js-repl. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/yas-expand.html: -------------------------------------------------------------------------------- 1 |

<tab> yas/expand

2 |

3 | If the preceeding text matches a yasnippet, expand it. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/eval-last-sexp.html: -------------------------------------------------------------------------------- 1 |

C-x C-e eval-last-sexp

2 |

3 | Evaluate sexp before point; print value in minibuffer. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/iy-go-to-char.html: -------------------------------------------------------------------------------- 1 |

M-m iy-go-to-char

2 |

3 | Move point to next occurrence of char. Can start typing immediately. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/key-chord-define-global.html: -------------------------------------------------------------------------------- 1 |

key-chord-define-global

2 |

3 | Define a two-char key chord to invoke a command. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/kmacro-name-last-macro.html: -------------------------------------------------------------------------------- 1 |

M-x kmacro-name-last-macro

2 |

3 | Assign a name to the last keyboard macro defined. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/sgml-delete-tag.html: -------------------------------------------------------------------------------- 1 |

C-c DEL sgml-delete-tag

2 |

3 | Delete the current tag along with its respective other tag. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/transpose-words.html: -------------------------------------------------------------------------------- 1 |

M-t transpose-words

2 |

3 | Switch the words around point, leaving point at end of them. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/dired-toggle-read-only.html: -------------------------------------------------------------------------------- 1 |

C-x C-q dired-toggle-read-only

2 |

3 | Edit dired buffer with Wdired, or set it read-only. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/insert-kbd-macro.html: -------------------------------------------------------------------------------- 1 |

M-x insert-kbd-macro

2 |

3 | Insert in buffer the definition of kbd macro NAME, as Lisp code. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/transpose-chars.html: -------------------------------------------------------------------------------- 1 |

C-t transpose-chars

2 |

3 | Switch the characters around point, moving forward one character. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/transpose-lines.html: -------------------------------------------------------------------------------- 1 |

C-x C-t transpose-lines

2 |

3 | Switch the current and previous lines, leaving point after both. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/yank-rectangle.html: -------------------------------------------------------------------------------- 1 |

C-x r y yank-rectangle

2 |

3 | Yank the last killed rectangle with upper left corner at point. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/inline-string-rectangle.html: -------------------------------------------------------------------------------- 1 |

C-x r t inline-string-rectangle

2 |

3 | Replace rectangle contents with visual feedback as you type. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/isearch-forward.html: -------------------------------------------------------------------------------- 1 |

C-s isearch-forward

2 |

3 | Search incrementally forward. 4 | Note that mark is set at start of search on exit. 5 |

6 | -------------------------------------------------------------------------------- /resources/commands/mark-next-like-this.html: -------------------------------------------------------------------------------- 1 |

C-> mark-next-like-this

2 |

3 | Find and mark the next part of the buffer matching the currently active region. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/expand-region.html: -------------------------------------------------------------------------------- 1 |

C-@ er/expand-region

2 |

3 | Increase selection by semantic units. Get it here. 4 |

5 | -------------------------------------------------------------------------------- /resources/commands/zencoding-expand-line.html: -------------------------------------------------------------------------------- 1 |

C-c C-j zencoding-expand-line

2 |

3 | Interactively create HTML out of CSS-selectors. 4 | Get it here. 5 |

6 | -------------------------------------------------------------------------------- /resources/commands/rename-sgml-tag.html: -------------------------------------------------------------------------------- 1 |

C-c C-r rename-sgml-tag

2 |

3 | Rename the current tag (closest from point nesting-wise). 4 | Get it here 5 |

6 | -------------------------------------------------------------------------------- /resources/commands/string-rectangle.html: -------------------------------------------------------------------------------- 1 |

C-x r t string-rectangle

2 |

3 | Point and mark together form a rectangle. 4 | This command removes the content in that rectangle, and 5 | inserts a given string on each line. 6 |

7 | -------------------------------------------------------------------------------- /resources/commands/eval-and-replace.html: -------------------------------------------------------------------------------- 1 |

C-x C-e eval-and-replace

2 |

3 | Evaluate the last sexp and replace it with the result. 4 | Get it here. 5 |

6 | -------------------------------------------------------------------------------- /resources/commands/cleanup-buffer.html: -------------------------------------------------------------------------------- 1 |

C-c n cleanup-buffer

2 |

3 | Perform a bunch of operations on the whitespace content of a buffer. 4 | Get it here. 5 |

6 | -------------------------------------------------------------------------------- /resources/commands/mc-mark-all-like-this.html: -------------------------------------------------------------------------------- 1 |

C-Æ mc/mark-all-like-this

2 |

3 | Find and mark all parts of the buffer matching the currently active 4 | region, while also keeping the current region. 5 | Get it here. 6 |

7 | -------------------------------------------------------------------------------- /resources/commands/mc-mark-next-like-this.html: -------------------------------------------------------------------------------- 1 |

C-æ mc/mark-next-like-this

2 |

3 | Find and mark the next part of the buffer matching the currently active 4 | region, while also keeping the current region. 5 | Get it here. 6 |

7 | -------------------------------------------------------------------------------- /src/emacs_rocks/server.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.server 2 | (:require [ring.adapter.jetty :as jetty])) 3 | 4 | (defn create-and-start 5 | [handler & {:keys [port]}] 6 | {:pre [(not (nil? port))]} 7 | (jetty/run-jetty handler {:port port :join? false})) 8 | 9 | (defn stop 10 | [server] 11 | (.stop server)) 12 | -------------------------------------------------------------------------------- /resources/commands/ido-imenu.html: -------------------------------------------------------------------------------- 1 |

C-x C-i ido-imenu-push-mark

2 |

3 | Jump to a symbol, depending on major mode. Original is ido-imenu, that does not set 4 | the mark. ido-imenu-push-mark is available at 5 | this gist. 6 |

7 | -------------------------------------------------------------------------------- /resources/commands/set-rectangular-region-anchor.html: -------------------------------------------------------------------------------- 1 |

H-SPC set-rectangular-region-anchor

2 |

3 | Think of this one as `set-mark` except you're marking a rectangular region. It 4 | is an exceedingly quick way of adding multiple cursors to multiple lines. 5 | Get it here. 6 |

7 | -------------------------------------------------------------------------------- /resources/commands/slime-js-jack-in-node.html: -------------------------------------------------------------------------------- 1 |

M-x slime-js-jack-in-node

2 |

3 | And its sibling slime-js-jack-in-browser are some conveniences 4 | that I've built around slime-js. You can find 5 | them here 6 | in my .emacs.d on github. 7 |

8 | -------------------------------------------------------------------------------- /src/emacs_rocks/content.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.content 2 | (:require [clojure.java.io :as io] 3 | [stasis.core :refer [slurp-directory]])) 4 | 5 | (defn- load-edn [name] 6 | (read-string (slurp (io/resource name)))) 7 | 8 | (defn load-content [] 9 | {:episodes (load-edn "episodes.edn") 10 | :extending-episodes (load-edn "extending-episodes.edn") 11 | :commands (slurp-directory "resources/commands/" #"\.html$")}) 12 | -------------------------------------------------------------------------------- /dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require [clojure.java.io :as io] 3 | [clojure.pprint :refer [pprint pp print-table]] 4 | [clojure.reflect] 5 | [clojure.repl :refer :all] 6 | [clojure.set :as set] 7 | [clojure.string :as str] 8 | [emacs-rocks.system] 9 | [quick-reset.core :refer [stop reset system]])) 10 | 11 | (quick-reset.core/set-constructor 'emacs-rocks.system/create-system) 12 | -------------------------------------------------------------------------------- /resources/public/scripts/ga.js: -------------------------------------------------------------------------------- 1 | var _gaq = _gaq || []; 2 | _gaq.push(['_setAccount', 'UA-26477646-1']); 3 | _gaq.push(['_trackPageview']); 4 | 5 | (function() { 6 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; 7 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; 8 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); 9 | })(); 10 | -------------------------------------------------------------------------------- /src/emacs_rocks/pages.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.pages 2 | (:require [emacs-rocks.page.episodes :refer [get-episode-pages]] 3 | [emacs-rocks.page.extending :refer [get-extending-pages]] 4 | [emacs-rocks.page.index :refer [render-index-page]] 5 | [stasis.core :refer [merge-page-sources]])) 6 | 7 | (defn create-pages [content] 8 | (merge-page-sources 9 | {:general-pages {"/" {:title "Emacs Rocks!" 10 | :body (render-index-page content)}} 11 | :episode-pages (get-episode-pages content) 12 | :extending-pages (get-extending-pages content)})) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Emacs Rocks 2 | 3 | The webpage for https://emacsrocks.com. 4 | 5 | Made with [Stasis](https://github.com/magnars/stasis) for static site 6 | generation, [Hiccup](https://github.com/weavejester/hiccup) to 7 | build HTML, and [Optimus](https://github.com/magnars/optimus) for 8 | frontend optimization. 9 | 10 | Start a local version: 11 | 12 | lein repl 13 | (reset) 14 | 15 | Build a new version of the site: 16 | 17 | ./build.sh 18 | 19 | ## License 20 | 21 | Copyright © (iterate inc 2014) Magnar Sveen 22 | 23 | Distributed under the Eclipse Public License either version 1.0 or (at 24 | your option) any later version. 25 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject emacs-rocks "0.1.0" 2 | :description "The webpage for emacsrocks.com" 3 | :url "https://emacsrocks.com" 4 | :jvm-opts ["-Djava.awt.headless=true"] 5 | :dependencies [[org.clojure/clojure "1.7.0"] 6 | [stasis "2.2.2"] 7 | [optimus "0.15.1"] 8 | [ring "1.3.1"] 9 | [enlive "1.1.5"] 10 | [hiccup "1.0.4"] 11 | [prone "0.8.0"] 12 | [org.clojure/data.xml "0.0.7"]] 13 | :main emacs-rocks.system 14 | :profiles {:dev {:dependencies [[ciderale/quick-reset "0.1.1"] 15 | [org.clojure/tools.namespace "0.2.8"]] 16 | :source-paths ["dev"]}}) 17 | -------------------------------------------------------------------------------- /src/emacs_rocks/system.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.system 2 | (:require [emacs-rocks.web :as web] 3 | [prone.middleware :as prone] 4 | [emacs-rocks.server :as server])) 5 | 6 | (defn start 7 | "Performs side effects to initialize the system, acquire resources, 8 | and start it running. Returns an updated instance of the system." 9 | [system] 10 | (let [handler (prone/wrap-exceptions (web/create-app)) 11 | server (server/create-and-start handler :port (:port system))] 12 | (assoc system 13 | :handler handler 14 | :server server))) 15 | 16 | (defn stop 17 | "Performs side effects to shut down the system and release its 18 | resources. Returns an updated instance of the system." 19 | [system] 20 | (when (:server system) 21 | (server/stop (:server system))) 22 | (dissoc system :handler :server)) 23 | 24 | (defn create-system 25 | "Returns a new instance of the whole application." 26 | [] 27 | {:port 3456 28 | :start start 29 | :stop stop}) 30 | 31 | (defn -main [& args] 32 | (let [system (create-system)] 33 | (start system))) 34 | -------------------------------------------------------------------------------- /src/emacs_rocks/rss.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.rss 2 | (:require [clojure.data.xml :as xml] 3 | [clojure.string :as str] 4 | [emacs-rocks.page.index :refer [episode-link]] 5 | [hiccup.core :refer [html]])) 6 | 7 | (defn to-id-str [str] 8 | "Replaces all special characters with dashes, avoiding leading, 9 | trailing and double dashes." 10 | (-> (.toLowerCase str) 11 | (str/replace #"[^a-zA-Z0-9]+" "-") 12 | (str/replace #"-$" "") 13 | (str/replace #"^-" ""))) 14 | 15 | (defn- entry [episode] 16 | [:entry 17 | [:title (str (:number episode) ": " (:name episode))] 18 | [:updated (str (:date episode) "T00:00:00+02:00")] 19 | [:author [:name "Magnar Sveen"]] 20 | [:link {:href (str "http://emacsrocks.com" (episode-link episode))}] 21 | [:id (str "urn:emacsrocks-com:feed:episode:" (:number episode))] 22 | [:content {:type "html"} (html [:a {:href (str "http://emacsrocks.com" (episode-link episode))} 23 | "See the video"])]]) 24 | 25 | (defn atom-xml [content] 26 | (xml/emit-str 27 | (xml/sexp-as-element 28 | [:feed {:xmlns "http://www.w3.org/2005/Atom" 29 | :xmlns:media "http://search.yahoo.com/mrss/"} 30 | [:id "urn:emacsrocks-com:feed"] 31 | [:updated 32 | (str (:date (last (:episodes content))) "T00:00:00+02:00")] 33 | [:title {:type "text"} "Emacs Rocks!"] 34 | [:link {:rel "self" :href "http://emacsrocks.com/atom.xml"}] 35 | (->> (:episodes content) 36 | reverse 37 | (map entry))]))) 38 | 39 | -------------------------------------------------------------------------------- /src/emacs_rocks/layout.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.layout 2 | (:require [clojure.java.io :as io] 3 | [clojure.string :as str] 4 | [hiccup.page :refer [html5]] 5 | [optimus.link :as link])) 6 | 7 | (defn- serve-to-media-query-capable-browsers [tag] 8 | (list "" tag "")) 9 | 10 | (defn- serve-to-media-query-clueless-browsers [tag] 11 | (list "")) 12 | 13 | (defn render-page [page content request] 14 | (html5 {:lang "en"} 15 | [:head 16 | [:meta {:charset "utf-8"}] 17 | [:meta {:name "viewport" 18 | :content "width=device-width, initial-scale=1.0"}] 19 | (serve-to-media-query-capable-browsers 20 | [:link {:rel "stylesheet" :href (link/file-path request "/styles/responsive.css")}]) 21 | (serve-to-media-query-clueless-browsers 22 | [:link {:rel "stylesheet" :href (link/file-path request "/styles/unresponsive.css")}]) 23 | [:link {:href "/atom.xml" :rel "alternate" :title "Emacs Rocks!" :type "application/atom+xml"}] 24 | [:title (:title page)]] 25 | [:body 26 | [:script (slurp (io/resource "public/scripts/ga.js"))] 27 | [:div {:id "main"} 28 | [:a {:id "logo_link" :href "/"} 29 | [:img {:src "/images/logo.png" :alt "Emacs Rocks!"}]] 30 | (:body page) 31 | [:div {:id "footer"} 32 | "This work is licensed under " [:a {:href "https://creativecommons.org/licenses/by-sa/3.0/"} 33 | "Creative Commons Attribution-ShareAlike 3.0"]]]])) 34 | -------------------------------------------------------------------------------- /resources/extending-episodes.edn: -------------------------------------------------------------------------------- 1 | [{:number "01" :date "2012-06-17" :size "97mb" :youtube "5axK-VUKJnk" :commits [{:hash "56e0b6e6" :msg "Initial commit."} 2 | {:hash "e123fabe" :msg "Tests up and running."}]} 3 | {:number "02" :date "2012-06-17" :size "129mb" :youtube "Zxt-c_N82_w" :commits [{:hash "b4a79c4e" :msg "Implemented toggle-deffered."}]} 4 | {:number "03" :date "2012-06-20" :size "205mb" :youtube "Dgcx5blog6s" :commits [{:hash "f3fe2dd2" :msg "Support hipster quotes."} 5 | {:hash "e83c3192" :msg "Create a buster minor-mode."}]} 6 | {:number "04" :date "2012-06-27" :size "197mb" :youtube "zI4KfUPRU5s" :commits [{:hash "6fb1cf17" :msg "Toggle without moving point."} 7 | {:hash "029eab32" :msg "Autotest."} 8 | {:hash "bf7fd38a" :msg "Remove bogus prose."}]} 9 | {:number "05" :date "2012-07-01" :size "173mb" :youtube "3-7nLXGf1Xg" :commits [{:hash "f5af5d31" :msg "Toggle focus rocket."}]} 10 | {:number "06" :date "2012-08-08" :size "159mb" :youtube "MrCSgAJJnc8" :commits [{:hash "714872b" :msg "Run tests within Emacs."}]} 11 | {:number "07" :date "2012-12-19" :size "118mb" :youtube "lcQWOSQ-PJo" :commits [{:hash "54f0681" :msg "Use compile and comint."}]} 12 | {:number "08" :date "2013-01-17" :size "90mb" :youtube "RSshC0vWoas" :commits [{:hash "ba7d8da" :msg "Clean up ansi mess."}]} 13 | ] 14 | -------------------------------------------------------------------------------- /resources/public/styles/reset.css: -------------------------------------------------------------------------------- 1 | /* 2 | * YUI CSS Reset 3 | * 4 | * Copyright (c) 2006, Yahoo! Inc. All rights reserved. 5 | * Code licensed under the BSD License: 6 | * http://developer.yahoo.net/yui/license.txt 7 | * version: 0.12.1 8 | */ 9 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td { 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | table { 15 | border-collapse: collapse; 16 | border-spacing: 0; 17 | } 18 | 19 | fieldset,img { 20 | border: 0; 21 | } 22 | 23 | address,caption,cite,code,dfn,em,strong,th,var { 24 | font-style: normal; 25 | font-weight: normal; 26 | } 27 | 28 | ol,ul { 29 | list-style: none; 30 | } 31 | 32 | caption,th { 33 | text-align: left; 34 | } 35 | 36 | h1,h2,h3,h4,h5,h6 { 37 | font-size: 100%; 38 | font-weight: normal; 39 | } 40 | 41 | q:before,q:after { 42 | content: ''; 43 | } 44 | 45 | abbr,acronym { 46 | border: 0; 47 | } 48 | 49 | /* 50 | Copyright (c) 2007, Yahoo! Inc. All rights reserved. 51 | Code licensed under the BSD License: 52 | http://developer.yahoo.net/yui/license.txt 53 | version: 2.4.1 54 | */ 55 | 56 | /** 57 | * Percents could work for IE, but for backCompat purposes, we are using keywords. 58 | * x-small is for IE6/7 quirks mode. 59 | */ 60 | body { 61 | font: 13px/1.231 arial,helvetica,clean,sans-serif; 62 | *font-size:small; 63 | *font:x-small; 64 | } 65 | 66 | table { 67 | font-size:inherit; 68 | font:100%; 69 | } 70 | 71 | /** 72 | * Bump up IE to get to 13px equivalent 73 | */ 74 | pre,code,kbd,samp,tt { 75 | font-family:monospace; 76 | *font-size:108%; 77 | line-height:100%; 78 | } 79 | 80 | -------------------------------------------------------------------------------- /resources/public/styles/base.css: -------------------------------------------------------------------------------- 1 | html {background: black url(../images/dark-leather.png) repeat;} 2 | 3 | #main {max-width: 560px; margin: 0 auto; padding: 20px; font-family: Palatino, Arial; font-size: 22px; color: #fff;} 4 | 5 | #logo_link {display: block; text-align: center; margin: 0 auto 20px;} 6 | 7 | a, 8 | .episode_list .numb {color: #ff9143;} 9 | .episode_list .name {display: block; margin-top: 1em; color: #ffaf91;} 10 | .episode_list a {display: block; margin: 14px auto; width: 197px; height: 113px; padding: 65px 0 0 54px; font-family: monaco, "consolas", "andale mono", "courier new"; font-size: 10px; background: url(../images/splash.png) no-repeat; color: #fff; text-decoration: none;} 11 | 12 | #welcome_text { margin: 15px 25px; text-align: center;} 13 | 14 | #download {font-size: 12px; margin: 10px 0 20px;} 15 | 16 | #episode h1 {font-size: 26px; margin-bottom: 10px; color: #ffaf91;} 17 | #episode h1 span {color: #fff; font-size: 18px;} 18 | 19 | #episode, 20 | #comments, 21 | #next {background: #000; padding: 20px; margin: 20px -20px; border-radius: 20px;} 22 | #next ul {list-style: disc; margin-left: 15px;} 23 | 24 | #commits {margin: 1em 0;} 25 | #commits a {font-family: monospace; font-size: 10px; margin-right: 10px;} 26 | #commits p {font-size: 16px; color: #777; margin-top: 5px;} 27 | 28 | #commands {font-size: 16px; margin-top: 2em;} 29 | #commands h3 {font-size: 18px; font-family: monaco, "consolas", "andale mono", "courier new";} 30 | #commands p {margin-bottom: 1em; color: #777;} 31 | 32 | #github {font-size: 12px;} 33 | 34 | #comments {padding-top: 10px; font-size: 15px;} 35 | #dsq-subscribe {display: none !important;} 36 | 37 | .special_series_heading { margin-top: 60px; text-align: center; font-size: 30px; color: #ff9143;} 38 | 39 | #feeds { padding-top: 30px; text-align: center; } 40 | 41 | #footer { margin-bottom: 200px;} 42 | 43 | .nowrap {white-space: nowrap;} 44 | 45 | .video-embed { position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden; max-width: 100%; height: auto; } 46 | .video-embed iframe, 47 | .video-embed object, 48 | .video-embed embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } 49 | 50 | #footer {font-size: 14px; text-align: center;} 51 | #footer a {white-space: nowrap;} -------------------------------------------------------------------------------- /src/emacs_rocks/page/episodes.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.page.episodes 2 | (:require [clojure.java.io :as io] 3 | [emacs-rocks.page.index :refer [episode-link]] 4 | [hiccup.core :refer [html]])) 5 | 6 | (defn render-episode-page [content episode next] 7 | (html 8 | [:div {:id "episode"} 9 | [:h1 "Emacs Rocks! " 10 | [:span.nowrap "Episode " (:number episode) ": " (:name episode)]] 11 | [:div.video-embed 12 | [:iframe {:src (str "https://www.youtube.com/embed/" (:youtube episode) "?hd=1") 13 | :frameborder "0" 14 | :allowfullscreen true}]] 15 | [:div {:id "download"} 16 | "Want to download this episode? Use youtube-dl, it's great!"] 17 | [:div {:id "commands"} 18 | (map #(get-in content [:commands (str "/" % ".html")]) 19 | (:commands episode))] 20 | [:div {:id "github"} 21 | [:a {:href "https://github.com/magnars/.emacs.d/"} 22 | "My .emacs.d on github"] "."]] 23 | [:div {:id "more"} 24 | [:div {:id "next"} 25 | "Want more?" 26 | [:ul 27 | (when next 28 | [:li [:a {:href (episode-link next)} "See episode " (:number next)]]) 29 | [:li [:a {:href "/"} "Browse all episodes"]] 30 | [:li [:a {:href "http://twitter.com/emacsrocks"} "Follow me on twitter"]] 31 | [:li "Read my blog: " [:a {:href "http://whattheemacsd.com"} "What the .emacs.d!?"]]]] 32 | [:div {:id "comments"} 33 | [:div {:id "disqus_thread"} 34 | [:script {:type "text/javascript"} 35 | "var disqus_shortname = 'emacsrocks';" 36 | "var disqus_identifier = 'episode" (:number episode) "';" 37 | "var disqus_url = 'http://emacsrocks.com" (episode-link episode) "';" 38 | "(function() {" 39 | "var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;" 40 | "dsq.src = 'https://' + disqus_shortname + '.disqus.com/embed.js';" 41 | "(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);" 42 | "})();"]]]])) 43 | 44 | 45 | (defn get-episode-page [content [episode next]] 46 | [(episode-link episode) 47 | {:title (str "Emacs Rocks! Episode " (:number episode) ": " (:name episode)) 48 | :body (render-episode-page content episode next)}]) 49 | 50 | (defn get-episode-pages [content] 51 | (->> (:episodes content) 52 | (partition-all 2 1) 53 | (map #(get-episode-page content %)) 54 | (into {}))) 55 | -------------------------------------------------------------------------------- /src/emacs_rocks/page/extending.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.page.extending 2 | (:require [emacs-rocks.page.index :refer [episode-link]] 3 | [hiccup.core :refer [html]])) 4 | 5 | (defn render-episode-page [content episode next] 6 | (html 7 | [:div {:id "episode"} 8 | [:h1 "Extending Emacs Rocks! " 9 | [:span.nowrap "Episode " (:number episode)]] 10 | [:div.video-embed 11 | [:iframe {:src (str "https://www.youtube.com/embed/" (:youtube episode) "?hd=1") 12 | :frameborder "0" 13 | :allowfullscreen true}]] 14 | [:div {:id "download"} 15 | "Want to download this episode? Use youtube-dl, it's great!"] 16 | [:div {:id "commits"} 17 | "In this episode: " 18 | (map (fn [commit] 19 | [:p [:a {:href (str "https://github.com/magnars/buster-mode/commit/" (:hash commit))} 20 | (:hash commit)] 21 | (:msg commit)]) 22 | (:commits episode))] 23 | [:div {:id "github"} 24 | [:a {:href "https://github.com/magnars/buster-mode/"} 25 | "The buster-mode project on github"] "."]] 26 | [:div {:id "more"} 27 | [:div {:id "next"} 28 | "Want more?" 29 | [:ul 30 | (when next 31 | [:li [:a {:href (episode-link next)} "See episode " (:number next) " of Extending Emacs Rocks"]]) 32 | [:li [:a {:href "/"} "Browse all episodes"]] 33 | [:li [:a {:href "http://twitter.com/emacsrocks"} "Follow me on twitter"]]]] 34 | [:div {:id "comments"} 35 | [:div {:id "disqus_thread"} 36 | [:script {:type "text/javascript"} 37 | "var disqus_shortname = 'emacsrocks';" 38 | "var disqus_identifier = 'extend-episode-" (:number episode) "';" 39 | "var disqus_url = 'http://emacsrocks.com" (episode-link episode) "';" 40 | "(function() {" 41 | "var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;" 42 | "dsq.src = 'https://' + disqus_shortname + '.disqus.com/embed.js';" 43 | "(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);" 44 | "})();"]]]])) 45 | 46 | (defn get-episode-page [content [episode next]] 47 | [(episode-link episode) 48 | {:title (str "Extending Emacs Rocks! Episode " (:number episode)) 49 | :body (render-episode-page content episode next)}]) 50 | 51 | (defn get-extending-pages [content] 52 | (->> (:extending-episodes content) 53 | (partition-all 2 1) 54 | (map #(get-episode-page content %)) 55 | (into {}))) 56 | -------------------------------------------------------------------------------- /src/emacs_rocks/page/index.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.page.index 2 | (:require [hiccup.core :refer [html]])) 3 | 4 | (defn episode-link [episode] 5 | (if (:commits episode) 6 | (str "/extend/e" (:number episode) ".html") 7 | (str "/e" (:number episode) ".html"))) 8 | 9 | (defn- render-episode [episode] 10 | (html 11 | [:li 12 | [:a {:href (episode-link episode)} 13 | "@emacsrocks " 14 | [:span.numb ";; episode " (:number episode)] 15 | [:span.name "\"" (:name episode) "\""]]])) 16 | 17 | (defn- render-extending [episode] 18 | (html 19 | [:li 20 | [:a {:href (episode-link episode)} 21 | "extending emacs " 22 | [:span.numb ";; " (:number episode)] 23 | (map (fn [commit] [:span.name (:msg commit)]) (:commits episode))]])) 24 | 25 | (defn render-index-page [content] 26 | (html 27 | [:p {:id "welcome_text"} 28 | "Yes, emacs does rock. And here are some episodes to prove it. " 29 | "Follow me on " 30 | [:a {:href "http://twitter.com/emacsrocks"} "@emacsrocks"] 31 | [:span.nowrap " for more."]] 32 | [:div.episode_list 33 | [:ul 34 | (map render-episode (reverse (:episodes content)))]] 35 | [:div {:style "clear: both;"}] 36 | [:div {:id "feeds"} 37 | [:a {:href "http://twitter.com/emacsrocks"} 38 | [:img {:src "/images/twitter.png"}]] " " 39 | [:a {:href "/atom.xml"} 40 | [:img {:src "/images/rss.png"}]]] 41 | [:h2 {:class "special_series_heading"} 42 | "Extending Emacs"] 43 | [:p 44 | "Join me and " [:a {:href "http://twitter.com/cjno"} "@cjno"] 45 | " in this special edition Emacs Rocks series where we extend emacs" 46 | " with a minor-mode for the " [:a {:href "http://busterjs.org"} "Buster.JS"] 47 | " testing framework."] 48 | [:div.episode_list 49 | [:ul 50 | (map render-extending (:extending-episodes content))]] 51 | [:div {:style "clear: both;"}] 52 | [:h2 {:class "special_series_heading"} 53 | "What the .emacs.d!?"] 54 | [:p 55 | "I've also got a blog about setting up your .emacs.d. In the spirit " 56 | "of Emacs Rocks! all the posts are very short and instantly useful. " 57 | [:a {:href "http://whattheemacsd.com"} 58 | [:span.nowrap "Check it out here"]]] 59 | [:div {:style "clear: both;"}] 60 | [:h2 {:class "special_series_heading"} 61 | "Parens of the Dead"] 62 | [:p 63 | "Want to see Emacs Rocks in action? I made a new video series, making a game 64 | with Clojure. And Emacs doth rock in it. " 65 | [:a {:href "http://parens-of-the-dead.com"} 66 | [:span.nowrap "Watch Parens of the Dead"]]] 67 | [:div {:style "clear: both;"}] 68 | [:div {:id "footer"}])) 69 | -------------------------------------------------------------------------------- /src/emacs_rocks/web.clj: -------------------------------------------------------------------------------- 1 | (ns emacs-rocks.web 2 | (:require [emacs-rocks.content :refer [load-content]] 3 | [emacs-rocks.layout :refer [render-page]] 4 | [emacs-rocks.pages :refer [create-pages]] 5 | [emacs-rocks.rss :as rss] 6 | [net.cgrand.enlive-html :refer [sniptest]] 7 | [optimus.assets :as assets] 8 | [optimus.assets.load-css :refer [external-url?]] 9 | [optimus.export] 10 | [optimus.link :as link] 11 | [optimus.optimizations :as optimizations] 12 | [optimus.prime :as optimus] 13 | [optimus.strategies :refer [serve-live-assets]] 14 | [prone.middleware :refer [wrap-exceptions]] 15 | [ring.middleware.content-type :refer [wrap-content-type]] 16 | [stasis.core :as stasis])) 17 | 18 | (defn get-assets [] 19 | (assets/load-assets "public" ["/styles/responsive.css" 20 | "/styles/unresponsive.css" 21 | "/favicon.ico" 22 | #"/images/.+\..+"])) 23 | 24 | (defn- optimize-path-fn [request] 25 | (fn [src] 26 | (if (or (external-url? src) 27 | (= "/atom.xml" src)) 28 | src 29 | (or (not-empty (link/file-path request src)) 30 | (throw (Exception. (str "Asset not loaded: " src))))))) 31 | 32 | (defn- use-optimized-assets [html request] 33 | (sniptest html [:img] #(update-in % [:attrs :src] (optimize-path-fn request)))) 34 | 35 | (defn- prepare-page [page content request] 36 | (-> page 37 | (render-page content request) 38 | (use-optimized-assets request))) 39 | 40 | (defn update-vals [m f] 41 | (into {} (for [[k v] m] [k (f v)]))) 42 | 43 | (defn get-pages [] 44 | (let [content (load-content)] 45 | (-> content 46 | create-pages 47 | (update-vals #(partial prepare-page % content)) 48 | (merge {"/atom.xml" (rss/atom-xml content)}) 49 | ))) 50 | 51 | (def optimize optimizations/all) 52 | 53 | (defn create-app [] 54 | (-> (stasis/serve-pages get-pages) 55 | wrap-exceptions 56 | (optimus/wrap get-assets optimize serve-live-assets) 57 | wrap-content-type)) 58 | 59 | (def export-directory "./build") 60 | 61 | (defn- load-export-dir [] 62 | (stasis/slurp-directory export-directory #"\.[^.]+$")) 63 | 64 | (defn export [] 65 | (let [assets (optimize (get-assets) {}) 66 | old-files (load-export-dir)] 67 | (stasis/empty-directory! export-directory) 68 | (optimus.export/save-assets assets export-directory) 69 | (stasis/export-pages (get-pages) export-directory {:optimus-assets assets}) 70 | (println) 71 | (println "Export complete:") 72 | (stasis/report-differences old-files (load-export-dir)) 73 | (println))) 74 | -------------------------------------------------------------------------------- /resources/episodes.edn: -------------------------------------------------------------------------------- 1 | [{:number "01" :name "From var to this" :date "2011-10-21" :size "21mb" :duration 167 :youtube "O0UgY-DmFbU" :commands ["string-rectangle" "isearch-forward"]} 2 | {:number "02" :name "A vimgolf eagle" :date "2011-10-21" :size "30mb" :duration 195 :youtube "dE2haYu0co8" :commands ["kmacro-start-macro" "digit-argument"]} 3 | {:number "03" :name "A vimgolf albatross" :date "2011-10-22" :size "14mb" :duration 135 :youtube "ePIHUfFz8-c" :commands ["transpose-lines" "transpose-chars" "kmacro-start-macro" "digit-argument"]} 4 | {:number "04" :name "A rebind controversy" :date "2011-10-24" :size "14mb" :duration 152 :youtube "OA0AjzBgWU4" :commands ["iy-go-to-char" "downcase-word" "transpose-words"]} 5 | {:number "05" :name "Macros in style" :date "2011-10-26" :size "12mb" :duration 92 :youtube "o1jJS_aibPA" :commands ["forward-list" "kmacro-start-macro"]} 6 | {:number "06" :name "Yeah! Snippets!" :date "2011-11-02" :size "8mb" :duration 101 :youtube "1W66B3CHaUo" :commands ["yas-expand"]} 7 | {:number "07" :name "Mind. Exploded." :date "2011-11-09" :size "14mb" :duration 90 :youtube "NXTf8_Arl1w" :commands ["key-chord-define-global"]} 8 | {:number "08" :name "mark-multiple" :date "2011-11-22" :size "12mb" :duration 90 :youtube "r2o9HYi7DOY" :commands ["mark-next-like-this" "rename-sgml-tag" "inline-string-rectangle"]} 9 | {:number "09" :name "expand-region" :date "2012-01-25" :size "16mb" :duration 160 :youtube "_RvHz3vJ3kA" :commands ["expand-region"]} 10 | {:number "10" :name "Jumping around" :date "2012-04-10" :size "23mb" :duration 128 :youtube "UZkpmegySnc" :commands ["ace-jump-mode" "ido-imenu"]} 11 | {:number "11" :name "swank-js" :date "2012-07-04" :size "42mb" :duration 275 :youtube "qwtVtcQQfqc" :commands ["slime-js-send-defun" "slime-js-jack-in-node"]} 12 | {:number "12" :name "Working with HTML" :date "2012-09-20" :size "27mb" :duration 114 :youtube "sBhQ2NIcrLQ" :commands ["query-replace" "cleanup-buffer" "isearch-forward" "expand-region" "rename-sgml-tag" "sgml-delete-tag" "describe-key" "eval-last-sexp" "flush-lines" "sgml-close-tag" "repeat" "zencoding-expand-line"]} 13 | {:number "13" :name "multiple-cursors" :date "2012-09-24" :size "35mb" :duration 236 :youtube "jNa3axo40qM" :commands ["mc-mark-next-like-this" "yank-rectangle" "set-rectangular-region-anchor" "dired-toggle-read-only" "mc-mark-all-like-this" "eval-and-replace" "kmacro-name-last-macro" "insert-kbd-macro"]} 14 | {:number "14" :name "Paredit" :date "2013-03-21" :size "41mb" :duration 219 :youtube "D6h5dFyyUX0" :commands []} 15 | {:number "15" :name "restclient-mode" :date "2014-11-17" :size "5mb" :duration 144 :youtube "fTvQTMOGJaw" :commands []} 16 | {:number "16" :name "dired" :date "2017-09-16" :size "5mb" :duration 95 :youtube "8l4YVttibiI" :commands ["dired-toggle-read-only"]} 17 | {:number "17" :name "magit" :date "2017-09-28" :size "5mb" :duration 159 :youtube "rzQEIRRJ2T0" :commands []} 18 | ] 19 | --------------------------------------------------------------------------------