git.fiddlerwoaroof.com
Browse code

Modularization config w/ ubiquitous + angularjs

- Added angular module for parenscript -> js translation
- Added dependencies to the asd file
- OID Connect
- Reworked the login-checking macros
- Reworked configuration loading: now uses Shinmera's ubiquitous

- Modularizing + naming demo.lisp: new project named "whitespace"
- Will eventually migrate to its own repository

- Basic theme/cosmetic improvements

- Added angularjs frontend

fiddlerwoaroof authored on 17/09/2015 08:15:04
Showing 30 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1 @@
1
+This is the stub README.txt for the "whitespace.rss" project.
0 2
new file mode 100644
... ...
@@ -0,0 +1,78 @@
1
+(ql:quickload :parenscript)
2
+
3
+(defpackage :angular
4
+  (:use :parenscript :cl)
5
+  (:export main))
6
+(declaim (optimize (speed 0) (safety 2) (debug 2)))
7
+
8
+(in-package :angular)
9
+
10
+(defpsmacro macros (&body body)
11
+  `(lisp (progn
12
+           ,@(loop for form in body
13
+                   collect `(defpsmacro ,@form))
14
+           "")))
15
+
16
+
17
+(defmacro+ps scope-vars (&rest bindings)
18
+  `(progn ,@(loop for (n v) in bindings collect `(scope-var ,n ,v))))
19
+
20
+(defmacro+ps scope-var (name value)
21
+  `(setf ($s ,name) ,value))
22
+
23
+(defmacro+ps resource (name url params &body body)
24
+  `(var ,name
25
+        ($resource ,url ,(cons 'create params)
26
+                   ,@(loop for (name form) in body
27
+                           collect (list 'create name (cons 'create form))))))
28
+
29
+(defmacro+ps scope-function (name arguments &body body)
30
+  `(scope-var ,name (lambda ,arguments
31
+                      ,@body)))
32
+
33
+(defmacro+ps $s (&rest values) `(chain $scope ,@values))
34
+
35
+(defun build-lambda (dependencies body)
36
+  `(list ,@(mapcar #'symbol-to-js-string dependencies)
37
+         (lambda ,(loop for x in dependencies
38
+                        collect x)
39
+           ,@body
40
+           nil)))
41
+
42
+
43
+; This is just for slimv's sake
44
+(defmacro defcontroller (name dependencies &body body)
45
+  (declare (ignore name dependencies body)))
46
+
47
+(defpsmacro def-module (module-name dependencies &body body)
48
+  `(macrolet ((defcontroller (name dependencies &body body)
49
+                (let ((dependencies (cons '$scope dependencies)))
50
+                  (list 'chain ',module-name `(controller ,(symbol-to-js-string name)
51
+                                                          ,(build-lambda dependencies body))))))
52
+     (progn (var ,module-name ((@ angular module) ,(symbol-to-js-string module-name) ,dependencies))
53
+            ,@body)))
54
+
55
+(defmacro+ps select-> (selector)
56
+  `(chain document (query-selector ,selector)))
57
+
58
+(defmacro+ps select->element (selector)
59
+  `(chain angular (element (select-> ,selector))))
60
+
61
+
62
+(defmacro+ps ng-ajax (mthd endpoint data resultS &body callback)
63
+  `(chain $http
64
+          (,mthd ,endpoint ,data)
65
+          (success (lambda (,resultS)
66
+                     ,@callback))))
67
+
68
+(defun translate-file (infile outfile)
69
+  (let ((*JS-TARGET-VERSION* 1.9))
70
+    (with-open-file (o outfile :direction :output :if-exists :supersede)
71
+      (with-open-file (i infile :direction :input)
72
+        (write-line (ps-compile-file i) o)))))
73
+
74
+(defun main (args)
75
+  (in-package :angular)
76
+  (apply #'translate-file (cdr args)))
77
+
78
+
... ...
@@ -16,7 +16,8 @@
16 16
                 :lass
17 17
                 :lquery
18 18
                 :plump
19
-                :cl-who)
19
+                :cl-who
20
+                :postmodern)
20 21
   :serial t
21 22
   :components ((:file "package")
22 23
                (:file "cl-oid-connect")))
... ...
@@ -93,7 +93,7 @@
93 93
                            :parameters `(("access_token" . ,access-token))))))
94 94
 
95 95
 (defmacro string-assoc (key alist) `(assoc ,key ,alist :test #'equal))
96
-(defmacro assoc-cdr (key alist) `(cdr (assoc ,key ,alist)))
96
+(defmacro assoc-cdr (key alist &optional (test '#'eql)) `(cdr (assoc ,key ,alist :test ,test)))
97 97
 
98 98
 (defun discover-endpoints (schema discovery-doc-url &key (gat nil gat-p) (gui nil gui-p))
99 99
   (let ((discovery-document (cl-json:decode-json-from-string (drakma:http-request discovery-doc-url))))
... ...
@@ -145,12 +145,20 @@
145 145
          ,then
146 146
          ,else))))
147 147
 
148
-(defmacro require-login (&body body)
148
+(defmacro check-login (&body body)
149 149
   (alexandria:with-gensyms (session)
150 150
     `(let ((,session (context :session)))
151 151
        (if (not (eql nil (gethash :userinfo ,session)))
152
+         (progn ,@body)
152 153
          (progn
153
-           ,@body)
154
+           (setf (gethash :next-page ,session) (lack.request:request-path-info *request*))
155
+           '(401 () "Unauthorized"))))))
156
+
157
+(defmacro require-login (&body body)
158
+  (alexandria:with-gensyms (session)
159
+    `(let ((,session (context :session)))
160
+       (if (not (eql nil (gethash :userinfo ,session)))
161
+         (progn ,@body)
154 162
          (progn
155 163
            (setf (gethash :next-page ,session) (lack.request:request-path-info *request*))
156 164
            '(302 (:location "/login")))))))
... ...
@@ -205,24 +213,23 @@
205 213
                            ))))
206 214
 
207 215
 
208
-(defun oauth2-login-middleware (&key google-info facebook-info)
209
-  (let ((clack-env nil))
210
-    (lambda (app)
211
-      (in-package :cl-oid-connect)
212
-      (load-facebook-info facebook-info)
213
-      (load-goog-endpoint-schema)
214
-      (load-google-info google-info)
216
+(defun oauth2-login-middleware (&key google-info facebook-info (login-callback #'identity))
217
+  (lambda (app)
218
+    ;(in-package :cl-oid-connect)
219
+    (load-facebook-info facebook-info)
220
+    (load-goog-endpoint-schema)
221
+    (load-google-info google-info)
215 222
 
216
-      (def-route ("/login/google" (params) :app app)
217
-        (with-context-variables (session)
218
-          (let ((state (gen-state 36)))
219
-            (setf (gethash :state session) state)
220
-            (with-endpoints *goog-endpoint-schema*
221
-              (setf (gethash :endpoint-schema session) *goog-endpoint-schema*)
222
-              (multiple-value-bind (content rcode headers) (do-auth-request *goog-endpoint-schema* state)
223
-                (if (< rcode 400)
224
-                  `(302 (:location ,(cdr (assoc :location headers))))
225
-                  content))))))
223
+    (def-route ("/login/google" (params) :app app)
224
+      (with-context-variables (session)
225
+        (let ((state (gen-state 36)))
226
+          (setf (gethash :state session) state)
227
+          (with-endpoints *goog-endpoint-schema*
228
+            (setf (gethash :endpoint-schema session) *goog-endpoint-schema*)
229
+            (multiple-value-bind (content rcode headers) (do-auth-request *goog-endpoint-schema* state)
230
+              (if (< rcode 400)
231
+                `(302 (:location ,(cdr (assoc :location headers))))
232
+                content))))))
226 233
 
227 234
 
228 235
     (def-route ("/login/facebook" (params) :app app)
... ...
@@ -246,10 +253,13 @@
246 253
                          (let* ((a-t (get-access-token *goog-endpoint-schema* code))
247 254
                                 (access-token (assoc-cdr :access--token a-t)) ;; Argh
248 255
                                 (id-token (assoc-cdr :id--token a-t))
249
-                                (decoded (cljwt:decode id-token :fail-if-unsupported nil)))
250
-                           (setf (gethash :idtoken session) (get-user-info *goog-endpoint-schema* id-token))
251
-                           (setf (gethash :accesstoken session) (get-user-info *goog-endpoint-schema* access-token))
252
-                           (setf (gethash :userinfo session) (get-user-info *goog-endpoint-schema* access-token))
256
+                                (decoded (cljwt:decode id-token :fail-if-unsupported nil))
257
+                                (user-info (get-user-info *goog-endpoint-schema* access-token)))
258
+                           (setf (gethash :idtoken session) id-token)
259
+                           (setf (gethash :accesstoken session) access-token)
260
+                           (setf (gethash :userinfo session) user-info)
261
+                           (setf (gethash :app-user session)
262
+                                 (funcall login-callback user-info decoded access-token))
253 263
                            '(302 (:location "/"))
254 264
                            )))
255 265
                      '(403 '() "Out, vile imposter!"))))
... ...
@@ -262,8 +272,13 @@
262 272
           (check-state received-state
263 273
                        (with-context-variables (session)
264 274
                          (let* ((a-t (get-access-token *fbook-endpoint-schema* code))
265
-                                (id-token (assoc-cdr :access--token a-t)))
266
-                           (setf (gethash :userinfo session) (get-user-info *fbook-endpoint-schema* id-token))
275
+                                (id-token (assoc-cdr :access--token a-t))
276
+                                (user-info (get-user-info *fbook-endpoint-schema* id-token)))
277
+                           (setf (gethash :accesstoken session) a-t)
278
+                           (setf (gethash :userinfo session) user-info)
279
+                           (setf (gethash :idtoken session) id-token)
280
+                           (setf (gethash :app-user session) (funcall login-callback user-info id-token a-t))
281
+
267 282
                            '(302 (:location "/"))))
268 283
                        '(403 '() "Out, vile imposter!")))))
269 284
 
... ...
@@ -278,7 +293,7 @@
278 293
         (setf (gethash :userinfo session) nil)
279 294
         '(302 (:location "/"))))
280 295
 
281
-    app)))
296
+    app))
282 297
 
283 298
 
284 299
 (defmacro redirect-if-necessary (sessionvar &body body)
... ...
@@ -1 +1 @@
1
-Subproject commit b203d4f1b4abdc5c72582d6e6465e7eebe5fec0e
1
+Subproject commit 15a9ca8dd67e8033bd62b559ffee86606fc67e9d
... ...
@@ -5,128 +5,51 @@
5 5
 (ql:quickload :lquery)
6 6
 (ql:quickload :plump)
7 7
 (ql:quickload :postmodern)
8
+(ql:quickload :sxql)
9
+(ql:quickload :clack-middleware-postmodern)
10
+(ql:quickload :dexador)
11
+(ql:quickload :spinneret)
12
+(ql:quickload :ubiquitous)
13
+(ql:quickload :iterate)
14
+(ql:quickload :jonathan)
15
+
16
+(declaim (optimize (speed 0) (safety 2) (debug 2)))
8 17
 
9 18
 (push (cons "application" "rdf+xml") drakma:*text-content-types*)
10 19
 (push (cons "application" "rss+xml") drakma:*text-content-types*)
11 20
 (push (cons "text" "rss+xml") drakma:*text-content-types*)
12 21
 
13
-(lquery:define-lquery-list-function tag-name (nodes &rest tags)
14
-  "Manipulate elements on the basis of there tag-name.
15
-   With no arguments, return their names else return
16
-   the corresponding tags."
17
-  (if (null tags)
18
-    (map 'vector #'plump:tag-name nodes)
19
-    (apply #'vector
20
-           (loop for node across nodes
21
-                 if (find (plump:tag-name node) tags :test #'string=)
22
-                 collect node))))
22
+(defpackage whitespace.utils
23
+  (:use #:cl))
24
+(load "utils.lisp")
23 25
 
24
-(defparameter *app* (make-instance 'ningle:<app>))
26
+(defpackage :whitespace.tables
27
+  (:use #:cl #:alexandria #:postmodern #:annot.class))
28
+(load "tables.lisp")
29
+
30
+(defpackage :whitespace.rss
31
+  (:shadow "to-json")
32
+  (:use #:cl #:alexandria #:postmodern #:lquery #:cl-syntax #:cl-annot.syntax #:cl-annot.class
33
+        #:whitespace.tables #:iterate))
34
+(load "rss.lisp")
35
+
36
+(defpackage :whitespace
37
+  (:use #:cl #:whitespace.utils #:whitespace.rss #:whitespace.tables))
38
+
39
+(in-package plump-dom)
25 40
 
26
-(defclass rss-feed ()
27
-  ((feed :accessor rss-feed-feed :initarg :feed)
28
-   (channel :accessor rss-feed-channel)
29
-   (title :accessor rss-feed-title)
30
-   (link :accessor rss-feed-link)
31
-   (description :accessor rss-feed-description)
32
-   (items :accessor rss-feed-items)))
33
-
34
-(defgeneric serialize (cls))
35
-
36
-
37
-(defclass rss-item ()
38
-  ((item :accessor rss-item-item  :initarg :item)
39
-   (title :accessor rss-item-title)
40
-   (link :accessor rss-item-link)
41
-   (description-raw :accessor rss-item-description-raw)
42
-   (description :accessor rss-item-description)
43
-   (category :accessor rss-item-category)
44
-   (comments :accessor rss-item-comments)
45
-   (enclosure :accessor rss-item-enclosure)
46
-   (guid :accessor rss-item-guid)
47
-   (pub-date :accessor rss-item-pub-date)
48
-   (source :accessor rss-item-source)))
49
-
50
-(defmethod serialize ((obj rss-feed))
51
-  (postmodern:make-dao 'rss)
52
-  )
53
-
54
-(defmacro get-elements (feed &optional (filter nil))
55
-  (let ((feed-sym (gensym))
56
-        (filter-lis `(lambda (x) (and (plump-dom:element-p x) ,@(loop for x in filter
57
-                                                                      collect `(funcall ,x x))))))
58
-    `(let ((,feed-sym ,feed))
59
-       (remove-if-not ,filter-lis (plump:children ,feed-sym)))))
60
-
61
-(defmacro get-elements-by-tagname (feed tagname)
62
-  `(get-elements ,feed ((lambda (x) (string= ,tagname (plump:tag-name x))))))
63
-
64
-(defmacro extract-text (selector &optional (default ""))
65
-  `(or (lquery:$ ,selector (text) (node)) ,default))
66
-
67
-(defun make-rss-item (item)
68
-  (lquery:initialize item)
69
-  (flet ((dehtml (h) (plump:text (plump:parse h)))
70
-         (get-category-names (it) ;;; TODO: simplify this---Ask Shinmera on IRC
71
-           (if (not (equalp #() it))
72
-             (map 'vector
73
-                  (lambda (x) (plump:text (elt (plump:children x) 0)))
74
-                  it)
75
-             #())))
76
-    (let* ((result (make-instance 'rss-item :item item))
77
-           (title (extract-text "title"))
78
-           (link (extract-text "link"))
79
-           (content-encoded (lquery:$ (children) (tag-name "content:encoded")))
80
-
81
-           (description-element (if (= 0 (length content-encoded))
82
-                                  (lquery:$ (children "description"))
83
-                                  content-encoded))
84
-
85
-           (description-raw (let ((plump:*html-tags*)
86
-                                  (ss (make-string-output-stream)))
87
-                              (if (= 0 (length description-element))
88
-                                ""
89
-                                (progn
90
-                                  (plump:serialize (plump:parse (plump:text (elt description-element 0))) ss)
91
-                                  (get-output-stream-string ss)))))
92
-
93
-           (description-munged (dehtml (extract-text "description")))
94
-           (category (get-category-names (lquery:$ "category")))
95
-           ;(comments)
96
-           ;(enclosure)
97
-           (guid (extract-text "aaguid"))
98
-           (pub-date (extract-text "pubDate"))
99
-           (source (extract-text "source")))
100
-      (setf (rss-item-title result) title)
101
-      (setf (rss-item-link result) link)
102
-      (setf (rss-item-description-raw result) description-raw)
103
-      (setf (rss-item-description result) description-munged)
104
-      (setf (rss-item-category result) category)
105
-      ;(setf (rss-item-comments result) comment)
106
-      ;(setf (rss-item-enclosure result) enclosur)
107
-      (setf (rss-item-guid result) guid)
108
-      (setf (rss-item-pub-date result) pub-date)
109
-      (setf (rss-item-source result) source)
110
-      result)))
111
-
112
-(defun make-rss-feed (feed)
113
-  (lquery:initialize feed)
114
-  (let* ((result (make-instance 'rss-feed :feed feed))
115
-         (channel (lquery:$ "channel" (node)))
116
-         (title (lquery:$  "title" (text) (node)))
117
-         (link (lquery:$ "link" (text) (node)))
118
-         (description (lquery:$ "description" (text) (node)))
119
-         (items (lquery:$ "item")))
120
-    (setf (rss-feed-channel result) channel)
121
-    (setf (rss-feed-title result) title)
122
-    (setf (rss-feed-link result) link)
123
-    (setf (rss-feed-description result) description)
124
-    (setf (rss-feed-items result)
125
-          (loop for it across items
126
-                collect (make-rss-item it)))
127
-    result))
41
+(defmethod serialize-object :around ((node element))
42
+  (when (string= (tag-name node) "iframe")
43
+    (make-text-node node))
44
+  (call-next-method))
128 45
 
129 46
 
47
+(in-package :whitespace)
48
+(ubiquitous:restore :whitespace)
49
+
50
+
51
+(defparameter *app* (make-instance 'ningle:<app>))
52
+
130 53
 (cl-oid-connect:def-route ("/login" (params) :app *app*)
131 54
   (cl-who:with-html-output-to-string (s)
132 55
     (:html
... ...
@@ -139,42 +62,35 @@
139 62
           :class "login-button google"
140 63
           (:a :href "/login/google" "Google"))))))
141 64
 
142
-(defparameter *feed-urls*
143
-  #(
144
-    "http://www.reddit.com/r/lisp.rss"
145
-    "http://www.reddit.com/r/scheme.rss"
146
-    "http://www.reddit.com/r/prolog.rss"
147
-    "http://www.reddit.com/r/haskell.rss"
148
-    "http://www.reddit.com/r/talesfromtechsupport.rss"
149
-    "https://drmeister.wordpress.com/feed/rss"
150
-    ))
151 65
 
152
-(let
153
-  ((plump-parser:*tag-dispatchers* plump-parser:*xml-tags*))
154
-  (defparameter *docs* (map 'vector
155
-                            (lambda (x)
156
-                              (format t "~a~%" x)
157
-                              (unwind-protect (plump-parser:parse
158
-                                                (drakma:http-request x))))
159
-                            *feed-urls*)))
66
+(handler-bind ((warning #'sb-ext::muffle-warning))
67
+  (let* ((feed-urls (ubiquitous:value 'feed 'urls))
68
+         (plump-parser:*tag-dispatchers* plump:*xml-tags*)
69
+         (docs (map 'vector (lambda (x) (plump:parse (drakma:http-request x))) feed-urls)))
70
+    (defparameter *feeds* (map 'vector (lambda (x) (make-rss-feed x)) docs))))
160 71
 
161
-(defparameter *feeds* (map 'vector (lambda (x) (unwind-protect (make-rss-feed x))) *docs*))
72
+(defparameter *db-connection-info* (ubiquitous:value 'db 'connection 'info))
162 73
 
74
+(defmacro def-markup (name (&rest args) &body body)
75
+  `(defmacro ,name ,args
76
+     (alexandria:once-only ,args
77
+      `(cl-markup:markup
78
+         ,,@body))))
163 79
 
164 80
 (defmacro item-markup (item)
165
-  (alexandria:with-gensyms (item-s)
166
-    `(let ((,item-s ,item))
81
+  (alexandria:once-only (item)
82
+    `(let ((,item ,item))
167 83
        (cl-markup:markup
168 84
          (:li :class "link closed"
169 85
           (:section :class "link-header"
170
-           (:h4 (rss-item-title ,item-s))
86
+           (:h4 (rss-item-title ,item))
171 87
            (:p :class "link-info"
172
-            (:a :target "_blank" :href (rss-item-link ,item-s)
173
-             (:span :class "link-url" (rss-item-link ,item-s)))
174
-            (:span :class "link-date") (rss-item-pub-date ,item-s)))
88
+            (:a :target "_blank" :href (rss-item-link ,item)
89
+             (:span :class "link-url" (rss-item-link ,item)))
90
+            (:span :class "link-date") (rss-item-pub-date ,item)))
175 91
           (:section :class "link-content"
176 92
            (:div
177
-             (cl-markup:raw (rss-item-description-raw ,item-s)))))))))
93
+             (cl-markup:raw (rss-item-description-raw ,item)))))))))
178 94
 
179 95
 (defmacro feed-markup (feed-v fc-v)
180 96
   (alexandria:with-gensyms (feed fc)
... ...
@@ -186,59 +102,120 @@
186 102
            (:h2 (rss-feed-title ,feed))
187 103
            (:h3 (rss-feed-description ,feed)))
188 104
           (:ul :class "post-list"
189
-           (loop for item in (rss-feed-items ,feed)
190
-                 collect (item-markup item))))))))
191
-
192
-(defmacro feedlist-markup (feedlist-v)
193
-  (alexandria:with-gensyms (feedlist)
194
-    `(let ((,feedlist ,feedlist-v))
195
-       (cl-markup:markup*
196
-         `(:ul :class "menu"
197
-           ,@(loop for feed across ,feedlist
198
-                   count feed into feed-count
199
-                   collect
200
-                   (list :li
201
-                         (list :a
202
-                               :href (format nil "#feed-~a" feed-count)
203
-                               (rss-feed-title feed)))))))))
204
-
205
-(defmacro base-template ()
206
-  `(cl-markup:html5
207
-     (:head
208
-       (:title "My Feeds")
209
-       (:script :src "https://code.jquery.com/jquery-2.1.4.min.js" :type "text/javascript" "")
210
-       (:script :src "/static/js/fold.js" :type "text/javascript" "")
211
-       (:link :rel "stylesheet" :href "/static/css/main.css")
212
-       (:link :rel "stylesheet" :href "/static/css/content.css")
213
-       (:link :rel "stylesheet" :href "/theme/light.css"))
214
-     (:body
215
-       (:header
216
-         (:button :class "flip-button" ">")
217
-         (:h1 "What?")
218
-         )
219
-       (:section :id "content"
220
-        (:section :id "sidebar"
221
-         (cl-markup:raw (feedlist-markup *feeds*)))
222
-        (:main
223
-          (loop for feed across *feeds*
224
-                count feed into feed-count
225
-                collect
226
-                (feed-markup feed feed-count))))
227
-       (:footer))))
228
-
229
-(defun base-template-f () (base-template))
230
-
231
-;(cl-oid-connect:def-route ("/" (params) :app *app*)
232
-;  (ningle:with-context-variables (session)
233
-;    (cl-oid-connect:redirect-if-necessary session
234
-;      (cl-oid-connect:require-login
235
-;        (anaphora:sunless (gethash :counter session) (setf anaphora:it 0))
236
-;        (incf (gethash :counter session))
237
-;        (format nil "~Ath visit<br/>~a<br/><br/>"
238
-;                (gethash :counter session))))))
239
-
240
-(cl-oid-connect:def-route ("/reflect" (params) :app *app* :method :post)
241
-  (format nil "~s<hr/>" (jonathan.encode:to-json (jonathan:parse (car (elt params 0))))))
105
+           (:li :class "link closed" :ng-repeat (format nil "item in feeds.result[~d].items" (- ,fc 1))
106
+            (:section :class "link-header"
107
+             (:h4 "{{item.title}}")
108
+             (:p :class "link-info"
109
+              (:a :target "_blank" :ng-href "{{item.link}}" :class "link-url" "{{item.link}}")
110
+              (:span :class "link-date" "{{item.date}}")))
111
+            (:section :class "link-content"
112
+             (:div
113
+               "{{item.description}}")))
114
+           ))))))
115
+
116
+(defmacro feedlist-markup (feedlist)
117
+  (alexandria:once-only (feedlist)
118
+    `(cl-markup:markup*
119
+       `(:ul :class "menu"
120
+         ,@(loop for feed across ,feedlist
121
+                 count feed into feed-count
122
+                 collect
123
+                 (list :li
124
+                       (list :a
125
+                             :href (format nil "#feed-~a" feed-count)
126
+                             (rss-feed-title feed))))))))
127
+
128
+(defun base-template-f (&optional demo)
129
+  (cl-markup:xhtml5
130
+    (:head
131
+      (:title "Whitespace")
132
+      (:script :src "https://code.jquery.com/jquery-2.1.4.min.js" :type "text/javascript" "")
133
+      (:script :src "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.5/angular.js" :type "text/javascript" "")
134
+      (:script :src "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.5/angular-resource.js" :type "text/javascript" "")
135
+      (:script :src "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.5/angular-sanitize.js" :type "text/javascript" "")
136
+      (:script :src "/static/js/fold.js" :type "text/javascript" "")
137
+      (:script :src "/static/js/whitespace-angular.js" :type "text/javascript" "")
138
+      (:link :rel "stylesheet" :href "/static/css/reset.css")
139
+      (:link :rel "stylesheet" :href "/static/css/baseline_post.css")
140
+      (:link :rel "stylesheet" :href "/static/css/formalize.css")
141
+      (:link :rel "stylesheet" :href "/static/css/main.css")
142
+      (:link :rel "stylesheet" :href "/static/css/content.css")
143
+      (:link :rel "stylesheet" :href "/theme/light.css")
144
+      (:link :rel "icon" :href "/static/images/Whitespace_favicon.png" :type "image/x-icon")
145
+      (:link :rel "shortcut icon" :href "/static/images/Whitespace_favicon.png" :type "image/x-icon"))
146
+
147
+    (:body :ng-app "whitespace" :ng-controller "MainCtrl"
148
+     (:header
149
+       (:button :class "flip-button" "…")
150
+       (:h1 "Whitespace")
151
+       )
152
+     (:section :id "content"
153
+      (:section :id "sidebar"
154
+       (cl-markup:raw (feedlist-markup *feeds*)))
155
+      (:main
156
+        (cl-markup:raw
157
+          (unless demo
158
+            (cl-markup:markup
159
+              (:form :action "/feeds/add" :name "add-form" :id "add-form" :method "post"
160
+               (:input :type "text" :name "url" :class "urltext" "")
161
+               (:input :type "hidden" :name "api" :value "no" "")
162
+               (:button :type "submit" :class "fsub" "+")))))
163
+        (:img :ng-class "{spinner: true, hide: feeds.result !== undefined}" :src "/static/images/spinner.gif" "")
164
+        (:section :ng-class "{feed: true, closed: !feed.closed}" :ng-repeat "feed in feeds.result"
165
+         (:section :class "feed-header" :ng-click "toggleClosed(feed)"
166
+          (:h2 "{{ feed.title }}")
167
+          (:h3 "{{ feed.description }}"))
168
+         (:ul :class "post-list"
169
+          (:li :ng-class "{link: true, closed: !item.closed}" :ng-repeat "item in feed.items"
170
+           (:section :class "link-header" :ng-click "toggleClosed(item)"
171
+            (:h4 "{{item.title}}")
172
+            (:p :class "link-info"
173
+             (:a :target "_blank" :ng-href "{{item.link}}" :class "link-url" "{{item.link}}")
174
+             (:span :class "link-date" "{{item.date}}")))
175
+           (:section :class "link-content"
176
+            (:div :ng-bind-html "renderHtml(item.description)" "")))))))
177
+      (:footer))))
178
+
179
+(defmacro defun-from-value (name value)
180
+  `(setf (symbol-function ',name) ,value))
181
+
182
+(defun-from-value jsonapi-encoder
183
+                  (jonathan.helper:compile-encoder () (success result)
184
+                    (list :|success| success
185
+                          :|result| result)))
186
+
187
+(defmacro neither (&rest forms) `(not (or ,@forms)))
188
+(defmacro neither-null (&rest forms)
189
+  `(neither ,@(loop for form in forms collecting `(null ,form))))
190
+
191
+; ; ;  Ultimately, this will only serialize the feed if the client
192
+(cl-oid-connect:def-route ("/feeds/add" (params) :method :post :app *app*)
193
+  (ningle.context:with-context-variables (session)
194
+    (cl-oid-connect:require-login
195
+      (let ((user-info (car (gethash :app-user session)))
196
+            (result '(302 (:location "/")))
197
+            (api (string= (cl-oid-connect:assoc-cdr "api" params) "yes"))) 
198
+        (when (neither-null params user-info)
199
+          (handler-case
200
+            (let* ((url (cl-oid-connect:assoc-cdr "url" params 'string=))
201
+                   (feed (drakma:http-request url))
202
+                   (plump-parser:*tag-dispatchers* plump-parser:*xml-tags*)
203
+                   (doc (plump:parse feed)))
204
+              (store-feed doc (slot-value user-info 'id))
205
+              (when api
206
+                (setf result `(200 (:Content-Type "application/json") ,(jsonapi-encoder t "success")))))
207
+            (cl-postgres-error:unique-violation ()
208
+                                                (when api
209
+                                                  (setf result
210
+                                                        `(400 () ,(jsonapi-encoder nil "Feed already saved")))))))
211
+        result))))
212
+;;; TODO: add needs to return the new content, so that angular can append it
213
+
214
+(cl-oid-connect:def-route ("/feeds/json" (params) :app *app*)
215
+  (ningle.context:with-context-variables (session)
216
+    (let* ((user-info (gethash :app-user session))
217
+           (*feeds* (if user-info (deserialize user-info) *feeds*)))
218
+      `(200 (:content-type "application/json" :cache-control "private, max-age=300") ,(jsonapi-encoder t *feeds*)))))
242 219
 
243 220
 (cl-oid-connect:def-route ("/feeds/:feeds/html" (params) :app *app*)
244 221
   (ningle.context:with-context-variables (session)
... ...
@@ -251,13 +228,50 @@
251 228
                                                           collect (elt *feeds* x)))))
252 229
         (base-template-f)))))
253 230
 
231
+(defun login-callback (userinfo &rest args)
232
+  (declare (ignore args))
233
+  (postmodern:with-transaction ()
234
+    (let* ((received-id (anaphora:aif (cl-oid-connect:assoc-cdr :id userinfo)
235
+                          anaphora:it
236
+                          (cl-oid-connect:assoc-cdr :sub userinfo)))
237
+           (db-user (postmodern:query-dao 'reader_user (:select :* :from 'reader_user
238
+                                                        :where (:= :foreign-id received-id)))))
239
+      (if (not (null db-user))
240
+        db-user
241
+        (progn
242
+          (let ((name (cl-oid-connect:assoc-cdr :name userinfo))
243
+                (first-name (anaphora:aif (cl-oid-connect:assoc-cdr :first--name userinfo)
244
+                              anaphora:it
245
+                              (cl-oid-connect:assoc-cdr :given--name userinfo)))
246
+                (last-name (anaphora:aif (cl-oid-connect:assoc-cdr :last--name userinfo)
247
+                             anaphora:it
248
+                             (cl-oid-connect:assoc-cdr :family--name userinfo)))
249
+                (email (cl-oid-connect:assoc-cdr :email userinfo))
250
+                (gender (cl-oid-connect:assoc-cdr :gender userinfo))
251
+                (link (anaphora:aif (cl-oid-connect:assoc-cdr :link userinfo)
252
+                        anaphora:it
253
+                        (cl-oid-connect:assoc-cdr :profile userinfo)))
254
+                (locale (cl-oid-connect:assoc-cdr :locale userinfo)))
255
+            (postmodern:make-dao 'reader_user
256
+                                 :foreign-id received-id
257
+                                 :first-name first-name
258
+                                 :last-name last-name
259
+                                 :name name
260
+                                 :email email
261
+                                 :gender gender
262
+                                 :link link
263
+                                 :locale locale)))))))
264
+
265
+(cl-oid-connect:def-route ("/demo" (params) :app *app*)
266
+  (base-template-f t))
267
+
254 268
 (cl-oid-connect:def-route ("/" (params) :app *app*)
255
-  (ningle.context:with-context-variables (session)
256
-      ;(cl-oid-connect:require-login
257
-        (cl-oid-connect:redirect-if-necessary session
258
-          (let ((*feeds* (gethash :feeds session *feeds*)))
259
-          (base-template-f)));)
260
-  ))
269
+  (ningle:with-context-variables (session)
270
+    (cl-oid-connect:require-login
271
+      (cl-oid-connect:redirect-if-necessary session
272
+        (let* ((user-info (gethash :app-user session))
273
+               (*feeds* (deserialize user-info)))
274
+          (base-template-f))))))
261 275
 
262 276
 ;;; this will be bound by calls to with-palette
263 277
 ;;; probably should be refactored out
... ...
@@ -265,25 +279,18 @@
265 279
 
266 280
 (defparameter *colorscheme* (make-instance 'colors:colorscheme))
267 281
 
268
-(cl-oid-connect:def-route ("/theme/dark.css" (params) :app *app*)
269
-  (colors:let-palette (make-instance 'colors:palette)
270
-    (eval '(get-theme-css))))
271
-
272
-(cl-oid-connect:def-route ("/theme/light.css" (params) :app *app*)
273
-  (colors:let-palette (colors:invert-palette (make-instance 'colors:palette))
274
-    (eval '(get-theme-css))))
275
-
276
-
277
-
278 282
 (defun get-theme-css ()
279 283
   (colors:with-palette (*palette*)
280 284
     (flet ((combine-unit-q (quant unit) (format nil "~d~a" quant unit)))
281 285
       (let* ((header-height 9)
286
+             (main-right-margin (* 0.618 (- 100 header-height)))
282 287
              (height-units "vh")
283 288
              (ss (lass:compile-and-write
284 289
                    `(* :color ,(colors:colorscheme-fg *colorscheme*))
285 290
 
286
-                   `(body :background-color ,(colors:colorscheme-bg *colorscheme*))
291
+                   `(body
292
+                      :transition "background-color 0.25s ease"
293
+                      :background-color ,(colors:colorscheme-bg *colorscheme*))
287 294
 
288 295
                    `((:or h1 h2 h3)
289 296
                      :color ,(colors:colorscheme-fg-highlight *colorscheme*))
... ...
@@ -298,33 +305,28 @@
298 305
                       :font-size ,(combine-unit-q (* 0.75 header-height) height-units)
299 306
                       :line-height ,(combine-unit-q header-height height-units)
300 307
                       (.flip-button
301
-                        :float right
302
-                        :width "3em"
303
-                        :height "3em"
304
-                        :padding-left "1em"
305
-                        :padding-bottom "1em"
306
-                        :border-bottom-left-radius "100%"
307
-                        :border none
308
-                        :transition "all 2s cubic-bezier(0.175, 0.885, 0.32, 1.275)"
309 308
                         :background-color ,(colors:colorscheme-fg *colorscheme*)
310 309
                         :color ,(colors:colorscheme-bg *colorscheme*))
311 310
                       ((:and .flip-button :focus)
312 311
                        :outline none)
313 312
                       ((:and .flip-button :hover)
314
-                       :width "6em"
315
-                       :height "6em"
316
-                       :padding-left "4em"
317
-                       :padding-bottom "3em")
318
-                      )
313
+                       :font-size ,(combine-unit-q (* 0.25 header-height) height-units)))
319 314
 
320 315
                    `(main
321 316
                       :border-left thin solid ,(colors:colorscheme-accent *colorscheme*)
322
-                      :height ,(combine-unit-q (- 100 header-height) height-units))
317
+                      :height ,(combine-unit-q (- 100 header-height) height-units)
318
+                      (.add-form
319
+                        ((:or input button)
320
+                          :background-color ,(colors:colorscheme-fg *colorscheme*)
321
+                          :color ,(colors:colorscheme-bg *colorscheme*))
322
+                        )
323
+                      )
323 324
 
324 325
                    `((:or a (:and a :visited) (:and a :active) code.url)
325 326
                      :color ,(colors:colorscheme-fg-highlight *colorscheme*))
326 327
 
327 328
                    `(section#sidebar
329
+                      :transition opacity "0.5s" ease
328 330
                       (ul.menu
329 331
                         ((li + li)
330 332
                          :border-top "thin" "solid" ,(colors:colorscheme-fg-highlight *colorscheme*))
... ...
@@ -350,7 +352,7 @@
350 352
                         ((:and a :hover)
351 353
                          :color ,(colors:colorscheme-fg *colorscheme*))
352 354
                         ))
353
-                   
355
+
354 356
                    `((:and .feed-header :hover)
355 357
                      :background-color ,(colors:colorscheme-hover-highlight *colorscheme*))
356 358
 
... ...
@@ -361,27 +363,39 @@
361 363
                      :background-color ,(colors:colorscheme-hover-highlight *colorscheme*)))))
362 364
         `(200 (:content-type "text/css") ,ss)))))
363 365
 
366
+(cl-oid-connect:def-route ("/theme/dark.css" (params) :app *app*)
367
+  (colors:let-palette (make-instance 'colors:palette)
368
+    (eval '(get-theme-css))))
369
+
370
+(cl-oid-connect:def-route ("/theme/light.css" (params) :app *app*)
371
+  (colors:let-palette (colors:invert-palette (make-instance 'colors:palette))
372
+    (eval '(get-theme-css))))
373
+
364 374
 (defvar *handler* nil)
365 375
 
366 376
 (defun stop ()
367 377
   (clack:stop (pop *handler*)))
368 378
 
379
+(ql:quickload :clack-middleware-postmodern)
380
+
369 381
 (defun start (&optional tmp)
370 382
   (let ((server (if (> (length tmp) 1)
371 383
                   (intern (string-upcase (elt tmp 1)) 'keyword)
372 384
                   :hunchentoot)))
373
-   (push (clack:clackup
374
-          (lack.builder:builder
375
-            :backtrace
376
-            :session
377
-            ;:csrf
378
-            (:static :path "/static/" :root #p"./static/")
379
-            (funcall
380
-              (cl-oid-connect:oauth2-login-middleware
381
-                :facebook-info (truename "~/github_repos/cl-oid-connect/facebook-secrets.json")
382
-                :google-info (truename "~/github_repos/cl-oid-connect/google-secrets.json"))
383
-              *app*)) :port 9090 :server server)
384
-        *handler*)))
385
-
386
-
385
+    (push (clack:clackup
386
+            (lack.builder:builder
387
+              :backtrace
388
+              :session
389
+              ;:csrf
390
+              (lambda (app) (lambda (env)
391
+                              (postmodern:with-connection *db-connection-info*
392
+                                (funcall app env))))
393
+              (:static :path "/static/" :root #p"./static/")
394
+              (funcall
395
+                (cl-oid-connect:oauth2-login-middleware
396
+                  :facebook-info (truename "~/github_repos/cl-oid-connect/facebook-secrets.json")
397
+                  :google-info (truename "~/github_repos/cl-oid-connect/google-secrets.json")
398
+                  :login-callback #'login-callback)
399
+                *app*)) :port 9090 :server server)
400
+          *handler*)))
387 401
 
... ...
@@ -11,7 +11,6 @@
11 11
   :padding "0px")
12 12
 
13 13
 (body
14
-  :transition "background-color 0.25s ease"
15 14
   :font-family "Lato")
16 15
 
17 16
 (((:or main "#sidebar" .feed) > (:or ul ol))
... ...
@@ -52,7 +51,9 @@
52 51
   (ul.menu
53 52
     :text-align right
54 53
     :font-variant small-caps
54
+    :opacity 0
55 55
     (li
56
+      :transition "all 0.25s ease"
56 57
       (a
57 58
         :color inherit
58 59
         :display block
... ...
@@ -60,16 +61,29 @@
60 61
         :font-weight 700
61 62
         :padding "0.5em"
62 63
         :text-decoration none
63
-        :transition "background-color 0.25s ease"
64 64
         :width "100%"
65
-        ))))
65
+        )))
66
+  (ul.menu.open
67
+    :opacity 1))
66 68
 
67 69
 (main
68 70
   :width "62vw"
69 71
   :float "right"
70 72
   :clear "right"
71 73
   :overflow-x "hidden"
72
-  :overflow-y "scroll")
74
+  :overflow-y "scroll"
75
+  :position "relative")
76
+
77
+(img.spinner
78
+  :position "absolute"
79
+  :top "50%"
80
+  :left "50%"
81
+  :transform "translate(-50%,-50%)"
82
+  )
83
+
84
+(.hide
85
+  :display none
86
+  )
73 87
 
74 88
 (.feed-header
75 89
   ((:or h2 h3) :padding "0.62em")
... ...
@@ -114,14 +128,25 @@
114 128
    :display block
115 129
    :clear both)
116 130
   (.link-content
131
+    :overflow hidden
132
+    
133
+    (img
134
+      :margin "1em" "0%" "1em" "10%"
135
+      :max-width "80%"
136
+      :max-height "70vh"
137
+      )
117 138
     ((> div)
118
-     :padding "1em")
139
+     :padding "1em"
140
+     :font-size "12pt")
119 141
     :transition "max-height 0.5s ease"))
120 142
 
121 143
 (.feed.closed
144
+  :border-bottom-width thin
122 145
   (.post-list 
123 146
     :max-height "0px"
124
-    :padding "0em"))
147
+    :padding "0em")
148
+  (h3
149
+    :display none))
125 150
 
126 151
 ((:and .feed :first-child)
127 152
  :border-top none)
... ...
@@ -131,3 +156,41 @@
131 156
   (.post-list
132 157
     :transition "max-height 0.5s ease"))
133 158
 
159
+("#add-form"
160
+  :font-size "1em"
161
+  :height "1.8em"
162
+  :display block
163
+  :overflow hidden
164
+  :width "100%"
165
+
166
+  (input
167
+    :border "none"
168
+    :width "95%"
169
+    :height "100%"
170
+    :font-size "0.8em"
171
+    :line-height "1.8em"
172
+    :padding "1em" "0.5em"
173
+    )
174
+  (button
175
+    :border none
176
+    :border-radius 0px
177
+    :font-size inherit
178
+    :width "5%" 
179
+    :height "100%"
180
+    ))
181
+
182
+
183
+(.flip-button
184
+  :position absolute
185
+  :right "0em"
186
+  :top "0em"
187
+  :z-index 1000
188
+  :width "3em"
189
+  :height "3em"
190
+  :padding-left "1em"
191
+  :padding-bottom "1em"
192
+  :border-bottom-left-radius "100%"
193
+  :border none
194
+  :transition "all 2s cubic-bezier(0.175, 0.885, 0.32, 1.275)"
195
+  :background-image none
196
+  )
... ...
@@ -22,5 +22,7 @@
22 22
     #:require-login
23 23
     #:oauth2-login-middleware
24 24
     #:with-session
25
+    #:assoc-cdr
25 26
     #:session ; private!!
26 27
     ))
28
+
27 29
new file mode 100644
... ...
@@ -0,0 +1,15 @@
1
+(in-package :angular)
2
+
3
+
4
+(def-module whitespace '(ng-resource ng-sanitize)
5
+  (defcontroller -Main-Ctrl ($http $resource $sce)
6
+    (resource feeds "feeds/json" nil)
7
+    (scope-var feeds (chain feeds (get)))
8
+    (scope-var data "hello world!")
9
+    (scope-function render-html (html-code)
10
+      (chain $sce (trust-as-html html-code)))
11
+    (scope-function toggle-closed (ent)
12
+      (setf (@ ent closed) (not (@ ent closed))))
13
+    ))
14
+
15
+; vim: ft=lisp
... ...
@@ -1,42 +1,42 @@
1
-(chain ($ document)
2
-       (ready
3
-         (lambda ()
4
-           (chain ($ ".link-header")
5
-                  (click
6
-                    (lambda ()
7
-                      (chain ($ this)
8
-                             (siblings ".link-content")
9
-                             (each (lambda ()
10
-                                     (if (= (chain ($ this) (css "max-height")) "0px")
11
-                                       (let ((added-height (chain ($ this) (children) (outer-height)))
12
-                                             (parent-height (chain ($ this)
13
-                                                                   (parents ".post-list")
14
-                                                                   (css "max-height"))))
15
-                                         (chain ($ this) (css "max-height" added-height))
16
-                                         (chain ($ this)
17
-                                                (parents ".post-list")
18
-                                                (css "max-height" (+ added-height parent-height))))
19
-                                       (chain ($ this) (css "max-height" "0px"))))))
20
-                      (chain ($ this) (parent) (toggle-class "closed")))))
21
-           (chain ($ ".feed-header")
22
-                  (click
23
-                    (lambda ()
24
-                      (chain ($ this)
25
-                             (siblings ".post-list")
26
-                             (each (lambda ()
27
-                                     (if (= (chain ($ this) (css "max-height")) "0px")
28
-                                       (chain ($ this) (css "max-height" (@ this scroll-height)))
29
-                                       (chain ($ this) (css "max-height" "0px"))))))
30
-                      (chain ($ this) (parent) (toggle-class "closed")))))
31
-           (setf invert-palette
32
-                 (lambda ()
33
-                   (let* ((style-sheet (chain ($ "link[href^=\"/theme\"]")))
34
-                         (style-sheet-name (chain style-sheet (attr "href"))))
35
-                     (chain style-sheet (attr "href"
36
-                                              (if (chain style-sheet-name (match (regex |dark|)))
37
-                                                     (chain style-sheet-name
38
-                                                            (replace (regex |dark|) "light"))
39
-                                                     (chain style-sheet-name
40
-                                                            (replace (regex |light|) "dark"))))))))
41
-           (chain ($ ".flip-button") (click invert-palette))
42
-           null)))
1
+(in-package :ps_translator)
2
+
3
+(macros 
4
+  (getOffset (el) `($ (,el) (offset) top)))
5
+
6
+(def-event document ready ()
7
+  (def-event ".link-header, .feed-header" click ()
8
+    ($ (".menu") (add-class "open")) t)
9
+
10
+  (def-event ".link-header" click ()
11
+
12
+    ($each (this (siblings ".link-content"))
13
+      (if (= ($this (css "max-height")) "0px")
14
+
15
+        (let ((added-height ($this (children) (outer-height)))
16
+              (parent-height ($this (parents ".post-list") (css "max-height"))))
17
+          ($this (css "max-height" added-height))
18
+
19
+          ($this (parents ".post-list")
20
+                 (css "max-height" (+ added-height parent-height))))
21
+
22
+        ($this (css "max-height" "0px"))))
23
+    ($this (parent) (toggle-class "closed")))
24
+
25
+  (def-event ".feed-header" click ()
26
+    ($each (this (siblings ".post-list"))
27
+      (if (= ($this (css "max-height")) "0px")
28
+        ($this (css "max-height" (@ this scroll-height)))
29
+        ($this (css "max-height" "0px"))))
30
+    ($this (parent) (toggle-class "closed")))
31
+
32
+  (def-event ".flip-button" click ()
33
+    (let* ((style-sheet ($ ("link[href^=\"/theme\"]")))
34
+           (style-sheet-name (chain style-sheet (attr "href"))))
35
+      (chain style-sheet
36
+             (attr "href"
37
+                   (if (chain style-sheet-name (match (regex |dark|)))
38
+                     (chain style-sheet-name (replace (regex |dark|) "light"))
39
+                     (chain style-sheet-name (replace (regex |light|) "dark")))))))
40
+  nil)
41
+
42
+; vim: set ft=lisp :
43 43
new file mode 100644
... ...
@@ -0,0 +1,228 @@
1
+(in-package :whitespace.rss)
2
+(cl-annot.syntax:enable-annot-syntax)
3
+
4
+
5
+@export
6
+(defmacro default-when (default test &body body)
7
+  (once-only (default)
8
+    `(or (when ,test
9
+           ,@body)
10
+         ,default)))
11
+
12
+@export-class
13
+(defclass rss-feed ()
14
+  ((feed :accessor rss-feed-feed :initarg :feed)
15
+   (channel :accessor rss-feed-channel :initarg :channel)
16
+   (title :accessor rss-feed-title :initarg :title)
17
+   (link :accessor rss-feed-link :initarg :link)
18
+   (description :accessor rss-feed-description :initarg :description)
19
+   (items :accessor rss-feed-items :initarg :items)))
20
+
21
+@export-class
22
+(defclass rss-item ()
23
+  ((item :accessor rss-item-item  :initarg :item)
24
+   (title :accessor rss-item-title :initarg :title)
25
+   (link :accessor rss-item-link :initarg :link)
26
+   (description-raw :accessor rss-item-description-raw :initarg :description-raw)
27
+   (description :accessor rss-item-description :initarg :description)
28
+   (category :accessor rss-item-category :initarg :category)
29
+   (comments :accessor rss-item-comments :initarg :comments)
30
+   (enclosure :accessor rss-item-enclosure :initarg :enclosure)
31
+   (guid :accessor rss-item-guid :initarg :guid)
32
+   (pub-date :accessor rss-item-pub-date :initarg :pub-date)
33
+   (source :accessor rss-item-source  :initarg :source)))
34
+
35
+(load "tables.lisp")
36
+
37
+(setf (symbol-function 'rss-item-encoder)
38
+      (jonathan.helper:compile-encoder () (title link description category comments enclosure guid
39
+                                                 pub-date source)
40
+        (list :|title| title :|link| link :|description| description
41
+              :|category| category :|comments| comments :|enclosure| enclosure
42
+              :|guid| guid :|pub-date| pub-date :|source| source)))
43
+
44
+(setf (symbol-function 'rss-feed-encoder)
45
+      (jonathan.helper:compile-encoder () (title link description items)
46
+        (list :|title| title :|link| link :|description| description :|items| items)))
47
+
48
+(defmethod jonathan:%to-json ((obj rss-feed))
49
+  (jonathan:with-object
50
+    (jonathan:write-key-value "title" (coerce (rss-feed-title obj) 'simple-string))
51
+    (jonathan:write-key-value "link" (coerce (rss-feed-link obj) 'simple-string))
52
+    (jonathan:write-key-value "description" (coerce (rss-feed-description obj) 'simple-string))
53
+    (jonathan:write-key-value "items" (rss-feed-items obj))))
54
+
55
+(defmethod jonathan:%to-json ((obj rss-item))
56
+  (jonathan:with-object
57
+    (jonathan:write-key-value "title" (coerce (rss-item-title obj) 'simple-string))
58
+    (jonathan:write-key-value "link" (coerce (rss-item-link obj) 'simple-string))
59
+    (jonathan:write-key-value "description" (coerce (rss-item-description-raw obj) 'simple-string))
60
+    ;(jonathan:write-key-value "category" (rss-item-category obj))
61
+    (jonathan:write-key-value "comments" (coerce (rss-item-comments obj) 'simple-string))
62
+    (jonathan:write-key-value "enclosure" "rss-item-enclosure obj")
63
+    (jonathan:write-key-value "guid" (coerce (rss-item-guid obj) 'simple-string))
64
+    (jonathan:write-key-value "date" (coerce (rss-item-pub-date obj) 'simple-string))
65
+    (jonathan:write-key-value "source" (coerce (rss-item-source obj) 'simple-string))))
66
+
67
+@export
68
+(defgeneric serialize (cls &rest links)
69
+  (:method ((obj list) &rest ignored)
70
+   (declare (ignore ignored))
71
+   (loop for item in obj
72
+         collect (serialize item)))
73
+
74
+  (:method ((obj vector) &rest ignored)
75
+   (declare (ignore ignored))
76
+   (loop for item across obj
77
+         collect (serialize item)))
78
+
79
+  (:method ((obj rss-feed) &rest ignored)
80
+   (declare (ignore ignored))
81
+   (let ((feed (postmodern:make-dao
82
+                 'rss_feed_store
83
+                 :title (rss-feed-title obj) :link (rss-feed-link obj) :description (rss-feed-description obj))))
84
+     (format t "~a~%" (rfs-link feed))
85
+     (loop for item in (rss-feed-items obj)
86
+           collect (serialize item (rfs-id feed)))
87
+     feed))
88
+
89
+  (:method ((obj rss-item) &rest links)
90
+   (let ((feed (car links)))
91
+     (format t "~a~%" feed)
92
+     (postmodern:make-dao 'rss_item_store
93
+                          :title (rss-item-title obj)
94
+                          :link (rss-item-link obj)
95
+                          :description (rss-item-description-raw obj)
96
+                          :guid (rss-item-guid obj) :pub-date (rss-item-pub-date obj)
97
+                          :source (rss-item-source obj)
98
+                          :feed feed))))
99
+
100
+@export
101
+(defmacro copy-slots (slots from-v to-v)
102
+   (with-gensyms (from to)
103
+     `(let ((,from ,from-v) (,to ,to-v))
104
+        ,@(loop for (fro-slot to-slot)
105
+                in (mapcar (lambda (x) (if (symbolp x) (list x x) x)) slots)
106
+                collect `(setf (slot-value ,to ',to-slot) (slot-value ,from ',fro-slot))))))
107
+
108
+
109
+@export
110
+(defun deserialize-item (item)
111
+  (let ((result (make-instance 'rss-item)))
112
+    (copy-slots (title link (description description-raw) comments enclosure guid pub-date source)
113
+                item
114
+                result)
115
+    result))
116
+
117
+@export
118
+(defun deserialize-items (feed-id)
119
+  (let ((items (postmodern:query-dao 'rss_item_store
120
+                                     (:select :* :from 'rss_item_store :where (:= :feed feed-id)))))
121
+    (loop for item in items collect (deserialize-item item))))
122
+
123
+@export
124
+(defun deserialize-feed (feed)
125
+  (let ((result (make-instance 'rss-feed)))
126
+    (copy-slots (title link description) feed result)
127
+    (setf (rss-feed-items result) (deserialize-items (rfs-id feed)))
128
+    result))
129
+
130
+@export
131
+(defun deserialize (&optional user-info)
132
+  (default-when #() (not (emptyp user-info))
133
+    (let ((feeds
134
+            (postmodern:query-dao 'rss_feed_store
135
+                                  (:select 'rssfeed.*
136
+                                   :from 'rssfeed
137
+                                   :inner-join  'subscriptions :on (:= 'rssfeed.id  'subscriptions.feedid)
138
+                                   :inner-join  'reader_user :on (:= 'reader_user.id  'subscriptions.uid)
139
+                                   :where (:= 'reader_user.foreign_id (user-foreign-id (car user-info)))))))
140
+      (apply #'vector (loop for feed in feeds collect (deserialize-feed feed))))))
141
+
142
+
143
+@export
144
+(defmacro get-elements (feed &optional (filter nil))
145
+  (let ((feed-sym (gensym))
146
+        (filter-lis `(lambda (x) (and (plump-dom:element-p x) ,@(loop for x in filter
147
+                                                                      collect `(funcall ,x x))))))
148
+    `(let ((,feed-sym ,feed))
149
+       (remove-if-not ,filter-lis (plump:children ,feed-sym)))))
150
+
151
+@export
152
+(defmacro get-elements-by-tagname (feed tagname)
153
+  `(get-elements ,feed ((lambda (x) (string= ,tagname (plump:tag-name x))))))
154
+
155
+@export
156
+(defmacro extract-text (selector &optional (default ""))
157
+  `(or (lquery:$ ,selector (text) (node)) ,default))
158
+
159
+@export
160
+(defun normalize-html (html)
161
+  (let ((plump-parser:*tag-dispatchers* plump:*html-tags*))
162
+    (with-output-to-string (ss)
163
+      (map 'vector
164
+           (lambda (x) (plump:serialize (plump:parse (plump:text x)) ss))
165
+           html)
166
+      ss)))
167
+
168
+@export
169
+(defmacro xml-text-bind (syms &body body)
170
+  "Bind the symbols passed in the second arg to the text of the matching
171
+   elements in the document lquery has been initialized with and then run the
172
+   body in the resulting lexical scope.  This assumes that lquery:initialize
173
+   has already been passed the proper xml document"
174
+  `(let* ,(loop for sym in syms
175
+           collect `(,sym (or (lquery:$ ,(symbol-name sym) (text) (node)) "")))
176
+     ,@body))
177
+
178
+@export
179
+(defun make-rss-item (item)
180
+  (lquery:initialize item)
181
+  (flet ((dehtml (h) (plump:text (plump:parse h)))
182
+         (get-category-names (it) ;;; TODO: simplify this---Ask Shinmera on IRC
183
+           (if (not (equalp #() it))
184
+             (map 'vector
185
+                  (lambda (x) (plump:text (elt (plump:children x) 0)))
186
+                  it)
187
+             #())))
188
+    (let* ((content-encoded (lquery:$ (children) (tag-name "content:encoded")))
189
+
190
+           (description-element (default-when content-encoded (emptyp content-encoded)
191
+                                  (lquery:$ (children "description"))))
192
+
193
+           (description-raw (normalize-html
194
+                              (default-when description-element (emptyp description-element)
195
+                                (extract-text "description"))))
196
+
197
+           (description-munged (dehtml description-raw))
198
+           (category (get-category-names (lquery:$ "category"))))
199
+           ;(enclosure) --- TODO: implement comment / enclosure handling
200
+           
201
+      (xml-text-bind (title link guid pub-date source comments)
202
+        (make-instance 'rss-item :item item
203
+                       :title title :link link :description-raw description-raw :description description-munged
204
+                       :category category :guid guid :pub-date pub-date :source source :comments comments)))))
205
+      ;(setf (rss-item-enclosure result) enclosure)      -- TODO: comment/enclosure . . .
206
+
207
+@export
208
+(defun make-rss-feed (feed)
209
+  (lquery:initialize feed)
210
+  (let* ((channel (lquery:$ "channel" (node)))
211
+         (link (extract-text "link"))
212
+         (link (if (string= link "") (lquery:$ "channel" (children) (tag-name "atom:link") ()) link))
213
+         (items (lquery:$ "item")))
214
+    (xml-text-bind (title description)
215
+      (make-instance 'rss-feed :feed feed
216
+                     :title title :link link :description description
217
+                     :channel channel :items (loop for it across items
218
+                                                   collect (make-rss-item it))))))
219
+@export
220
+(defun store-feed (doc uid)
221
+  (postmodern:with-transaction ()
222
+    (let* ((rss-feed- (make-rss-feed doc))
223
+           (feedid (anaphora:aif (postmodern:query (:select 'id :from 'rssfeed
224
+                                                    :where (:= 'link (rss-feed-link rss-feed-))))
225
+                     (caar anaphora:it) ;; The postmodern query returns a nested list
226
+                     (slot-value (serialize rss-feed-) 'id))))
227
+      (postmodern:query
228
+        (:insert-into 'subscriptions :set 'uid uid 'feedid feedid)))))
0 229
new file mode 100644
... ...
@@ -0,0 +1,84 @@
1
+/*
2
+Baseline - a designer framework
3
+Copyright (C) 2009 Stephane Curzi, ProjetUrbain.com
4
+Creative Commons Attribution-Share Alike 3.0 License
5
+version 0.5.2
6
+*/
7
+
8
+li.link > section.link-content > div {
9
+
10
+  /******************** Base ********************/
11
+  h1, h2, h3, h4, h5, h6							{ line-height: 1.2; }
12
+  h4, h5, h6										{ font-weight: bold; }
13
+  b, strong, caption, th, thead, dt, legend		{ font-weight: bold; }
14
+  cite, dfn, em, i								{ font-style: italic; }
15
+  code, kbd, samp, pre, tt, var					{ font-family: mono-space, monospace; }
16
+  h1, h2, h3, h4, h5, h6							{ word-spacing: -0.125em; }
17
+  p												{ word-spacing: 0.125em; hyphenate: auto; hyphenate-lines: 3; }
18
+  p+p												{ text-indent: 1.5em; }
19
+  p+p.no-indent									{ text-indent: 0; }
20
+  pre												{ white-space: pre; }
21
+  del												{ text-decoration: line-through; }
22
+  mark											{ background: rgba(255, 255, 0, 0.4); padding: 0 .25em; }
23
+  ins												{ color: #f00; }
24
+  small, sup, sub									{ font-size: 80%; }
25
+  big												{ font-size: 125%; line-height: 80%; }
26
+  abbr, acronym									{ font-size: 85%; text-transform: uppercase; letter-spacing: .1em; }
27
+  abbr[title], acronym[title], dfn[title]			{ border-bottom: 1px dotted black; cursor: help; }
28
+  sup, sub										{ line-height: 0; }
29
+  sup												{ vertical-align: super; }
30
+  sub												{ vertical-align: sub; }
31
+  blockquote										{ padding: 1.5em; }
32
+  hr												{ border: none; background: #ddd; width: 100%; }
33
+  ul, ol											{ margin-left: 1.5em; }
34
+  ul												{ list-style: disc outside; }
35
+  ol												{ list-style: decimal outside; }
36
+  input, select, button							{ cursor: pointer; }
37
+  table											{ font: inherit; width: 100%; }
38
+
39
+  /* html 5 */
40
+  article, aside, header, hgroup,
41
+  nav, figure, section, footer					{ display: block; }
42
+
43
+  /* Debug */
44
+  .debug											{ outline: solid gold 1px; }
45
+  .debug-background								{ background: rgba(255, 215, 0, 0.2) !important; }/*
46
+  Baseline - a designer framework
47
+  Copyright (C) 2009 Stephane Curzi, ProjetUrbain.com
48
+  Creative Commons Attribution-Share Alike 3.0 License
49
+  version 0.5
50
+                                                                                             */
51
+
52
+/******************** Baseline grid: 13/18px ********************/
53
+body								{ font-size: 75%; line-height: 1.5; /*12/18*/ }
54
+
55
+h1, h2, h3, h4, h5, h6				{ position: relative; }
56
+h1, h2								{ line-height: 36px; margin-bottom: 18px; }
57
+h1, h2, h3, h4						{ margin-top: 18px; }
58
+h3, h4, h5, h6						{ line-height: 18px; }
59
+h1									{ font-size: 36px; top: 5px; }
60
+h2									{ font-size: 28px; top: 8px; }
61
+h3									{ font-size: 22px; top: 1px; }
62
+h4									{ font-size: 18px; top: 2px; }
63
+h5									{ font-size: 15px; top: 4px; }
64
+h6									{ font-size: 13px; top: 5px; }
65
+h1:first-child,
66
+h2:first-child,
67
+h3:first-child,
68
+h4:first-child						{ margin-top: 0; }
69
+
70
+p, pre, address						{ font-size: 13px; line-height: 18px; position: relative; top: 5px; }
71
+small								{ font-size: 11px; }
72
+abbr, code, kbd,
73
+samp, small, var					{ line-height: 15px; }
74
+ul, ol, dl, dialog					{ font-size: 13px; line-height: 18px; position: relative; top: 5px; margin-top: 18px; margin-bottom: 18px; }
75
+li ul, li ol, ul ul, ol ol			{ top: 0; margin-top: 0; margin-bottom: 0; }
76
+li h1, li h2, li h3,
77
+li h4, li h5, li h6,
78
+li p								{ top: 0; }
79
+form, legend, label					{ font-size: 13px; line-height: 18px; }
80
+legend								{ position: relative; top: 5px; }
81
+table								{ font-size: 13px; }
82
+caption								{ font-size: 13px; line-height: 18px; position: relative; }
83
+hr									{ position: relative; height: 4px; margin: 18px 0 14px 0; }
84
+}
0 85
new file mode 100644
... ...
@@ -0,0 +1,165 @@
1
+/*
2
+Baseline - a designer framework
3
+Copyright (C) 2009 Stephane Curzi, ProjetUrbain.com
4
+Creative Commons Attribution-Share Alike 3.0 License
5
+version 0.5.2
6
+*/
7
+li.link > section.link-content > div {
8
+  /******************** Base ********************/
9
+  /* html 5 */
10
+  /* Debug */
11
+                 								                                                  /*
12
+Baseline - a designer framework
13
+Copyright (C) 2009 Stephane Curzi, ProjetUrbain.com
14
+Creative Commons Attribution-Share Alike 3.0 License
15
+version 0.5
16
+                                                                                           */
17
+  /******************** Baseline grid: 13/18px ********************/ }
18
+  li.link > section.link-content > div h1, li.link > section.link-content > div h2, li.link > section.link-content > div h3, li.link > section.link-content > div h4, li.link > section.link-content > div h5, li.link > section.link-content > div h6 {
19
+    line-height: 1.2; }
20
+  li.link > section.link-content > div h4, li.link > section.link-content > div h5, li.link > section.link-content > div h6 {
21
+    font-weight: bold; }
22
+  li.link > section.link-content > div b, li.link > section.link-content > div strong, li.link > section.link-content > div caption, li.link > section.link-content > div th, li.link > section.link-content > div thead, li.link > section.link-content > div dt, li.link > section.link-content > div legend {
23
+    font-weight: bold; }
24
+  li.link > section.link-content > div cite, li.link > section.link-content > div dfn, li.link > section.link-content > div em, li.link > section.link-content > div i {
25
+    font-style: italic; }
26
+  li.link > section.link-content > div code, li.link > section.link-content > div kbd, li.link > section.link-content > div samp, li.link > section.link-content > div pre, li.link > section.link-content > div tt, li.link > section.link-content > div var {
27
+    font-family: mono-space, monospace; }
28
+  li.link > section.link-content > div h1, li.link > section.link-content > div h2, li.link > section.link-content > div h3, li.link > section.link-content > div h4, li.link > section.link-content > div h5, li.link > section.link-content > div h6 {
29
+    word-spacing: -0.125em; }
30
+  li.link > section.link-content > div p {
31
+    word-spacing: 0.125em;
32
+    hyphenate: auto;
33
+    hyphenate-lines: 3; }
34
+  li.link > section.link-content > div p + p {
35
+    text-indent: 1.5em; }
36
+  li.link > section.link-content > div p + p.no-indent {
37
+    text-indent: 0; }
38
+  li.link > section.link-content > div pre {
39
+    white-space: pre; }
40
+  li.link > section.link-content > div del {
41
+    text-decoration: line-through; }
42
+  li.link > section.link-content > div mark {
43
+    background: rgba(255, 255, 0, 0.4);
44
+    padding: 0 .25em; }
45
+  li.link > section.link-content > div ins {
46
+    color: #f00; }
47
+  li.link > section.link-content > div small, li.link > section.link-content > div sup, li.link > section.link-content > div sub {
48
+    font-size: 80%; }
49
+  li.link > section.link-content > div big {
50
+    font-size: 125%;
51
+    line-height: 80%; }
52
+  li.link > section.link-content > div abbr, li.link > section.link-content > div acronym {
53
+    font-size: 85%;
54
+    text-transform: uppercase;
55
+    letter-spacing: .1em; }
56
+  li.link > section.link-content > div abbr[title], li.link > section.link-content > div acronym[title], li.link > section.link-content > div dfn[title] {
57
+    border-bottom: 1px dotted black;
58
+    cursor: help; }
59
+  li.link > section.link-content > div sup, li.link > section.link-content > div sub {
60
+    line-height: 0; }
61
+  li.link > section.link-content > div sup {
62
+    vertical-align: super; }
63
+  li.link > section.link-content > div sub {
64
+    vertical-align: sub; }
65
+  li.link > section.link-content > div blockquote {
66
+    padding: 1.5em; }
67
+  li.link > section.link-content > div hr {
68
+    border: none;
69
+    background: #ddd;
70
+    width: 100%; }
71
+  li.link > section.link-content > div ul, li.link > section.link-content > div ol {
72
+    margin-left: 1.5em; }
73
+  li.link > section.link-content > div ul {
74
+    list-style: disc outside; }
75
+  li.link > section.link-content > div ol {
76
+    list-style: decimal outside; }
77
+  li.link > section.link-content > div input, li.link > section.link-content > div select, li.link > section.link-content > div button {
78
+    cursor: pointer; }
79
+  li.link > section.link-content > div table {
80
+    font: inherit;
81
+    width: 100%; }
82
+  li.link > section.link-content > div article, li.link > section.link-content > div aside, li.link > section.link-content > div header, li.link > section.link-content > div hgroup,
83
+  li.link > section.link-content > div nav, li.link > section.link-content > div figure, li.link > section.link-content > div section, li.link > section.link-content > div footer {
84
+    display: block; }
85
+  li.link > section.link-content > div .debug {
86
+    outline: solid gold 1px; }
87
+  li.link > section.link-content > div .debug-background {
88
+    background: rgba(255, 215, 0, 0.2) !important; }
89
+  li.link > section.link-content > div body {
90
+    font-size: 75%;
91
+    line-height: 1.5;
92
+    /*12/18*/ }
93
+  li.link > section.link-content > div h1, li.link > section.link-content > div h2, li.link > section.link-content > div h3, li.link > section.link-content > div h4, li.link > section.link-content > div h5, li.link > section.link-content > div h6 {
94
+    position: relative; }
95
+  li.link > section.link-content > div h1, li.link > section.link-content > div h2 {
96
+    line-height: 36px;
97
+    margin-bottom: 18px; }
98
+  li.link > section.link-content > div h1, li.link > section.link-content > div h2, li.link > section.link-content > div h3, li.link > section.link-content > div h4 {
99
+    margin-top: 18px; }
100
+  li.link > section.link-content > div h3, li.link > section.link-content > div h4, li.link > section.link-content > div h5, li.link > section.link-content > div h6 {
101
+    line-height: 18px; }
102
+  li.link > section.link-content > div h1 {
103
+    font-size: 36px;
104
+    top: 5px; }
105
+  li.link > section.link-content > div h2 {
106
+    font-size: 28px;
107
+    top: 8px; }
108
+  li.link > section.link-content > div h3 {
109
+    font-size: 22px;
110
+    top: 1px; }
111
+  li.link > section.link-content > div h4 {
112
+    font-size: 18px;
113
+    top: 2px; }
114
+  li.link > section.link-content > div h5 {
115
+    font-size: 15px;
116
+    top: 4px; }
117
+  li.link > section.link-content > div h6 {
118
+    font-size: 13px;
119
+    top: 5px; }
120
+  li.link > section.link-content > div h1:first-child,
121
+  li.link > section.link-content > div h2:first-child,
122
+  li.link > section.link-content > div h3:first-child,
123
+  li.link > section.link-content > div h4:first-child {
124
+    margin-top: 0; }
125
+  li.link > section.link-content > div p, li.link > section.link-content > div pre, li.link > section.link-content > div address {
126
+    font-size: 13px;
127
+    line-height: 18px;
128
+    position: relative;
129
+    top: 5px; }
130
+  li.link > section.link-content > div small {
131
+    font-size: 11px; }
132
+  li.link > section.link-content > div abbr, li.link > section.link-content > div code, li.link > section.link-content > div kbd,
133
+  li.link > section.link-content > div samp, li.link > section.link-content > div small, li.link > section.link-content > div var {
134
+    line-height: 15px; }
135
+  li.link > section.link-content > div ul, li.link > section.link-content > div ol, li.link > section.link-content > div dl, li.link > section.link-content > div dialog {
136
+    font-size: 13px;
137
+    line-height: 18px;
138
+    position: relative;
139
+    top: 5px;
140
+    margin-top: 18px;
141
+    margin-bottom: 18px; }
142
+  li.link > section.link-content > div li ul, li.link > section.link-content > div li ol, li.link > section.link-content > div ul ul, li.link > section.link-content > div ol ol {
143
+    top: 0;
144
+    margin-top: 0;
145
+    margin-bottom: 0; }
146
+  li.link > section.link-content > div li h1, li.link > section.link-content > div li h2, li.link > section.link-content > div li h3,
147
+  li.link > section.link-content > div li h4, li.link > section.link-content > div li h5, li.link > section.link-content > div li h6,
148
+  li.link > section.link-content > div li p {
149
+    top: 0; }
150
+  li.link > section.link-content > div form, li.link > section.link-content > div legend, li.link > section.link-content > div label {
151
+    font-size: 13px;
152
+    line-height: 18px; }
153
+  li.link > section.link-content > div legend {
154
+    position: relative;
155
+    top: 5px; }
156
+  li.link > section.link-content > div table {
157
+    font-size: 13px; }
158
+  li.link > section.link-content > div caption {
159
+    font-size: 13px;
160
+    line-height: 18px;
161
+    position: relative; }
162
+  li.link > section.link-content > div hr {
163
+    position: relative;
164
+    height: 4px;
165
+    margin: 18px 0 14px 0; }
0 166
new file mode 100644
... ...
@@ -0,0 +1,654 @@
1
+a,
2
+abbr,
3
+acronym,
4
+address,
5
+applet,
6
+article,
7
+aside,
8
+audio,
9
+b,
10
+big,
11
+blockquote,
12
+body,
13
+canvas,
14
+caption,
15
+center,
16
+cite,
17
+code,
18
+dd,
19
+del,
20
+details,
21
+dfn,
22
+dialog,
23
+div,
24
+dl,
25
+dt,
26
+em,
27
+embed,
28
+fieldset,
29
+figcaption,
30
+figure,
31
+font,
32
+footer,
33
+form,
34
+h1,
35
+h2,
36
+h3,
37
+h4,
38
+h5,
39
+h6,
40
+header,
41
+hgroup,
42
+hr,
43
+html,
44
+i,
45
+iframe,
46
+img,
47
+ins,
48
+kbd,
49
+label,
50
+legend,
51
+li,
52
+mark,
53
+menu,
54
+meter,
55
+nav,
56
+object,
57
+ol,
58
+output,
59
+p,
60
+pre,
61
+progress,
62
+q,
63
+rp,
64
+rt,
65
+ruby,
66
+s,
67
+samp,
68
+section,
69
+small,
70
+span,
71
+strike,
72
+strong,
73
+sub,
74
+summary,
75
+sup,
76
+table,
77
+tbody,
78
+td,
79
+tfoot,
80
+th,
81
+thead,
82
+time,
83
+tr,
84
+tt,
85
+u,
86
+ul,
87
+var,
88
+video,
89
+xmp {
90
+  border: 0;
91
+  margin: 0;
92
+  padding: 0;
93
+  font-size: 100%;
94
+}
95
+
96
+html,
97
+body {
98
+  height: 100%;
99
+}
100
+
101
+article,
102
+aside,
103
+details,
104
+figcaption,
105
+figure,
106
+footer,
107
+header,
108
+hgroup,
109
+menu,
110
+nav,
111
+section {
112
+  display: block;
113
+}
114
+
115
+b,
116
+strong {
117
+  font-weight: bold;
118
+}
119
+
120
+img {
121
+  color: transparent;
122
+  font-size: 0;
123
+  vertical-align: middle;
124
+  -ms-interpolation-mode: bicubic;
125
+}
126
+
127
+ul,
128
+ol {
129
+  list-style: none;
130
+}
131
+
132
+li {
133
+  display: list-item;
134
+}
135
+
136
+table {
137
+  border-collapse: collapse;
138
+  border-spacing: 0;
139
+}
140
+
141
+th,
142
+td,
143
+caption {
144
+  font-weight: normal;
145
+  vertical-align: top;
146
+  text-align: left;
147
+}
148
+
149
+q {
150
+  quotes: none;
151
+}
152
+
153
+q:before,
154
+q:after {
155
+  content: "";
156
+  content: none;
157
+}
158
+
159
+sub,
160
+sup,
161
+small {
162
+  font-size: 75%;
163
+}
164
+
165
+sub,
166
+sup {
167
+  line-height: 0;
168
+  position: relative;
169
+  vertical-align: baseline;
170
+}
171
+
172
+sub {
173
+  bottom: -0.25em;
174
+}
175
+
176
+sup {
177
+  top: -0.5em;
178
+}
179
+
180
+svg {
181
+  overflow: hidden;
182
+}
183
+
184
+body {
185
+  font: 13px/1.5 "Helvetica Neue", Arial, "Liberation Sans", FreeSans, sans-serif;
186
+}
187
+
188
+pre,
189
+code {
190
+  font-family: "DejaVu Sans Mono", Monaco, Consolas, monospace;
191
+}
192
+
193
+hr {
194
+  border: 0 #cccccc solid;
195
+  border-top-width: 1px;
196
+  clear: both;
197
+  height: 0;
198
+}
199
+
200
+h1 {
201
+  font-size: 25px;
202
+}
203
+
204
+h2 {
205
+  font-size: 23px;
206
+}
207
+
208
+h3 {
209
+  font-size: 21px;
210
+}
211
+
212
+h4 {
213
+  font-size: 19px;
214
+}
215
+
216
+h5 {
217
+  font-size: 17px;
218
+}
219
+
220
+h6 {
221
+  font-size: 15px;
222
+}
223
+
224
+ol {
225
+  list-style: decimal;
226
+}
227
+
228
+ul {
229
+  list-style: disc;
230
+}
231
+
232
+li {
233
+  margin-left: 30px;
234
+}
235
+
236
+p,
237
+dl,
238
+hr,
239
+h1,
240
+h2,
241
+h3,
242
+h4,
243
+h5,
244
+h6,
245
+ol,
246
+ul,
247
+pre,
248
+table,
249
+address,
250
+fieldset,
251
+figure {
252
+  margin-bottom: 20px;
253
+}
254
+
255
+.input_tiny {
256
+  width: 50px;
257
+}
258
+
259
+.input_small {
260
+  width: 100px;
261
+}
262
+
263
+.input_medium {
264
+  width: 150px;
265
+}
266
+
267
+.input_large {
268
+  width: 200px;
269
+}
270
+
271
+.input_xlarge {
272
+  width: 250px;
273
+}
274
+
275
+.input_xxlarge {
276
+  width: 300px;
277
+}
278
+
279
+.input_full {
280
+  width: 100%;
281
+}
282
+
283
+.input_full_wrap {
284
+  display: block;
285
+  padding-right: 8px;
286
+}
287
+
288
+input[type="search"]::-webkit-search-decoration {
289
+  display: none;
290
+}
291
+
292
+input:invalid,
293
+button:invalid,
294
+a.button:invalid,
295
+select:invalid,
296
+textarea:invalid {
297
+  -webkit-box-shadow: none;
298
+  -moz-box-shadow: none;
299
+  box-shadow: none;
300
+}
301
+input:focus,
302
+button:focus,
303
+a.button:focus,
304
+select:focus,
305
+textarea:focus {
306
+  -webkit-box-shadow: #0066ff 0 0 5px 0;
307
+  -moz-box-shadow: #0066ff 0 0 5px 0;
308
+  box-shadow: #0066ff 0 0 5px 0;
309
+  z-index: 1;
310
+}
311
+
312
+input[type="file"]:focus, input[type="file"]:active,
313
+input[type="radio"]:focus,
314
+input[type="radio"]:active,
315
+input[type="checkbox"]:focus,
316
+input[type="checkbox"]:active {
317
+  -webkit-box-shadow: none;
318
+  -moz-box-shadow: none;
319
+  box-shadow: none;
320
+}
321
+
322
+button,
323
+a.button,
324
+input[type="reset"],
325
+input[type="submit"],
326
+input[type="button"] {
327
+  -webkit-appearance: none;
328
+  -webkit-border-radius: 4px;
329
+  -moz-border-radius: 4px;
330
+  -ms-border-radius: 4px;
331
+  -o-border-radius: 4px;
332
+  border-radius: 4px;
333
+  -webkit-background-clip: padding;
334
+  -moz-background-clip: padding;
335
+  background-clip: padding-box;
336
+  background: #dddddd url('../images/button.png?1298351022') repeat-x;
337
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
338
+  background-image: -webkit-linear-gradient(#ffffff, #dddddd);
339
+  background-image: -moz-linear-gradient(#ffffff, #dddddd);
340
+  background-image: -o-linear-gradient(#ffffff, #dddddd);
341
+  background-image: -ms-linear-gradient(#ffffff, #dddddd);
342
+  background-image: linear-gradient(#ffffff, #dddddd);
343
+  border: 1px solid;
344
+  border-color: #dddddd #bbbbbb #999999;
345
+  cursor: pointer;
346
+  color: #333333;
347
+  display: inline-block;
348
+  font: bold 12px/1.3 "Helvetica Neue", Arial, "Liberation Sans", FreeSans, sans-serif;
349
+  outline: 0;
350
+  overflow: visible;
351
+  margin: 0;
352
+  padding: 3px 10px;
353
+  text-shadow: white 0 1px 1px;
354
+  text-decoration: none;
355
+  vertical-align: top;
356
+  width: auto;
357
+  *padding-top: 2px;
358
+  *padding-bottom: 0;
359
+}
360
+button:hover,
361
+a.button:hover,
362
+input[type="reset"]:hover,
363
+input[type="submit"]:hover,
364
+input[type="button"]:hover {
365
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(1px, #eeeeee), color-stop(100%, #cccccc));
366
+  background-image: -webkit-linear-gradient(#ffffff, #eeeeee 1px, #cccccc);
367
+  background-image: -moz-linear-gradient(#ffffff, #eeeeee 1px, #cccccc);
368
+  background-image: -o-linear-gradient(#ffffff, #eeeeee 1px, #cccccc);
369
+  background-image: -ms-linear-gradient(#ffffff, #eeeeee 1px, #cccccc);
370
+  background-image: linear-gradient(#ffffff, #eeeeee 1px, #cccccc);
371
+}
372
+button:active,
373
+a.button:active,
374
+input[type="reset"]:active,
375
+input[type="submit"]:active,
376
+input[type="button"]:active {
377
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(1px, #dddddd), color-stop(100%, #eeeeee));
378
+  background-image: -webkit-linear-gradient(#ffffff, #dddddd 1px, #eeeeee);
379
+  background-image: -moz-linear-gradient(#ffffff, #dddddd 1px, #eeeeee);
380
+  background-image: -o-linear-gradient(#ffffff, #dddddd 1px, #eeeeee);
381
+  background-image: -ms-linear-gradient(#ffffff, #dddddd 1px, #eeeeee);
382
+  background-image: linear-gradient(#ffffff, #dddddd 1px, #eeeeee);
383
+  -webkit-box-shadow: inset rgba(0, 0, 0, 0.25) 0 1px 2px 0;
384
+  -moz-box-shadow: inset rgba(0, 0, 0, 0.25) 0 1px 2px 0;
385
+  box-shadow: inset rgba(0, 0, 0, 0.25) 0 1px 2px 0;
386
+  border-color: #999999 #bbbbbb #dddddd;
387
+}
388
+button::-moz-focus-inner,
389
+a.button::-moz-focus-inner,
390
+input[type="reset"]::-moz-focus-inner,
391
+input[type="submit"]::-moz-focus-inner,
392
+input[type="button"]::-moz-focus-inner {
393
+  border: 0;
394
+  padding: 0;
395
+}
396
+
397
+a.button {
398
+  *padding-bottom: 3px;
399
+}
400
+
401
+button {
402
+  *padding-top: 1px;
403
+  *padding-bottom: 1px;
404
+}
405
+
406
+textarea,
407
+select,
408
+input[type="date"],
409
+input[type="datetime"],
410
+input[type="datetime-local"],
411
+input[type="email"],
412
+input[type="month"],
413
+input[type="number"],
414
+input[type="password"],
415
+input[type="search"],
416
+input[type="tel"],
417
+input[type="text"],
418
+input[type="time"],
419
+input[type="url"],
420
+input[type="week"] {
421
+  -webkit-box-sizing: border-box;
422
+  -moz-box-sizing: border-box;
423
+  box-sizing: border-box;
424
+  -webkit-background-clip: padding;
425
+  -moz-background-clip: padding;
426
+  background-clip: padding-box;
427
+  -webkit-border-radius: 0;
428
+  -moz-border-radius: 0;
429
+  -ms-border-radius: 0;
430
+  -o-border-radius: 0;
431
+  border-radius: 0;
432
+  -webkit-appearance: none;
433
+  background-color: white;
434
+  border: 1px solid;
435
+  border-color: #848484 #c1c1c1 #e1e1e1;
436
+  color: black;
437
+  outline: 0;
438
+  margin: 0;
439
+  padding: 2px 3px;
440
+  text-align: left;
441
+  font-size: 13px;
442
+  font-family: Arial, "Liberation Sans", FreeSans, sans-serif;
443
+  height: 1.8em;
444
+  vertical-align: top;
445
+  *padding-top: 2px;
446
+  *padding-bottom: 1px;
447
+  *height: auto;
448
+}
449
+textarea[disabled],
450
+select[disabled],
451
+input[type="date"][disabled],
452
+input[type="datetime"][disabled],
453
+input[type="datetime-local"][disabled],
454
+input[type="email"][disabled],
455
+input[type="month"][disabled],
456
+input[type="number"][disabled],
457
+input[type="password"][disabled],
458
+input[type="search"][disabled],
459
+input[type="tel"][disabled],
460
+input[type="text"][disabled],
461
+input[type="time"][disabled],
462
+input[type="url"][disabled],
463
+input[type="week"][disabled] {
464
+  background-color: #eeeeee;
465
+}
466
+
467
+button[disabled],
468
+input[disabled],
469
+select[disabled],
470
+select[disabled] option,
471
+select[disabled] optgroup,
472
+textarea[disabled],
473
+a.button_disabled {
474
+  -webkit-box-shadow: none;
475
+  -moz-box-shadow: none;
476
+  box-shadow: none;
477
+  -moz-user-select: -moz-none;
478
+  -webkit-user-select: none;
479
+  -khtml-user-select: none;
480
+  user-select: none;
481
+  color: #888888;
482
+  cursor: default;
483
+}
484
+
485
+input::-webkit-input-placeholder,
486
+textarea::-webkit-input-placeholder {
487
+  color: #888888;
488
+}
489
+
490
+input:-moz-placeholder,
491
+textarea:-moz-placeholder {
492
+  color: #888888;
493
+}
494
+
495
+input.placeholder_text,
496
+textarea.placeholder_text {
497
+  color: #888888;
498
+}
499
+
500
+textarea,
501
+select[size],
502
+select[multiple] {
503
+  height: auto;
504
+}
505
+
506
+select[size="0"],
507
+select[size="1"] {
508
+  height: 1.8em;
509
+  *height: auto;
510
+}
511
+
512
+@media (-webkit-min-device-pixel-ratio: 0) {
513
+  select[size],
514
+  select[multiple],
515
+  select[multiple][size] {
516
+    background-image: none;
517
+    padding-right: 3px;
518
+  }
519
+
520
+  select,
521
+  select[size="0"],
522
+  select[size="1"] {
523
+    background-image: url(data:image/png;base64,R0lGODlhDQAEAIAAAAAAAP8A/yH5BAEHAAEALAAAAAANAAQAAAILhA+hG5jMDpxvhgIAOw==);
524
+    background-repeat: no-repeat;
525
+    background-position: right center;
526
+    padding-right: 20px;
527
+  }
528
+
529
+  ::-webkit-validation-bubble-message {
530
+    -webkit-box-shadow: none;
531
+    box-shadow: none;
532
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #666666), color-stop(1, black));
533
+    border: 1px solid;
534
+    border-color: #747474 #5e5e5e #4f4f4f;
535
+    color: white;
536
+    font: 13px/17px "Lucida Grande", Arial, "Liberation Sans", FreeSans, sans-serif;
537
+    overflow: hidden;
538
+    padding: 15px 15px 17px;
539
+    text-shadow: black 0 0 1px;
540
+    height: 16px;
541
+  }
542
+
543
+  ::-webkit-validation-bubble-arrow,
544
+  ::-webkit-validation-bubble-top-outer-arrow,
545
+  ::-webkit-validation-bubble-top-inner-arrow {
546
+    -webkit-box-shadow: none;
547
+    box-shadow: none;
548
+    background: #666666;
549
+    border: 0;
550
+  }
551
+}
552
+textarea {
553
+  min-height: 40px;
554
+  overflow: auto;
555
+  resize: vertical;
556
+  width: 100%;
557
+}
558
+
559
+optgroup {
560
+  color: black;
561
+  font-style: normal;
562
+  font-weight: normal;
563
+  font-family: Arial, "Liberation Sans", FreeSans, sans-serif;
564
+}
565
+optgroup::-moz-focus-inner {
566
+  border: 0;
567
+  padding: 0;
568
+}
569
+
570
+.ie6_button,
571
+* html button,
572
+* html a.button {
573
+  background: #dddddd url('../images/button.png?1298351022') repeat-x;
574
+  border: 1px solid;
575
+  border-color: #dddddd #bbbbbb #999999;
576
+  cursor: pointer;
577
+  color: #333333;
578
+  font: bold 12px/1.2 Arial, sans-serif;
579
+  padding: 2px 10px 0px;
580
+  text-decoration: none;
581
+  overflow: visible;
582
+  vertical-align: top;
583
+  width: auto;
584
+}
585
+
586
+* html a.button {
587
+  position: relative;
588
+  top: 3px;
589
+  padding-bottom: 2px;
590
+}
591
+
592
+* html button {
593
+  padding-top: 1px;
594
+  padding-bottom: 1px;
595
+}
596
+
597
+.ie6_input,
598
+* html textarea,
599
+* html select {
600
+  background: white;
601
+  border: 1px solid;
602
+  border-color: #848484 #c1c1c1 #e1e1e1;
603
+  color: black;
604
+  padding: 2px 3px 1px;
605
+  font-size: 13px;
606
+  font-family: Arial, sans-serif;
607
+  vertical-align: top;
608
+}
609
+
610
+* html select {
611
+  margin-top: 1px;
612
+}
613
+
614
+.placeholder_text,
615
+.ie6_input_disabled,
616
+.ie6_button_disabled {
617
+  color: #888888;
618
+}
619
+
620
+.ie6_input_disabled {
621
+  background: #eeeeee;
622
+}
623
+
624
+body {
625
+  background: white;
626
+  color: black;
627
+}
628
+
629
+#wrapper {
630
+  margin: 0 auto;
631
+  padding: 20px;
632
+  width: 800px;
633
+}
634
+
635
+label.error {
636
+  background: #ffffcc;
637
+  color: #cc0000;
638
+  font-style: italic;
639
+}
640
+
641
+input.error,
642
+select.error,
643
+textarea.error {
644
+  background-color: #ffeeee;
645
+}
646
+
647
+.horiz {
648
+  margin-top: -20px;
649
+  margin-left: -20px;
650
+}
651
+
652
+.horiz td {
653
+  padding: 20px 0 0 20px;
654
+}
0 655
new file mode 100644
... ...
@@ -0,0 +1,365 @@
1
+.input_tiny {
2
+  width: 50px;
3
+}
4
+
5
+.input_small {
6
+  width: 100px;
7
+}
8
+
9
+.input_medium {
10
+  width: 150px;
11
+}
12
+
13
+.input_large {
14
+  width: 200px;
15
+}
16
+
17
+.input_xlarge {
18
+  width: 250px;
19
+}
20
+
21
+.input_xxlarge {
22
+  width: 300px;
23
+}
24
+
25
+.input_full {
26
+  width: 100%;
27
+}
28
+
29
+.input_full_wrap {
30
+  display: block;
31
+  padding-right: 8px;
32
+}
33
+
34
+input[type="search"]::-webkit-search-decoration {
35
+  display: none;
36
+}
37
+
38
+input:invalid,
39
+button:invalid,
40
+a.button:invalid,
41
+select:invalid,
42
+textarea:invalid {
43
+  -webkit-box-shadow: none;
44
+  -moz-box-shadow: none;
45
+  box-shadow: none;
46
+}
47
+input:focus,
48
+button:focus,
49
+a.button:focus,
50
+select:focus,
51
+textarea:focus {
52
+  -webkit-box-shadow: #0066ff 0 0 5px 0;
53
+  -moz-box-shadow: #0066ff 0 0 5px 0;
54
+  box-shadow: #0066ff 0 0 5px 0;
55
+  z-index: 1;
56
+}
57
+
58
+input[type="file"]:focus, input[type="file"]:active,
59
+input[type="radio"]:focus,
60
+input[type="radio"]:active,
61
+input[type="checkbox"]:focus,
62
+input[type="checkbox"]:active {
63
+  -webkit-box-shadow: none;
64
+  -moz-box-shadow: none;
65
+  box-shadow: none;
66
+}
67
+
68
+button,
69
+a.button,
70
+input[type="reset"],
71
+input[type="submit"],
72
+input[type="button"] {
73
+  -webkit-appearance: none;
74
+  -webkit-border-radius: 4px;
75
+  -moz-border-radius: 4px;
76
+  -ms-border-radius: 4px;
77
+  -o-border-radius: 4px;
78
+  border-radius: 4px;
79
+  -webkit-background-clip: padding;
80
+  -moz-background-clip: padding;
81
+  background-clip: padding-box;
82
+  background: #dddddd url('../images/button.png?1298351022') repeat-x;
83
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
84
+  background-image: -webkit-linear-gradient(#ffffff, #dddddd);
85
+  background-image: -moz-linear-gradient(#ffffff, #dddddd);
86
+  background-image: -o-linear-gradient(#ffffff, #dddddd);
87
+  background-image: linear-gradient(#ffffff, #dddddd);
88
+  border: 1px solid;
89
+  border-color: #dddddd #bbbbbb #999999;
90
+  cursor: pointer;
91
+  color: #333333;
92
+  display: inline-block;
93
+  font: bold 12px/1.3 "Helvetica Neue", Arial, "Liberation Sans", FreeSans, sans-serif;
94
+  outline: 0;
95
+  overflow: visible;
96
+  margin: 0;
97
+  padding: 3px 10px;
98
+  text-shadow: white 0 1px 1px;
99
+  text-decoration: none;
100
+  vertical-align: top;
101
+  width: auto;
102
+  *padding-top: 2px;
103
+  *padding-bottom: 0;
104
+}
105
+button:hover,
106
+a.button:hover,
107
+input[type="reset"]:hover,
108
+input[type="submit"]:hover,
109
+input[type="button"]:hover {
110
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(1px, #eeeeee), color-stop(100%, #cccccc));
111
+  background-image: -webkit-linear-gradient(#ffffff, #eeeeee 1px, #cccccc);
112
+  background-image: -moz-linear-gradient(#ffffff, #eeeeee 1px, #cccccc);
113
+  background-image: -o-linear-gradient(#ffffff, #eeeeee 1px, #cccccc);
114
+  background-image: linear-gradient(#ffffff, #eeeeee 1px, #cccccc);
115
+  text-decoration: none;
116
+}
117
+button:active,
118
+a.button:active,
119
+input[type="reset"]:active,
120
+input[type="submit"]:active,
121
+input[type="button"]:active {
122
+  background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #dddddd), color-stop(100%, #eeeeee));
123
+  background-image: -webkit-linear-gradient(#dddddd, #eeeeee);
124
+  background-image: -moz-linear-gradient(#dddddd, #eeeeee);
125
+  background-image: -o-linear-gradient(#dddddd, #eeeeee);
126
+  background-image: linear-gradient(#dddddd, #eeeeee);
127
+  -webkit-box-shadow: inset rgba(0, 0, 0, 0.25) 0 1px 2px 0;
128
+  -moz-box-shadow: inset rgba(0, 0, 0, 0.25) 0 1px 2px 0;
129
+  box-shadow: inset rgba(0, 0, 0, 0.25) 0 1px 2px 0;
130
+  border-color: #999999 #bbbbbb #dddddd;
131
+}
132
+button::-moz-focus-inner,
133
+a.button::-moz-focus-inner,
134
+input[type="reset"]::-moz-focus-inner,
135
+input[type="submit"]::-moz-focus-inner,
136
+input[type="button"]::-moz-focus-inner {
137
+  border: 0;
138
+  padding: 0;
139
+}
140
+
141
+a.button {
142
+  *padding-bottom: 3px;
143
+}
144
+
145
+button {
146
+  *padding-top: 1px;
147
+  *padding-bottom: 1px;
148
+}
149
+
150
+textarea,
151
+select,
152
+input[type="date"],
153
+input[type="datetime"],
154
+input[type="datetime-local"],
155
+input[type="email"],
156
+input[type="month"],
157
+input[type="number"],
158
+input[type="password"],
159
+input[type="search"],
160
+input[type="tel"],
161
+input[type="text"],
162
+input[type="time"],
163
+input[type="url"],
164
+input[type="week"] {
165
+  -webkit-box-sizing: border-box;
166
+  -moz-box-sizing: border-box;
167
+  box-sizing: border-box;
168
+  -webkit-background-clip: padding;
169
+  -moz-background-clip: padding;
170
+  background-clip: padding-box;
171
+  -webkit-border-radius: 0;
172
+  -moz-border-radius: 0;
173
+  -ms-border-radius: 0;
174
+  -o-border-radius: 0;
175
+  border-radius: 0;
176
+  -webkit-appearance: none;
177
+  background-color: white;
178
+  border: 1px solid;
179
+  border-color: #848484 #c1c1c1 #e1e1e1;
180
+  color: black;
181
+  outline: 0;
182
+  margin: 0;
183
+  padding: 2px 3px;
184
+  text-align: left;
185
+  font-size: 13px;
186
+  font-family: Arial, "Liberation Sans", FreeSans, sans-serif;
187
+  height: 1.8em;
188
+  vertical-align: top;
189
+  *padding-top: 2px;
190
+  *padding-bottom: 1px;
191
+  *height: auto;
192
+}
193
+textarea[disabled],
194
+select[disabled],
195
+input[type="date"][disabled],
196
+input[type="datetime"][disabled],
197
+input[type="datetime-local"][disabled],
198
+input[type="email"][disabled],
199
+input[type="month"][disabled],
200
+input[type="number"][disabled],
201
+input[type="password"][disabled],
202
+input[type="search"][disabled],
203
+input[type="tel"][disabled],
204
+input[type="text"][disabled],
205
+input[type="time"][disabled],
206
+input[type="url"][disabled],
207
+input[type="week"][disabled] {
208
+  background-color: #eeeeee;
209
+}
210
+
211
+button[disabled],
212
+input[disabled],
213
+select[disabled],
214
+select[disabled] option,
215
+select[disabled] optgroup,
216
+textarea[disabled],
217
+a.button_disabled {
218
+  -webkit-box-shadow: none;
219
+  -moz-box-shadow: none;
220
+  box-shadow: none;
221
+  -moz-user-select: -moz-none;
222
+  -webkit-user-select: none;
223
+  -khtml-user-select: none;
224
+  user-select: none;
225
+  color: #888888;
226
+  cursor: default;
227
+}
228
+
229
+input::-webkit-input-placeholder,
230
+textarea::-webkit-input-placeholder {
231
+  color: #888888;
232
+}
233
+
234
+input:-moz-placeholder,
235
+textarea:-moz-placeholder {
236
+  color: #888888;
237
+}
238
+
239
+input.placeholder_text,
240
+textarea.placeholder_text {
241
+  color: #888888;
242
+}
243
+
244
+textarea,
245
+select[size],
246
+select[multiple] {
247
+  height: auto;
248
+}
249
+
250
+select[size="0"],
251
+select[size="1"] {
252
+  height: 1.8em;
253
+  *height: auto;
254
+}
255
+
256
+@media (-webkit-min-device-pixel-ratio: 0) {
257
+  select[size],
258
+  select[multiple],
259
+  select[multiple][size] {
260
+    background-image: none;
261
+    padding-right: 3px;
262
+  }
263
+
264
+  select,
265
+  select[size="0"],
266
+  select[size="1"] {
267
+    background-image: url(data:image/png;base64,R0lGODlhDQAEAIAAAAAAAP8A/yH5BAEHAAEALAAAAAANAAQAAAILhA+hG5jMDpxvhgIAOw==);
268
+    background-repeat: no-repeat;
269
+    background-position: right center;
270
+    padding-right: 20px;
271
+  }
272
+
273
+  ::-webkit-validation-bubble-message {
274
+    -webkit-box-shadow: none;
275
+    box-shadow: none;
276
+    background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #666666), color-stop(1, black));
277
+    border: 0;
278
+    color: white;
279
+    font: 13px/17px "Lucida Grande", Arial, "Liberation Sans", FreeSans, sans-serif;
280
+    overflow: hidden;
281
+    padding: 15px 15px 17px;
282
+    text-shadow: black 0 0 1px;
283
+    min-height: 16px;
284
+  }
285
+
286
+  ::-webkit-validation-bubble-arrow,
287
+  ::-webkit-validation-bubble-top-outer-arrow,
288
+  ::-webkit-validation-bubble-top-inner-arrow {
289
+    -webkit-box-shadow: none;
290
+    box-shadow: none;
291
+    background: #666666;
292
+    border-color: #666666;
293
+  }
294
+}
295
+textarea {
296
+  min-height: 40px;
297
+  overflow: auto;
298
+  resize: vertical;
299
+  width: 100%;
300
+}
301
+
302
+optgroup {
303
+  color: black;
304
+  font-style: normal;
305
+  font-weight: normal;
306
+  font-family: Arial, "Liberation Sans", FreeSans, sans-serif;
307
+}
308
+optgroup::-moz-focus-inner {
309
+  border: 0;
310
+  padding: 0;
311
+}
312
+
313
+.ie6_button,
314
+* html button,
315
+* html a.button {
316
+  background: #dddddd url('../images/button.png?1298351022') repeat-x;
317
+  border: 1px solid;
318
+  border-color: #dddddd #bbbbbb #999999;
319
+  cursor: pointer;
320
+  color: #333333;
321
+  font: bold 12px/1.2 Arial, sans-serif;
322
+  padding: 2px 10px 0;
323
+  text-decoration: none;
324
+  overflow: visible;
325
+  vertical-align: top;
326
+  width: auto;
327
+}
328
+
329
+* html a.button {
330
+  position: relative;
331
+  top: 3px;
332
+  padding-bottom: 2px;
333
+}
334
+
335
+* html button {
336
+  padding-top: 1px;
337
+  padding-bottom: 1px;
338
+}
339
+
340
+.ie6_input,
341
+* html textarea,
342
+* html select {
343
+  background: white;
344
+  border: 1px solid;
345
+  border-color: #848484 #c1c1c1 #e1e1e1;
346
+  color: black;
347
+  padding: 2px 3px 1px;
348
+  font-size: 13px;
349
+  font-family: Arial, sans-serif;
350
+  vertical-align: top;
351
+}
352
+
353
+* html select {
354
+  margin-top: 1px;
355
+}
356
+
357
+.placeholder_text,
358
+.ie6_input_disabled,
359
+.ie6_button_disabled {
360
+  color: #888888;
361
+}
362
+
363
+.ie6_input_disabled {
364
+  background: #eeeeee;
365
+}
... ...
@@ -1 +1 @@
1
-@import url(https://fonts.googleapis.com/css?family=Lato:400,100,300,400italic,300italic,700,700italic,900&subset=latin,latin-ext);@import url(https://fonts.googleapis.com/css?family=Caudex);.feed,main{-webkit-backface-visibility:hidden;-webkit-transform:translateZ(0);}*{box-sizing:border-box;margin:0px;padding:0px;}body{-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;font-family:Lato;}main > ul,main > ol,#sidebar > ul,#sidebar > ol,.feed > ul,.feed > ol{list-style:none;margin:0px;}ul{margin:1em;}h1,h2{font-size:153.9%;}:h3{font-size:146.5%;}h4,h5,h6{font-size:138.5%;}ul + h1,ul + h2,ul + h3,ul + h4,ul + h5,ul + h6{width:initial;}header{color:white;}header h1{font-family:Caudex;font-size:inherit;margin-top:0em;padding-left:10vw;font-weight:200;}#sidebar,main{border-top:none;}section#sidebar{width:38vw;height:90vh;position:fixed;overflow:auto;}section#sidebar ul.menu{text-align:right;font-variant:small-caps;}section#sidebar ul.menu li a{color:inherit;display:block;font-size:125%;font-weight:700;padding:0.5em;text-decoration:none;-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;width:100%;}main{width:62vw;float:right;clear:right;overflow-x:hidden;overflow-y:scroll;}.feed-header{-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;padding-bottom:0em;}.feed-header h2,.feed-header h3{padding:0.62em;}.feed-header h2{padding-bottom:0.38em;}.feed-header h3{padding-top:0.38em;}.link.closed .link-content{max-height:0px;padding:0em;}.link.closed{padding-bottom:0em;}.link{text-decoration:none;display:block;overflow:hidden;font-size:0.8em;}.link .link-header{padding:1em;padding-bottom:0em;cursor:pointer;-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;}.link .link-header h4{margin-bottom:0.5em;display:inline-block;}.link .link-info{margin-left:-1em;margin-right:-1em;padding-left:1em;padding-right:1em;padding-bottom:0.32em;}.link .link-info .link-url{float:left;}.link .link-info .link-date{float:right;display:block;}.link .link-info:after{content:" ";display:block;clear:both;}.link .link-content{-moz-transition:max-height 0.5s ease;-o-transition:max-height 0.5s ease;-webkit-transition:max-height 0.5s ease;-ms-transition:max-height 0.5s ease;transition:max-height 0.5s ease;}.link .link-content > div{padding:1em;}.feed.closed .post-list{max-height:0px;padding:0em;}.feed:first-child{border-top:none;}.feed{overflow:hidden;}.feed .post-list{-moz-transition:max-height 0.5s ease;-o-transition:max-height 0.5s ease;-webkit-transition:max-height 0.5s ease;-ms-transition:max-height 0.5s ease;transition:max-height 0.5s ease;}
2 1
\ No newline at end of file
2
+@import url(https://fonts.googleapis.com/css?family=Lato:400,100,300,400italic,300italic,700,700italic,900&subset=latin,latin-ext);@import url(https://fonts.googleapis.com/css?family=Caudex);.feed,main{-webkit-backface-visibility:hidden;-webkit-transform:translateZ(0);}*{box-sizing:border-box;margin:0px;padding:0px;}body{font-family:Lato;}main > ul,main > ol,#sidebar > ul,#sidebar > ol,.feed > ul,.feed > ol{list-style:none;margin:0px;}ul{margin:1em;}h1,h2{font-size:153.9%;}:h3{font-size:146.5%;}h4,h5,h6{font-size:138.5%;}ul + h1,ul + h2,ul + h3,ul + h4,ul + h5,ul + h6{width:initial;}header{color:white;}header h1{font-family:Caudex;font-size:inherit;margin-top:0em;padding-left:10vw;font-weight:200;}#sidebar,main{border-top:none;}section#sidebar{width:38vw;height:90vh;position:fixed;overflow:auto;}section#sidebar ul.menu{text-align:right;font-variant:small-caps;opacity:0;}section#sidebar ul.menu li{-moz-transition:all 0.25s ease;-o-transition:all 0.25s ease;-webkit-transition:all 0.25s ease;-ms-transition:all 0.25s ease;transition:all 0.25s ease;}section#sidebar ul.menu li a{color:inherit;display:block;font-size:125%;font-weight:700;padding:0.5em;text-decoration:none;width:100%;}section#sidebar ul.menu.open{opacity:1;}main{width:62vw;float:right;clear:right;overflow-x:hidden;overflow-y:scroll;position:relative;}img.spinner{position:absolute;top:50%;left:50%;-moz-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%);-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);}.hide{display:none;}.feed-header{-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;padding-bottom:0em;}.feed-header h2,.feed-header h3{padding:0.62em;}.feed-header h2{padding-bottom:0.38em;}.feed-header h3{padding-top:0.38em;}.link.closed .link-content{max-height:0px;padding:0em;}.link.closed{padding-bottom:0em;}.link{text-decoration:none;display:block;overflow:hidden;font-size:0.8em;}.link .link-header{padding:1em;padding-bottom:0em;cursor:pointer;-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;}.link .link-header h4{margin-bottom:0.5em;display:inline-block;}.link .link-info{margin-left:-1em;margin-right:-1em;padding-left:1em;padding-right:1em;padding-bottom:0.32em;}.link .link-info .link-url{float:left;}.link .link-info .link-date{float:right;display:block;}.link .link-info:after{content:" ";display:block;clear:both;}.link .link-content{overflow:hidden;-moz-transition:max-height 0.5s ease;-o-transition:max-height 0.5s ease;-webkit-transition:max-height 0.5s ease;-ms-transition:max-height 0.5s ease;transition:max-height 0.5s ease;}.link .link-content img{margin:1em 0% 1em 10%;max-width:80%;max-height:70vh;}.link .link-content > div{padding:1em;font-size:12pt;}.feed.closed{border-bottom-width:thin;}.feed.closed .post-list{max-height:0px;padding:0em;}.feed.closed h3{display:none;}.feed:first-child{border-top:none;}.feed{overflow:hidden;}.feed .post-list{-moz-transition:max-height 0.5s ease;-o-transition:max-height 0.5s ease;-webkit-transition:max-height 0.5s ease;-ms-transition:max-height 0.5s ease;transition:max-height 0.5s ease;}#add-form{font-size:1em;height:1.8em;display:block;overflow:hidden;width:100%;}#add-form input{border:none;width:95%;height:100%;font-size:0.8em;line-height:1.8em;padding:1em 0.5em;}#add-form button{border:none;border-radius:0px;font-size:inherit;width:5%;height:100%;}.flip-button{position:absolute;right:0em;top:0em;z-index:1000;width:3em;height:3em;padding-left:1em;padding-bottom:1em;border-bottom-left-radius:100%;border:none;-moz-transition:all 2s cubic-bezier(0.175, 0.885, 0.32, 1.275);-o-transition:all 2s cubic-bezier(0.175, 0.885, 0.32, 1.275);-webkit-transition:all 2s cubic-bezier(0.175, 0.885, 0.32, 1.275);-ms-transition:all 2s cubic-bezier(0.175, 0.885, 0.32, 1.275);transition:all 2s cubic-bezier(0.175, 0.885, 0.32, 1.275);background-image:none;}
3 3
\ No newline at end of file
4 4
new file mode 100644
... ...
@@ -0,0 +1,213 @@
1
+/* `XHTML, HTML4, HTML5 Reset
2
+----------------------------------------------------------------------------------------------------*/
3
+
4
+a,
5
+abbr,
6
+acronym,
7
+address,
8
+applet,
9
+article,
10
+aside,
11
+audio,
12
+b,
13
+big,
14
+blockquote,
15
+body,
16
+canvas,
17
+caption,
18
+center,
19
+cite,
20
+code,
21
+dd,
22
+del,
23
+details,
24
+dfn,
25
+dialog,
26
+div,
27
+dl,
28
+dt,
29
+em,
30
+embed,
31
+fieldset,
32
+figcaption,
33
+figure,
34
+font,
35
+footer,
36
+form,
37
+h1,
38
+h2,
39
+h3,
40
+h4,
41
+h5,
42
+h6,
43
+header,
44
+hgroup,
45
+hr,
46
+html,
47
+i,
48
+iframe,
49
+img,
50
+ins,
51
+kbd,
52
+label,
53
+legend,
54
+li,
55
+main,
56
+mark,
57
+menu,
58
+meter,
59
+nav,
60
+object,
61
+ol,
62
+output,
63
+p,
64
+pre,
65
+progress,
66
+q,
67
+rp,
68
+rt,
69
+ruby,
70
+s,
71
+samp,
72
+section,
73
+small,
74
+span,
75
+strike,
76
+strong,
77
+sub,
78
+summary,
79
+sup,
80
+table,
81
+tbody,
82
+td,
83
+tfoot,
84
+th,
85
+thead,
86
+time,
87
+tr,
88
+tt,
89
+u,
90
+ul,
91
+var,
92
+video,
93
+xmp {
94
+  border: 0;
95
+  margin: 0;
96
+  padding: 0;
97
+  font-size: 100%;
98
+}
99
+
100
+html,
101
+body {
102
+  height: 100%;
103
+}
104
+
105
+article,
106
+aside,
107
+details,
108
+figcaption,
109
+figure,
110
+footer,
111
+header,
112
+hgroup,
113
+main,
114
+menu,
115
+nav,
116
+section {
117
+/*
118
+  Override the default (display: inline) for
119
+  browsers that do not recognize HTML5 tags.
120
+
121
+  IE8 (and lower) requires a shiv:
122
+  http://ejohn.org/blog/html5-shiv
123
+*/
124
+  display: block;
125
+}
126
+
127
+b,
128
+strong {
129
+/*
130
+  Makes browsers agree.
131
+  IE + Opera = font-weight: bold.
132
+  Gecko + WebKit = font-weight: bolder.
133
+*/
134
+  font-weight: bold;
135
+}
136
+
137
+img {
138
+  color: transparent;
139
+  font-size: 0;
140
+  vertical-align: middle;
141
+/*
142
+  For IE.
143
+  http://css-tricks.com/ie-fix-bicubic-scaling-for-images
144
+*/
145
+  -ms-interpolation-mode: bicubic;
146
+}
147
+
148
+ul,
149
+ol {
150
+  list-style: none;
151
+}
152
+
153
+li {
154
+/*
155
+  For IE6 + IE7:
156
+
157
+  "display: list-item" keeps bullets from
158
+  disappearing if hasLayout is triggered.
159
+*/
160
+  display: list-item;
161
+}
162
+
163
+table {
164
+  border-collapse: collapse;
165
+  border-spacing: 0;
166
+}
167
+
168
+th,
169
+td,
170
+caption {
171
+  font-weight: normal;
172
+  vertical-align: top;
173
+  text-align: left;
174
+}
175
+
176
+q {
177
+  quotes: none;
178
+}
179
+
180
+q:before,
181
+q:after {
182
+  content: '';
183
+  content: none;
184
+}
185
+
186
+sub,
187
+sup,
188
+small {
189
+  font-size: 75%;
190
+}
191
+
192
+sub,
193
+sup {
194
+  line-height: 0;
195
+  position: relative;
196
+  vertical-align: baseline;
197
+}
198
+
199
+sub {
200
+  bottom: -0.25em;
201
+}
202
+
203
+sup {
204
+  top: -0.5em;
205
+}
206
+
207
+svg {
208
+/*
209
+  For IE9. Without, occasionally draws shapes
210
+  outside the boundaries of <svg> rectangle.
211
+*/
212
+  overflow: hidden;
213
+}
0 214
\ No newline at end of file
1 215
new file mode 100644
... ...
@@ -0,0 +1,81 @@
1
+/*
2
+  960 Grid System ~ Text CSS.
3
+  Learn more ~ http://960.gs/
4
+
5
+  Licensed under GPL and MIT.
6
+*/
7
+
8
+/* `Basic HTML
9
+----------------------------------------------------------------------------------------------------*/
10
+
11
+body {
12
+  font: 13px/1.5 'Helvetica Neue', Arial, 'Liberation Sans', FreeSans, sans-serif;
13
+}
14
+
15
+hr {
16
+  border: 0 #ccc solid;
17
+  border-top-width: 1px;
18
+  clear: both;
19
+  height: 0;
20
+}
21
+
22
+/* `Headings
23
+----------------------------------------------------------------------------------------------------*/
24
+
25
+h1 {
26
+  font-size: 25px;
27
+}
28
+
29
+h2 {
30
+  font-size: 23px;
31
+}
32
+
33
+h3 {
34
+  font-size: 21px;
35
+}
36
+
37
+h4 {
38
+  font-size: 19px;
39
+}
40
+
41
+h5 {
42
+  font-size: 17px;
43
+}
44
+
45
+h6 {
46
+  font-size: 15px;
47
+}
48
+
49
+/* `Spacing
50
+----------------------------------------------------------------------------------------------------*/
51
+
52
+ol {
53
+  list-style: decimal;
54
+}
55
+
56
+ul {
57
+  list-style: disc;
58
+}
59
+
60
+li {
61
+  margin-left: 30px;
62
+}
63
+
64
+p,
65
+dl,
66
+hr,
67
+h1,
68
+h2,
69
+h3,
70
+h4,
71
+h5,
72
+h6,
73
+ol,
74
+ul,
75
+pre,
76
+table,
77
+address,
78
+fieldset,
79
+figure {
80
+  margin-bottom: 20px;
81
+}
0 82
\ No newline at end of file
1 83
new file mode 100644
2 84
Binary files /dev/null and b/static/images/Whitespace_favicon.png differ
3 85
new file mode 100644
4 86
Binary files /dev/null and b/static/images/button.png differ
5 87
new file mode 100644
6 88
Binary files /dev/null and b/static/images/select_arrow.gif differ
7 89
new file mode 100644
8 90
Binary files /dev/null and b/static/images/spinner.gif differ
... ...
@@ -1,29 +1,33 @@
1
-$(document).ready(function () {
2
-    $('.link-header').click(function () {
3
-        $(this).siblings('.link-content').each(function () {
4
-            if ($(this).css('max-height') === '0px') {
5
-                var addedHeight = $(this).children().outerHeight();
6
-                var parentHeight = $(this).parents('.post-list').css('max-height');
7
-                $(this).css('max-height', addedHeight);
8
-                return $(this).parents('.post-list').css('max-height', addedHeight + parentHeight);
1
+'';
2
+jQuery(document).ready(function () {
3
+    jQuery('.link-header, .feed-header').click(function () {
4
+        jQuery('.menu').addClass('open');
5
+        return true;
6
+    });
7
+    jQuery('.link-header').click(function () {
8
+        jQuery(this).siblings('.link-content').each(function () {
9
+            if (jQuery(this).css('max-height') === '0px') {
10
+                var addedHeight = jQuery(this).children().outerHeight();
11
+                var parentHeight = jQuery(this).parents('.post-list').css('max-height');
12
+                jQuery(this).css('max-height', addedHeight);
13
+                return jQuery(this).parents('.post-list').css('max-height', addedHeight + parentHeight);
9 14
             } else {
10
-                return $(this).css('max-height', '0px');
15
+                return jQuery(this).css('max-height', '0px');
11 16
             };
12 17
         });
13
-        return $(this).parent().toggleClass('closed');
18
+        return jQuery(this).parent().toggleClass('closed');
14 19
     });
15
-    $('.feed-header').click(function () {
16
-        $(this).siblings('.post-list').each(function () {
17
-            return $(this).css('max-height') === '0px' ? $(this).css('max-height', this.scrollHeight) : $(this).css('max-height', '0px');
20
+    jQuery('.feed-header').click(function () {
21
+        jQuery(this).siblings('.post-list').each(function () {
22
+            return jQuery(this).css('max-height') === '0px' ? jQuery(this).css('max-height', this.scrollHeight) : jQuery(this).css('max-height', '0px');
18 23
         });
19
-        return $(this).parent().toggleClass('closed');
24
+        return jQuery(this).parent().toggleClass('closed');
20 25
     });
21
-    invertPalette = function () {
22
-        var styleSheet = $('link[href^="/theme"]');
26
+    jQuery('.flip-button').click(function () {
27
+        var styleSheet = jQuery('link[href^="/theme"]');
23 28
         var styleSheetName = styleSheet.attr('href');
24 29
         return styleSheet.attr('href', styleSheetName.match(/dark/) ? styleSheetName.replace(/dark/, 'light') : styleSheetName.replace(/light/, 'dark'));
25
-    };
26
-    $('.flip-button').click(invertPalette);
30
+    });
27 31
     return null;
28 32
 });
29 33
 
30 34
new file mode 100644
... ...
@@ -0,0 +1 @@
1
+var FORMALIZE=function(e,t,n,r){function i(e){var t=n.createElement("b");return t.innerHTML="<!--[if IE "+e+"]><br><![endif]-->",!!t.getElementsByTagName("br").length}var s="placeholder"in n.createElement("input"),o="autofocus"in n.createElement("input"),u=i(6),a=i(7);return{go:function(){var e,t=this.init;for(e in t)t.hasOwnProperty(e)&&t[e]()},init:{disable_link_button:function(){e(n.documentElement).on("click","a.button_disabled",function(){return!1})},full_input_size:function(){if(!a||!e("textarea, input.input_full").length)return;e("textarea, input.input_full").wrap('<span class="input_full_wrap"></span>')},ie6_skin_inputs:function(){if(!u||!e("input, select, textarea").length)return;var t=/button|submit|reset/,n=/date|datetime|datetime-local|email|month|number|password|range|search|tel|text|time|url|week/;e("input").each(function(){var r=e(this);this.getAttribute("type").match(t)?(r.addClass("ie6_button"),this.disabled&&r.addClass("ie6_button_disabled")):this.getAttribute("type").match(n)&&(r.addClass("ie6_input"),this.disabled&&r.addClass("ie6_input_disabled"))}),e("textarea, select").each(function(){this.disabled&&e(this).addClass("ie6_input_disabled")})},autofocus:function(){if(o||!e(":input[autofocus]").length)return;var t=e("[autofocus]")[0];t.disabled||t.focus()},placeholder:function(){if(s||!e(":input[placeholder]").length)return;FORMALIZE.misc.add_placeholder(),e(":input[placeholder]").each(function(){if(this.type==="password")return;var t=e(this),n=t.attr("placeholder");t.focus(function(){t.val()===n&&t.val("").removeClass("placeholder_text")}).blur(function(){FORMALIZE.misc.add_placeholder()}),t.closest("form").submit(function(){t.val()===n&&t.val("").removeClass("placeholder_text")}).on("reset",function(){setTimeout(FORMALIZE.misc.add_placeholder,50)})})}},misc:{add_placeholder:function(){if(s||!e(":input[placeholder]").length)return;e(":input[placeholder]").each(function(){if(this.type==="password")return;var t=e(this),n=t.attr("placeholder");(!t.val()||t.val()===n)&&t.val(n).addClass("placeholder_text")})}}}}(jQuery,this,this.document);jQuery(document).ready(function(){FORMALIZE.go()});
0 2
new file mode 100644
... ...
@@ -0,0 +1,14 @@
1
+var whitespace = angular.module('whitespace', ['ngResource', 'ngSanitize']);
2
+whitespace.controller('MainCtrl', ['$scope', '$http', '$resource', '$sce', function ($scope, $http, $resource, $sce) {
3
+    var feeds = $resource('feeds/json', {  });
4
+    $scope.feeds = feeds.get();
5
+    $scope.data = 'hello world!';
6
+    $scope.renderHtml = function (htmlCode) {
7
+        return $sce.trustAsHtml(htmlCode);
8
+    };
9
+    $scope.toggleClosed = function (ent) {
10
+        return ent.closed = !ent.closed;
11
+    };
12
+    return null;
13
+}]);
14
+
... ...
@@ -1,21 +1,25 @@
1
-(ql:quickload :postmodern)
1
+(in-package :whitespace.tables)
2
+(cl-annot.syntax:enable-annot-syntax)
2 3
 
3
-(defclass rss-feed-store ()
4
+@export-class
5
+(defclass rss_feed_store ()
4 6
   ((id          :col-type serial :initarg :id          :accessor rfs-id)
5
-   (title       :col-type text   :initarg :title       :accessor rss-feed-title       :col-default "")
6
-   (link        :col-type text   :initarg :link        :accessor rss-feed-line        :col-default "")
7
-   (description :col-type text   :initarg :description :accessor rss-feed-description :col-default ""))
7
+   (title       :col-type text   :initarg :title       :accessor rfs-title       :col-default "")
8
+   (link        :col-type text   :initarg :link        :accessor rfs-link        :col-default "")
9
+   (description :col-type text   :initarg :description :accessor rfs-description :col-default ""))
8 10
   (:metaclass postmodern:dao-class)
9 11
   (:table-name "rssFeed")
10 12
   (:unique link)
11 13
   (:keys id))
12 14
 
13
-(postmodern:deftable rss-feed-store
14
-  (postmodern:!dao-def))
15
+(postmodern:deftable rss_feed_store
16
+  (postmodern:!dao-def)
17
+  (postmodern:!unique "link")
18
+  )
15 19
 
16
-; (postmodern:create-table 'rss-feed-store)
17 20
 
18
-(defclass rss-item-store ()
21
+@export-class
22
+(defclass rss_item_store ()
19 23
   ((id          :col-type serial  :initarg :id          :accessor ris-id)
20 24
    (title       :col-type text    :initarg :title       :accessor ris-title       :col-default "")
21 25
    (link        :col-type text    :initarg :link        :accessor ris-link        :col-default "")
... ...
@@ -29,22 +33,45 @@
29 33
   (:metaclass postmodern:dao-class)
30 34
   (:keys id))
31 35
 
32
-(postmodern:deftable rss-item-store
36
+(postmodern:deftable rss_item_store
33 37
   (postmodern:!dao-def)
34 38
   (postmodern:!foreign "rssfeed" "feed" "id" :on-delete :cascade :on-update :cascade))
35 39
 
36 40
 
37
-; (postmodern:create-table 'rss-item-store)
41
+@export-class
42
+(defclass reader_user ()
43
+  ((id :col-type serial)
44
+   (join-date :col-type timestamp :accessor join-date :col-default (:now))
45
+   (foreign-id :col-type string :initarg :foreign-id :accessor user-foreign-id :unique t)
46
+   (name :col-type string :initarg :name :accessor user-name)
47
+   (email :col-type string :initarg :email :accessor user-email)
48
+   (first-name :col-type (or string s-sql:db-null) :initarg :first-name :accessor user-first-name)
49
+   (gender :col-type (or string s-sql:db-null) :initarg :gender :accessor user-gender)
50
+   (last-name :col-type (or string s-sql:db-null) :initarg :last-name :accessor user-last-name)
51
+   (link :col-type (or string s-sql:db-null) :initarg :link :accessor user-link)
52
+   (locale :col-type (or string s-sql:db-null) :initarg :locale :accessor user-locale))
53
+  (:metaclass postmodern:dao-class)
54
+  (:keys id))
55
+
56
+(postmodern:deftable reader_user
57
+  (postmodern:!dao-def))
58
+
59
+@export-class
60
+(defclass subscriptions ()
61
+  ((id :col-type serial)
62
+   (uid :col-type integer :initarg :uid :accessor subscription-uid)
63
+   (feedid :col-type integer :initarg :feedid :accessor subscription-feedid))
64
+  (:unique (uid feedid))
65
+  (:metaclass postmodern:dao-class)
66
+  (:keys id))
67
+
68
+(postmodern:deftable subscriptions
69
+  (postmodern:!dao-def)
70
+  (postmodern:!unique '(uid feedid)))
71
+
72
+; (postmodern:create-table 'rss_feed_store)
73
+; (postmodern:create-table 'rss_item_store)
74
+; (postmodern:create-table 'reader_user)
75
+; (postmodern:create-table 'subscriptions)
76
+
38 77
 
39
-(defclass user ()
40
-  ((id)
41
-   (foreign-id)
42
-   (email)
43
-   (first-name)
44
-   (gender)
45
-   (last-name)
46
-   (link)
47
-   (locale)
48
-   
49
-   )
50
-  )
52 79
new file mode 100644
... ...
@@ -0,0 +1,41 @@
1
+(ql:quickload :parenscript)
2
+
3
+(defpackage :ps_translator
4
+  (:use :parenscript :cl)
5
+  (:export main))
6
+
7
+(in-package :ps_translator)
8
+(use-package :parenscript)
9
+
10
+(defpsmacro macros (&body body)
11
+  `(lisp (progn
12
+           ,@(loop for form in body
13
+                   collect `(defpsmacro ,@form))
14
+           "")))
15
+
16
+(defmacro+ps $ ( (selector) &rest values) `(chain (j-query ,selector) ,@values))
17
+
18
+(defmacro+ps $this (&rest values) `($ (this) ,@values))
19
+
20
+(defmacro+ps $each ((selector &rest actions) &body code)
21
+  `($ (,selector) ,@actions
22
+      (each (lambda ()
23
+              ,@code))))
24
+
25
+(defmacro+ps def-event (target event args &body run)
26
+ `($ (,target)
27
+          (,event
28
+            (lambda ,args
29
+              ,@run))))
30
+
31
+(defun translate-file (infile outfile)
32
+  (let ((*JS-TARGET-VERSION* 1.9))
33
+    (with-open-file (o outfile :direction :output :if-exists :supersede)
34
+      (with-open-file (i infile :direction :input)
35
+        (write-line (ps-compile-file i) o)))))
36
+
37
+(defun main (args)
38
+  (in-package :ps_translator)
39
+  (apply #'translate-file (cdr args)))
40
+
41
+
0 42
new file mode 100644
... ...
@@ -0,0 +1,13 @@
1
+(in-package whitespace.utils)
2
+(lquery:define-lquery-list-function tag-name (nodes &rest tags)
3
+  "Manipulate elements on the basis of there tag-name.
4
+   With no arguments, return their names else return
5
+   the corresponding tags."
6
+  (if (null tags)
7
+    (map 'vector #'plump:tag-name nodes)
8
+    (apply #'vector
9
+           (loop for node across nodes
10
+                 if (find (plump:tag-name node) tags :test #'string=)
11
+                 collect node))))
12
+
13
+