summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortim b <timb@camcompu.home>2010-04-08 18:22:25 -0700
committertim b <timb@camcompu.home>2010-04-08 18:22:25 -0700
commit0db7b7ef7a35070bf802a6c3313701d6faa5b3dc (patch)
tree9025a6347992309f01397ad5b9d8ae904749a538
parent6440348618ade4fbcb541c40473074833a47d198 (diff)
favs
-rw-r--r--db/0-create.psql2
-rw-r--r--src/site.clj163
-rw-r--r--src/tags.clj241
-rwxr-xr-xsrc/utils.clj19
-rwxr-xr-xstatic/css/dump.css46
-rw-r--r--static/css/dumpnewuser.css1
-rwxr-xr-xstatic/css/header.css54
-rw-r--r--static/img/palette.gifbin0 -> 292 bytes
-rw-r--r--static/img/thumbs/bw.left.gifbin0 -> 279 bytes
-rw-r--r--static/img/thumbs/bw.right.gifbin0 -> 278 bytes
-rw-r--r--static/img/thumbs/bw.right.tiny.gifbin0 -> 271 bytes
-rw-r--r--static/img/thumbs/circle.gifbin0 -> 264 bytes
-rw-r--r--static/img/thumbs/color.left.2x.gifbin0 -> 162 bytes
-rw-r--r--static/img/thumbs/color.left.3x.gifbin0 -> 239 bytes
-rw-r--r--static/img/thumbs/color.left.4x.gifbin0 -> 323 bytes
-rw-r--r--static/img/thumbs/color.left.gifbin0 -> 291 bytes
-rw-r--r--static/img/thumbs/color.right.2x.gifbin0 -> 366 bytes
-rw-r--r--static/img/thumbs/color.right.3x.gifbin0 -> 447 bytes
-rw-r--r--static/img/thumbs/color.right.4x.gifbin0 -> 537 bytes
-rw-r--r--static/img/thumbs/color.right.gifbin0 -> 290 bytes
-rw-r--r--static/img/thumbs/pink.circle.gifbin0 -> 258 bytes
-rw-r--r--static/img/thumbs/pink.diamond.gifbin0 -> 258 bytes
-rw-r--r--static/img/thumbs/pink.right.4x.gifbin0 -> 178 bytes
-rw-r--r--static/img/thumbs/transparent.circle.gifbin0 -> 62 bytes
-rw-r--r--static/js/jquery-ui-1.8.effects.min.js7
-rw-r--r--static/js/pichat.js300
-rw-r--r--template/chat.st12
-rw-r--r--template/chat_help_bubbles.st14
-rw-r--r--template/directory.st14
-rw-r--r--template/head.st2
-rw-r--r--template/log.st2
-rw-r--r--template/share_buttons.st6
-rw-r--r--template/tagged_dumps.st49
33 files changed, 750 insertions, 182 deletions
diff --git a/db/0-create.psql b/db/0-create.psql
index cad20e9..2771676 100644
--- a/db/0-create.psql
+++ b/db/0-create.psql
@@ -12,6 +12,7 @@ CREATE TABLE users (
bio text NOT NULL DEFAULT '',
profile_bg text
);
+CREATE INDEX users_nick_idx ON users (lower(nick));
CREATE TABLE rooms (
room_id SERIAL PRIMARY KEY,
@@ -64,6 +65,7 @@ CREATE TABLE tags (
);
CREATE INDEX tags_user_id_idx ON tags (user_id);
CREATE INDEX tags_message_id_idx ON tags (message_id);
+CREATE INDEX tags_created_on_id_idx ON tags (created_on DESC);
CREATE INDEX tags_tag_idx ON tags (lower(tag));
INSERT INTO rooms (key, name, description, admin_only)
diff --git a/src/site.clj b/src/site.clj
index 713dc9d..bf745c9 100644
--- a/src/site.clj
+++ b/src/site.clj
@@ -15,6 +15,7 @@
clojure.contrib.json.write
clojure.contrib.sql
clojure.contrib.str-utils
+ clojure.contrib.def
compojure
email
utils
@@ -202,6 +203,7 @@
(assoc m "topic" topic)
m)))
+
(defn count-messages-by-room [room-id image-only]
(let [query (str "SELECT COUNT(*)
FROM messages m, users u
@@ -261,6 +263,46 @@
:topic (ref nil)
})
+;; User-id/nick cache
+;; I keep needing to grab user-id from a nick or nick from a user-id so I thought I'd cache them
+
+(def user-id-nick-cache (ref {}))
+(def *user-id-nick-cache-size* 500)
+
+;; this is really ugly, need to make this nicer or get rid of it
+(defnk user-id-from-nick-or-nick-from-user-id [:nick false :user-id false]
+ (let [cache-key (or nick (str "~" user-id))
+ found (@user-id-nick-cache cache-key)]
+ (if found
+ found
+ (let [query (if nick
+ (str "SELECT user_id FROM users WHERE lower(nick) = ?")
+ (str "SELECT nick FROM users WHERE user_id = ?"))
+ res (first (do-select [query (or nick user-id)]))]
+ (if (nil? res)
+ nil
+ (let [found (if nick
+ (res :user_id)
+ (res :nick))
+ cache-key2 (if nick
+ (str "~" found)
+ (lower-case found))]
+ (dosync
+ (if (> (count @user-id-nick-cache) *user-id-nick-cache-size*) (ref-set user-id-nick-cache {}))
+ (alter user-id-nick-cache assoc cache-key found cache-key2 (or nick user-id))
+ )
+ found))))))
+
+(defn user-id-from-nick [nick] (user-id-from-nick-or-nick-from-user-id :nick (lower-case nick)))
+(defn nick-from-user-id [user-id] (user-id-from-nick-or-nick-from-user-id :user-id user-id))
+
+;; Favorites cache
+
+(def favorites-cache (ref []))
+(def *favorites-cache-size* 50)
+
+
+
;; Login code
(defn is-vip? [session]
@@ -385,7 +427,7 @@
has-avatar (non-empty-string? (user-info :avatar))
offset (maybe-parse-int offset 0)
dump-offset (* offset *dumps-per-page*)
- raw-dumps (logger tags/fetch-dumps-by-nick profile-nick true (+ 1 *dumps-per-page*) dump-offset)
+ raw-dumps (logger tags/fetch-dumps-by-nick :nick profile-nick :amount (+ 1 *dumps-per-page*) :offset dump-offset)
dumps (map tags/add-favorited-flag (take *dumps-per-page* raw-dumps) (repeat session))
dumps (map tags/remove-tags-for-output dumps)
dumps (logger doall (map process-message-for-output dumps))]
@@ -580,7 +622,7 @@
nick (session :nick)
st (fetch-template template session)
; TODO: remove db query
- message-list (reverse (tags/fetch-dumps-by-room (room :room_id) false))
+ message-list (reverse (tags/fetch-dumps-by-room :room-id (room :room_id) :image-only false))
message-list (map tags/add-favorited-flag message-list (repeat session))
message-list (to-array (map process-message-for-output message-list))]
(if nick
@@ -698,7 +740,10 @@
dump-offset (* offset *dumps-per-page*)
image-only (and (not (room :admin_only))
(not= (params :show) "all"))
- raw-dumps (logger tags/fetch-dumps-by-room (room :room_id) image-only (+ 1 *dumps-per-page*) dump-offset)
+ raw-dumps (logger tags/fetch-dumps-by-room :room-id (room :room_id)
+ :image-only image-only
+ :amount (+ 1 *dumps-per-page*)
+ :offset dump-offset)
dumps (map tags/add-favorited-flag (take *dumps-per-page* raw-dumps) (repeat session))
;; json-tags (for [dump dumps :when (not (empty? (dump :tags)))]
;; (json-str {"id" (dump :message_id) "tags" (dump :tags) }))
@@ -720,6 +765,103 @@
(log session (@rooms room-key) offset params)
(resp-error "UNKNOWN_ROOM")))
+;; Tags
+
+(defn undecoded-url-piece [url position]
+ "Get nth thing out of a url path.
+ For example, (undecoded-url-piece 'http://example.com/a/b/c?foo' 2) will return 'c'"
+ (let [path-without-domain (nth (re-find #"//[^/]+/(.+)" url) 1)]
+ (nth (re-split #"/|\?" path-without-domain) position)))
+
+(defn add-tag [user-id message-id tag]
+ (try
+ (do-insert "tags" ["user_id" "message_id" "tag"] [user-id (maybe-parse-int message-id) (normalize-tag-for-db tag)])
+ true
+ ; catch error when inserting duplicate tags
+ (catch Exception e false)))
+
+
+; to do: don't let people set tags on messages they can't access
+(defn validated-add-tag [session params]
+ (if (session :nick)
+ (if (add-tag (session :user_id) (params :message_id) (params :tag))
+ (resp-success "OK")
+ (resp-error "TAG_EXISTS_ALREADY_OR_SOMETHING_ELSE_IS_FUCKED"))
+ (resp-error "NO_USER")))
+
+(defn remove-tag [user-id message-id tag]
+ (let [query "user_id = ? AND message_id = ? AND lower(tag) = ?"]
+ (do-delete "tags" [query user-id (maybe-parse-int message-id) (normalize-tag-for-db (.toLowerCase tag))])
+ (resp-success "OK")))
+
+(defn validated-remove-tag [session params]
+ (if (session :nick)
+ (remove-tag (session :user_id) (params :message_id) (params :tag))
+ (resp-error "NO_USER")))
+
+(defnk tagged-dumps-template [session params tags url page-title :message-user-id false
+ :tag-user-id false
+ :logger (make-time-logger)]
+ (let [st (fetch-template "tagged_dumps" session)
+ offset (maybe-parse-int (params :offset) 0)
+ dump-offset (* offset *dumps-per-page*)
+ raw-dumps (logger tags/fetch-dumps-by-tag :tags tags
+ :image-only false
+ :amount (+ 1 *dumps-per-page*)
+ :offset dump-offset
+ :message-user-id message-user-id
+ :tag-user-id tag-user-id)
+ dumps (map tags/add-favorited-flag (take *dumps-per-page* raw-dumps) (repeat session))
+ dumps (map tags/remove-tags-for-output dumps)
+ dumps (logger doall (map process-message-for-output dumps))]
+ (if (> (count raw-dumps) *dumps-per-page*)
+ (.setAttribute st "next" (inc offset)))
+ (if (not= offset 0)
+ (.setAttribute st "prev" (max (dec offset) 0)))
+ (.setAttribute st "dumps" dumps)
+ (.setAttribute st "page_title" page-title)
+ (.setAttribute st "page_url" url)
+ (.setAttribute st "debug_log_items" (logger))
+ (.toString st)))
+
+;; gotta parse tag intersections myself because +'s get decoded into spaces
+;; there's probably a less hacky way to do this
+(defn tagged-dumps-by-nick [session params url]
+ (let [nick (params :nick)
+ tags (map url-decode (re-split #"\+" (undecoded-url-piece url 3)))
+ user-id (user-id-from-nick nick)
+ url (str "u/" nick "/tag/" (str-join "+" (map url-encode tags)))
+ page-title (str "dumps " nick " tagged as '" (escape-html (str-join "' and '" tags)) "'")]
+ (tagged-dumps-template session params tags url page-title :tag-user-id user-id)))
+
+(defn tagged-dumps [session params url]
+ (let [tags (map url-decode (re-split #"\+" (undecoded-url-piece url 1)))
+ url (str "tag/" (str-join "+" (map url-encode tags)))
+ page-title (str "dumps tagged as '" (escape-html (str-join "' and '" tags)) "'")]
+ (tagged-dumps-template session params tags url page-title)))
+
+(defn favorites [session params]
+ (let [nick (params :nick)
+ user-id (user-id-from-nick nick)
+ url (str "u/" nick "/favorites")
+ page-title (str nick "'s favorites")]
+ (tagged-dumps-template session params "favorite" url page-title :tag-user-id user-id)))
+
+(defn json-favorites [session params]
+ (let [nick (params :nick)
+ user-id (user-id-from-nick nick)
+ raw-favs (tags/fetch-dumps-by-tag :tags "favorite"
+ :image-only false
+ :amount 50
+ :offset 0
+ :tag-user-id user-id
+ :with-tags false)
+ favs (reduce (fn [m fav] (assoc m (str (fav :message_id)) (fav :content))) {} raw-favs)]
+ (str "RawFavs=" (json-str favs))))
+
+
+
+
;; Account resets
(defn reset-page [session]
@@ -884,13 +1026,14 @@
(GET "/favicon.ico" (serve-static "static" "favicon.ico"))
(GET "/u/:nick" (profile session (params :nick) "0"))
(GET "/u/:nick/" (profile session (params :nick) "0"))
- (GET "/u/:nick/tag/:tag" ("nuthin yet"))
- (GET "/u/:nick/tag/:tag/:offset" ("nuthin yet"))
- (GET "/u/:nick/favorites" ("nuthin yet"))
- (GET "/u/:nick/favorites/:offset" ("nuthing yet"))
+ (GET "/u/:nick/tag/:tag" (tagged-dumps-by-nick session params (request-url request)))
+ (GET "/u/:nick/tag/:tag/:offset" (tagged-dumps-by-nick session params (request-url request)))
+ (GET "/u/:nick/favorites" (favorites session params))
+ (GET "/u/:nick/favorites/:offset" (favorites session params))
+ (GET "/json/:nick/favorites" (json-favorites session params))
(GET "/u/:nick/:offset" (profile session
(params :nick)
- (params :offset)))
+ (params :offset))) ; have to put this route after favs
(GET "/p/:nick/:postid" (single-message session (params :nick) (params :postid)))
(GET "/login" (login session params cookies))
(GET "/logout" (logout session))
@@ -900,8 +1043,8 @@
(GET "/chat/:t" (no-cache (validated-chat session "dumpfm" (-> request :route-params :t))))
(GET "/browser" (browser session))
(GET "/refresh" (validated-refresh session params))
- (GET "/tag/:tag" ("nuthin yet"))
- (GET "/tag/:tag/:offset" ("nuthin yet"))
+ (GET "/tag/:tag" (tagged-dumps session params (request-url request)))
+ (GET "/tag/:tag/:offset" (tagged-dumps session params (request-url request)))
(POST "/cmd/tag/add" (validated-add-tag session params))
(POST "/cmd/tag/rm" (validated-remove-tag session params))
(GET "/log" (validated-log session "dumpfm" "0" params))
diff --git a/src/tags.clj b/src/tags.clj
index ba32635..77933d6 100644
--- a/src/tags.clj
+++ b/src/tags.clj
@@ -1,7 +1,9 @@
(ns tags
(:import java.lang.System)
(:use clojure.contrib.sql
+ clojure.contrib.def
clojure.contrib.json.write
+ clojure.contrib.str-utils
compojure
utils))
@@ -10,38 +12,10 @@
; (.replace tag " " "-"))
; todo: remove unicode escape sequences and line breaks and stuff?
-
; maybe show all dashes as spaces on output?
(defn normalize-tag-for-out [tag] (str tag))
-(defn add-tag [user-id message-id tag]
- (let [query "INSERT INTO tags2(user_id, message_id, tag) VALUES(?, ?, ?)"]
- (try
- (do-select [query user-id (maybe-parse-int message-id) (normalize-tag-for-db tag)])
- ; catch error when inserting duplicate tags
- (catch org.postgresql.util.PSQLException e
- resp-error "TAG_EXISTS_ALREADY_OR_SOMETHING_ELSE_IS_FUCKED")
- )))
-
-; to do: don't let people set tags on messages they can't access
-(defn validated-add-tag [session params]
- (if (session :nick)
- (add-tag (session :user_id) (params :message_id) (params :tag))
- (resp-error "NO_USER")))
-
-(defn remove-tag [user-id message-id tag]
- (let [query "DELETE FROM tags2 WHERE user_id = ? AND message_id = ? AND lower(tag) = ?"]
- (do-select [query user-id (maybe-parse-int message-id) (normalize-tag-for-db (.toLowerCase tag))])))
-
-(defn validated-remove-tag [session params]
- (if (session :nick)
- (remove-tag (session :user_id) (params :message_id) (params :tag))
- (resp-error "NO_USER")))
-
-
-(defn fetch-messages-by-tag [])
-
-; lol "Can't remove struct key"
+; lol "Can't remove struct key" (jdbc thing returns structified data)
(defn remove-tags-for-output [row]
(dissoc (into {} row) :tags))
@@ -51,15 +25,15 @@
; turn "a 1 b 2" to ({:nick a :tag 1}, {:nick b :tag 2})
; this seems to be a waste of cpu and memory, but i'm trying to make these easily accessible from templates
; also: i can't figure out how to access array indexes directly in StringTemplate templates
-(defn parse-tags-from-row [row]
- (assoc row :tags
- (for [t (partition 2 (.split (row :tags) " "))]
- {"nick" (first t) "tag" (last t)})))
+;; (defn parse-tags-from-row [row]
+;; (assoc row :tags
+;; (for [t (partition 2 (.split (row :tags) " "))]
+;; {"nick" (first t) "tag" (last t)})))
(defn parse-tags-from-string [string]
(partition 2 (.split string " ")))
-(defn build-tag-map-by-tag [tags nick-tag]
+(defn build-tag-map [tags nick-tag]
(let [nick (first nick-tag) tag (last nick-tag)]
(if (tags tag)
(assoc tags tag (conj (tags tag) nick))
@@ -67,9 +41,11 @@
; making something like {"tag1": ["nick a", "nick b"], "tag2": ["nick c", "nick d"]}
; why is building data structures in clojure so hard for me
-(defn parse-tags-from-row-as-map-by-tag [row]
- (assoc row :tags
- (reduce build-tag-map-by-tag {} (parse-tags-from-string (row :tags)))))
+(defn parse-tags-from-row-as-tag-map [row]
+ (if (row :tags)
+ (assoc row :tags
+ (reduce build-tag-map {} (parse-tags-from-string (row :tags))))
+ row))
(defn does-tag-exist?
([tags tag]
@@ -86,36 +62,173 @@
(defn explain-query [query] (str "EXPLAIN ANALYZE " query))
-; todo: offset is slow. probably need to include message_id in 'next page' url (tumblr & reddit do that)
-(defn fetch-messages-with-tags-by-room-query [image-only]
- (str "SELECT m.content, m.message_id, m.created_on, u.nick, u.avatar,
+;; OFFSET is very slow when it is large
+;; so, a subquery could be used when offset is large
+;; one other thing we could do is include message_id in 'next page' url (tumblr & reddit do that for example)
+;; but probably people don't page back too far anyway
+;; (not used yet)
+(defn fetch-dumps-by-room-query-faster [image-only] (str
+" SELECT
+ m.content, m.message_id, m.created_on,
+ u.nick, u.avatar,
+ array_to_string(ARRAY(SELECT nick || ' ' || tag
+ FROM tags, users
+ WHERE message_id = m.message_id AND tags.user_id = users.user_id), ' ') as tags
+ FROM (SELECT message_id
+ FROM messages
+ WHERE room_id = ? "
+ (if image-only "AND is_image = true " "")
+ " ORDER BY created_on DESC
+ LIMIT ? OFFSET ?) as sq, users u, messages m
+ WHERE sq.message_id = m.message_id
+ AND u.user_id = m.user_id"))
+
+(defn fetch-dumps-by-room-query [image-only] (str
+" SELECT
+ m.content, m.message_id, m.created_on,
+ u.nick, u.avatar,
array_to_string(ARRAY(SELECT nick || ' ' || tag
- FROM tags2, users
- WHERE message_id = m.message_id AND tags2.user_id = users.user_id), ' ') as tags
+ FROM tags, users
+ WHERE message_id = m.message_id AND tags.user_id = users.user_id), ' ') as tags
FROM users u, messages m
- WHERE room_id = ? AND m.user_id = u.user_id "
- (if image-only "AND m.is_image = true " "")
- "ORDER BY created_on DESC
+ WHERE room_id = ?
+ AND m.user_id = u.user_id "
+ (if image-only "AND m.is_image = true " "")
+" ORDER BY created_on DESC
LIMIT ? OFFSET ?"))
-; offset is slow... problem with this one though it doesn't take into account deleted messages
-(defn fetch-messages-with-tags-by-room-fast-paging-query [image-only]
- (str "SELECT m.content, m.message_id, m.created_on, u.nick, u.avatar,
+;; unoptimized
+;; the room join here is very bad on large offsets...
+(defn fetch-dumps-by-nick-query [image-only] (str
+" SELECT
+ m.content, m.message_id, m.created_on,
+ u.nick, u.avatar, r.key,
array_to_string(ARRAY(SELECT nick || ' ' || tag
- FROM tags2, users
- WHERE message_id = m.message_id AND tags2.user_id = users.user_id), ' ') as tags
- FROM users u, messages m
- WHERE room_id = ? AND m.user_id = u.user_id
- AND m.message_id BETWEEN ? AND ? "
- (if image-only "AND m.is_image = true " "")
- "ORDER BY created_on DESC
- LIMIT ?"))
+ FROM tags, users
+ WHERE message_id = m.message_id AND tags.user_id = users.user_id), ' ') as tags
+ FROM messages m, users u, rooms r
+ WHERE m.user_id = u.user_id AND u.nick = ?
+ AND r.room_id = m.room_id AND r.admin_only = false "
+ (if image-only "AND m.is_image = true " "")
+" ORDER BY created_on DESC
+ LIMIT ? OFFSET ?"))
+
+;; SPEED HACK
+;; explicit use of room ids, but fast
+;; (not used yet)
+(defnk fetch-dumps-by-user-id-query [:image-only true :with-tags true] (str
+" SELECT
+ m.content, m.message_id, m.created_on,
+ u.nick, u.avatar, r.key"
+ (if with-tags ",
+ array_to_string(ARRAY(SELECT nick || ' ' || tag
+ FROM tags, users
+ WHERE message_id = m.message_id AND tags.user_id = users.user_id), ' ') as tags " "")
+" FROM (SELECT message_id
+ FROM messages as m
+ WHERE m.user_id = ?
+ AND m.room_id IN (1) "
+ (if image-only "AND m.is_image = true " "")
+ " ORDER BY m.message_id DESC
+ LIMIT ? OFFSET ?
+ ) as sq, messages m, users u, rooms r
+ WHERE m.message_id = sq.message_id
+ AND m.user_id = u.user_id
+ AND r.room_id = m.room_id"))
+
+;; todo: only return distinct dumps? sorted by tag.created_on?
+(defnk fetch-dumps-by-tag-query [:image-only true :message-user-id false :tag-user-id false :with-tags true] (str
+" SELECT
+ m.content, m.message_id, m.created_on,
+ u.nick, u.avatar, r.key "
+ (if with-tags ",
+ array_to_string(ARRAY(SELECT nick || ' ' || tag
+ FROM tags, users
+ WHERE message_id = m.message_id AND tags.user_id = users.user_id), ' ') as tags " "")
+" FROM (SELECT
+ m.message_id
+ FROM messages as m, tags as t
+ WHERE lower(t.tag) = ? "
+ (if message-user-id "AND m.user_id = ? " "")
+ (if tag-user-id "AND t.user_id = ? " "")
+ (if image-only "AND m.is_image = true " "")
+ " AND m.message_id = t.message_id
+ ORDER BY m.message_id DESC
+ LIMIT ? OFFSET ?
+ ) as sq, messages m, users u, rooms r
+ WHERE m.message_id = sq.message_id
+ AND m.user_id = u.user_id
+ AND r.room_id = m.room_id"))
+
+;; tag intersections
+(defnk fetch-dumps-by-tags-query [:image-only true :num-tags 1 :message-user-id false :tag-user-id false :with-tags true] (str
+" SELECT
+ m.content, m.message_id, m.created_on,
+ u.nick, u.avatar, r.key "
+ (if with-tags ",
+ array_to_string(ARRAY(SELECT nick || ' ' || tag
+ FROM tags, users
+ WHERE message_id = m.message_id AND tags.user_id = users.user_id), ' ') as tags " "")
+" FROM (SELECT
+ m.message_id"
+ " FROM messages as m
+ INNER JOIN tags USING (message_id)
+ WHERE lower(tags.tag) IN (" (str-join ", " (take num-tags (repeat "?"))) ") "
+ (if message-user-id "AND m.user_id = ? " "")
+ (if tag-user-id "AND tags.user_id = ? " "")
+ (if image-only "AND m.is_image = true " "")
+ " GROUP BY m.message_id HAVING COUNT(*) = " (str num-tags) "
+ ORDER BY m.message_id DESC
+ LIMIT ? OFFSET ?
+ ) as sq, messages m, users u, rooms r
+ WHERE m.message_id = sq.message_id
+ AND m.user_id = u.user_id
+ AND r.room_id = m.room_id"))
+
+
+(defnk fetch-dumps-by-room [:room-id 1 :image-only true :amount *dumps-per-page* :offset 0]
+ (let [query (fetch-dumps-by-room-query image-only)]
+ (let [rows (do-select [query room-id amount offset])]
+ (map parse-tags-from-row-as-tag-map rows))))
+
+(defnk fetch-dumps-by-nick [:nick "anon" :image-only true :amount *dumps-per-page* :offset 0]
+ (let [query (fetch-dumps-by-nick-query image-only)]
+ (let [rows (do-select [query nick amount offset])]
+ (map parse-tags-from-row-as-tag-map rows))))
+
+(defnk fetch-dumps-by-user-id [user-id :image-only true :amount *dumps-per-page* :offset 0]
+ (let [query (fetch-dumps-by-user-id-query image-only)]
+ (let [rows (do-select [query user-id amount offset])]
+ (map parse-tags-from-row-as-tag-map rows))))
+
+(defnk fetch-dumps-by-tag [:tags "favorite"
+ :message-user-id false
+ :tag-user-id false
+ :image-only true
+ :amount *dumps-per-page*
+ :offset 0
+ :with-tags true]
+ (let [tags (if (string? tags) [tags] tags)
+ query (if (= 1 (count tags))
+ (fetch-dumps-by-tag-query :image-only image-only
+ :message-user-id message-user-id
+ :tag-user-id tag-user-id
+ :with-tags with-tags)
+ (fetch-dumps-by-tags-query :image-only image-only
+ :message-user-id message-user-id
+ :tag-user-id tag-user-id
+ :num-tags (count tags)
+ :with-tags with-tags))
+ query-vars [amount offset]
+ query-vars (if message-user-id (concat [message-user-id] query-vars ) query-vars)
+ query-vars (if tag-user-id (concat [tag-user-id] query-vars ) query-vars)
+ rows (do-select (vec (concat [query] tags query-vars)))]
+ (map parse-tags-from-row-as-tag-map rows)))
-(defn fetch-messages-with-tags-by-room
- ([] (fetch-messages-with-tags-by-room 1 true *dumps-per-page* 0))
- ([room-id] (fetch-messages-with-tags-by-room room-id true *dumps-per-page* 0))
- ([room-id image-only] (fetch-messages-with-tags-by-room room-id image-only *dumps-per-page* 0))
- ([room-id image-only amount offset]
- (let [query (fetch-messages-with-tags-by-room-query image-only)]
- (let [rows (do-select [query room-id amount offset])]
- (map parse-tags-from-row-as-map-by-tag rows)))))
+
+
+
+
+
+
+ \ No newline at end of file
diff --git a/src/utils.clj b/src/utils.clj
index 92fdad6..3f2f4e4 100755
--- a/src/utils.clj
+++ b/src/utils.clj
@@ -1,6 +1,7 @@
(ns utils
(:import java.text.SimpleDateFormat
- java.util.Date)
+ java.util.Date
+ java.net.URLDecoder)
(:use clojure.contrib.json.write
clojure.contrib.sql))
@@ -13,6 +14,8 @@
:user "postgres"
:password "root"}))
+;; moved this to here which doesn't seem right... maybe a 'settings.clj' or something?
+(def *dumps-per-page* 20)
;; Misc
@@ -70,6 +73,10 @@
(with-connection *db*
(delete-rows table query)))
+(defn do-insert [table cols values]
+ (with-connection *db*
+ (insert-values table cols values)))
+
;; Parsing
(defn maybe-parse-int
@@ -79,4 +86,12 @@
(catch NumberFormatException _ default))))
(defn maybe-parse-long [s f]
- (if s (Long/parseLong s) f)) \ No newline at end of file
+ (if s (Long/parseLong s) f))
+
+(defn url-decode [text]
+ (URLDecoder/decode text "UTF-8"))
+
+(defn #^String lower-case
+ "Converts string to all lower-case."
+ [#^String s]
+ (.toLowerCase s)) \ No newline at end of file
diff --git a/static/css/dump.css b/static/css/dump.css
index 101f7f5..5c912e7 100755
--- a/static/css/dump.css
+++ b/static/css/dump.css
@@ -315,6 +315,52 @@ margin:-2;
color: #666;
}
+#palette-button {
+ border-top-right-radius:10px;
+ -webkit-border-top-right-radius:5px;
+ -moz-border-radius-topright:5px;
+ border-bottom-right-radius:5px;
+ -webkit-border-bottom-right-radius:5px;
+ -moz-border-radius-bottomright:5px;
+ border-radius:5px;
+ position:absolute;
+/* display:inline-block;*/
+ display: none;
+ width:40px;
+ height:34px;
+ right:260px;
+ top:15px;
+ padding-bottom:1;
+ text-align:center;
+ z-index:100;
+ cursor:pointer;
+ background-color:#0c8fff;
+ font-size:12px;
+ color:#fff;
+ text-shadow:1px 1px 3px rgba(0,0,0,1);
+}
+
+#palette {
+ position: absolute;
+ right: 30px;
+ bottom: 80px;
+ width: 420px;
+ height: 400px;
+ background-color: black;
+ z-index: 1000000000;
+ display: none;
+ overflow-y: scroll;
+}
+
+#palette-thumbs {
+}
+
+#palette-thumbs img {
+ max-width: 100px;
+ max-height: 100px;
+}
+
+
#userList {
overflow-x: hidden;
overflow-y:auto;
diff --git a/static/css/dumpnewuser.css b/static/css/dumpnewuser.css
index 492b4ac..1c92e62 100644
--- a/static/css/dumpnewuser.css
+++ b/static/css/dumpnewuser.css
@@ -1,4 +1,3 @@
-/* pichat.css */
body {
diff --git a/static/css/header.css b/static/css/header.css
index 2e6a3e6..b56b73e 100755
--- a/static/css/header.css
+++ b/static/css/header.css
@@ -260,4 +260,58 @@ background-image:url(/static/img/ieburnbtn.gif); font-size:40;color:white;height
padding-right:400;padding-left:50;
font-weight:bold;
+}
+
+.thumb {
+ cursor:pointer;
+}
+
+img.chat-thumb {
+ position: absolute;
+ /*margin-top: -10px;*/
+ width: 16px;
+ height: 16px;
+ bottom: 4px;
+ right: 4px;
+ /*left: 4px;*/
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+
+
+.dump .nick {
+ position: relative;
+ padding-right: 15px;
+}
+
+/* sharing */
+.buttons{
+ cursor:pointer;
+ padding-top: 8px;
+}
+.buttons .share {
+ opacity: 0.2;
+}
+.buttons .share:hover {
+ opacity: 1;
+}
+.buttons .other-sites {
+ padding-left: 20px;
+}
+.buttons img.thumb {
+ position: absolute;
+ margin-left: 0px;
+ margin-top: 0px;
+ height: 16px;
+ width: 16px;
+ display: block;
+}
+.buttons .thumb.favorite {
+ position: absolute;
+ margin-left: 0px;
+ margin-top: 0px;
+ height: 16px;
+ width: 16px;
+ display: block;
} \ No newline at end of file
diff --git a/static/img/palette.gif b/static/img/palette.gif
new file mode 100644
index 0000000..ffe5b3e
--- /dev/null
+++ b/static/img/palette.gif
Binary files differ
diff --git a/static/img/thumbs/bw.left.gif b/static/img/thumbs/bw.left.gif
new file mode 100644
index 0000000..5639c26
--- /dev/null
+++ b/static/img/thumbs/bw.left.gif
Binary files differ
diff --git a/static/img/thumbs/bw.right.gif b/static/img/thumbs/bw.right.gif
new file mode 100644
index 0000000..9d0c65b
--- /dev/null
+++ b/static/img/thumbs/bw.right.gif
Binary files differ
diff --git a/static/img/thumbs/bw.right.tiny.gif b/static/img/thumbs/bw.right.tiny.gif
new file mode 100644
index 0000000..c3b5406
--- /dev/null
+++ b/static/img/thumbs/bw.right.tiny.gif
Binary files differ
diff --git a/static/img/thumbs/circle.gif b/static/img/thumbs/circle.gif
new file mode 100644
index 0000000..69acdf2
--- /dev/null
+++ b/static/img/thumbs/circle.gif
Binary files differ
diff --git a/static/img/thumbs/color.left.2x.gif b/static/img/thumbs/color.left.2x.gif
new file mode 100644
index 0000000..1601b56
--- /dev/null
+++ b/static/img/thumbs/color.left.2x.gif
Binary files differ
diff --git a/static/img/thumbs/color.left.3x.gif b/static/img/thumbs/color.left.3x.gif
new file mode 100644
index 0000000..beab03f
--- /dev/null
+++ b/static/img/thumbs/color.left.3x.gif
Binary files differ
diff --git a/static/img/thumbs/color.left.4x.gif b/static/img/thumbs/color.left.4x.gif
new file mode 100644
index 0000000..ddde895
--- /dev/null
+++ b/static/img/thumbs/color.left.4x.gif
Binary files differ
diff --git a/static/img/thumbs/color.left.gif b/static/img/thumbs/color.left.gif
new file mode 100644
index 0000000..a855ffc
--- /dev/null
+++ b/static/img/thumbs/color.left.gif
Binary files differ
diff --git a/static/img/thumbs/color.right.2x.gif b/static/img/thumbs/color.right.2x.gif
new file mode 100644
index 0000000..778b31d
--- /dev/null
+++ b/static/img/thumbs/color.right.2x.gif
Binary files differ
diff --git a/static/img/thumbs/color.right.3x.gif b/static/img/thumbs/color.right.3x.gif
new file mode 100644
index 0000000..748813c
--- /dev/null
+++ b/static/img/thumbs/color.right.3x.gif
Binary files differ
diff --git a/static/img/thumbs/color.right.4x.gif b/static/img/thumbs/color.right.4x.gif
new file mode 100644
index 0000000..082de93
--- /dev/null
+++ b/static/img/thumbs/color.right.4x.gif
Binary files differ
diff --git a/static/img/thumbs/color.right.gif b/static/img/thumbs/color.right.gif
new file mode 100644
index 0000000..dbf2052
--- /dev/null
+++ b/static/img/thumbs/color.right.gif
Binary files differ
diff --git a/static/img/thumbs/pink.circle.gif b/static/img/thumbs/pink.circle.gif
new file mode 100644
index 0000000..19f6e6c
--- /dev/null
+++ b/static/img/thumbs/pink.circle.gif
Binary files differ
diff --git a/static/img/thumbs/pink.diamond.gif b/static/img/thumbs/pink.diamond.gif
new file mode 100644
index 0000000..cec57ff
--- /dev/null
+++ b/static/img/thumbs/pink.diamond.gif
Binary files differ
diff --git a/static/img/thumbs/pink.right.4x.gif b/static/img/thumbs/pink.right.4x.gif
new file mode 100644
index 0000000..a7dc608
--- /dev/null
+++ b/static/img/thumbs/pink.right.4x.gif
Binary files differ
diff --git a/static/img/thumbs/transparent.circle.gif b/static/img/thumbs/transparent.circle.gif
new file mode 100644
index 0000000..fd95786
--- /dev/null
+++ b/static/img/thumbs/transparent.circle.gif
Binary files differ
diff --git a/static/js/jquery-ui-1.8.effects.min.js b/static/js/jquery-ui-1.8.effects.min.js
new file mode 100644
index 0000000..7011928
--- /dev/null
+++ b/static/js/jquery-ui-1.8.effects.min.js
@@ -0,0 +1,7 @@
+/* add more effects in here if u need em --timb */
+/* effects core and easing */
+jQuery.effects||(function(g){g.effects={};g.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","color","outlineColor"],function(l,k){g.fx.step[k]=function(m){if(!m.colorInit){m.start=j(m.elem,k);m.end=i(m.end);m.colorInit=true}m.elem.style[k]="rgb("+Math.max(Math.min(parseInt((m.pos*(m.end[0]-m.start[0]))+m.start[0],10),255),0)+","+Math.max(Math.min(parseInt((m.pos*(m.end[1]-m.start[1]))+m.start[1],10),255),0)+","+Math.max(Math.min(parseInt((m.pos*(m.end[2]-m.start[2]))+m.start[2],10),255),0)+")"}});function i(l){var k;if(l&&l.constructor==Array&&l.length==3){return l}if(k=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(l)){return[parseInt(k[1],10),parseInt(k[2],10),parseInt(k[3],10)]}if(k=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(l)){return[parseFloat(k[1])*2.55,parseFloat(k[2])*2.55,parseFloat(k[3])*2.55]}if(k=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(l)){return[parseInt(k[1],16),parseInt(k[2],16),parseInt(k[3],16)]}if(k=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(l)){return[parseInt(k[1]+k[1],16),parseInt(k[2]+k[2],16),parseInt(k[3]+k[3],16)]}if(k=/rgba\(0, 0, 0, 0\)/.exec(l)){return a.transparent}return a[g.trim(l).toLowerCase()]}function j(m,k){var l;do{l=g.curCSS(m,k);if(l!=""&&l!="transparent"||g.nodeName(m,"body")){break}k="backgroundColor"}while(m=m.parentNode);return i(l)}var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]};var e=["add","remove","toggle"],c={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};function f(){var n=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,o={},l,m;if(n&&n.length&&n[0]&&n[n[0]]){var k=n.length;while(k--){l=n[k];if(typeof n[l]=="string"){m=l.replace(/\-(\w)/g,function(p,q){return q.toUpperCase()});o[m]=n[l]}}}else{for(l in n){if(typeof n[l]==="string"){o[l]=n[l]}}}return o}function b(l){var k,m;for(k in l){m=l[k];if(m==null||g.isFunction(m)||k in c||(/scrollbar/).test(k)||(!(/color/i).test(k)&&isNaN(parseFloat(m)))){delete l[k]}}return l}function h(k,m){var n={_:0},l;for(l in m){if(k[l]!=m[l]){n[l]=m[l]}}return n}g.effects.animateClass=function(k,l,n,m){if(g.isFunction(n)){m=n;n=null}return this.each(function(){var r=g(this),o=r.attr("style")||" ",s=b(f.call(this)),q,p=r.attr("className");g.each(e,function(t,u){if(k[u]){r[u+"Class"](k[u])}});q=b(f.call(this));r.attr("className",p);r.animate(h(s,q),l,n,function(){g.each(e,function(t,u){if(k[u]){r[u+"Class"](k[u])}});if(typeof r.attr("style")=="object"){r.attr("style").cssText="";r.attr("style").cssText=o}else{r.attr("style",o)}if(m){m.apply(this,arguments)}})})};g.fn.extend({_addClass:g.fn.addClass,addClass:function(l,k,n,m){return k?g.effects.animateClass.apply(this,[{add:l},k,n,m]):this._addClass(l)},_removeClass:g.fn.removeClass,removeClass:function(l,k,n,m){return k?g.effects.animateClass.apply(this,[{remove:l},k,n,m]):this._removeClass(l)},_toggleClass:g.fn.toggleClass,toggleClass:function(m,l,k,o,n){if(typeof l=="boolean"||l===undefined){if(!k){return this._toggleClass(m,l)}else{return g.effects.animateClass.apply(this,[(l?{add:m}:{remove:m}),k,o,n])}}else{return g.effects.animateClass.apply(this,[{toggle:m},l,k,o])}},switchClass:function(k,m,l,o,n){return g.effects.animateClass.apply(this,[{add:m,remove:k},l,o,n])}});g.extend(g.effects,{version:"1.8",save:function(l,m){for(var k=0;k<m.length;k++){if(m[k]!==null){l.data("ec.storage."+m[k],l[0].style[m[k]])}}},restore:function(l,m){for(var k=0;k<m.length;k++){if(m[k]!==null){l.css(m[k],l.data("ec.storage."+m[k]))}}},setMode:function(k,l){if(l=="toggle"){l=k.is(":hidden")?"show":"hide"}return l},getBaseline:function(l,m){var n,k;switch(l[0]){case"top":n=0;break;case"middle":n=0.5;break;case"bottom":n=1;break;default:n=l[0]/m.height}switch(l[1]){case"left":k=0;break;case"center":k=0.5;break;case"right":k=1;break;default:k=l[1]/m.width}return{x:k,y:n}},createWrapper:function(k){if(k.parent().is(".ui-effects-wrapper")){return k.parent()}var l={width:k.outerWidth(true),height:k.outerHeight(true),"float":k.css("float")},m=g("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});k.wrap(m);m=k.parent();if(k.css("position")=="static"){m.css({position:"relative"});k.css({position:"relative"})}else{g.extend(l,{position:k.css("position"),zIndex:k.css("z-index")});g.each(["top","left","bottom","right"],function(n,o){l[o]=k.css(o);if(isNaN(parseInt(l[o],10))){l[o]="auto"}});k.css({position:"relative",top:0,left:0})}return m.css(l).show()},removeWrapper:function(k){if(k.parent().is(".ui-effects-wrapper")){return k.parent().replaceWith(k)}return k},setTransition:function(l,n,k,m){m=m||{};g.each(n,function(p,o){unit=l.cssUnit(o);if(unit[0]>0){m[o]=unit[0]*k+unit[1]}});return m}});function d(l,k,m,n){if(typeof l=="object"){n=k;m=null;k=l;l=k.effect}if(g.isFunction(k)){n=k;m=null;k={}}if(g.isFunction(m)){n=m;m=null}if(typeof k=="number"||g.fx.speeds[k]){n=m;m=k;k={}}k=k||{};m=m||k.duration;m=g.fx.off?0:typeof m=="number"?m:g.fx.speeds[m]||g.fx.speeds._default;n=n||k.complete;return[l,k,m,n]}g.fn.extend({effect:function(n,m,p,q){var l=d.apply(this,arguments),o={options:l[1],duration:l[2],callback:l[3]},k=g.effects[n];return k&&!g.fx.off?k.call(this,o):this},_show:g.fn.show,show:function(l){if(!l||typeof l=="number"||g.fx.speeds[l]){return this._show.apply(this,arguments)}else{var k=d.apply(this,arguments);k[1].mode="show";return this.effect.apply(this,k)}},_hide:g.fn.hide,hide:function(l){if(!l||typeof l=="number"||g.fx.speeds[l]){return this._hide.apply(this,arguments)}else{var k=d.apply(this,arguments);k[1].mode="hide";return this.effect.apply(this,k)}},__toggle:g.fn.toggle,toggle:function(l){if(!l||typeof l=="number"||g.fx.speeds[l]||typeof l=="boolean"||g.isFunction(l)){return this.__toggle.apply(this,arguments)}else{var k=d.apply(this,arguments);k[1].mode="toggle";return this.effect.apply(this,k)}},cssUnit:function(k){var l=this.css(k),m=[];g.each(["em","px","%","pt"],function(n,o){if(l.indexOf(o)>0){m=[parseFloat(l),o]}});return m}});g.easing.jswing=g.easing.swing;g.extend(g.easing,{def:"easeOutQuad",swing:function(l,m,k,o,n){return g.easing[g.easing.def](l,m,k,o,n)},easeInQuad:function(l,m,k,o,n){return o*(m/=n)*m+k},easeOutQuad:function(l,m,k,o,n){return -o*(m/=n)*(m-2)+k},easeInOutQuad:function(l,m,k,o,n){if((m/=n/2)<1){return o/2*m*m+k}return -o/2*((--m)*(m-2)-1)+k},easeInCubic:function(l,m,k,o,n){return o*(m/=n)*m*m+k},easeOutCubic:function(l,m,k,o,n){return o*((m=m/n-1)*m*m+1)+k},easeInOutCubic:function(l,m,k,o,n){if((m/=n/2)<1){return o/2*m*m*m+k}return o/2*((m-=2)*m*m+2)+k},easeInQuart:function(l,m,k,o,n){return o*(m/=n)*m*m*m+k},easeOutQuart:function(l,m,k,o,n){return -o*((m=m/n-1)*m*m*m-1)+k},easeInOutQuart:function(l,m,k,o,n){if((m/=n/2)<1){return o/2*m*m*m*m+k}return -o/2*((m-=2)*m*m*m-2)+k},easeInQuint:function(l,m,k,o,n){return o*(m/=n)*m*m*m*m+k},easeOutQuint:function(l,m,k,o,n){return o*((m=m/n-1)*m*m*m*m+1)+k},easeInOutQuint:function(l,m,k,o,n){if((m/=n/2)<1){return o/2*m*m*m*m*m+k}return o/2*((m-=2)*m*m*m*m+2)+k},easeInSine:function(l,m,k,o,n){return -o*Math.cos(m/n*(Math.PI/2))+o+k},easeOutSine:function(l,m,k,o,n){return o*Math.sin(m/n*(Math.PI/2))+k},easeInOutSine:function(l,m,k,o,n){return -o/2*(Math.cos(Math.PI*m/n)-1)+k},easeInExpo:function(l,m,k,o,n){return(m==0)?k:o*Math.pow(2,10*(m/n-1))+k},easeOutExpo:function(l,m,k,o,n){return(m==n)?k+o:o*(-Math.pow(2,-10*m/n)+1)+k},easeInOutExpo:function(l,m,k,o,n){if(m==0){return k}if(m==n){return k+o}if((m/=n/2)<1){return o/2*Math.pow(2,10*(m-1))+k}return o/2*(-Math.pow(2,-10*--m)+2)+k},easeInCirc:function(l,m,k,o,n){return -o*(Math.sqrt(1-(m/=n)*m)-1)+k},easeOutCirc:function(l,m,k,o,n){return o*Math.sqrt(1-(m=m/n-1)*m)+k},easeInOutCirc:function(l,m,k,o,n){if((m/=n/2)<1){return -o/2*(Math.sqrt(1-m*m)-1)+k}return o/2*(Math.sqrt(1-(m-=2)*m)+1)+k},easeInElastic:function(l,n,k,u,r){var o=1.70158;var q=0;var m=u;if(n==0){return k}if((n/=r)==1){return k+u}if(!q){q=r*0.3}if(m<Math.abs(u)){m=u;var o=q/4}else{var o=q/(2*Math.PI)*Math.asin(u/m)}return -(m*Math.pow(2,10*(n-=1))*Math.sin((n*r-o)*(2*Math.PI)/q))+k},easeOutElastic:function(l,n,k,u,r){var o=1.70158;var q=0;var m=u;if(n==0){return k}if((n/=r)==1){return k+u}if(!q){q=r*0.3}if(m<Math.abs(u)){m=u;var o=q/4}else{var o=q/(2*Math.PI)*Math.asin(u/m)}return m*Math.pow(2,-10*n)*Math.sin((n*r-o)*(2*Math.PI)/q)+u+k},easeInOutElastic:function(l,n,k,u,r){var o=1.70158;var q=0;var m=u;if(n==0){return k}if((n/=r/2)==2){return k+u}if(!q){q=r*(0.3*1.5)}if(m<Math.abs(u)){m=u;var o=q/4}else{var o=q/(2*Math.PI)*Math.asin(u/m)}if(n<1){return -0.5*(m*Math.pow(2,10*(n-=1))*Math.sin((n*r-o)*(2*Math.PI)/q))+k}return m*Math.pow(2,-10*(n-=1))*Math.sin((n*r-o)*(2*Math.PI)/q)*0.5+u+k},easeInBack:function(l,m,k,p,o,n){if(n==undefined){n=1.70158}return p*(m/=o)*m*((n+1)*m-n)+k},easeOutBack:function(l,m,k,p,o,n){if(n==undefined){n=1.70158}return p*((m=m/o-1)*m*((n+1)*m+n)+1)+k},easeInOutBack:function(l,m,k,p,o,n){if(n==undefined){n=1.70158}if((m/=o/2)<1){return p/2*(m*m*(((n*=(1.525))+1)*m-n))+k}return p/2*((m-=2)*m*(((n*=(1.525))+1)*m+n)+2)+k},easeInBounce:function(l,m,k,o,n){return o-g.easing.easeOutBounce(l,n-m,0,o,n)+k},easeOutBounce:function(l,m,k,o,n){if((m/=n)<(1/2.75)){return o*(7.5625*m*m)+k}else{if(m<(2/2.75)){return o*(7.5625*(m-=(1.5/2.75))*m+0.75)+k}else{if(m<(2.5/2.75)){return o*(7.5625*(m-=(2.25/2.75))*m+0.9375)+k}else{return o*(7.5625*(m-=(2.625/2.75))*m+0.984375)+k}}}},easeInOutBounce:function(l,m,k,o,n){if(m<n/2){return g.easing.easeInBounce(l,m*2,0,o,n)*0.5+k}return g.easing.easeOutBounce(l,m*2-n,0,o,n)*0.5+o*0.5+k}})})(jQuery);
+/* scale effects */
+(function(a){a.effects.puff=function(b){return this.queue(function(){var f=a(this),g=a.effects.setMode(f,b.options.mode||"hide"),e=parseInt(b.options.percent,10)||150,d=e/100,c={height:f.height(),width:f.width()};a.extend(b.options,{fade:true,mode:g,percent:g=="hide"?e:100,from:g=="hide"?c:{height:c.height*d,width:c.width*d}});f.effect("scale",b.options,b.duration,b.callback);f.dequeue()})};a.effects.scale=function(b){return this.queue(function(){var g=a(this);var d=a.extend(true,{},b.options);var j=a.effects.setMode(g,b.options.mode||"effect");var h=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:(j=="hide"?0:100));var i=b.options.direction||"both";var c=b.options.origin;if(j!="effect"){d.origin=c||["middle","center"];d.restore=true}var f={height:g.height(),width:g.width()};g.from=b.options.from||(j=="show"?{height:0,width:0}:f);var e={y:i!="horizontal"?(h/100):1,x:i!="vertical"?(h/100):1};g.to={height:f.height*e.y,width:f.width*e.x};if(b.options.fade){if(j=="show"){g.from.opacity=0;g.to.opacity=1}if(j=="hide"){g.from.opacity=1;g.to.opacity=0}}d.from=g.from;d.to=g.to;d.mode=j;g.effect("size",d,b.duration,b.callback);g.dequeue()})};a.effects.size=function(b){return this.queue(function(){var c=a(this),n=["position","top","left","width","height","overflow","opacity"];var m=["position","top","left","overflow","opacity"];var j=["width","height","overflow"];var p=["fontSize"];var k=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"];var f=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"];var g=a.effects.setMode(c,b.options.mode||"effect");var i=b.options.restore||false;var e=b.options.scale||"both";var o=b.options.origin;var d={height:c.height(),width:c.width()};c.from=b.options.from||d;c.to=b.options.to||d;if(o){var h=a.effects.getBaseline(o,d);c.from.top=(d.height-c.from.height)*h.y;c.from.left=(d.width-c.from.width)*h.x;c.to.top=(d.height-c.to.height)*h.y;c.to.left=(d.width-c.to.width)*h.x}var l={from:{y:c.from.height/d.height,x:c.from.width/d.width},to:{y:c.to.height/d.height,x:c.to.width/d.width}};if(e=="box"||e=="both"){if(l.from.y!=l.to.y){n=n.concat(k);c.from=a.effects.setTransition(c,k,l.from.y,c.from);c.to=a.effects.setTransition(c,k,l.to.y,c.to)}if(l.from.x!=l.to.x){n=n.concat(f);c.from=a.effects.setTransition(c,f,l.from.x,c.from);c.to=a.effects.setTransition(c,f,l.to.x,c.to)}}if(e=="content"||e=="both"){if(l.from.y!=l.to.y){n=n.concat(p);c.from=a.effects.setTransition(c,p,l.from.y,c.from);c.to=a.effects.setTransition(c,p,l.to.y,c.to)}}a.effects.save(c,i?n:m);c.show();a.effects.createWrapper(c);c.css("overflow","hidden").css(c.from);if(e=="content"||e=="both"){k=k.concat(["marginTop","marginBottom"]).concat(p);f=f.concat(["marginLeft","marginRight"]);j=n.concat(k).concat(f);c.find("*[width]").each(function(){child=a(this);if(i){a.effects.save(child,j)}var q={height:child.height(),width:child.width()};child.from={height:q.height*l.from.y,width:q.width*l.from.x};child.to={height:q.height*l.to.y,width:q.width*l.to.x};if(l.from.y!=l.to.y){child.from=a.effects.setTransition(child,k,l.from.y,child.from);child.to=a.effects.setTransition(child,k,l.to.y,child.to)}if(l.from.x!=l.to.x){child.from=a.effects.setTransition(child,f,l.from.x,child.from);child.to=a.effects.setTransition(child,f,l.to.x,child.to)}child.css(child.from);child.animate(child.to,b.duration,b.options.easing,function(){if(i){a.effects.restore(child,j)}})})}c.animate(c.to,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){if(c.to.opacity===0){c.css("opacity",c.from.opacity)}if(g=="hide"){c.hide()}a.effects.restore(c,i?n:m);a.effects.removeWrapper(c);if(b.callback){b.callback.apply(this,arguments)}c.dequeue()}})})}})(jQuery);
+/* explode effects */
+(function(a){a.effects.explode=function(b){return this.queue(function(){var k=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3;var e=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3;b.options.mode=b.options.mode=="toggle"?(a(this).is(":visible")?"hide":"show"):b.options.mode;var h=a(this).show().css("visibility","hidden");var l=h.offset();l.top-=parseInt(h.css("marginTop"),10)||0;l.left-=parseInt(h.css("marginLeft"),10)||0;var g=h.outerWidth(true);var c=h.outerHeight(true);for(var f=0;f<k;f++){for(var d=0;d<e;d++){h.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-d*(g/e),top:-f*(c/k)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:g/e,height:c/k,left:l.left+d*(g/e)+(b.options.mode=="show"?(d-Math.floor(e/2))*(g/e):0),top:l.top+f*(c/k)+(b.options.mode=="show"?(f-Math.floor(k/2))*(c/k):0),opacity:b.options.mode=="show"?0:1}).animate({left:l.left+d*(g/e)+(b.options.mode=="show"?0:(d-Math.floor(e/2))*(g/e)),top:l.top+f*(c/k)+(b.options.mode=="show"?0:(f-Math.floor(k/2))*(c/k)),opacity:b.options.mode=="show"?1:0},b.duration||500)}}setTimeout(function(){b.options.mode=="show"?h.css({visibility:"visible"}):h.css({visibility:"visible"}).hide();if(b.callback){b.callback.apply(h[0])}h.dequeue();a("div.ui-effects-explode").remove()},b.duration||500)})}})(jQuery);
diff --git a/static/js/pichat.js b/static/js/pichat.js
index 2cb577f..bdd2471 100644
--- a/static/js/pichat.js
+++ b/static/js/pichat.js
@@ -1,11 +1,27 @@
var cache = {}
var PendingMessages = {}
+var MessageContentCache = {}
var MaxImagePosts = 40
// Utils
-// use e.g. "backgroundColor" not "background-color"
+/*Object.size = function(obj) {
+ var size = 0, key;
+ for (key in obj) {
+ if (obj.hasOwnProperty(key)) size++;
+ }
+ return size;
+};*/
+
+isEmptyObject = function(obj) {
+ for (key in obj) {
+ if (obj.hasOwnProperty(key)) return false;
+ }
+ return true
+}
+
+
function isCSSPropertySupported(prop){
return prop in document.body.style;
}
@@ -15,16 +31,30 @@ function escapeHtml(txt) {
else { return $("<span>").text(txt).html(); }
}
+
+URLRegex = /((\b(http\:\/\/|https\:\/\/|ftp\:\/\/)|(www\.))+(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
+PicRegex = /\.(jpg|jpeg|png|gif|bmp)$/i;
+
+function getImagesAsArray(text) {
+ var imgs = []
+ var urls = text.match(URLRegex)
+ for (var i = 0; i<urls.length; i++){
+ var url = urls[i]
+ var urlWithoutParams = url.replace(/\?.*$/i, "");
+ if (PicRegex.test(urlWithoutParams))
+ imgs.push(url)
+ }
+ return imgs
+}
+
function linkify(text) {
LastMsgContainsImage = false
- var URLRegex = /((\b(http\:\/\/|https\:\/\/|ftp\:\/\/)|(www\.))+(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
return text.replace(URLRegex, linkReplace);
}
// durty hack to use a global to check this... but otherwise i'd have to rewrite the String.replace function? :/
var LastMsgContainsImage = false
function linkReplace(url) {
- var PicRegex = /\.(jpg|jpeg|png|gif|bmp)$/i;
var urlWithoutParams = url.replace(/\?.*$/i, "");
if (url.indexOf('http://') == 0 || url.indexOf('https://') == 0 || url.indexOf('ftp://') == 0)
@@ -42,12 +72,10 @@ function linkReplace(url) {
function linkifyWithoutImage(text) {
LastMsgContainsImage = false
- var URLRegex = /((\b(http\:\/\/|https\:\/\/|ftp\:\/\/)|(www\.))+(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
return text.replace(URLRegex, linkReplaceWithoutImage);
}
function linkReplaceWithoutImage(url){
- var PicRegex = /\.(jpg|jpeg|png|gif|bmp)$/i;
var urlWithoutParams = url.replace(/\?.*$/i, "");
linkUrl = url.indexOf('http://') == 0 ? url : 'http://' + url;
@@ -80,8 +108,10 @@ function buildMessageDiv(msg, isLoading) {
var msgId = !isLoading ? 'id="message-' + msg.msg_id + '"' : '';
var loadingClass = isLoading ? ' loading' : '';
var containsImageClass = LastMsgContainsImage ? ' contains-image' : '';
- return '<div class="msgDiv ' + loadingClass + containsImageClass + '" ' + msgId + '>'
- + '<b><a href="/u/' + nick + ' ">' + nick + '</a>: </b>'
+ return '<div class="msgDiv dump ' + loadingClass + containsImageClass + '" ' + msgId + '>'
+ + '<span class="nick"><b><a href="/u/' + nick + ' ">' + nick + '</a></b>'
+ + ' <img src="'+Imgs.chatThumbDot+'" class="thumb chat-thumb" onclick="Tag.favorite(this)"> '
+ + '</span>'
+ buildMsgContent(msg.content)
+ '</div>';
}
@@ -130,7 +160,7 @@ function submitMessage() {
var content = $.trim($('#msgInput').val());
$('#msgInput').val('');
if (content == '') { return; }
- if (content.length > 1000) {
+ if (content.length > 1337) {
alert("POST TOO LONG DUDE!");
return;
} // this shouldn't just be client side :V
@@ -193,18 +223,19 @@ function flattenUserJson(users) {
}
function updateUI(msgs, users) {
- if (window['growlize'] && msgs && msgs.length > 0) {
- $.map(msgs, buildGrowlDataAndPopDatShit)
- } else if (msgs && msgs.length > 0) {
- addNewMessages(msgs);
- }
- if (users !== null) {
- var flattened = flattenUserJson(users);
- if (!('userlist' in cache) || flattened != cache.userlist) {
- $("#userList").html($.map(users.sort(sortUsersByAlpha), buildUserDiv).join(''));
- }
- cache.userlist = flattened
- }
+
+ if (window['growlize'] && msgs && msgs.length > 0) {
+ $.map(msgs, buildGrowlDataAndPopDatShit)
+ } else if (msgs && msgs.length > 0) {
+ addNewMessages(msgs);
+ }
+ if (users !== null) {
+ var flattened = flattenUserJson(users);
+ if (!('userlist' in cache) || flattened != cache.userlist) {
+ $("#userList").html($.map(users.sort(sortUsersByAlpha), buildUserDiv).join(''));
+ }
+ cache.userlist = flattened
+ }
}
function sortUsersByAlpha(a, b){
@@ -248,6 +279,9 @@ function refresh() {
var onSuccess = function(json) {
try {
Timestamp = json.timestamp;
+
+ $.map(json.messages, function(msg){ MessageContentCache[msg.msg_id.toString()] = msg.content })
+
var messages = $.grep(
json.messages,
function(m) { return !isDuplicateMessage(m) });
@@ -292,9 +326,14 @@ function initChat() {
$('#msgInput').keyup(ifEnter(submitMessage));
$('#msgSubmit').click(submitMessage);
+ $('#palette-button').click(paletteToggle);
messageList = $("#messageList")[0]
+ if (!isEmptyObject(RawFavs)) paletteButtonShow()
+
+ initChatThumb()
+
scrollToEnd()
scrollWatcher()
@@ -372,6 +411,7 @@ function initProfile() {
var t = $(this);
t.html(buildMsgContent(t.text()));
});
+ initLogThumb()
};
function initLog() {
@@ -379,57 +419,154 @@ function initLog() {
var t = $(this);
t.html(buildMsgContent(t.text()));
});
- initAnimThumb();
+ initLogThumb();
+}
+
+// todo: preload these. also, look into image sprites (no go on animating their sizes tho)
+Imgs = {
+ "chatThumb": "/static/img/thumbs/color.right.gif",
+ "chatThumbBig": "/static/img/thumbs/color.right.4x.gif",
+ "chatThumbOff": "/static/img/thumbs/bw.right.gif",
+ "chatThumbDot": "/static/img/thumbs/pink.circle.gif",
+ "logThumb": "/static/img/thumbs/color.left.gif",
+ "logThumbBig": "/static/img/thumbs/color.left.4x.gif",
+ "logThumbOff": "/static/img/thumbs/bw.left.gif"
+}
+
+Anim = {
+ "chatThumbBig": {"width": "56px", "height": "60px", "right": "-35px", "bottom": "-10px"},
+ "chatThumbTiny": {"width": "8px", "height": "8px", "right": "8px", "bottom": "8px"},
+ "chatThumb": {"width": "16px", "height": "16px", "right": "4px", "bottom": "4px"},
+ "logThumb": {"width": "16px", "height": "16px", "marginLeft": "0px", "marginTop": "0px"},
+ "logThumbBig": {"width": "56px", "height": "60px", "marginLeft": "-40px", "marginTop": "-27px"}
}
// jesus this logic is ugly
-function initAnimThumb(){
+function initLogThumb(){
$(".buttons .thumb").bind('mouseover mouseout',
function(e) {
- var favorited = $(this).hasClass("favorite") ? true : false;
+ var favorited = $(this).parents(".dump").hasClass("favorite") ? true : false;
if (e.type == "mouseover") {
if (favorited) {
- $(this).attr("src", "/static/thumbup.gif");
-/* $(this).stop().animate({
- "width": "14px",
- "height": "15px",
- "marginLeft": "0px",
- "marginTop": "0px"
- }, 'fast'); */
+ $(this).attr("src", Imgs.logThumbOff);
} else {
- $(this).attr("src", "/static/thumbup.colored.4x.gif");
- $(this).stop().animate({
- "width": "56px",
- "height": "60px",
- "marginLeft": "-44px",
- "marginTop": "-27px"
- }, 'fast');
+ $(this).attr("src", Imgs.logThumbBig);
+ $(this).stop().animate(Anim.logThumbBig, 'fast');
}
} else { // mouseout
if (favorited) {
- $(this).attr("src", "/static/thumbup.colored.gif");
- $(this).stop().animate({
- "width": "14px",
- "height": "15px",
- "marginLeft": "0px",
- "marginTop": "0px"
- }, 'fast');
+ $(this).attr("src", Imgs.logThumb);
+ $(this).stop().animate(Anim.logThumb, 'fast');
} else {
- $(this).attr("src", "/static/thumbup.gif");
- $(this).stop().animate({
- "width": "14px",
- "height": "15px",
- "marginLeft": "0px",
- "marginTop": "0px"
- }, 'fast');
+ $(this).attr("src", Imgs.logThumbOff);
+ $(this).stop().animate(Anim.logThumb, 'fast');
}
}
})
}
+function initChatThumb(){
+
+ $(".chat-thumb").live('mouseover mouseout',
+ function(e) {
+ var favorited = $(this).parents(".dump").hasClass("favorite") ? true : false;
+ if (e.type == "mouseover") {
+ if (favorited) {
+ $(this).attr("src", Imgs.chatThumbOff);
+ } else {
+ $(this).attr("src", Imgs.chatThumbBig);
+ $(this).stop().animate(Anim.chatThumbBig, 'fast')
+ }
+ } else { // mouseout
+ if (favorited) {
+ $(this).attr("src", Imgs.chatThumb);
+ $(this).stop().animate(Anim.chatThumb, 'fast');
+ } else {
+ $(this).stop().animate(Anim.chatThumbTiny, 'fast', 'swing',
+ function(){
+ $(this).attr("src", Imgs.chatThumbDot)
+ $(this).animate(Anim.chatThumb, 0)
+ })
+ }
+ }
+ })
+}
+
+function paletteButtonHideAnim(){
+ $("#msginputrapper").stop().animate({"marginRight": "374px"}, 'fast')
+ $("#msgSubmit").stop().animate({"right": "260px"}, 'fast')
+ $("#palette-button").stop().animate({"width": "0px"}, 'fast', 'swing', function(){ $("#palette-button").css("display", "none") })
+}
+function paletteButtonHide(){
+ $("#msginputrapper").css("marginRight", "374px")
+ $("#msgSubmit").css("right", "260px")
+ $("#palette-button").css("width", "0px")
+ $("#palette-button").css("display", "none")
+}
+function paletteButtonShowAnim(){
+ $("#msginputrapper").stop().animate({"marginRight": "415px"}, 'fast')
+ $("#msgSubmit").stop().animate({"right": "300px"}, 'fast')
+ $("#palette-button").css("display", "inline-block")
+ $("#palette-button").stop().animate({"width": "40px"}, 'fast')
+}
+function paletteButtonShow(){
+ $("#msginputrapper").css("marginRight", "415px")
+ $("#msgSubmit").css("right", "300px")
+ $("#palette-button").css("display", "inline-block")
+ $("#palette-button").css("width", "40px")
+}
+
+function paletteToChat(img){
+ var chatText = $("#msgInput").val()
+ if (chatText.length && chatText[chatText.length - 1] != " ")
+ chatText += " "
+ chatText += $(img).attr("src") + " "
+ $("#msgInput").val(chatText)
+ $("#msgInput").focus().val($("#msgInput").val()) //http://stackoverflow.com/questions/1056359/
+ paletteHide()
+}
+
+paletteImageCache = false
+function paletteBuildImageThumbs(){
+ if (paletteImageCache) {
+ var imgs = paletteImageCache
+ } else {
+ var imgs = []
+ var dupeFilter = {}
+ for(fav in RawFavs){
+ var parsedImgs = getImagesAsArray(RawFavs[fav])
+ for (var i=0; i<parsedImgs.length; i++){
+ var img = parsedImgs[i]
+ if (!dupeFilter[img]) {
+ imgs.push(img)
+ dupeFilter[img] = true
+ }
+ }
+ }
+ paletteImageCache = imgs
+ }
+
+ for(var i=0; i<imgs.length; i++){
+ $("#palette-thumbs").append("<img onclick='paletteToChat(this)' src='"+imgs[i]+"'>")
+ }
+}
+
+function paletteShow(){
+ $("#palette").css("display", "block")
+ paletteBuildImageThumbs()
+}
+function paletteHide(){
+ $("#palette").css("display", "none")
+ $("#palette-thumbs").html("")
+}
+
+function paletteToggle(){
+ if ($("#palette").css("display") == "none")
+ paletteShow()
+ else
+ paletteHide()
+}
-// TODO
-function favoriteImage() {};
function setupUpload(elementId, roomKey) {
var onSubmit = function(file, ext) {
@@ -551,7 +688,6 @@ function initDirectory() {
//big hand stuff
// TODO: replace this with simple pointer-events thing.
-
function initBigHand(id){
var cursorId = "#cursor-big"
var cursor = $(cursorId)[0]
@@ -617,70 +753,66 @@ function initBigHand(id){
}
-Share = {
-<<<<<<< HEAD
- "getMessage": function(button){
- var message = $(button).parents(".logged-dump")
+// grab message id etc from some element e that's inside a message
+function getMessageInfo(e){
+ var message = $(e).parents(".dump")
var id = message.attr("id").substr(8) // cut "message-001" to "001"
- var nick = message.attr("nick") // cut "/u/timb" to "timb"
+ var nick = message.attr("nick")
var link = "http://dump.fm/p/" + nick + "/" + id
var content = message.find(".linkify")
if (!content.length) content = message.find(".content")
var rawContent = content.html()
var img = content.find("img").attr("src")
var via = "via " + nick + " on dump.fm"
- return {"nick": nick, "id": id, "link": encodeURIComponent(link), "content": content, "img": encodeURIComponent(img), "via": encodeURIComponent(via)}
- },
+ return {"nick": nick, "id": id, "link": encodeURIComponent(link),
+ "content": rawContent, "img": encodeURIComponent(img),
+ "via": encodeURIComponent(via)}
+}
+
+Share = {
"openLink": function(url){
window.open(url, "_blank")
},
"facebook": function(button){
- var message = Share.getMessage(button)
+ var message = getMessageInfo(button)
var url = "http://www.facebook.com/share.php?u=" + message.img + "&t=" + message.via
Share.openLink(url)
},
"tumblr": function(button){
- var message = Share.getMessage(button)
+ var message = getMessageInfo(button)
var url = "http://www.tumblr.com/share?v=3&u=" + message.img + "&t=" + message.via
Share.openLink(url)
},
"twitter": function(button){
- var message = Share.getMessage(button)
+ var message = getMessageInfo(button)
var url = "http://twitter.com/home?status=" + message.img + encodeURIComponent(" ") + message.via
Share.openLink(url)
},
"delicious": function(button){
- var message = Share.getMessage(button)
+ var message = getMessageInfo(button)
var url = "http://delicious.com/save?url=" + message.img + "&title=" + message.img + "&notes=" + message.via
Share.openLink(url)
}
}
Tag = {
- // todo: get rid of all the duplicated code here and in share
- "getMessage": function(button){
- var message = $(button).parents(".logged-dump")
- var id = message.attr("id").substr(8) // cut "message-001" to "001"
- var nick = message.attr("nick") // cut "/u/timb" to "timb"
- var link = "http://dump.fm/p/" + nick + "/" + id
- var content = message.find(".linkify")
- if (!content.length) content = message.find(".content")
- var rawContent = content.html()
- var img = content.find("img").attr("src")
- var via = "via " + nick + " on dump.fm"
- return {"nick": nick, "id": id, "link": encodeURIComponent(link),
- "content": content, "img": encodeURIComponent(img),
- "via": encodeURIComponent(via)}
- },
"favorite": function(button){
- var message = Share.getMessage(button)
- var favorited = ($(button).hasClass("favorite")) ? true : false
+ var message = getMessageInfo(button)
+ var favorited = ($(button).parents(".dump").hasClass("favorite")) ? true : false
if (favorited) {
Tag.rm(message.id, "favorite")
- $(button).removeClass("favorite")
+ $(button).parents(".dump").removeClass("favorite")
+ if (RawFavs && RawFavs[message.id]) {
+ delete RawFavs[message.id]
+ paletteImageCache = false
+ }
} else {
Tag.add(message.id, "favorite")
- $(button).addClass("favorite")
+ $(button).parents(".dump").addClass("favorite")
+ if (RawFavs && MessageContentCache[message.id]) {
+ RawFavs[message.id] = MessageContentCache[message.id]
+ paletteImageCache = false
+ }
}
},
"add": function(message_id, tag){
diff --git a/template/chat.st b/template/chat.st
index ef866ff..2ab2369 100644
--- a/template/chat.st
+++ b/template/chat.st
@@ -10,6 +10,9 @@ $endif$
<script type="text/javascript" src="/static/js/tooltip.js"></script>
<script type="text/javascript" src="/static/js/away.js"></script>
<script type="text/javascript" src="/static/js/ajaxupload.js"></script>
+$if(user_nick)$
+ <script type="text/javascript" src="/json/$user_nick$/favorites"></script>
+$endif$
<script>
jQuery(document).ready(initChat);
var Nick = $json_user_nick$;
@@ -61,7 +64,7 @@ $messages: { m |
$if(m.favorited)$
<img src="/static/img/thumbs/color.right.gif" class="thumb chat-thumb" onclick="Tag.favorite(this)">
$else$
- <img src="/static/img/thumbs/pink.diamond.gif" class="thumb chat-thumb" onclick="Tag.favorite(this)">
+ <img src="/static/img/thumbs/pink.circle.gif" class="thumb chat-thumb" onclick="Tag.favorite(this)">
$endif$
</span>
<span class="content">$m.content$<span>
@@ -73,20 +76,22 @@ $if(user_avatar)$
<div id="msgInputDiv">
<div id="msginputrapper"> <input id="msgInput" class="msgInput" type="input" /> </div>
<input id="msgSubmit" type="submit" value="Send"/>
+ <button id="palette-button"><img src="/static/img/palette.gif"></button>
<input id="upload" value="Upload" type="submit">
<input id="webcam-button-upload" value="Webcam" type="submit">
<input id="webcam-button-snap" value="Send Pic" type="submit" class="invisible blink">
- <div id="trophy"><a href="javascript:pop('http://www.likeneveralways.com/trash/count/index.php');"class="tooltip" title="Contest Counter"><img src="/static/trophyicon.gif"></a></div>
+ <div id="trophy"><a href="javascript:pop('http://www.likeneveralways.com/trash/count/index.php');"class="tooltip" title="Contest Counter"><img src="/static/img/trophyicon.gif"></a></div>
</div><div id="effects-msg"class="invisible">click on your face for effects!</div>
</div>
$else$
<div id="msgInputDiv">
<div id="msginputrapper"> <input id="msgInput" class="msgInput" type="input" /> </div>
<input id="msgSubmit" type="submit" value="Send"onClick="showAlert();"/>
+ <button id="palette-button"><img src="/static/img/palette.gif"></button>
<input id="upload" value="Upload" type="submit"onClick="showAlert();">
<input id="webcam-button-upload" value="Webcam" type="submit"onClick="showAlert();">
<input id="webcam-button-snap" value="Send Pic" type="submit" class="invisible blink" onClick="showAlert();">
- <div id="trophy"><a href="javascript:pop('http://www.likeneveralways.com/trash/count/index.php');"class="tooltip" title="Contest Counter"onClick="showAlert();"><img src="/static/trophyicon.gif"></a></div>
+ <div id="trophy"><a href="javascript:pop('http://www.likeneveralways.com/trash/count/index.php');"class="tooltip" title="Contest Counter"onClick="showAlert();"><img src="/static/img/trophyicon.gif"></a></div>
</div>
</div>
$chat_help_bubbles()$
@@ -95,6 +100,7 @@ $endif$
</div>
</div>
</div>
+ <div id="palette"><div id="palette-thumbs"></div></div>
<div id="footerc">
<p>
$footer()$
diff --git a/template/chat_help_bubbles.st b/template/chat_help_bubbles.st
index fadf9dc..77a3e7d 100644
--- a/template/chat_help_bubbles.st
+++ b/template/chat_help_bubbles.st
@@ -1,11 +1,11 @@
<div id="newuserwrap">
-<div id="posthelp"><img src="/static/posthere.png"></div>
-<div id="webcamhelp"><img src="/static/webcamhelp.png"></div>
-<div id="userlisthelp"><img src="/static/userlisthelp.png"></div>
-<div id="imageboard"><img src="/static/imageboard.png"></div>
-<div id="uploadstuff"><img src="/static/uploadstuff.png"></div>
-<div id="signinblurb"><img src="/static/signinblurb.png"></div>
-<div id="beingpostednow"><img src="/static/beingpostednow.png"></div>
+<div id="posthelp"><img src="/static/img/blurbs/posthere.png"></div>
+<div id="webcamhelp"><img src="/static/img/blurbs/webcamhelp.png"></div>
+<div id="userlisthelp"><img src="/static/img/blurbs/userlisthelp.png"></div>
+<div id="imageboard"><img src="/static/img/blurbs/imageboard.png"></div>
+<div id="uploadstuff"><img src="/static/img/blurbs/uploadstuff.png"></div>
+<div id="signinblurb"><img src="/static/img/blurbs/signinblurb.png"></div>
+<div id="beingpostednow"><img src="/static/img/blurbs/beingpostednow.png"></div>
<script>
jQuery(document).ready(function(){ setTimeout( function(){ jQuery("#newuserwrap div").hide("puff", 1000) }, 10000 ) })
</script>
diff --git a/template/directory.st b/template/directory.st
index 05d13e2..bea2f58 100644
--- a/template/directory.st
+++ b/template/directory.st
@@ -29,13 +29,13 @@
</div>
</center>
$if(users)$
- $users:{ u |
- <div class="logged-dump" id="message-$u.message_id$">
- <a href="/u/$u.nick$">
- <div id="usernicks"> <b>$u.nick$</b></div>
- $if(u.avatar)$
+ $users:{ dump |
+ <div class="logged-dump" id="message-$dump.message_id$">
+ <a href="/u/$dump.nick$">
+ <div id="usernicks"> <b>$dump.nick$</b></div>
+ $if(dump.avatar)$
<div id="logavatar">
- <img height="50" width="50" src="$u.avatar$" />
+ <img height="50" width="50" src="$dump.avatar$" />
</div>
$else$
<div id="logavatar">
@@ -44,7 +44,7 @@
$endif$
</a>
<div id="infotxt"><b>last post</b></div>
- <span class="linkify">$u.content$</span>
+ <span class="linkify">$dump.content$</span>
<hr />
$share_buttons()$
</div>
diff --git a/template/head.st b/template/head.st
index 8634534..18b5d16 100644
--- a/template/head.st
+++ b/template/head.st
@@ -6,7 +6,7 @@
<script type="text/javascript" src="/static/js/jquery-ui-1.8.effects.min.js"></script>
<script type="text/javascript" src="/static/js/sha1.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/reset.css">
-<link rel="stylesheet" type="text/css" href="/static/css/common.css">
+<link rel="stylesheet" type="text/css" href="/static/css/header.css">
$if(!user_nick)$
<link href="/static/form_login/front.css" media="screen, projection" rel="stylesheet" type="text/css">
$endif$
diff --git a/template/log.st b/template/log.st
index 8af0154..88ab991 100644
--- a/template/log.st
+++ b/template/log.st
@@ -15,7 +15,7 @@ $head()$
<br>
<div id="posts">
<div id="lolbanner">
- <img src="/static/welcomebanner.gif">
+ <img src="/static/img/welcomebanner.gif">
</div>
$if(dumps)$
$dumps: { d | $log_dump(dump=d)$ }$
diff --git a/template/share_buttons.st b/template/share_buttons.st
index d061743..3f1f472 100644
--- a/template/share_buttons.st
+++ b/template/share_buttons.st
@@ -1,8 +1,10 @@
<div class="buttons">
-$if(dump.favorited)$
+$if(dump)$
+ $if(dump.favorited)$
<img src="/static/img/thumbs/color.left.gif" class="thumb favorite" onclick="Tag.favorite(this)">
-$else$
+ $else$
<img src="/static/img/thumbs/bw.left.gif" class="thumb" onclick="Tag.favorite(this)">
+ $endif$
$endif$
<span class="other-sites">
<img src="/static/img/share/tumblricon.png" class="share" onclick="Share.tumblr(this)">
diff --git a/template/tagged_dumps.st b/template/tagged_dumps.st
new file mode 100644
index 0000000..037a52b
--- /dev/null
+++ b/template/tagged_dumps.st
@@ -0,0 +1,49 @@
+<html>
+<head>
+ <title>dump.fm - $page_title$</title>
+$head()$
+ <link rel="stylesheet" type="text/css" href="/static/css/log.css">
+ <script>
+ jQuery(document).ready(initLog);
+ </script>
+</head>
+<body>
+ $banner()$
+ <div id="chatrap">
+ <div id="log">
+ <div id="loghead"></div>
+ <br>
+ <div id="posts">
+ <center><h2>$page_title$</h2></center>
+ <div id="lolbanner">
+ <img src="/static/img/welcomebanner.gif">
+ </div>
+$if(dumps)$
+ $dumps: { d | $log_dump(dump=d)$ }$
+ $if(json_tags)$
+<script>
+ $json_tags: { j | $j$;
+}$
+</script>
+ $endif$
+$else$
+ No dumps!
+$endif$
+ <div id="pnav">
+$if(next)$
+ <div id="pnavn"><a href="/$page_url$/$next$">next &#9758;</a></div>
+$endif$
+ &nbsp;
+$if(prev)$
+ <div id="pnavo"> <a href="/$page_url$/$prev$">&#9756; prev</a> </div>
+$endif$
+ <br><br>
+ </div>
+ </div>
+
+ <div id="footer">
+$footer()$
+ </div>
+ </div>
+ </body>
+</html> \ No newline at end of file