diff --git a/project.clj b/project.clj index a0da04f..42b4412 100644 --- a/project.clj +++ b/project.clj @@ -4,6 +4,7 @@ [org.clojure/core.cache "0.6.4"] [hiccup "1.0.5"] [zeus "0.1.0"] + [garden "1.2.5"] [org.pegdown/pegdown "1.4.2"] [iokv "0.1.1"] [cheshire "5.3.1"] diff --git a/resources/public/js/main.js b/resources/public/js/publishing.js similarity index 98% rename from resources/public/js/main.js rename to resources/public/js/publishing.js index 5a8d18d..e3819c1 100644 --- a/resources/public/js/main.js +++ b/resources/public/js/publishing.js @@ -11,7 +11,7 @@ function md2html(input){ return marked(input); } -function onLoad() { +function onLoad () { $note = $("note"); $action = $("action"); $preview = $("preview"); diff --git a/resources/public/js/themes.js b/resources/public/js/themes.js deleted file mode 100644 index 8fe81ea..0000000 --- a/resources/public/js/themes.js +++ /dev/null @@ -1,113 +0,0 @@ -var $ = function(id){ return document.getElementById(id); } -var show = function(elem) { elem.style.display = "block" } - -var themes = { - "dark": { - background: { - normal: '#333', - halftone: '#444' - }, - foreground: { - normal: '#ccc', - halftone: '#bbb' - }, - link: { - fresh: '#6b8', - visited: '#496', - hover: '#7c9' - } - }, - "solarized-light": { - background: { - normal: '#fdf6e3', - halftone: '#eee8d5' - }, - foreground: { - normal: '#657b83', - halftone: '#839496' - }, - link: { - fresh: '#b58900', - visited: '#cb4b16', - hover: '#dc322f' - } - }, - "solarized-dark": { - background: { - normal: '#073642', - halftone: '#002b36' - }, - foreground: { - normal: '#93a1a1', - halftone: '#839191' - }, - link: { - fresh: '#cb4b16', - visited: '#b58900', - hover: '#dc322f' - } - }, - "default": { - background: { - normal: '#fff', - halftone: '#efefef' - }, - foreground: { - normal: '#333', - halftone: '#888' - }, - link: { - fresh: '#097', - visited: '#054', - hover: '#0a8' - } - } -}; - - -var ui = { theme: "default" }; -if (location.search.length > 0) { - location.search.slice(1).split("&").reduce(function(acc, e){ - var p = e.split("="); - acc[p[0]] = p[1]; - return acc - }, ui); -} - -var vars = { - '@background': themes[ui.theme].background.normal, - '@background_halftone': themes[ui.theme].background.halftone, - '@foreground': themes[ui.theme].foreground.normal, - '@foreground_halftone': themes[ui.theme].foreground.halftone, - '@link_fresh': themes[ui.theme].link.fresh, - '@link_visited': themes[ui.theme].link.visited, - '@link_hover': themes[ui.theme].link.hover -}; - -var fontURL = "https://fonts.googleapis.com/" + - "css?family=PT+Serif:700|Noticia+Text:700%s" + - "&subset=latin,cyrillic", - injection = ""; - -if(ui["header-font"] || ui["text-font"]) { - injection = ["header-font", "text-font"].reduce(function(acc, font){ - if(ui[font]) { - vars["@" + font.replace(/-/, "_")] = ui[font].replace(/\+/g," "); - return acc + "|" + ui[font]; - } else return acc; - }, ""); -} - -if(ui["text-size"]) vars["@text_size_factor"] = ui["text-size"]; -if(ui["header-size"]) vars["@header_size_factor"] = ui["header-size"]; - -fontURL = fontURL.replace(/%s/, injection); -var fileref = document.createElement("link") -fileref.setAttribute("rel", "stylesheet") -fileref.setAttribute("type", "text/css") -fileref.setAttribute("href", fontURL) -document.getElementsByTagName("head")[0].appendChild(fileref) - -function onLoad () { - if(ui.theme != "default" || Object.keys(ui).length > 1) less.modifyVars(vars); -} diff --git a/resources/public/styles/main.less b/resources/public/styles/main.less deleted file mode 100644 index 6a5d8af..0000000 --- a/resources/public/styles/main.less +++ /dev/null @@ -1,214 +0,0 @@ -// variables - -@background: #fff; -@foreground: #333; -@background_halftone: #efefef; -@foreground_halftone: #888; -@link_fresh: #097; -@link_visited: #054; -@link_hover: #0a8; - -@width: 800px; - -@header_font: 'Noticia Text'; -@text_font: 'Georgia'; - -@header_size_factor: 1; -@text_size_factor: 1; - -// mixins -.helvetica { - font-weight: 300; - font-family: 'Helvetica Neue','Helvetica','Arial','Lucida Grande','sans-serif'; -} - -.central-element { - @media screen and (min-width: 1024px) { - width: @width; - } - @media screen and (max-width: 1023px) { - width: 90%; - } - margin-left: auto; - margin-right: auto; -} -.thin-border { - border: 1px solid @foreground; -} -// end mixins - -.ui-border { - border-radius: 3px; - .thin-border; -} -a { - color: @link_fresh; - text-decoration: none; - border-bottom: 1px dotted; -} -a:hover { - color: @link_hover; -} -a:visited { - color: @link_visited; -} -#draft { - margin-bottom: 3em; -} -.button { - cursor: pointer; -} -.ui-elem { - .helvetica; - border-radius: 3px; - .thin-border; - padding: 0.3em; - opacity: 0.8; - font-size: 1em; - background: @background; -} -.landing-button, textarea, fieldset { - border: none; -} -.landing-button { - box-shadow: 0 2px 5px #aaa; - text-decoration: none; - font-size: 1.5em; - background: #0a2; - border-radius: 10px; - padding: 10px; - .helvetica; -} -.landing-button:hover { - background: #0b2; -} -#footer { - .helvetica; - width: 100%; - font-size: 0.8em; - padding-bottom: 1em; - text-align: center; - @media screen and (max-width: 767px) { - font-size: 0.4em; - } -} -#footer a { - border: none; -} -html, body { - background: @background; - color: @foreground; - margin: 0; - padding: 0; -} -#hero { - padding-top: 5em; - padding-bottom: 5em; - text-align: center; -} -h1, h2, h3, h4, h5, h6 { - font-weight: bold; - font-family: @header_font,'Noticia Text','PT Serif','Georgia'; -} -h1 { - font-size: 1.8em * @header_size_factor; -} -h2 { - font-size: 1.6em * @header_size_factor; -} -h3 { - font-size: 1.4em * @header_size_factor; -} -h4 { - font-size: 1.2em * @header_size_factor; -} -h5 { - font-size: 1.1em * @header_size_factor; -} -h6 { - font-size: 1em * @header_size_factor; -} -#hero h1 { - font-size: 2.5em; -} -#hero h2 { - .helvetica; - margin: 2em; -} -article { - font-family: @text_font, 'Georgia'; - .central-element; - margin-top: 5em; - text-align: justify; - flex: 1; - -webkit-flex: 1; -} -article p { - font-size: 1.2em * @text_size_factor; - line-height: 140%; -} -article > h1:first-child { - text-align: center; - font-size: 2em * @header_size_factor; - margin: 2em; -} -.centered { - text-align: center; -} -.bottom-space { - margin-bottom: 7em; -} -code, pre { - font-family: monospace; - background: @background_halftone; - font-size: 1.2em * @text_size_factor; -} -pre { - border-radius: 3px; - padding: 0.5em; - border: 1px dotted @foreground_halftone; -} -*:focus { - outline: 0px none transparent; -} -textarea { - @media screen and (min-width: 1024px) { - width: @width; - } - border-radius: 5px; - font-family: Courier; - font-size: 1em; - height: 500px; -} -.hidden { - display: none; -} -#dashed-line { - border-bottom: 1px dashed @foreground_halftone; - margin-top: 3em; - margin-bottom: 3em; -} -table { - width: 100%; - border-collapse: collapse; -} -th { - padding: 0.3em; - background-color: @background_halftone; -} -td { - border-top: 1px dotted @foreground_halftone; - padding: 0.3em; - line-height: 2.5em; -} -.middot { - padding: 0.5em; -} - -body { - display: flex; - display: -webkit-flex; - min-height: 100vh; - flex-direction: column; - -webkit-flex-direction: column; -} diff --git a/src/notehub/css.clj b/src/notehub/css.clj new file mode 100644 index 0000000..39e0dee --- /dev/null +++ b/src/notehub/css.clj @@ -0,0 +1,271 @@ +(ns notehub.css + (:require [garden.core :refer [css]] + [garden.stylesheet :refer [at-media]] + [garden.units :as u :refer [px pt em]])) + +(def themes + { + "dark" + { + :background { + :normal "#333", + :halftone "#444" + }, + :foreground { + :normal "#ccc", + :halftone "#bbb" + }, + :link { + :fresh "#6b8", + :visited "#496", + :hover "#7c9" + } + }, + "solarized-light" + { + :background { + :normal "#fdf6e3", + :halftone "#eee8d5" + }, + :foreground { + :normal "#657b83", + :halftone "#839496" + }, + :link { + :fresh "#b58900", + :visited "#cb4b16", + :hover "#dc322f" + } + }, + "solarized-dark" + { + :background { + :normal "#073642", + :halftone "#002b36" + }, + :foreground { + :normal "#93a1a1", + :halftone "#839191" + }, + :link { + :fresh "#cb4b16", + :visited "#b58900", + :hover "#dc322f" + } + }, + "default" + { + :background { + :normal "#fff", + :halftone "#efefef" + }, + :foreground { + :normal "#333", + :halftone "#888" + }, + :link { + :fresh "#097", + :visited "#054", + :hover "#0a8" + } + } + } + ) + +(defn generate [params] + (let [theme (themes (params "theme" "default")) + ; VARIABLES + background (get-in theme [:background :normal]) + foreground (get-in theme [:foreground :normal]) + background-halftone (get-in theme [:background :halftone]) + foreground-halftone (get-in theme [:foreground :halftone]) + link-fresh (get-in theme [:link :fresh]) + link-visited (get-in theme [:link :visited]) + link-hover (get-in theme [:link :hover]) + width (px 800) + header-font (str "'" (or (params "header-font") "Noticia Text") "'") + text-font (str "'" (or (params "text-font") "Georgia") "'") + header-size-factor (Float/parseFloat (or (params "header-size") "1")) + text-size-factor (Float/parseFloat (or (params "text-size") "1")) + + ; MIXINS + helvetica { + :font-weight 300 + :font-family "'Helvetica Neue','Helvetica','Arial','Lucida Grande','sans-serif'" + } + central-element { + :margin-left "auto" + :margin-right "auto" + } + thin-border { + :border (print-str "1px solid" foreground) + }] + (css + [:.ui-border { :border-radius (px 3) } thin-border] + + [:a { + :color link-fresh + :text-decoration "none" + :border-bottom "1px dotted" + }] + [:a:hover { :color link-hover }] + [:a:visited { :color link-visited }] + [:#draft { + :margin-bottom (em 3) + }] + [:.button { + :cursor "pointer" + }] + [:.ui-elem { + :border-radius (px 3) + :padding (em 0.3) + :opacity 0.8 + :font-size (em 1) + :background background + } + helvetica + thin-border] + [:.landing-button, :textarea, :fieldset { :border "none" }] + [:.landing-button { + :box-shadow "0 2px 5px #aaa" + :text-decoration "none" + :font-size (em 1.5) + :background "#0a2" + :border-radius (px 10) + :padding (px 10) + } + helvetica] + [:.landing-button:hover { :background "#0b2" }] + + [:.helvetica helvetica] + + [:#footer { + :width "100%" + :font-size (em 0.8) + :padding-bottom (em 1) + :text-align "center" + } + helvetica] + (at-media {:screen true :max-width (px 767)} [:#footer {:font-size (em 0.4)}]) + ["#footer a" { :border "none" }] + + [:html, :body { + :background background + :color foreground + :margin 0 + :padding 0 + }] + [:#hero { + :padding-top (em 5) + :padding-bottom (em 5) + :text-align "center" + }] + [:h1, :h2, :h3, :h4, :h5, :h6 { + :font-weight "bold" + :font-family (str header-font ",'Noticia Text','PT Serif','Georgia'") + }] + [:h1 { :font-size (em (* 1.8 header-size-factor)) }] + [:h2 { :font-size (em (* 1.6 header-size-factor)) }] + [:h3 { :font-size (em (* 1.4 header-size-factor)) }] + [:h4 { :font-size (em (* 1.2 header-size-factor)) }] + [:h5 { :font-size (em (* 1.1 header-size-factor)) }] + [:h6 { :font-size (em (* 1 header-size-factor)) }] + + ["#hero h1" { :font-size (em 2.5) }] + ["#hero h2" { :margin (em 2) } helvetica ] + + [:article { + :font-family (str text-font ", 'Georgia'") + :margin-top (em 5) + :text-align "justify" + :flex 1 + :-webkit-flex 1 + } + central-element] + + (at-media {:screen true :min-width (px 1024)} [:article {:width width}]) + (at-media {:screen true :max-width (px 1023)} [:article {:width "90%"}]) + + [:.central-element central-element] + + (at-media {:screen true :min-width (px 1024)} [:central-element {:width width}]) + (at-media {:screen true :max-width (px 1023)} [:central-element {:width "90%"}]) + + ["article p" { + :font-size (em (* 1.2 text-size-factor)) + :line-height "140%" + }] + ["article > h1:first-child" { + :text-align "center" + :font-size (em (* 2 header-size-factor)) + :margin (em 2) + }] + + [:.centered { + :text-align "center" + }] + [:.bottom-space { + :margin-bottom (em 7) + }] + [:code, :pre { + :font-family "monospace" + :background background-halftone + :font-size (em (* 1.2 text-size-factor)) + }] + +[:pre { + :border-radius (px 3) + :padding (em 0.5) + :border (str "1px dotted" foreground-halftone) + }] + +["*:focus" { + :outline "0px none transparent" + }] +(at-media {:screen true :min-width (px 1024)} [:textarea {:width width}]) + +[:textarea { + :border-radius (px 5) + :font-family "Courier" + :font-size (em 1) + :height (px 500) + }] +[:.hidden { + :display "none" + }] +[:#dashed-line { + :border-bottom (str "1px dashed" foreground-halftone) + :margin-top (em 3) + :margin-bottom (em 3) + }] +[:table { + :width "100%" + :border-collapse "collapse" + }] +[:th { + :padding (em 0.3) + :background-color background-halftone + }] +[:td { + :border-top (str "1px dotted" foreground-halftone) + :padding (em 0.3) + :line-height (em 2.5) + }] +[:.middot { + :padding (em 0.5) + }] + +[:body { + :display "-webkit-flex" + }] + +[:body { + :display "flex" + :min-height "100vh" + :flex-direction "column" + :-webkit-flex-direction "column" + }] + + +)) +) diff --git a/src/notehub/handler.clj b/src/notehub/handler.clj index 794cf2e..219068b 100644 --- a/src/notehub/handler.clj +++ b/src/notehub/handler.clj @@ -28,7 +28,7 @@ [code] {:status code :body (let [message (get-message (keyword (str "status-" code)))] - (layout :no-js message + (layout :no-js {} message [:article [:h1 message]]))}) (defn redirect [url] @@ -51,7 +51,7 @@ (generate-string (api/version-manager api/update-note params)))) (defroutes app-routes - (GET "/api" [] (layout :no-js (get-message :api-title) + (GET "/api" [] (layout :no-js {} (get-message :api-title) [:article (md-to-html (slurp "API.md"))])) (context "/api" [] @@ -92,7 +92,8 @@ (storage/increment-note-view note-id)) (swap! page-cache cache/miss short-url (note-page (api/get-note {:noteID note-id}) - (api/url short-url)))) + (api/url short-url) + params))) (cache/lookup @page-cache short-url)))) (GET "/:short-url" [short-url] diff --git a/src/notehub/views.clj b/src/notehub/views.clj index 3d93f8a..bf8e8c9 100644 --- a/src/notehub/views.clj +++ b/src/notehub/views.clj @@ -7,6 +7,7 @@ [hiccup.element] [hiccup.util :only [escape-html]] [hiccup.page :only [include-js html5]]) + (:require [notehub.css :as css]) (:import (org.pegdown PegDownProcessor Extensions))) (def get-message (get-map "messages")) @@ -19,22 +20,27 @@ ; Creates the main html layout (defn layout - [js? title & content] + [js? style title & content] (html5 [:head [:title (print-str (get-message :name) "—" title)] [:meta {:charset "UTF-8"}] [:meta {:name "viewport" :content "width=device-width, initial-scale=1.0"}] - [:link {:rel "stylesheet/less" :type "text/css" :href "/styles/main.less"}] - (html - (include-js "//cdnjs.cloudflare.com/ajax/libs/less.js/2.1.2/less.min.js") - (include-js "/js/themes.js")) - (when (= :js js?) + [:link {:rel "stylesheet" :type "text/css" + :href + (format "https://fonts.googleapis.com/css?family=PT+Serif:700|Noticia+Text:700%s&subset=latin,cyrillic" + (reduce (fn [acc e] + (if-let [font (style e)] + (str acc "|" (sreplace font #" " "+")) + "")) "" ["text-font" "header-font"]))}] + [:style (css/generate style)] + (if (= :js js?) (html (include-js "//cdnjs.cloudflare.com/ajax/libs/marked/0.3.2/marked.min.js") (include-js "/js/md5.js") - (include-js "/js/main.js")))] - [:body {:onload "onLoad()"} content])) + (include-js "/js/publishing.js") + [:body {:onload "onLoad()"} content]) + [:body content])])) (defn- sanitize "Breakes all usages of