ZeroBlog.coffee 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. class ZeroBlog extends ZeroFrame
  2. init: ->
  3. @data = null
  4. @site_info = null
  5. @server_info = null
  6. @page = 1
  7. @my_post_votes = {}
  8. @event_page_load = $.Deferred()
  9. @event_site_info = $.Deferred()
  10. # Editable items on own site
  11. $.when(@event_page_load, @event_site_info).done =>
  12. if @site_info.settings.own or @data.demo
  13. @addInlineEditors()
  14. @checkPublishbar()
  15. $(".publishbar").off("click").on "click", @publish
  16. $(".posts .button.new").css("display", "inline-block")
  17. $(".editbar .icon-help").off("click").on "click", =>
  18. $(".editbar .markdown-help").css("display", "block")
  19. $(".editbar .markdown-help").toggleClassLater("visible", 10)
  20. $(".editbar .icon-help").toggleClass("active")
  21. return false
  22. $.when(@event_site_info).done =>
  23. @log "event site info"
  24. # Set avatar
  25. imagedata = new Identicon(@site_info.address, 70).toString();
  26. $("body").append("<style>.avatar { background-image: url(data:image/png;base64,#{imagedata}) }</style>")
  27. @initFollowButton()
  28. $(".left-more-link").on "click", =>
  29. $(".left .left-more").slideToggle()
  30. $(".left").toggleClass("show-more")
  31. return false
  32. @log "inited!"
  33. initFollowButton: ->
  34. @follow = new Follow($(".feed-follow"))
  35. @follow.addFeed("Posts", "
  36. SELECT
  37. post_id AS event_uri,
  38. 'post' AS type,
  39. date_published AS date_added,
  40. title AS title,
  41. body AS body,
  42. '?Post:' || post_id AS url
  43. FROM post", true)
  44. if Page.site_info.cert_user_id
  45. username = Page.site_info.cert_user_id.replace /@.*/, ""
  46. @follow.addFeed("Username mentions", "
  47. SELECT
  48. 'mention' AS type,
  49. date_added,
  50. post.title AS title,
  51. keyvalue.value || ': ' || comment.body AS body,
  52. '?Post:' || comment.post_id || '#Comments' AS url
  53. FROM comment
  54. LEFT JOIN json USING (json_id)
  55. LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json')
  56. LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id')
  57. LEFT JOIN post ON (comment.post_id = post.post_id)
  58. WHERE
  59. comment.body LIKE '%[#{username}%' OR comment.body LIKE '%@#{username}%'
  60. ", true)
  61. @follow.addFeed("Comments", "
  62. SELECT
  63. 'comment' AS type,
  64. date_added,
  65. post.title AS title,
  66. keyvalue.value || ': ' || comment.body AS body,
  67. '?Post:' || comment.post_id || '#Comments' AS url
  68. FROM comment
  69. LEFT JOIN json USING (json_id)
  70. LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json')
  71. LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id')
  72. LEFT JOIN post ON (comment.post_id = post.post_id)")
  73. @follow.init()
  74. loadData: (query="new") ->
  75. # Get blog parameters
  76. if query == "old" # Old type query for pre 0.3.0
  77. query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE path = 'data.json'"
  78. else
  79. query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE directory = '' AND file_name = 'data.json'"
  80. @cmd "dbQuery", [query], (res) =>
  81. @data = {}
  82. if res
  83. for row in res
  84. @data[row.key] = row.value
  85. if @data.title then $(".left h1 a:not(.editable-edit)").html(@data.title).data("content", @data.title)
  86. if @data.description then $(".left h2").html(Text.renderMarked(@data.description)).data("content", @data.description)
  87. if @data.links then $(".left .links").html(Text.renderMarked(@data.links)).data("content", @data.links)
  88. loadLastcomments: (type="show", cb=false) ->
  89. query = "
  90. SELECT comment.*, json_content.json_id AS content_json_id, keyvalue.value AS cert_user_id, json.directory, post.title AS post_title
  91. FROM comment
  92. LEFT JOIN json USING (json_id)
  93. LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json')
  94. LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id')
  95. LEFT JOIN post ON (comment.post_id = post.post_id)
  96. WHERE post.title IS NOT NULL
  97. ORDER BY date_added DESC LIMIT 3"
  98. @cmd "dbQuery", [query], (res) =>
  99. if res.length
  100. $(".lastcomments").css("display", "block")
  101. res.reverse()
  102. for lastcomment in res
  103. elem = $("#lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}")
  104. if elem.length == 0 # Not exits yet
  105. elem = $(".lastcomment.template").clone().removeClass("template").attr("id", "lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}")
  106. if type != "noanim"
  107. elem.cssSlideDown()
  108. elem.prependTo(".lastcomments ul")
  109. @applyLastcommentdata(elem, lastcomment)
  110. if cb then cb()
  111. applyLastcommentdata: (elem, lastcomment) ->
  112. elem.find(".user_name").text(lastcomment.cert_user_id.replace(/@.*/, "")+":")
  113. body = Text.renderMarked(lastcomment.body)
  114. body = body.replace /[\r\n]/g, " " # Remove whitespace
  115. body = body.replace /\<blockquote\>.*?\<\/blockquote\>/g, " " # Remove quotes
  116. body = body.replace /\<.*?\>/g, " " # Remove html codes
  117. if body.length > 60 # Strip if too long
  118. body = body[0..60].replace(/(.*) .*?$/, "$1") + " ..." # Keep the last 60 character and strip back until last space
  119. elem.find(".body").html(body)
  120. title_hash = lastcomment.post_title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+")
  121. elem.find(".postlink").text(lastcomment.post_title).attr("href", "?Post:#{lastcomment.post_id}:#{title_hash}#Comments")
  122. applyPagerdata: (page, limit, has_next) ->
  123. pager = $(".pager")
  124. if page > 1
  125. pager.find(".prev").css("display", "inline-block").attr("href", "?page=#{page-1}")
  126. if has_next
  127. pager.find(".next").css("display", "inline-block").attr("href", "?page=#{page+1}")
  128. routeUrl: (url) ->
  129. @log "Routing url:", url
  130. if match = url.match /Post:([0-9]+)/
  131. $("body").addClass("page-post")
  132. @post_id = parseInt(match[1])
  133. @pagePost()
  134. else
  135. $("body").addClass("page-main")
  136. if match = url.match /page=([0-9]+)/
  137. @page = parseInt(match[1])
  138. @pageMain()
  139. # - Pages -
  140. pagePost: () ->
  141. s = (+ new Date)
  142. @cmd "dbQuery", ["SELECT *, (SELECT COUNT(*) FROM post_vote WHERE post_vote.post_id = post.post_id) AS votes FROM post WHERE post_id = #{@post_id} LIMIT 1"], (res) =>
  143. parse_res = (res) =>
  144. if res.length
  145. post = res[0]
  146. @applyPostdata($(".post-full"), post, true)
  147. $(".post-full").css("display", "block")
  148. $(".post-full .like").attr("id", "post_like_#{post.post_id}").off("click").off("click").on "click", @submitPostVote
  149. $(".notfound").css("display", "none")
  150. Comments.pagePost(@post_id)
  151. else
  152. $(".notfound").css("display", "block")
  153. $(".post-full").css("display", "none")
  154. @pageLoaded()
  155. Comments.checkCert()
  156. # Temporary dbschema bug workaround
  157. if res.error
  158. @cmd "dbQuery", ["SELECT *, -1 AS votes FROM post WHERE post_id = #{@post_id} LIMIT 1"], parse_res
  159. else
  160. parse_res(res)
  161. pageMain: ->
  162. limit = 15
  163. query = """
  164. SELECT
  165. post.*, COUNT(comment_id) AS comments,
  166. (SELECT COUNT(*) FROM post_vote WHERE post_vote.post_id = post.post_id) AS votes
  167. FROM post
  168. LEFT JOIN comment USING (post_id)
  169. GROUP BY post_id
  170. ORDER BY date_published DESC
  171. LIMIT #{(@page-1)*limit}, #{limit+1}
  172. """
  173. @cmd "dbQuery", [query], (res) =>
  174. parse_res = (res) =>
  175. s = (+ new Date)
  176. if res.length > limit # Has next page
  177. res.pop()
  178. @applyPagerdata(@page, limit, true)
  179. else
  180. @applyPagerdata(@page, limit, false)
  181. res.reverse()
  182. for post in res
  183. elem = $("#post_#{post.post_id}")
  184. if elem.length == 0 # Not exits yet
  185. elem = $(".post.template").clone().removeClass("template").attr("id", "post_#{post.post_id}")
  186. elem.prependTo(".posts")
  187. # elem.find(".score").attr("id", "post_score_#{post.post_id}").on "click", @submitPostVote # Submit vote
  188. elem.find(".like").attr("id", "post_like_#{post.post_id}").off("click").on "click", @submitPostVote
  189. @applyPostdata(elem, post)
  190. @pageLoaded()
  191. @log "Posts loaded in", ((+ new Date)-s),"ms"
  192. $(".posts .new").off("click").on "click", => # Create new blog post
  193. @cmd "fileGet", ["data/data.json"], (res) =>
  194. data = JSON.parse(res)
  195. # Add to data
  196. data.post.unshift
  197. post_id: data.next_post_id
  198. title: "New blog post"
  199. date_published: (+ new Date)/1000
  200. body: "Blog post body"
  201. data.next_post_id += 1
  202. # Create html elements
  203. elem = $(".post.template").clone().removeClass("template")
  204. @applyPostdata(elem, data.post[0])
  205. elem.hide()
  206. elem.prependTo(".posts").slideDown()
  207. @addInlineEditors(elem)
  208. @writeData(data)
  209. return false
  210. # Temporary dbschema bug workaround
  211. if res.error
  212. query = """
  213. SELECT
  214. post.*, COUNT(comment_id) AS comments,
  215. -1 AS votes
  216. FROM post
  217. LEFT JOIN comment USING (post_id)
  218. GROUP BY post_id
  219. ORDER BY date_published DESC
  220. LIMIT #{(@page-1)*limit}, #{limit+1}
  221. """
  222. @cmd "dbQuery", [query], parse_res
  223. else
  224. parse_res(res)
  225. # - EOF Pages -
  226. # All page content loaded
  227. pageLoaded: =>
  228. $("body").addClass("loaded") # Back/forward button keep position support
  229. $('pre code').each (i, block) -> # Higlight code blocks
  230. hljs.highlightBlock(block)
  231. @event_page_load.resolve()
  232. @cmd "innerLoaded", true
  233. addInlineEditors: (parent) ->
  234. @logStart "Adding inline editors"
  235. elems = $("[data-editable]:visible", parent)
  236. for elem in elems
  237. elem = $(elem)
  238. if not elem.data("editor") and not elem.hasClass("editor")
  239. editor = new InlineEditor(elem, @getContent, @saveContent, @getObject)
  240. elem.data("editor", editor)
  241. @logEnd "Adding inline editors"
  242. addImageZoom: (parent) ->
  243. $("img", parent).each (i, img_elem) =>
  244. img_elem.onload = =>
  245. img_elem = $(img_elem)
  246. size = img_elem.attr("alt")?.match("([0-9]+)x([0-9]+)")
  247. if not size
  248. return
  249. if img_elem.width() < parseInt(size[1]) or img_elem.height() < parseInt(size[2])
  250. img_elem.attr("data-action", "zoom")
  251. img_elem.onload = null
  252. if img_elem.complete
  253. img_elem.onload()
  254. # Check if publishing is necessary
  255. checkPublishbar: ->
  256. if @data? and (not @data["modified"] or @data["modified"] > @site_info.content.modified)
  257. $(".publishbar").addClass("visible")
  258. else
  259. $(".publishbar").removeClass("visible")
  260. # Sign and Publish site
  261. publish: =>
  262. if @site_info.privatekey # Privatekey stored in users.json
  263. @cmd "sitePublish", ["stored"], (res) =>
  264. @log "Publish result:", res
  265. else
  266. @cmd "wrapperPrompt", ["Enter your private key:", "password"], (privatekey) => # Prompt the private key
  267. $(".publishbar .button").addClass("loading")
  268. @cmd "sitePublish", [privatekey], (res) =>
  269. $(".publishbar .button").removeClass("loading")
  270. @log "Publish result:", res
  271. return false # Ignore link default event
  272. # Apply from data to post html element
  273. applyPostdata: (elem, post, full=false) ->
  274. title_hash = post.title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+")
  275. elem.data("object", "Post:"+post.post_id)
  276. $(".title .editable", elem).html(post.title).attr("href", "?Post:#{post.post_id}:#{title_hash}").data("content", post.title)
  277. date_published = Time.since(post.date_published)
  278. # Published date
  279. post.body = post.body.replace(/^\* \* \*/m, "---")
  280. if post.body.match /^---/m # Has more over fold
  281. date_published += " &middot; #{Time.readtime(post.body)}" # If has break add readtime
  282. $(".more", elem).css("display", "inline-block").attr("href", "?Post:#{post.post_id}:#{title_hash}")
  283. $(".details .published", elem).html(date_published).data("content", post.date_published)
  284. # Comments num
  285. if post.comments > 0
  286. $(".details .comments-num", elem).css("display", "inline").attr("href", "?Post:#{post.post_id}:#{title_hash}#Comments")
  287. if post.comments > 1
  288. $(".details .comments-num .num", elem).text("#{post.comments} comments")
  289. else
  290. $(".details .comments-num .num", elem).text("#{post.comments} comment")
  291. else
  292. $(".details .comments-num", elem).css("display", "none")
  293. ###
  294. if @my_post_votes[post.post_id] # Voted on it
  295. $(".score-inactive .score-num", elem).text post.votes-1
  296. $(".score-active .score-num", elem).text post.votes
  297. $(".score", elem).addClass("active")
  298. else # Not voted on it
  299. $(".score-inactive .score-num", elem).text post.votes
  300. $(".score-active .score-num", elem).text post.votes+1
  301. if post.votes == 0
  302. $(".score", elem).addClass("noscore")
  303. else
  304. $(".score", elem).removeClass("noscore")
  305. ###
  306. if post.votes > 0
  307. $(".like .num", elem).text post.votes
  308. else if post.votes == -1 # DB bug
  309. $(".like", elem).css("display", "none")
  310. else
  311. $(".like .num", elem).text ""
  312. if @my_post_votes[post.post_id] # Voted on it
  313. $(".like", elem).addClass("active")
  314. if full
  315. body = post.body
  316. else # On main page only show post until the first --- hr separator
  317. body = post.body.replace(/^([\s\S]*?)\n---\n[\s\S]*$/, "$1")
  318. if $(".body", elem).data("content") != post.body
  319. $(".body", elem).html(Text.renderMarked(body)).data("content", post.body)
  320. @addImageZoom(elem)
  321. # Wrapper websocket connection ready
  322. onOpenWebsocket: (e) =>
  323. @loadData()
  324. @cmd "siteInfo", {}, (site_info) =>
  325. @setSiteinfo(site_info)
  326. query_my_votes = """
  327. SELECT
  328. 'post_vote' AS type,
  329. post_id AS uri
  330. FROM json
  331. LEFT JOIN post_vote USING (json_id)
  332. WHERE directory = 'users/#{@site_info.auth_address}' AND file_name = 'data.json'
  333. """
  334. @cmd "dbQuery", [query_my_votes], (res) =>
  335. for row in res
  336. @my_post_votes[row["uri"]] = 1
  337. @routeUrl(window.location.search.substring(1))
  338. @cmd "serverInfo", {}, (ret) => # Get server info
  339. @server_info = ret
  340. if @server_info.rev < 160
  341. @loadData("old")
  342. @loadLastcomments("noanim")
  343. # Returns the elem parent object
  344. getObject: (elem) =>
  345. return elem.parents("[data-object]:first")
  346. # Get content from data.json
  347. getContent: (elem, raw=false) =>
  348. [type, id] = @getObject(elem).data("object").split(":")
  349. id = parseInt(id)
  350. content = elem.data("content")
  351. if elem.data("editable-mode") == "timestamp" # Convert to time
  352. content = Time.date(content, "full")
  353. if elem.data("editable-mode") == "simple" or raw # No markdown
  354. return content
  355. else
  356. return Text.renderMarked(content)
  357. # Save content to data.json
  358. saveContent: (elem, content, cb=false) =>
  359. if elem.data("deletable") and content == null then return @deleteObject(elem, cb) # Its a delete request
  360. if elem.data('editableMode') == "timestamp" then elem.data("content", Time.timestamp(content)) else elem.data("content", content)
  361. [type, id] = @getObject(elem).data("object").split(":")
  362. id = parseInt(id)
  363. if type == "Post" or type == "Site"
  364. @saveSite(elem, type, id, content, cb)
  365. else if type == "Comment"
  366. @saveComment(elem, type, id, content, cb)
  367. saveSite: (elem, type, id, content, cb) ->
  368. @cmd "fileGet", ["data/data.json"], (res) =>
  369. data = JSON.parse(res)
  370. if type == "Post"
  371. post = (post for post in data.post when post.post_id == id)[0]
  372. if elem.data("editable-mode") == "timestamp" # Time parse to timestamp
  373. content = Time.timestamp(content)
  374. post[elem.data("editable")] = content
  375. else if type == "Site"
  376. data[elem.data("editable")] = content
  377. @writeData data, (res) =>
  378. if cb
  379. if res == true # OK
  380. @cleanupImages()
  381. if elem.data("editable-mode") == "simple" # No markdown
  382. cb(content)
  383. else if elem.data("editable-mode") == "timestamp" # Format timestamp
  384. cb(Time.since(content))
  385. else
  386. cb(Text.renderMarked(content))
  387. else # Error
  388. cb(false)
  389. saveComment: (elem, type, id, content, cb) ->
  390. @log "Saving comment...", id
  391. @getObject(elem).css "height", "auto"
  392. inner_path = "data/users/#{Page.site_info.auth_address}/data.json"
  393. Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) =>
  394. data = JSON.parse(data)
  395. comment = (comment for comment in data.comment when comment.comment_id == id)[0]
  396. comment[elem.data("editable")] = content
  397. json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t')))
  398. @writePublish inner_path, btoa(json_raw), (res) =>
  399. if res == true
  400. Comments.checkCert("updaterules")
  401. if cb then cb(Text.renderMarked(content, {"sanitize": true}))
  402. else
  403. @cmd "wrapperNotification", ["error", "File write error: #{res}"]
  404. if cb then cb(false)
  405. deleteObject: (elem, cb=False) ->
  406. [type, id] = elem.data("object").split(":")
  407. id = parseInt(id)
  408. if type == "Post"
  409. @cmd "fileGet", ["data/data.json"], (res) =>
  410. data = JSON.parse(res)
  411. if type == "Post"
  412. post = (post for post in data.post when post.post_id == id)[0]
  413. if not post then return false # No post found for this id
  414. data.post.splice(data.post.indexOf(post), 1) # Remove from data
  415. @writeData data, (res) =>
  416. if cb then cb()
  417. if res == true then elem.slideUp()
  418. else if type == "Comment"
  419. inner_path = "data/users/#{Page.site_info.auth_address}/data.json"
  420. @cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) =>
  421. data = JSON.parse(data)
  422. comment = (comment for comment in data.comment when comment.comment_id == id)[0]
  423. data.comment.splice(data.comment.indexOf(comment), 1)
  424. json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t')))
  425. @writePublish inner_path, btoa(json_raw), (res) =>
  426. if res == true
  427. elem.slideUp()
  428. if cb then cb()
  429. writeData: (data, cb=null) ->
  430. if not data
  431. return @log "Data missing"
  432. @data["modified"] = data.modified = Time.timestamp()
  433. json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) # Encode to json, encode utf8
  434. @cmd "fileWrite", ["data/data.json", btoa(json_raw)], (res) => # Convert to to base64 and send
  435. if res == "ok"
  436. if cb then cb(true)
  437. else
  438. @cmd "wrapperNotification", ["error", "File write error: #{res}"]
  439. if cb then cb(false)
  440. @checkPublishbar()
  441. # Updating title in content.json
  442. @cmd "fileGet", ["content.json"], (content) =>
  443. content = content.replace /"title": ".*?"/, "\"title\": \"#{data.title}\"" # Load as raw html to prevent js bignumber problems
  444. content = unescape(encodeURIComponent(content))
  445. @cmd "fileWrite", ["content.json", btoa(content)], (res) =>
  446. if res != "ok"
  447. @cmd "wrapperNotification", ["error", "Content.json write error: #{res}"]
  448. # If the privatekey is stored sign the new content
  449. if @site_info["privatekey"]
  450. @cmd "siteSign", ["stored", "content.json"], (res) =>
  451. @log "Sign result", res
  452. writePublish: (inner_path, data, cb) ->
  453. @cmd "fileWrite", [inner_path, data], (res) =>
  454. if res != "ok" # fileWrite failed
  455. @cmd "wrapperNotification", ["error", "File write error: #{res}"]
  456. cb(false)
  457. return false
  458. @cmd "sitePublish", {"inner_path": inner_path}, (res) =>
  459. if res == "ok"
  460. cb(true)
  461. else
  462. cb(res)
  463. submitPostVote: (e) =>
  464. if not Page.site_info.cert_user_id # No selected cert
  465. Page.cmd "certSelect", [["zeroid.bit"]]
  466. return false
  467. elem = $(e.currentTarget)
  468. elem.toggleClass("active").addClass("loading")
  469. inner_path = "data/users/#{@site_info.auth_address}/data.json"
  470. Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) =>
  471. if data
  472. data = JSON.parse(data)
  473. else # Default data
  474. data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, "post_vote": {} }
  475. if not data.post_vote
  476. data.post_vote = {}
  477. post_id = elem.attr("id").match("_([0-9]+)$")[1]
  478. if elem.hasClass("active")
  479. data.post_vote[post_id] = 1
  480. else
  481. delete data.post_vote[post_id]
  482. json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t')))
  483. current_num = parseInt elem.find(".num").text()
  484. if not current_num
  485. current_num = 0
  486. if elem.hasClass("active")
  487. elem.find(".num").text(current_num+1)
  488. else
  489. elem.find(".num").text(current_num-1)
  490. Page.writePublish inner_path, btoa(json_raw), (res) =>
  491. elem.removeClass("loading")
  492. @log "Writepublish result", res
  493. return false
  494. # Delete non-referenced images
  495. cleanupImages: ->
  496. @cmd "fileGet", ["data/data.json"], (data) =>
  497. Page.cmd "fileList", "data/img", (files) =>
  498. for file in files
  499. if file.indexOf("post_") != 0
  500. continue
  501. if data.indexOf(file) == -1
  502. @log "Deleting image", file, "..."
  503. @cmd "fileDelete", "data/img/#{file}"
  504. # Parse incoming requests
  505. onRequest: (cmd, message) ->
  506. if cmd == "setSiteInfo" # Site updated
  507. @actionSetSiteInfo(message)
  508. else
  509. @log "Unknown command", message
  510. # Siteinfo changed
  511. actionSetSiteInfo: (message) =>
  512. @setSiteinfo(message.params)
  513. @checkPublishbar()
  514. setSiteinfo: (site_info) =>
  515. @site_info = site_info
  516. @event_site_info.resolve(site_info)
  517. if $("body").hasClass("page-post") then Comments.checkCert() # Update if username changed
  518. # User commented
  519. if site_info.event?[0] == "file_done" and site_info.event[1].match /.*users.*data.json$/
  520. if $("body").hasClass("page-post")
  521. @pagePost()
  522. Comments.loadComments() # Post page, reload comments
  523. @loadLastcomments()
  524. if $("body").hasClass("page-main")
  525. RateLimit 500, =>
  526. @pageMain()
  527. @loadLastcomments()
  528. else if site_info.event?[0] == "file_done" and site_info.event[1] == "data/data.json"
  529. @loadData()
  530. if $("body").hasClass("page-main") then @pageMain()
  531. if $("body").hasClass("page-post") then @pagePost()
  532. else if site_info.event?[0] == "cert_changed" and site_info.cert_user_id
  533. # Auto click follow username mentions on cert change
  534. @initFollowButton()
  535. mentions_menu_elem = @follow.feeds["Username mentions"][1]
  536. setTimeout ( =>
  537. if not mentions_menu_elem.hasClass("selected")
  538. mentions_menu_elem.trigger("click")
  539. ), 100
  540. window.Page = new ZeroBlog()