ZeroBlog.coffee 21 KB

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