From 7f755008ecc3c97c54c1947050c70a2e10af6006 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 21 Apr 2016 12:02:57 +0800 Subject: [PATCH 01/17] replace all tab to 2 spaces --- js/ZeroBlog.coffee | 1174 ++++++++++++++++++++++---------------------- 1 file changed, 587 insertions(+), 587 deletions(-) diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 4ecf3b2..a2c4ab4 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -1,591 +1,591 @@ class ZeroBlog extends ZeroFrame - init: -> - @data = null - @site_info = null - @server_info = null - @page = 1 - @my_post_votes = {} - - @event_page_load = $.Deferred() - @event_site_info = $.Deferred() - - # Editable items on own site - $.when(@event_page_load, @event_site_info).done => - if @site_info.settings.own or @data.demo - @addInlineEditors() - @checkPublishbar() - $(".publishbar").off("click").on "click", @publish - $(".posts .button.new").css("display", "inline-block") - $(".editbar .icon-help").off("click").on "click", => - $(".editbar .markdown-help").css("display", "block") - $(".editbar .markdown-help").toggleClassLater("visible", 10) - $(".editbar .icon-help").toggleClass("active") - return false - - $.when(@event_site_info).done => - @log "event site info" - # Set avatar - imagedata = new Identicon(@site_info.address, 70).toString(); - $("body").append("") - @initFollowButton() - @log "inited!" - - - initFollowButton: -> - @follow = new Follow($(".feed-follow")) - @follow.addFeed("Posts", " - SELECT - post_id AS event_uri, - 'post' AS type, - date_published AS date_added, - title AS title, - body AS body, - '?Post:' || post_id AS url - FROM post", true) - - if Page.site_info.cert_user_id - username = Page.site_info.cert_user_id.replace /@.*/, "" - @follow.addFeed("Username mentions", " - SELECT - 'comment' AS type, - date_added, - post.title AS title, - keyvalue.value || ': ' || comment.body AS body, - '?Post:' || comment.post_id || '#Comments' AS url - FROM comment - LEFT JOIN json USING (json_id) - LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') - LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') - LEFT JOIN post ON (comment.post_id = post.post_id) - WHERE - comment.body LIKE '%[#{username}%' OR comment.body LIKE '%@#{username}%' - ", true) - - @follow.addFeed("Comments", " - SELECT - 'comment' AS type, - date_added, - post.title AS title, - keyvalue.value || ': ' || comment.body AS body, - '?Post:' || comment.post_id || '#Comments' AS url - FROM comment - LEFT JOIN json USING (json_id) - LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') - LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') - LEFT JOIN post ON (comment.post_id = post.post_id)") - @follow.init() - - - loadData: (query="new") -> - # Get blog parameters - if query == "old" # Old type query for pre 0.3.0 - query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE path = 'data.json'" - else - query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE directory = '' AND file_name = 'data.json'" - @cmd "dbQuery", [query], (res) => - @data = {} - if res - for row in res - @data[row.key] = row.value - $(".left h1 a:not(.editable-edit)").html(@data.title).data("content", @data.title) - $(".left h2").html(Text.renderMarked(@data.description)).data("content", @data.description) - $(".left .links").html(Text.renderMarked(@data.links)).data("content", @data.links) - - loadLastcomments: (type="show", cb=false) -> - query = " - SELECT comment.*, json_content.json_id AS content_json_id, keyvalue.value AS cert_user_id, json.directory, post.title AS post_title - FROM comment - LEFT JOIN json USING (json_id) - LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') - LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') - LEFT JOIN post ON (comment.post_id = post.post_id) - WHERE post.title IS NOT NULL - ORDER BY date_added DESC LIMIT 3" - - @cmd "dbQuery", [query], (res) => - if res.length - $(".lastcomments").css("display", "block") - res.reverse() - for lastcomment in res - elem = $("#lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") - if elem.length == 0 # Not exits yet - elem = $(".lastcomment.template").clone().removeClass("template").attr("id", "lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") - if type != "noanim" - elem.cssSlideDown() - elem.prependTo(".lastcomments ul") - @applyLastcommentdata(elem, lastcomment) - if cb then cb() - - applyLastcommentdata: (elem, lastcomment) -> - elem.find(".user_name").text(lastcomment.cert_user_id.replace(/@.*/, "")+":") - - body = Text.renderMarked(lastcomment.body) - body = body.replace /[\r\n]/g, " " # Remove whitespace - body = body.replace /\.*?\<\/blockquote\>/g, " " # Remove quotes - body = body.replace /\<.*?\>/g, " " # Remove html codes - if body.length > 60 # Strip if too long - body = body[0..60].replace(/(.*) .*?$/, "$1") + " ..." # Keep the last 60 character and strip back until last space - elem.find(".body").html(body) - - title_hash = lastcomment.post_title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") - elem.find(".postlink").text(lastcomment.post_title).attr("href", "?Post:#{lastcomment.post_id}:#{title_hash}#Comments") - - applyPagerdata: (page, limit, has_next) -> - pager = $(".pager") - if page > 1 - pager.find(".prev").css("display", "inline-block").attr("href", "?page=#{page-1}") - if has_next - pager.find(".next").css("display", "inline-block").attr("href", "?page=#{page+1}") - - routeUrl: (url) -> - @log "Routing url:", url - if match = url.match /Post:([0-9]+)/ - $("body").addClass("page-post") - @post_id = parseInt(match[1]) - @pagePost() - else - $("body").addClass("page-main") - if match = url.match /page=([0-9]+)/ - @page = parseInt(match[1]) - @pageMain() - - # - Pages - - - pagePost: () -> - s = (+ new Date) - @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) => - parse_res = (res) => - if res.length - post = res[0] - @applyPostdata($(".post-full"), post, true) - $(".post-full .like").attr("id", "post_like_#{post.post_id}").off("click").off("click").on "click", @submitPostVote - Comments.pagePost(@post_id) - else - $(".post-full").html("

Not found

") - @pageLoaded() - Comments.checkCert() - - # Temporary dbschema bug workaround - if res.error - @cmd "dbQuery", ["SELECT *, -1 AS votes FROM post WHERE post_id = #{@post_id} LIMIT 1"], parse_res - else - parse_res(res) - - - pageMain: -> - limit = 15 - query = """ - SELECT - post.*, COUNT(comment_id) AS comments, - (SELECT COUNT(*) FROM post_vote WHERE post_vote.post_id = post.post_id) AS votes - FROM post - LEFT JOIN comment USING (post_id) - GROUP BY post_id - ORDER BY date_published DESC - LIMIT #{(@page-1)*limit}, #{limit+1} - """ - @cmd "dbQuery", [query], (res) => - parse_res = (res) => - s = (+ new Date) - if res.length > limit # Has next page - res.pop() - @applyPagerdata(@page, limit, true) - else - @applyPagerdata(@page, limit, false) - - res.reverse() - for post in res - elem = $("#post_#{post.post_id}") - if elem.length == 0 # Not exits yet - elem = $(".post.template").clone().removeClass("template").attr("id", "post_#{post.post_id}") - elem.prependTo(".posts") - # elem.find(".score").attr("id", "post_score_#{post.post_id}").on "click", @submitPostVote # Submit vote - elem.find(".like").attr("id", "post_like_#{post.post_id}").off("click").on "click", @submitPostVote - @applyPostdata(elem, post) - @pageLoaded() - @log "Posts loaded in", ((+ new Date)-s),"ms" - - $(".posts .new").off("click").on "click", => # Create new blog post - @cmd "fileGet", ["data/data.json"], (res) => - data = JSON.parse(res) - # Add to data - data.post.unshift - post_id: data.next_post_id - title: "New blog post" - date_published: (+ new Date)/1000 - body: "Blog post body" - data.next_post_id += 1 - - # Create html elements - elem = $(".post.template").clone().removeClass("template") - @applyPostdata(elem, data.post[0]) - elem.hide() - elem.prependTo(".posts").slideDown() - @addInlineEditors(elem) - - @writeData(data) - return false - - # Temporary dbschema bug workaround - if res.error - query = """ - SELECT - post.*, COUNT(comment_id) AS comments, - -1 AS votes - FROM post - LEFT JOIN comment USING (post_id) - GROUP BY post_id - ORDER BY date_published DESC - LIMIT #{(@page-1)*limit}, #{limit+1} - """ - @cmd "dbQuery", [query], parse_res - else - parse_res(res) - - - # - EOF Pages - - - - # All page content loaded - pageLoaded: => - $("body").addClass("loaded") # Back/forward button keep position support - $('pre code').each (i, block) -> # Higlight code blocks - hljs.highlightBlock(block) - @event_page_load.resolve() - @cmd "innerLoaded", true - - - addInlineEditors: (parent) -> - @logStart "Adding inline editors" - elems = $("[data-editable]:visible", parent) - for elem in elems - elem = $(elem) - if not elem.data("editor") and not elem.hasClass("editor") - editor = new InlineEditor(elem, @getContent, @saveContent, @getObject) - elem.data("editor", editor) - @logEnd "Adding inline editors" - - - # Check if publishing is necessary - checkPublishbar: -> - if not @data["modified"] or @data["modified"] > @site_info.content.modified - $(".publishbar").addClass("visible") - else - $(".publishbar").removeClass("visible") - - - # Sign and Publish site - publish: => - if @site_info.privatekey # Privatekey stored in users.json - @cmd "sitePublish", ["stored"], (res) => - @log "Publish result:", res - else - @cmd "wrapperPrompt", ["Enter your private key:", "password"], (privatekey) => # Prompt the private key - $(".publishbar .button").addClass("loading") - @cmd "sitePublish", [privatekey], (res) => - $(".publishbar .button").removeClass("loading") - @log "Publish result:", res - - return false # Ignore link default event - - - # Apply from data to post html element - applyPostdata: (elem, post, full=false) -> - title_hash = post.title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") - elem.data("object", "Post:"+post.post_id) - $(".title .editable", elem).html(post.title).attr("href", "?Post:#{post.post_id}:#{title_hash}").data("content", post.title) - date_published = Time.since(post.date_published) - # Published date - if post.body.match /^---/m # Has more over fold - date_published += " · #{Time.readtime(post.body)}" # If has break add readtime - $(".more", elem).css("display", "inline-block").attr("href", "?Post:#{post.post_id}:#{title_hash}") - $(".details .published", elem).html(date_published).data("content", post.date_published) - # Comments num - if post.comments > 0 - $(".details .comments-num", elem).css("display", "inline").attr("href", "?Post:#{post.post_id}:#{title_hash}#Comments") - if post.comments > 1 - $(".details .comments-num .num", elem).text("#{post.comments} comments") - else - $(".details .comments-num .num", elem).text("#{post.comments} comment") - else - $(".details .comments-num", elem).css("display", "none") - - ### - if @my_post_votes[post.post_id] # Voted on it - $(".score-inactive .score-num", elem).text post.votes-1 - $(".score-active .score-num", elem).text post.votes - $(".score", elem).addClass("active") - else # Not voted on it - $(".score-inactive .score-num", elem).text post.votes - $(".score-active .score-num", elem).text post.votes+1 - - if post.votes == 0 - $(".score", elem).addClass("noscore") - else - $(".score", elem).removeClass("noscore") - ### - if post.votes > 0 - $(".like .num", elem).text post.votes - else if post.votes == -1 # DB bug - $(".like", elem).css("display", "none") - else - $(".like .num", elem).text "" - - if @my_post_votes[post.post_id] # Voted on it - $(".like", elem).addClass("active") - - - if full - body = post.body - else # On main page only show post until the first --- hr separator - body = post.body.replace(/^([\s\S]*?)\n---\n[\s\S]*$/, "$1") - - if $(".body", elem).data("content") != post.body - $(".body", elem).html(Text.renderMarked(body)).data("content", post.body) - - - # Wrapper websocket connection ready - onOpenWebsocket: (e) => - @loadData() - @cmd "siteInfo", {}, (site_info) => - @setSiteinfo(site_info) - query_my_votes = """ - SELECT - 'post_vote' AS type, - post_id AS uri - FROM json - LEFT JOIN post_vote USING (json_id) - WHERE directory = 'users/#{@site_info.auth_address}' AND file_name = 'data.json' - """ - @cmd "dbQuery", [query_my_votes], (res) => - for row in res - @my_post_votes[row["uri"]] = 1 - @routeUrl(window.location.search.substring(1)) - - @cmd "serverInfo", {}, (ret) => # Get server info - @server_info = ret - if @server_info.rev < 160 - @loadData("old") - @loadLastcomments("noanim") - - - # Returns the elem parent object - getObject: (elem) => - return elem.parents("[data-object]:first") - - - # Get content from data.json - getContent: (elem, raw=false) => - [type, id] = @getObject(elem).data("object").split(":") - id = parseInt(id) - content = elem.data("content") - if elem.data("editable-mode") == "timestamp" # Convert to time - content = Time.date(content, "full") - - if elem.data("editable-mode") == "simple" or raw # No markdown - return content - else - return Text.renderMarked(content) - - - # Save content to data.json - saveContent: (elem, content, cb=false) => - if elem.data("deletable") and content == null then return @deleteObject(elem, cb) # Its a delete request - elem.data("content", content) - [type, id] = @getObject(elem).data("object").split(":") - id = parseInt(id) - if type == "Post" or type == "Site" - @saveSite(elem, type, id, content, cb) - else if type == "Comment" - @saveComment(elem, type, id, content, cb) - - - - saveSite: (elem, type, id, content, cb) -> - @cmd "fileGet", ["data/data.json"], (res) => - data = JSON.parse(res) - if type == "Post" - post = (post for post in data.post when post.post_id == id)[0] - - if elem.data("editable-mode") == "timestamp" # Time parse to timestamp - content = Time.timestamp(content) - - post[elem.data("editable")] = content - else if type == "Site" - data[elem.data("editable")] = content - - @writeData data, (res) => - if cb - if res == true # OK - if elem.data("editable-mode") == "simple" # No markdown - cb(content) - else if elem.data("editable-mode") == "timestamp" # Format timestamp - cb(Time.since(content)) - else - cb(Text.renderMarked(content)) - else # Error - cb(false) - - - - saveComment: (elem, type, id, content, cb) -> - @log "Saving comment...", id - @getObject(elem).css "height", "auto" - inner_path = "data/users/#{Page.site_info.auth_address}/data.json" - Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => - data = JSON.parse(data) - comment = (comment for comment in data.comment when comment.comment_id == id)[0] - comment[elem.data("editable")] = content - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) - @writePublish inner_path, btoa(json_raw), (res) => - if res == true - Comments.checkCert("updaterules") - if cb then cb(Text.renderMarked(content, {"sanitize": true})) - else - @cmd "wrapperNotification", ["error", "File write error: #{res}"] - if cb then cb(false) - - - - - deleteObject: (elem, cb=False) -> - [type, id] = elem.data("object").split(":") - id = parseInt(id) - - if type == "Post" - @cmd "fileGet", ["data/data.json"], (res) => - data = JSON.parse(res) - if type == "Post" - post = (post for post in data.post when post.post_id == id)[0] - if not post then return false # No post found for this id - data.post.splice(data.post.indexOf(post), 1) # Remove from data - - @writeData data, (res) => - if cb then cb() - if res == true then elem.slideUp() - else if type == "Comment" - inner_path = "data/users/#{Page.site_info.auth_address}/data.json" - @cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => - data = JSON.parse(data) - comment = (comment for comment in data.comment when comment.comment_id == id)[0] - data.comment.splice(data.comment.indexOf(comment), 1) - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) - @writePublish inner_path, btoa(json_raw), (res) => - if res == true - elem.slideUp() - if cb then cb() - - - - writeData: (data, cb=null) -> - if not data - return @log "Data missing" - @data["modified"] = data.modified = Time.timestamp() - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) # Encode to json, encode utf8 - @cmd "fileWrite", ["data/data.json", btoa(json_raw)], (res) => # Convert to to base64 and send - if res == "ok" - if cb then cb(true) - else - @cmd "wrapperNotification", ["error", "File write error: #{res}"] - if cb then cb(false) - @checkPublishbar() - - # Updating title in content.json - @cmd "fileGet", ["content.json"], (content) => - content = content.replace /"title": ".*?"/, "\"title\": \"#{data.title}\"" # Load as raw html to prevent js bignumber problems - @cmd "fileWrite", ["content.json", btoa(content)], (res) => - if res != "ok" - @cmd "wrapperNotification", ["error", "Content.json write error: #{res}"] - - # If the privatekey is stored sign the new content - if @site_info["privatekey"] - @cmd "siteSign", ["stored", "content.json"], (res) => - @log "Sign result", res - - - writePublish: (inner_path, data, cb) -> - @cmd "fileWrite", [inner_path, data], (res) => - if res != "ok" # fileWrite failed - @cmd "wrapperNotification", ["error", "File write error: #{res}"] - cb(false) - return false - - @cmd "sitePublish", {"inner_path": inner_path}, (res) => - if res == "ok" - cb(true) - else - cb(res) - - submitPostVote: (e) => - if not Page.site_info.cert_user_id # No selected cert - Page.cmd "certSelect", [["zeroid.bit"]] - return false - - elem = $(e.currentTarget) - elem.toggleClass("active").addClass("loading") - inner_path = "data/users/#{@site_info.auth_address}/data.json" - Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => - if data - data = JSON.parse(data) - else # Default data - data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, "post_vote": {} } - - if not data.post_vote - data.post_vote = {} - post_id = elem.attr("id").match("_([0-9]+)$")[1] - - if elem.hasClass("active") - data.post_vote[post_id] = 1 - else - delete data.post_vote[post_id] - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) - - current_num = parseInt elem.find(".num").text() - if not current_num - current_num = 0 - if elem.hasClass("active") - elem.find(".num").text(current_num+1) - else - elem.find(".num").text(current_num-1) - - Page.writePublish inner_path, btoa(json_raw), (res) => - elem.removeClass("loading") - @log "Writepublish result", res - - return false - - # Parse incoming requests - onRequest: (cmd, message) -> - if cmd == "setSiteInfo" # Site updated - @actionSetSiteInfo(message) - else - @log "Unknown command", message - - - # Siteinfo changed - actionSetSiteInfo: (message) => - @setSiteinfo(message.params) - @checkPublishbar() - - - setSiteinfo: (site_info) => - @site_info = site_info - @event_site_info.resolve(site_info) - if $("body").hasClass("page-post") then Comments.checkCert() # Update if username changed - # User commented - if site_info.event?[0] == "file_done" and site_info.event[1].match /.*users.*data.json$/ - if $("body").hasClass("page-post") - @pagePost() - Comments.loadComments() # Post page, reload comments - @loadLastcomments() - if $("body").hasClass("page-main") - RateLimit 500, => - @pageMain() - @loadLastcomments() - else if site_info.event?[0] == "file_done" and site_info.event[1] == "data/data.json" - @loadData() - if $("body").hasClass("page-main") then @pageMain() - if $("body").hasClass("page-post") then @pagePost() + init: -> + @data = null + @site_info = null + @server_info = null + @page = 1 + @my_post_votes = {} + + @event_page_load = $.Deferred() + @event_site_info = $.Deferred() + + # Editable items on own site + $.when(@event_page_load, @event_site_info).done => + if @site_info.settings.own or @data.demo + @addInlineEditors() + @checkPublishbar() + $(".publishbar").off("click").on "click", @publish + $(".posts .button.new").css("display", "inline-block") + $(".editbar .icon-help").off("click").on "click", => + $(".editbar .markdown-help").css("display", "block") + $(".editbar .markdown-help").toggleClassLater("visible", 10) + $(".editbar .icon-help").toggleClass("active") + return false + + $.when(@event_site_info).done => + @log "event site info" + # Set avatar + imagedata = new Identicon(@site_info.address, 70).toString(); + $("body").append("") + @initFollowButton() + @log "inited!" + + + initFollowButton: -> + @follow = new Follow($(".feed-follow")) + @follow.addFeed("Posts", " + SELECT + post_id AS event_uri, + 'post' AS type, + date_published AS date_added, + title AS title, + body AS body, + '?Post:' || post_id AS url + FROM post", true) + + if Page.site_info.cert_user_id + username = Page.site_info.cert_user_id.replace /@.*/, "" + @follow.addFeed("Username mentions", " + SELECT + 'comment' AS type, + date_added, + post.title AS title, + keyvalue.value || ': ' || comment.body AS body, + '?Post:' || comment.post_id || '#Comments' AS url + FROM comment + LEFT JOIN json USING (json_id) + LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') + LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') + LEFT JOIN post ON (comment.post_id = post.post_id) + WHERE + comment.body LIKE '%[#{username}%' OR comment.body LIKE '%@#{username}%' + ", true) + + @follow.addFeed("Comments", " + SELECT + 'comment' AS type, + date_added, + post.title AS title, + keyvalue.value || ': ' || comment.body AS body, + '?Post:' || comment.post_id || '#Comments' AS url + FROM comment + LEFT JOIN json USING (json_id) + LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') + LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') + LEFT JOIN post ON (comment.post_id = post.post_id)") + @follow.init() + + + loadData: (query="new") -> + # Get blog parameters + if query == "old" # Old type query for pre 0.3.0 + query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE path = 'data.json'" + else + query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE directory = '' AND file_name = 'data.json'" + @cmd "dbQuery", [query], (res) => + @data = {} + if res + for row in res + @data[row.key] = row.value + $(".left h1 a:not(.editable-edit)").html(@data.title).data("content", @data.title) + $(".left h2").html(Text.renderMarked(@data.description)).data("content", @data.description) + $(".left .links").html(Text.renderMarked(@data.links)).data("content", @data.links) + + loadLastcomments: (type="show", cb=false) -> + query = " + SELECT comment.*, json_content.json_id AS content_json_id, keyvalue.value AS cert_user_id, json.directory, post.title AS post_title + FROM comment + LEFT JOIN json USING (json_id) + LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') + LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') + LEFT JOIN post ON (comment.post_id = post.post_id) + WHERE post.title IS NOT NULL + ORDER BY date_added DESC LIMIT 3" + + @cmd "dbQuery", [query], (res) => + if res.length + $(".lastcomments").css("display", "block") + res.reverse() + for lastcomment in res + elem = $("#lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") + if elem.length == 0 # Not exits yet + elem = $(".lastcomment.template").clone().removeClass("template").attr("id", "lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") + if type != "noanim" + elem.cssSlideDown() + elem.prependTo(".lastcomments ul") + @applyLastcommentdata(elem, lastcomment) + if cb then cb() + + applyLastcommentdata: (elem, lastcomment) -> + elem.find(".user_name").text(lastcomment.cert_user_id.replace(/@.*/, "")+":") + + body = Text.renderMarked(lastcomment.body) + body = body.replace /[\r\n]/g, " " # Remove whitespace + body = body.replace /\.*?\<\/blockquote\>/g, " " # Remove quotes + body = body.replace /\<.*?\>/g, " " # Remove html codes + if body.length > 60 # Strip if too long + body = body[0..60].replace(/(.*) .*?$/, "$1") + " ..." # Keep the last 60 character and strip back until last space + elem.find(".body").html(body) + + title_hash = lastcomment.post_title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") + elem.find(".postlink").text(lastcomment.post_title).attr("href", "?Post:#{lastcomment.post_id}:#{title_hash}#Comments") + + applyPagerdata: (page, limit, has_next) -> + pager = $(".pager") + if page > 1 + pager.find(".prev").css("display", "inline-block").attr("href", "?page=#{page-1}") + if has_next + pager.find(".next").css("display", "inline-block").attr("href", "?page=#{page+1}") + + routeUrl: (url) -> + @log "Routing url:", url + if match = url.match /Post:([0-9]+)/ + $("body").addClass("page-post") + @post_id = parseInt(match[1]) + @pagePost() + else + $("body").addClass("page-main") + if match = url.match /page=([0-9]+)/ + @page = parseInt(match[1]) + @pageMain() + + # - Pages - + + pagePost: () -> + s = (+ new Date) + @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) => + parse_res = (res) => + if res.length + post = res[0] + @applyPostdata($(".post-full"), post, true) + $(".post-full .like").attr("id", "post_like_#{post.post_id}").off("click").off("click").on "click", @submitPostVote + Comments.pagePost(@post_id) + else + $(".post-full").html("

Not found

") + @pageLoaded() + Comments.checkCert() + + # Temporary dbschema bug workaround + if res.error + @cmd "dbQuery", ["SELECT *, -1 AS votes FROM post WHERE post_id = #{@post_id} LIMIT 1"], parse_res + else + parse_res(res) + + + pageMain: -> + limit = 15 + query = """ + SELECT + post.*, COUNT(comment_id) AS comments, + (SELECT COUNT(*) FROM post_vote WHERE post_vote.post_id = post.post_id) AS votes + FROM post + LEFT JOIN comment USING (post_id) + GROUP BY post_id + ORDER BY date_published DESC + LIMIT #{(@page-1)*limit}, #{limit+1} + """ + @cmd "dbQuery", [query], (res) => + parse_res = (res) => + s = (+ new Date) + if res.length > limit # Has next page + res.pop() + @applyPagerdata(@page, limit, true) + else + @applyPagerdata(@page, limit, false) + + res.reverse() + for post in res + elem = $("#post_#{post.post_id}") + if elem.length == 0 # Not exits yet + elem = $(".post.template").clone().removeClass("template").attr("id", "post_#{post.post_id}") + elem.prependTo(".posts") + # elem.find(".score").attr("id", "post_score_#{post.post_id}").on "click", @submitPostVote # Submit vote + elem.find(".like").attr("id", "post_like_#{post.post_id}").off("click").on "click", @submitPostVote + @applyPostdata(elem, post) + @pageLoaded() + @log "Posts loaded in", ((+ new Date)-s),"ms" + + $(".posts .new").off("click").on "click", => # Create new blog post + @cmd "fileGet", ["data/data.json"], (res) => + data = JSON.parse(res) + # Add to data + data.post.unshift + post_id: data.next_post_id + title: "New blog post" + date_published: (+ new Date)/1000 + body: "Blog post body" + data.next_post_id += 1 + + # Create html elements + elem = $(".post.template").clone().removeClass("template") + @applyPostdata(elem, data.post[0]) + elem.hide() + elem.prependTo(".posts").slideDown() + @addInlineEditors(elem) + + @writeData(data) + return false + + # Temporary dbschema bug workaround + if res.error + query = """ + SELECT + post.*, COUNT(comment_id) AS comments, + -1 AS votes + FROM post + LEFT JOIN comment USING (post_id) + GROUP BY post_id + ORDER BY date_published DESC + LIMIT #{(@page-1)*limit}, #{limit+1} + """ + @cmd "dbQuery", [query], parse_res + else + parse_res(res) + + + # - EOF Pages - + + + # All page content loaded + pageLoaded: => + $("body").addClass("loaded") # Back/forward button keep position support + $('pre code').each (i, block) -> # Higlight code blocks + hljs.highlightBlock(block) + @event_page_load.resolve() + @cmd "innerLoaded", true + + + addInlineEditors: (parent) -> + @logStart "Adding inline editors" + elems = $("[data-editable]:visible", parent) + for elem in elems + elem = $(elem) + if not elem.data("editor") and not elem.hasClass("editor") + editor = new InlineEditor(elem, @getContent, @saveContent, @getObject) + elem.data("editor", editor) + @logEnd "Adding inline editors" + + + # Check if publishing is necessary + checkPublishbar: -> + if not @data["modified"] or @data["modified"] > @site_info.content.modified + $(".publishbar").addClass("visible") + else + $(".publishbar").removeClass("visible") + + + # Sign and Publish site + publish: => + if @site_info.privatekey # Privatekey stored in users.json + @cmd "sitePublish", ["stored"], (res) => + @log "Publish result:", res + else + @cmd "wrapperPrompt", ["Enter your private key:", "password"], (privatekey) => # Prompt the private key + $(".publishbar .button").addClass("loading") + @cmd "sitePublish", [privatekey], (res) => + $(".publishbar .button").removeClass("loading") + @log "Publish result:", res + + return false # Ignore link default event + + + # Apply from data to post html element + applyPostdata: (elem, post, full=false) -> + title_hash = post.title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") + elem.data("object", "Post:"+post.post_id) + $(".title .editable", elem).html(post.title).attr("href", "?Post:#{post.post_id}:#{title_hash}").data("content", post.title) + date_published = Time.since(post.date_published) + # Published date + if post.body.match /^---/m # Has more over fold + date_published += " · #{Time.readtime(post.body)}" # If has break add readtime + $(".more", elem).css("display", "inline-block").attr("href", "?Post:#{post.post_id}:#{title_hash}") + $(".details .published", elem).html(date_published).data("content", post.date_published) + # Comments num + if post.comments > 0 + $(".details .comments-num", elem).css("display", "inline").attr("href", "?Post:#{post.post_id}:#{title_hash}#Comments") + if post.comments > 1 + $(".details .comments-num .num", elem).text("#{post.comments} comments") + else + $(".details .comments-num .num", elem).text("#{post.comments} comment") + else + $(".details .comments-num", elem).css("display", "none") + + ### + if @my_post_votes[post.post_id] # Voted on it + $(".score-inactive .score-num", elem).text post.votes-1 + $(".score-active .score-num", elem).text post.votes + $(".score", elem).addClass("active") + else # Not voted on it + $(".score-inactive .score-num", elem).text post.votes + $(".score-active .score-num", elem).text post.votes+1 + + if post.votes == 0 + $(".score", elem).addClass("noscore") + else + $(".score", elem).removeClass("noscore") + ### + if post.votes > 0 + $(".like .num", elem).text post.votes + else if post.votes == -1 # DB bug + $(".like", elem).css("display", "none") + else + $(".like .num", elem).text "" + + if @my_post_votes[post.post_id] # Voted on it + $(".like", elem).addClass("active") + + + if full + body = post.body + else # On main page only show post until the first --- hr separator + body = post.body.replace(/^([\s\S]*?)\n---\n[\s\S]*$/, "$1") + + if $(".body", elem).data("content") != post.body + $(".body", elem).html(Text.renderMarked(body)).data("content", post.body) + + + # Wrapper websocket connection ready + onOpenWebsocket: (e) => + @loadData() + @cmd "siteInfo", {}, (site_info) => + @setSiteinfo(site_info) + query_my_votes = """ + SELECT + 'post_vote' AS type, + post_id AS uri + FROM json + LEFT JOIN post_vote USING (json_id) + WHERE directory = 'users/#{@site_info.auth_address}' AND file_name = 'data.json' + """ + @cmd "dbQuery", [query_my_votes], (res) => + for row in res + @my_post_votes[row["uri"]] = 1 + @routeUrl(window.location.search.substring(1)) + + @cmd "serverInfo", {}, (ret) => # Get server info + @server_info = ret + if @server_info.rev < 160 + @loadData("old") + @loadLastcomments("noanim") + + + # Returns the elem parent object + getObject: (elem) => + return elem.parents("[data-object]:first") + + + # Get content from data.json + getContent: (elem, raw=false) => + [type, id] = @getObject(elem).data("object").split(":") + id = parseInt(id) + content = elem.data("content") + if elem.data("editable-mode") == "timestamp" # Convert to time + content = Time.date(content, "full") + + if elem.data("editable-mode") == "simple" or raw # No markdown + return content + else + return Text.renderMarked(content) + + + # Save content to data.json + saveContent: (elem, content, cb=false) => + if elem.data("deletable") and content == null then return @deleteObject(elem, cb) # Its a delete request + elem.data("content", content) + [type, id] = @getObject(elem).data("object").split(":") + id = parseInt(id) + if type == "Post" or type == "Site" + @saveSite(elem, type, id, content, cb) + else if type == "Comment" + @saveComment(elem, type, id, content, cb) + + + + saveSite: (elem, type, id, content, cb) -> + @cmd "fileGet", ["data/data.json"], (res) => + data = JSON.parse(res) + if type == "Post" + post = (post for post in data.post when post.post_id == id)[0] + + if elem.data("editable-mode") == "timestamp" # Time parse to timestamp + content = Time.timestamp(content) + + post[elem.data("editable")] = content + else if type == "Site" + data[elem.data("editable")] = content + + @writeData data, (res) => + if cb + if res == true # OK + if elem.data("editable-mode") == "simple" # No markdown + cb(content) + else if elem.data("editable-mode") == "timestamp" # Format timestamp + cb(Time.since(content)) + else + cb(Text.renderMarked(content)) + else # Error + cb(false) + + + + saveComment: (elem, type, id, content, cb) -> + @log "Saving comment...", id + @getObject(elem).css "height", "auto" + inner_path = "data/users/#{Page.site_info.auth_address}/data.json" + Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => + data = JSON.parse(data) + comment = (comment for comment in data.comment when comment.comment_id == id)[0] + comment[elem.data("editable")] = content + json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) + @writePublish inner_path, btoa(json_raw), (res) => + if res == true + Comments.checkCert("updaterules") + if cb then cb(Text.renderMarked(content, {"sanitize": true})) + else + @cmd "wrapperNotification", ["error", "File write error: #{res}"] + if cb then cb(false) + + + + + deleteObject: (elem, cb=False) -> + [type, id] = elem.data("object").split(":") + id = parseInt(id) + + if type == "Post" + @cmd "fileGet", ["data/data.json"], (res) => + data = JSON.parse(res) + if type == "Post" + post = (post for post in data.post when post.post_id == id)[0] + if not post then return false # No post found for this id + data.post.splice(data.post.indexOf(post), 1) # Remove from data + + @writeData data, (res) => + if cb then cb() + if res == true then elem.slideUp() + else if type == "Comment" + inner_path = "data/users/#{Page.site_info.auth_address}/data.json" + @cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => + data = JSON.parse(data) + comment = (comment for comment in data.comment when comment.comment_id == id)[0] + data.comment.splice(data.comment.indexOf(comment), 1) + json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) + @writePublish inner_path, btoa(json_raw), (res) => + if res == true + elem.slideUp() + if cb then cb() + + + + writeData: (data, cb=null) -> + if not data + return @log "Data missing" + @data["modified"] = data.modified = Time.timestamp() + json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) # Encode to json, encode utf8 + @cmd "fileWrite", ["data/data.json", btoa(json_raw)], (res) => # Convert to to base64 and send + if res == "ok" + if cb then cb(true) + else + @cmd "wrapperNotification", ["error", "File write error: #{res}"] + if cb then cb(false) + @checkPublishbar() + + # Updating title in content.json + @cmd "fileGet", ["content.json"], (content) => + content = content.replace /"title": ".*?"/, "\"title\": \"#{data.title}\"" # Load as raw html to prevent js bignumber problems + @cmd "fileWrite", ["content.json", btoa(content)], (res) => + if res != "ok" + @cmd "wrapperNotification", ["error", "Content.json write error: #{res}"] + + # If the privatekey is stored sign the new content + if @site_info["privatekey"] + @cmd "siteSign", ["stored", "content.json"], (res) => + @log "Sign result", res + + + writePublish: (inner_path, data, cb) -> + @cmd "fileWrite", [inner_path, data], (res) => + if res != "ok" # fileWrite failed + @cmd "wrapperNotification", ["error", "File write error: #{res}"] + cb(false) + return false + + @cmd "sitePublish", {"inner_path": inner_path}, (res) => + if res == "ok" + cb(true) + else + cb(res) + + submitPostVote: (e) => + if not Page.site_info.cert_user_id # No selected cert + Page.cmd "certSelect", [["zeroid.bit"]] + return false + + elem = $(e.currentTarget) + elem.toggleClass("active").addClass("loading") + inner_path = "data/users/#{@site_info.auth_address}/data.json" + Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => + if data + data = JSON.parse(data) + else # Default data + data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, "post_vote": {} } + + if not data.post_vote + data.post_vote = {} + post_id = elem.attr("id").match("_([0-9]+)$")[1] + + if elem.hasClass("active") + data.post_vote[post_id] = 1 + else + delete data.post_vote[post_id] + json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) + + current_num = parseInt elem.find(".num").text() + if not current_num + current_num = 0 + if elem.hasClass("active") + elem.find(".num").text(current_num+1) + else + elem.find(".num").text(current_num-1) + + Page.writePublish inner_path, btoa(json_raw), (res) => + elem.removeClass("loading") + @log "Writepublish result", res + + return false + + # Parse incoming requests + onRequest: (cmd, message) -> + if cmd == "setSiteInfo" # Site updated + @actionSetSiteInfo(message) + else + @log "Unknown command", message + + + # Siteinfo changed + actionSetSiteInfo: (message) => + @setSiteinfo(message.params) + @checkPublishbar() + + + setSiteinfo: (site_info) => + @site_info = site_info + @event_site_info.resolve(site_info) + if $("body").hasClass("page-post") then Comments.checkCert() # Update if username changed + # User commented + if site_info.event?[0] == "file_done" and site_info.event[1].match /.*users.*data.json$/ + if $("body").hasClass("page-post") + @pagePost() + Comments.loadComments() # Post page, reload comments + @loadLastcomments() + if $("body").hasClass("page-main") + RateLimit 500, => + @pageMain() + @loadLastcomments() + else if site_info.event?[0] == "file_done" and site_info.event[1] == "data/data.json" + @loadData() + if $("body").hasClass("page-main") then @pageMain() + if $("body").hasClass("page-post") then @pagePost() window.Page = new ZeroBlog() From 0aba8ad21bca6faeaa28c2054947dff1ce9196e8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 21 Apr 2016 12:36:08 +0800 Subject: [PATCH 02/17] quiet coffeelint --- js/ZeroBlog.coffee | 173 ++++++++++++++++++++++++++++++--------------- 1 file changed, 116 insertions(+), 57 deletions(-) diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index a2c4ab4..9da5fbf 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -16,7 +16,7 @@ class ZeroBlog extends ZeroFrame @checkPublishbar() $(".publishbar").off("click").on "click", @publish $(".posts .button.new").css("display", "inline-block") - $(".editbar .icon-help").off("click").on "click", => + $(".editbar .icon-help").off("click").on "click", -> $(".editbar .markdown-help").css("display", "block") $(".editbar .markdown-help").toggleClassLater("visible", 10) $(".editbar .icon-help").toggleClass("active") @@ -25,8 +25,9 @@ class ZeroBlog extends ZeroFrame $.when(@event_site_info).done => @log "event site info" # Set avatar - imagedata = new Identicon(@site_info.address, 70).toString(); - $("body").append("") + imagedata = new Identicon(@site_info.address, 70).toString() + $("body").append("") @initFollowButton() @log "inited!" @@ -54,11 +55,15 @@ class ZeroBlog extends ZeroFrame '?Post:' || comment.post_id || '#Comments' AS url FROM comment LEFT JOIN json USING (json_id) - LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') - LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') + LEFT JOIN json AS json_content ON + (json_content.directory = json.directory + AND json_content.file_name='content.json') + LEFT JOIN keyvalue ON + (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') LEFT JOIN post ON (comment.post_id = post.post_id) WHERE - comment.body LIKE '%[#{username}%' OR comment.body LIKE '%@#{username}%' + comment.body LIKE '%[#{username}%' OR + comment.body LIKE '%@#{username}%' ", true) @follow.addFeed("Comments", " @@ -70,8 +75,11 @@ class ZeroBlog extends ZeroFrame '?Post:' || comment.post_id || '#Comments' AS url FROM comment LEFT JOIN json USING (json_id) - LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') - LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') + LEFT JOIN json AS json_content ON + (json_content.directory = json.directory AND + json_content.file_name='content.json') + LEFT JOIN keyvalue ON + (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') LEFT JOIN post ON (comment.post_id = post.post_id)") @follow.init() @@ -79,25 +87,35 @@ class ZeroBlog extends ZeroFrame loadData: (query="new") -> # Get blog parameters if query == "old" # Old type query for pre 0.3.0 - query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE path = 'data.json'" + query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) + WHERE path = 'data.json'" else - query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE directory = '' AND file_name = 'data.json'" + query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) + WHERE directory = '' AND file_name = 'data.json'" @cmd "dbQuery", [query], (res) => @data = {} if res for row in res @data[row.key] = row.value - $(".left h1 a:not(.editable-edit)").html(@data.title).data("content", @data.title) - $(".left h2").html(Text.renderMarked(@data.description)).data("content", @data.description) - $(".left .links").html(Text.renderMarked(@data.links)).data("content", @data.links) + $(".left h1 a:not(.editable-edit)").html(@data.title) + .data("content", @data.title) + $(".left h2").html(Text.renderMarked(@data.description)) + .data("content", @data.description) + $(".left .links").html(Text.renderMarked(@data.links)) + .data("content", @data.links) loadLastcomments: (type="show", cb=false) -> query = " - SELECT comment.*, json_content.json_id AS content_json_id, keyvalue.value AS cert_user_id, json.directory, post.title AS post_title + SELECT + comment.*, json_content.json_id AS content_json_id, + keyvalue.value AS cert_user_id, json.directory, post.title AS post_title FROM comment LEFT JOIN json USING (json_id) - LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') - LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') + LEFT JOIN json AS json_content ON + (json_content.directory = json.directory AND + json_content.file_name='content.json') + LEFT JOIN keyvalue ON + (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') LEFT JOIN post ON (comment.post_id = post.post_id) WHERE post.title IS NOT NULL ORDER BY date_added DESC LIMIT 3" @@ -107,9 +125,12 @@ class ZeroBlog extends ZeroFrame $(".lastcomments").css("display", "block") res.reverse() for lastcomment in res - elem = $("#lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") + elem = + $("#lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") if elem.length == 0 # Not exits yet - elem = $(".lastcomment.template").clone().removeClass("template").attr("id", "lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") + elem = $(".lastcomment.template").clone(). + removeClass("template").attr("id", + "lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") if type != "noanim" elem.cssSlideDown() elem.prependTo(".lastcomments ul") @@ -117,25 +138,32 @@ class ZeroBlog extends ZeroFrame if cb then cb() applyLastcommentdata: (elem, lastcomment) -> - elem.find(".user_name").text(lastcomment.cert_user_id.replace(/@.*/, "")+":") + elem.find(".user_name") + .text(lastcomment.cert_user_id.replace(/@.*/, "")+":") body = Text.renderMarked(lastcomment.body) body = body.replace /[\r\n]/g, " " # Remove whitespace - body = body.replace /\.*?\<\/blockquote\>/g, " " # Remove quotes + #Remove quotes + body = body.replace /\.*?\<\/blockquote\>/g, " " body = body.replace /\<.*?\>/g, " " # Remove html codes if body.length > 60 # Strip if too long - body = body[0..60].replace(/(.*) .*?$/, "$1") + " ..." # Keep the last 60 character and strip back until last space + #Keep the last 60 character and strip back until last space + body = body[0..60].replace(/(.*) .*?$/, "$1") + " ..." elem.find(".body").html(body) - title_hash = lastcomment.post_title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") - elem.find(".postlink").text(lastcomment.post_title).attr("href", "?Post:#{lastcomment.post_id}:#{title_hash}#Comments") + title_hash = lastcomment.post_title.replace(/[#?& ]/g, "+") + .replace(/[+]+/g, "+") + elem.find(".postlink").text(lastcomment.post_title) + .attr("href", "?Post:#{lastcomment.post_id}:#{title_hash}#Comments") applyPagerdata: (page, limit, has_next) -> pager = $(".pager") if page > 1 - pager.find(".prev").css("display", "inline-block").attr("href", "?page=#{page-1}") + pager.find(".prev").css("display", "inline-block") + .attr("href", "?page=#{page-1}") if has_next - pager.find(".next").css("display", "inline-block").attr("href", "?page=#{page+1}") + pager.find(".next").css("display", "inline-block") + .attr("href", "?page=#{page+1}") routeUrl: (url) -> @log "Routing url:", url @@ -153,12 +181,15 @@ class ZeroBlog extends ZeroFrame pagePost: () -> s = (+ new Date) - @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) => + @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) => parse_res = (res) => if res.length post = res[0] @applyPostdata($(".post-full"), post, true) - $(".post-full .like").attr("id", "post_like_#{post.post_id}").off("click").off("click").on "click", @submitPostVote + $(".post-full .like").attr("id", "post_like_#{post.post_id}") + .off("click").off("click").on "click", @submitPostVote Comments.pagePost(@post_id) else $(".post-full").html("

Not found

") @@ -167,7 +198,8 @@ class ZeroBlog extends ZeroFrame # Temporary dbschema bug workaround if res.error - @cmd "dbQuery", ["SELECT *, -1 AS votes FROM post WHERE post_id = #{@post_id} LIMIT 1"], parse_res + @cmd "dbQuery", ["SELECT *, -1 AS votes FROM post + WHERE post_id = #{@post_id} LIMIT 1"], parse_res else parse_res(res) @@ -177,7 +209,8 @@ class ZeroBlog extends ZeroFrame query = """ SELECT post.*, COUNT(comment_id) AS comments, - (SELECT COUNT(*) FROM post_vote WHERE post_vote.post_id = post.post_id) AS votes + (SELECT COUNT(*) FROM post_vote + WHERE post_vote.post_id = post.post_id) AS votes FROM post LEFT JOIN comment USING (post_id) GROUP BY post_id @@ -197,10 +230,13 @@ class ZeroBlog extends ZeroFrame for post in res elem = $("#post_#{post.post_id}") if elem.length == 0 # Not exits yet - elem = $(".post.template").clone().removeClass("template").attr("id", "post_#{post.post_id}") + elem = $(".post.template").clone().removeClass("template") + .attr("id", "post_#{post.post_id}") elem.prependTo(".posts") - # elem.find(".score").attr("id", "post_score_#{post.post_id}").on "click", @submitPostVote # Submit vote - elem.find(".like").attr("id", "post_like_#{post.post_id}").off("click").on "click", @submitPostVote + # elem.find(".score").attr("id", "post_score_#{post.post_id}") + # .on "click", @submitPostVote # Submit vote + elem.find(".like").attr("id", "post_like_#{post.post_id}") + .off("click").on "click", @submitPostVote @applyPostdata(elem, post) @pageLoaded() @log "Posts loaded in", ((+ new Date)-s),"ms" @@ -280,7 +316,8 @@ class ZeroBlog extends ZeroFrame @cmd "sitePublish", ["stored"], (res) => @log "Publish result:", res else - @cmd "wrapperPrompt", ["Enter your private key:", "password"], (privatekey) => # Prompt the private key + @cmd "wrapperPrompt", ["Enter your private key:", "password"], + (privatekey) => # Prompt the private key $(".publishbar .button").addClass("loading") @cmd "sitePublish", [privatekey], (res) => $(".publishbar .button").removeClass("loading") @@ -293,16 +330,22 @@ class ZeroBlog extends ZeroFrame applyPostdata: (elem, post, full=false) -> title_hash = post.title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") elem.data("object", "Post:"+post.post_id) - $(".title .editable", elem).html(post.title).attr("href", "?Post:#{post.post_id}:#{title_hash}").data("content", post.title) + $(".title .editable", elem).html(post.title) + .attr("href", "?Post:#{post.post_id}:#{title_hash}") + .data("content", post.title) date_published = Time.since(post.date_published) # Published date if post.body.match /^---/m # Has more over fold - date_published += " · #{Time.readtime(post.body)}" # If has break add readtime - $(".more", elem).css("display", "inline-block").attr("href", "?Post:#{post.post_id}:#{title_hash}") - $(".details .published", elem).html(date_published).data("content", post.date_published) + # If has break add readtime + date_published += " · #{Time.readtime(post.body)}" + $(".more", elem).css("display", "inline-block") + .attr("href", "?Post:#{post.post_id}:#{title_hash}") + $(".details .published", elem).html(date_published) + .data("content", post.date_published) # Comments num if post.comments > 0 - $(".details .comments-num", elem).css("display", "inline").attr("href", "?Post:#{post.post_id}:#{title_hash}#Comments") + $(".details .comments-num", elem).css("display", "inline") + .attr("href", "?Post:#{post.post_id}:#{title_hash}#Comments") if post.comments > 1 $(".details .comments-num .num", elem).text("#{post.comments} comments") else @@ -355,7 +398,8 @@ class ZeroBlog extends ZeroFrame post_id AS uri FROM json LEFT JOIN post_vote USING (json_id) - WHERE directory = 'users/#{@site_info.auth_address}' AND file_name = 'data.json' + WHERE directory = 'users/#{@site_info.auth_address}' + AND file_name = 'data.json' """ @cmd "dbQuery", [query_my_votes], (res) => for row in res @@ -390,7 +434,8 @@ class ZeroBlog extends ZeroFrame # Save content to data.json saveContent: (elem, content, cb=false) => - if elem.data("deletable") and content == null then return @deleteObject(elem, cb) # Its a delete request + if elem.data("deletable") and content == null + then return @deleteObject(elem, cb) # Its a delete request elem.data("content", content) [type, id] = @getObject(elem).data("object").split(":") id = parseInt(id) @@ -414,7 +459,7 @@ class ZeroBlog extends ZeroFrame else if type == "Site" data[elem.data("editable")] = content - @writeData data, (res) => + @writeData data, (res) -> if cb if res == true # OK if elem.data("editable-mode") == "simple" # No markdown @@ -434,9 +479,11 @@ class ZeroBlog extends ZeroFrame inner_path = "data/users/#{Page.site_info.auth_address}/data.json" Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => data = JSON.parse(data) - comment = (comment for comment in data.comment when comment.comment_id == id)[0] + comment = ( + comment for comment in data.comment when comment.comment_id == id)[0] comment[elem.data("editable")] = content - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) + json_raw = unescape(encodeURIComponent( + JSON.stringify(data, undefined, '\t'))) @writePublish inner_path, btoa(json_raw), (res) => if res == true Comments.checkCert("updaterules") @@ -460,17 +507,19 @@ class ZeroBlog extends ZeroFrame if not post then return false # No post found for this id data.post.splice(data.post.indexOf(post), 1) # Remove from data - @writeData data, (res) => + @writeData data, (res) -> if cb then cb() if res == true then elem.slideUp() else if type == "Comment" inner_path = "data/users/#{Page.site_info.auth_address}/data.json" @cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => data = JSON.parse(data) - comment = (comment for comment in data.comment when comment.comment_id == id)[0] + comment = ( + comment for comment in data.comment when comment.comment_id == id)[0] data.comment.splice(data.comment.indexOf(comment), 1) - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) - @writePublish inner_path, btoa(json_raw), (res) => + json_raw = unescape(encodeURIComponent( + JSON.stringify(data, undefined, '\t'))) + @writePublish inner_path, btoa(json_raw), (res) -> if res == true elem.slideUp() if cb then cb() @@ -481,8 +530,11 @@ class ZeroBlog extends ZeroFrame if not data return @log "Data missing" @data["modified"] = data.modified = Time.timestamp() - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) # Encode to json, encode utf8 - @cmd "fileWrite", ["data/data.json", btoa(json_raw)], (res) => # Convert to to base64 and send + json_raw = unescape( + # Encode to json, encode utf8 + encodeURIComponent(JSON.stringify(data, undefined, '\t'))) + # Convert to to base64 and send + @cmd "fileWrite", ["data/data.json", btoa(json_raw)], (res) => if res == "ok" if cb then cb(true) else @@ -492,10 +544,12 @@ class ZeroBlog extends ZeroFrame # Updating title in content.json @cmd "fileGet", ["content.json"], (content) => - content = content.replace /"title": ".*?"/, "\"title\": \"#{data.title}\"" # Load as raw html to prevent js bignumber problems + # Load as raw html to prevent js bignumber problems + content = content.replace /"title": ".*?"/,"\"title\": \"#{data.title}\"" @cmd "fileWrite", ["content.json", btoa(content)], (res) => if res != "ok" - @cmd "wrapperNotification", ["error", "Content.json write error: #{res}"] + @cmd "wrapperNotification", + ["error", "Content.json write error: #{res}"] # If the privatekey is stored sign the new content if @site_info["privatekey"] @@ -510,7 +564,7 @@ class ZeroBlog extends ZeroFrame cb(false) return false - @cmd "sitePublish", {"inner_path": inner_path}, (res) => + @cmd "sitePublish", {"inner_path": inner_path}, (res) -> if res == "ok" cb(true) else @@ -528,7 +582,8 @@ class ZeroBlog extends ZeroFrame if data data = JSON.parse(data) else # Default data - data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, "post_vote": {} } + data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, + "post_vote": {} } if not data.post_vote data.post_vote = {} @@ -538,7 +593,8 @@ class ZeroBlog extends ZeroFrame data.post_vote[post_id] = 1 else delete data.post_vote[post_id] - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) + json_raw = unescape(encodeURIComponent( + JSON.stringify(data, undefined, '\t'))) current_num = parseInt elem.find(".num").text() if not current_num @@ -571,9 +627,11 @@ class ZeroBlog extends ZeroFrame setSiteinfo: (site_info) => @site_info = site_info @event_site_info.resolve(site_info) - if $("body").hasClass("page-post") then Comments.checkCert() # Update if username changed + # Update if username changed + if $("body").hasClass("page-post") then Comments.checkCert() # User commented - if site_info.event?[0] == "file_done" and site_info.event[1].match /.*users.*data.json$/ + if site_info.event?[0] == "file_done" and + site_info.event[1].match /.*users.*data.json$/ if $("body").hasClass("page-post") @pagePost() Comments.loadComments() # Post page, reload comments @@ -582,7 +640,8 @@ class ZeroBlog extends ZeroFrame RateLimit 500, => @pageMain() @loadLastcomments() - else if site_info.event?[0] == "file_done" and site_info.event[1] == "data/data.json" + else if site_info.event?[0] == "file_done" and + site_info.event[1] == "data/data.json" @loadData() if $("body").hasClass("page-main") then @pageMain() if $("body").hasClass("page-post") then @pagePost() From 5aad065f4cfc06f7fc6f176707caba28409fa17b Mon Sep 17 00:00:00 2001 From: nobody Date: Thu, 21 Apr 2016 19:10:07 +0000 Subject: [PATCH 03/17] get total post when query posts when selecting posts, select count(*) from post as first row so we can do better page display --- js/ZeroBlog.coffee | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 9da5fbf..82341e2 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -156,12 +156,12 @@ class ZeroBlog extends ZeroFrame elem.find(".postlink").text(lastcomment.post_title) .attr("href", "?Post:#{lastcomment.post_id}:#{title_hash}#Comments") - applyPagerdata: (page, limit, has_next) -> + applyPagerdata: (page, limit, total) -> pager = $(".pager") if page > 1 pager.find(".prev").css("display", "inline-block") .attr("href", "?page=#{page-1}") - if has_next + if page * limit < total pager.find(".next").css("display", "inline-block") .attr("href", "?page=#{page+1}") @@ -207,6 +207,12 @@ class ZeroBlog extends ZeroFrame pageMain: -> limit = 15 query = """ + SELECT COUNT(*) as post_id, + NULL as title,NULL as body,NULL as date_published, + NULL as json_id, NULL as comments,NULL as votes + FROM post + UNION ALL + SELECT * FROM ( SELECT post.*, COUNT(comment_id) AS comments, (SELECT COUNT(*) FROM post_vote @@ -215,16 +221,16 @@ class ZeroBlog extends ZeroFrame LEFT JOIN comment USING (post_id) GROUP BY post_id ORDER BY date_published DESC - LIMIT #{(@page-1)*limit}, #{limit+1} + LIMIT #{(@page-1)*limit}, #{limit} + ) """ @cmd "dbQuery", [query], (res) => parse_res = (res) => + total = res[0].post_id + res = res[1..] s = (+ new Date) - if res.length > limit # Has next page - res.pop() - @applyPagerdata(@page, limit, true) - else - @applyPagerdata(@page, limit, false) + + @applyPagerdata(@page, limit, total) res.reverse() for post in res From 02dc86a258f8d33fdce52553c8e2f1fa2e415f1b Mon Sep 17 00:00:00 2001 From: nobody Date: Thu, 21 Apr 2016 21:09:46 +0000 Subject: [PATCH 04/17] better add blog page support previously only show prev/next, hard to jump. this improve it by: support jump to first/last page show current page and neighbour page links (instead of prev/next) if not all next pages can showup, ... added to last link to notice user --- css/ZeroBlog.css | 6 ++-- index.html | 5 ++-- js/ZeroBlog.coffee | 71 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/css/ZeroBlog.css b/css/ZeroBlog.css index 5557b39..b8935f7 100644 --- a/css/ZeroBlog.css +++ b/css/ZeroBlog.css @@ -159,10 +159,10 @@ blockquote { border-left: 3px solid #333; margin-left: 0px; padding-left: 1em } .pager a { border: 2px solid #333; padding: 10px 20px; font-size: 15px; display: none; transition: all 0.3s } .pager a:hover { color: white; box-shadow: inset 150px 0px 0px 0px #333; } .pager a:active { color: white; box-shadow: inset 150px 0px 0px 0px #AF3BFF; transition: none; border-color: #AF3BFF } -.pager .next { float: right } -.pager .prev:hover { box-shadow: inset -150px 0px 0px 0px #333; } -.pager .prev:active { box-shadow: inset -150px 0px 0px 0px #AF3BFF; } +.pager .currentpage:hover{} +.pager .currentpage { color: white; box-shadow: inset 150px 0px 0px 0px #AF3BFF; transition: none; border-color: #AF3BFF } +.pager .pagershow {display:inline-block; margin:1px} /* Score */ /* .score { diff --git a/index.html b/index.html index d4a3d00..bd2f975 100644 --- a/index.html +++ b/index.html @@ -152,8 +152,9 @@

0 Comments:

diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 82341e2..80bed71 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -158,12 +158,73 @@ class ZeroBlog extends ZeroFrame applyPagerdata: (page, limit, total) -> pager = $(".pager") + + total_page = (total+limit-1)//limit + if total_page <1 + return + + current_page =pager.find(".currentpage") + current_page.text(page).css("display","inline-block") + has_first = 0 if page > 1 - pager.find(".prev").css("display", "inline-block") - .attr("href", "?page=#{page-1}") - if page * limit < total - pager.find(".next").css("display", "inline-block") - .attr("href", "?page=#{page+1}") + pager.find(".first").css("display", "inline-block") + has_first = 1 + + + has_last = 0 + if page != total_page + pager.find(".last").css("display", "inline-block") + .attr("href", "?page=#{total_page}") + has_last = 1 + + if total_page<4 + return + + #margin , or number larger + element_width = current_page.width() + 7 + # how many buttons we can insert ? + number = pager.width() // element_width - has_first - has_last - 1 + + half = number//2 + # exclude left pages + # but not underflow 1 + left_pages_max = Math.min(page-1, + #half of insertable, or page near last + Math.max(half, number-(total_page-page))) + + right_pages_max = Math.min(total_page-page, + Math.max(half, total_page-page)) + + left_pages = 0 + right_pages = 0 + + if left_pages_max < half + left_pages = left_pages_max + right_pages = Math.min(number - left_pages,right_pages_max) + else if right_pages_max < half + right_pages = right_pages_max + left_pages = Math.min(number - right_pages,left_pages_max) + else + left_pages = half + right_pages = number-half + + i=0 + + while i 1 so first button always show + n = page - left_pages+i + current_page.before("#{n}") + ++i + + i=0 + while i#{text}") + ++i routeUrl: (url) -> @log "Routing url:", url From b43aef0be586c12f01b2cc3f3c86ca86fffd3b55 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Apr 2016 12:10:18 +0800 Subject: [PATCH 05/17] replace all tab to 2 spaces --- js/Comments.coffee | 338 ++++++++++++++++++++++----------------------- 1 file changed, 169 insertions(+), 169 deletions(-) diff --git a/js/Comments.coffee b/js/Comments.coffee index 8f9ad97..862eea1 100644 --- a/js/Comments.coffee +++ b/js/Comments.coffee @@ -1,173 +1,173 @@ class Comments extends Class - pagePost: (post_id, cb=false) -> - @post_id = post_id - @rules = {} - $(".button-submit-comment").off("click").on "click", => - @submitComment() - return false - @loadComments("noanim", cb) - @autoExpand $(".comment-textarea") - - $(".certselect").off("click").on "click", => - if Page.server_info.rev < 160 - Page.cmd "wrapperNotification", ["error", "Comments requires at least ZeroNet 0.3.0 Please upgade!"] - else - Page.cmd "certSelect", [["zeroid.bit"]] - return false - - - loadComments: (type="show", cb=false) -> - query = "SELECT comment.*, json_content.json_id AS content_json_id, keyvalue.value AS cert_user_id, json.directory, - (SELECT COUNT(*) FROM comment_vote WHERE comment_vote.comment_uri = comment.comment_id || '@' || json.directory)+1 AS votes - FROM comment - LEFT JOIN json USING (json_id) - LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') - LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') - WHERE post_id = #{@post_id} ORDER BY date_added DESC" - - Page.cmd "dbQuery", query, (comments) => - $("#Comments").text(comments.length + if comments.length > 1 then " Comments:" else " Comment:") - for comment in comments - user_address = comment.directory.replace("users/", "") - comment_address = "#{comment.comment_id}_#{user_address}" - elem = $("#comment_"+comment_address) - if elem.length == 0 # Create if not exits - elem = $(".comment.template").clone().removeClass("template").attr("id", "comment_"+comment_address).data("post_id", @post_id) - if type != "noanim" - elem.cssSlideDown() - $(".reply", elem).off("click").on "click", (e) => # Reply link - return @buttonReply $(e.target).parents(".comment") - @applyCommentData(elem, comment) - elem.appendTo(".comments") - setTimeout (-> - Page.addInlineEditors(".comments") - ), 1000 - - - applyCommentData: (elem, comment) -> - [user_name, cert_domain] = comment.cert_user_id.split("@") - user_address = comment.directory.replace("users/", "") - $(".comment-body", elem).html Text.renderMarked(comment.body, {"sanitize": true}) - $(".user_name", elem).text(user_name).css("color": Text.toColor(comment.cert_user_id)).attr("title", "#{user_name}@#{cert_domain}: #{user_address}") - $(".added", elem).text(Time.since(comment.date_added)).attr("title", Time.date(comment.date_added, "long")) - #$(".cert_domain", elem).html("@#{cert_domain}").css("display", "none") - # Add inline editor - if user_address == Page.site_info.auth_address - $(elem).attr("data-object", "Comment:#{comment.comment_id}").attr("data-deletable", "yes") - $(".comment-body", elem).attr("data-editable", "body").data("content", comment.body) - - - buttonReply: (elem) -> - @log "Reply to", elem - user_name = $(".user_name", elem).text() - post_id = elem.attr("id") - body_add = "> [#{user_name}](\##{post_id}): " - elem_quote = $(".comment-body", elem).clone() - $("blockquote", elem_quote).remove() # Remove other people's quotes - body_add+= elem_quote.text().trim("\n").replace(/\n/g, "\n> ") - body_add+= "\n\n" - $(".comment-new .comment-textarea").val( $(".comment-new .comment-textarea").val()+body_add ) - $(".comment-new .comment-textarea").trigger("input").focus() # Autosize - return false - - - submitComment: -> - if not Page.site_info.cert_user_id # Not registered - Page.cmd "wrapperNotification", ["info", "Please, select your account."] - return false - - body = $(".comment-new .comment-textarea").val() - if not body - $(".comment-new .comment-textarea").focus() - return false - - $(".comment-new .button-submit").addClass("loading") - inner_path = "data/users/#{Page.site_info.auth_address}/data.json" - Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => - if data - data = JSON.parse(data) - else # Default data - data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, "topic_vote": {} } - - data.comment.push { - "comment_id": data.next_comment_id, - "body": body, - "post_id": @post_id, - "date_added": Time.timestamp() - } - data.next_comment_id += 1 - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) - Page.writePublish inner_path, btoa(json_raw), (res) => - $(".comment-new .button-submit").removeClass("loading") - @loadComments() - setTimeout (-> - Page.loadLastcomments() - ), 1000 - @checkCert("updaterules") - @log "Writepublish result", res - if res != false - $(".comment-new .comment-textarea").val("") - - - checkCert: (type) -> - last_cert_user_id = $(".comment-new .user_name").text() - if Page.site_info.cert_user_id - $(".comment-new").removeClass("comment-nocert") - $(".comment-new .user_name").text(Page.site_info.cert_user_id) - else - $(".comment-new").addClass("comment-nocert") - $(".comment-new .user_name").text("Please sign in") - - if $(".comment-new .user_name").text() != last_cert_user_id or type == "updaterules" # User changed - # Update used/allowed space - if Page.site_info.cert_user_id - Page.cmd "fileRules", "data/users/#{Page.site_info.auth_address}/content.json", (rules) => - @rules = rules - if rules.max_size - @setCurrentSize(rules.current_size) - else - @setCurrentSize(0) - else - @setCurrentSize(0) - - - setCurrentSize: (current_size) -> - if current_size - current_size_kb = current_size/1000 - $(".user-size").text("used: #{current_size_kb.toFixed(1)}k/#{Math.round(@rules.max_size/1000)}k") - $(".user-size-used").css("width", Math.round(70*current_size/@rules.max_size)) - else - $(".user-size").text("") - - - autoExpand: (elem) -> - editor = elem[0] - # Autoexpand - if elem.height() > 0 then elem.height(1) - - elem.off("input").on "input", => - if editor.scrollHeight > elem.height() - old_height = elem.height() - elem.height(1) - new_height = editor.scrollHeight - new_height += parseFloat elem.css("borderTopWidth") - new_height += parseFloat elem.css("borderBottomWidth") - new_height -= parseFloat elem.css("paddingTop") - new_height -= parseFloat elem.css("paddingBottom") - - min_height = parseFloat(elem.css("lineHeight"))*2 # 2 line minimum - if new_height < min_height then new_height = min_height+4 - - elem.height(new_height-4) - # Update used space - if @rules.max_size - if elem.val().length > 0 - current_size = @rules.current_size + elem.val().length + 90 - else - current_size = @rules.current_size - @setCurrentSize(current_size) - if elem.height() > 0 then elem.trigger "input" - else elem.height("48px") + pagePost: (post_id, cb=false) -> + @post_id = post_id + @rules = {} + $(".button-submit-comment").off("click").on "click", => + @submitComment() + return false + @loadComments("noanim", cb) + @autoExpand $(".comment-textarea") + + $(".certselect").off("click").on "click", => + if Page.server_info.rev < 160 + Page.cmd "wrapperNotification", ["error", "Comments requires at least ZeroNet 0.3.0 Please upgade!"] + else + Page.cmd "certSelect", [["zeroid.bit"]] + return false + + + loadComments: (type="show", cb=false) -> + query = "SELECT comment.*, json_content.json_id AS content_json_id, keyvalue.value AS cert_user_id, json.directory, + (SELECT COUNT(*) FROM comment_vote WHERE comment_vote.comment_uri = comment.comment_id || '@' || json.directory)+1 AS votes + FROM comment + LEFT JOIN json USING (json_id) + LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') + LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') + WHERE post_id = #{@post_id} ORDER BY date_added DESC" + + Page.cmd "dbQuery", query, (comments) => + $("#Comments").text(comments.length + if comments.length > 1 then " Comments:" else " Comment:") + for comment in comments + user_address = comment.directory.replace("users/", "") + comment_address = "#{comment.comment_id}_#{user_address}" + elem = $("#comment_"+comment_address) + if elem.length == 0 # Create if not exits + elem = $(".comment.template").clone().removeClass("template").attr("id", "comment_"+comment_address).data("post_id", @post_id) + if type != "noanim" + elem.cssSlideDown() + $(".reply", elem).off("click").on "click", (e) => # Reply link + return @buttonReply $(e.target).parents(".comment") + @applyCommentData(elem, comment) + elem.appendTo(".comments") + setTimeout (-> + Page.addInlineEditors(".comments") + ), 1000 + + + applyCommentData: (elem, comment) -> + [user_name, cert_domain] = comment.cert_user_id.split("@") + user_address = comment.directory.replace("users/", "") + $(".comment-body", elem).html Text.renderMarked(comment.body, {"sanitize": true}) + $(".user_name", elem).text(user_name).css("color": Text.toColor(comment.cert_user_id)).attr("title", "#{user_name}@#{cert_domain}: #{user_address}") + $(".added", elem).text(Time.since(comment.date_added)).attr("title", Time.date(comment.date_added, "long")) + #$(".cert_domain", elem).html("@#{cert_domain}").css("display", "none") + # Add inline editor + if user_address == Page.site_info.auth_address + $(elem).attr("data-object", "Comment:#{comment.comment_id}").attr("data-deletable", "yes") + $(".comment-body", elem).attr("data-editable", "body").data("content", comment.body) + + + buttonReply: (elem) -> + @log "Reply to", elem + user_name = $(".user_name", elem).text() + post_id = elem.attr("id") + body_add = "> [#{user_name}](\##{post_id}): " + elem_quote = $(".comment-body", elem).clone() + $("blockquote", elem_quote).remove() # Remove other people's quotes + body_add+= elem_quote.text().trim("\n").replace(/\n/g, "\n> ") + body_add+= "\n\n" + $(".comment-new .comment-textarea").val( $(".comment-new .comment-textarea").val()+body_add ) + $(".comment-new .comment-textarea").trigger("input").focus() # Autosize + return false + + + submitComment: -> + if not Page.site_info.cert_user_id # Not registered + Page.cmd "wrapperNotification", ["info", "Please, select your account."] + return false + + body = $(".comment-new .comment-textarea").val() + if not body + $(".comment-new .comment-textarea").focus() + return false + + $(".comment-new .button-submit").addClass("loading") + inner_path = "data/users/#{Page.site_info.auth_address}/data.json" + Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => + if data + data = JSON.parse(data) + else # Default data + data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, "topic_vote": {} } + + data.comment.push { + "comment_id": data.next_comment_id, + "body": body, + "post_id": @post_id, + "date_added": Time.timestamp() + } + data.next_comment_id += 1 + json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) + Page.writePublish inner_path, btoa(json_raw), (res) => + $(".comment-new .button-submit").removeClass("loading") + @loadComments() + setTimeout (-> + Page.loadLastcomments() + ), 1000 + @checkCert("updaterules") + @log "Writepublish result", res + if res != false + $(".comment-new .comment-textarea").val("") + + + checkCert: (type) -> + last_cert_user_id = $(".comment-new .user_name").text() + if Page.site_info.cert_user_id + $(".comment-new").removeClass("comment-nocert") + $(".comment-new .user_name").text(Page.site_info.cert_user_id) + else + $(".comment-new").addClass("comment-nocert") + $(".comment-new .user_name").text("Please sign in") + + if $(".comment-new .user_name").text() != last_cert_user_id or type == "updaterules" # User changed + # Update used/allowed space + if Page.site_info.cert_user_id + Page.cmd "fileRules", "data/users/#{Page.site_info.auth_address}/content.json", (rules) => + @rules = rules + if rules.max_size + @setCurrentSize(rules.current_size) + else + @setCurrentSize(0) + else + @setCurrentSize(0) + + + setCurrentSize: (current_size) -> + if current_size + current_size_kb = current_size/1000 + $(".user-size").text("used: #{current_size_kb.toFixed(1)}k/#{Math.round(@rules.max_size/1000)}k") + $(".user-size-used").css("width", Math.round(70*current_size/@rules.max_size)) + else + $(".user-size").text("") + + + autoExpand: (elem) -> + editor = elem[0] + # Autoexpand + if elem.height() > 0 then elem.height(1) + + elem.off("input").on "input", => + if editor.scrollHeight > elem.height() + old_height = elem.height() + elem.height(1) + new_height = editor.scrollHeight + new_height += parseFloat elem.css("borderTopWidth") + new_height += parseFloat elem.css("borderBottomWidth") + new_height -= parseFloat elem.css("paddingTop") + new_height -= parseFloat elem.css("paddingBottom") + + min_height = parseFloat(elem.css("lineHeight"))*2 # 2 line minimum + if new_height < min_height then new_height = min_height+4 + + elem.height(new_height-4) + # Update used space + if @rules.max_size + if elem.val().length > 0 + current_size = @rules.current_size + elem.val().length + 90 + else + current_size = @rules.current_size + @setCurrentSize(current_size) + if elem.height() > 0 then elem.trigger "input" + else elem.height("48px") window.Comments = new Comments() From a68a64654a9118117d6fc62fc85c4d61d5a45a1e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Apr 2016 12:11:00 +0800 Subject: [PATCH 06/17] quiet coffeelint --- js/Comments.coffee | 62 +++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/js/Comments.coffee b/js/Comments.coffee index 862eea1..a1121ef 100644 --- a/js/Comments.coffee +++ b/js/Comments.coffee @@ -8,31 +8,40 @@ class Comments extends Class @loadComments("noanim", cb) @autoExpand $(".comment-textarea") - $(".certselect").off("click").on "click", => + $(".certselect").off("click").on "click", -> if Page.server_info.rev < 160 - Page.cmd "wrapperNotification", ["error", "Comments requires at least ZeroNet 0.3.0 Please upgade!"] + Page.cmd "wrapperNotification", ["error", + "Comments requires at least ZeroNet 0.3.0 Please upgade!"] else Page.cmd "certSelect", [["zeroid.bit"]] return false loadComments: (type="show", cb=false) -> - query = "SELECT comment.*, json_content.json_id AS content_json_id, keyvalue.value AS cert_user_id, json.directory, - (SELECT COUNT(*) FROM comment_vote WHERE comment_vote.comment_uri = comment.comment_id || '@' || json.directory)+1 AS votes + query = "SELECT comment.*, json_content.json_id AS content_json_id, + keyvalue.value AS cert_user_id, json.directory, + (SELECT COUNT(*) FROM comment_vote + WHERE comment_vote.comment_uri = comment.comment_id + || '@' || json.directory)+1 AS votes FROM comment LEFT JOIN json USING (json_id) - LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') - LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') + LEFT JOIN json AS json_content + ON (json_content.directory = json.directory + AND json_content.file_name='content.json') + LEFT JOIN keyvalue + ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') WHERE post_id = #{@post_id} ORDER BY date_added DESC" Page.cmd "dbQuery", query, (comments) => - $("#Comments").text(comments.length + if comments.length > 1 then " Comments:" else " Comment:") + $("#Comments").text(comments.length + + if comments.length > 1 then " Comments:" else " Comment:") for comment in comments user_address = comment.directory.replace("users/", "") comment_address = "#{comment.comment_id}_#{user_address}" elem = $("#comment_"+comment_address) if elem.length == 0 # Create if not exits - elem = $(".comment.template").clone().removeClass("template").attr("id", "comment_"+comment_address).data("post_id", @post_id) + elem = $(".comment.template").clone().removeClass("template") + .attr("id", "comment_"+comment_address).data("post_id", @post_id) if type != "noanim" elem.cssSlideDown() $(".reply", elem).off("click").on "click", (e) => # Reply link @@ -47,14 +56,20 @@ class Comments extends Class applyCommentData: (elem, comment) -> [user_name, cert_domain] = comment.cert_user_id.split("@") user_address = comment.directory.replace("users/", "") - $(".comment-body", elem).html Text.renderMarked(comment.body, {"sanitize": true}) - $(".user_name", elem).text(user_name).css("color": Text.toColor(comment.cert_user_id)).attr("title", "#{user_name}@#{cert_domain}: #{user_address}") - $(".added", elem).text(Time.since(comment.date_added)).attr("title", Time.date(comment.date_added, "long")) + $(".comment-body", elem).html Text.renderMarked( + comment.body, {"sanitize": true}) + $(".user_name", elem).text(user_name) + .css("color": Text.toColor(comment.cert_user_id)) + .attr("title", "#{user_name}@#{cert_domain}: #{user_address}") + $(".added", elem).text(Time.since(comment.date_added)) + .attr("title", Time.date(comment.date_added, "long")) #$(".cert_domain", elem).html("@#{cert_domain}").css("display", "none") # Add inline editor if user_address == Page.site_info.auth_address - $(elem).attr("data-object", "Comment:#{comment.comment_id}").attr("data-deletable", "yes") - $(".comment-body", elem).attr("data-editable", "body").data("content", comment.body) + $(elem).attr("data-object", "Comment:#{comment.comment_id}") + .attr("data-deletable", "yes") + $(".comment-body", elem).attr("data-editable", "body") + .data("content", comment.body) buttonReply: (elem) -> @@ -66,7 +81,8 @@ class Comments extends Class $("blockquote", elem_quote).remove() # Remove other people's quotes body_add+= elem_quote.text().trim("\n").replace(/\n/g, "\n> ") body_add+= "\n\n" - $(".comment-new .comment-textarea").val( $(".comment-new .comment-textarea").val()+body_add ) + $(".comment-new .comment-textarea") + .val( $(".comment-new .comment-textarea").val()+body_add ) $(".comment-new .comment-textarea").trigger("input").focus() # Autosize return false @@ -87,7 +103,8 @@ class Comments extends Class if data data = JSON.parse(data) else # Default data - data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, "topic_vote": {} } + data = {"next_comment_id": 1, "comment": [],\ + "comment_vote": {}, "topic_vote": {} } data.comment.push { "comment_id": data.next_comment_id, @@ -96,7 +113,8 @@ class Comments extends Class "date_added": Time.timestamp() } data.next_comment_id += 1 - json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) + json_raw = unescape(encodeURIComponent( + JSON.stringify(data, undefined, '\t'))) Page.writePublish inner_path, btoa(json_raw), (res) => $(".comment-new .button-submit").removeClass("loading") @loadComments() @@ -118,10 +136,12 @@ class Comments extends Class $(".comment-new").addClass("comment-nocert") $(".comment-new .user_name").text("Please sign in") - if $(".comment-new .user_name").text() != last_cert_user_id or type == "updaterules" # User changed + if $(".comment-new .user_name").text() != last_cert_user_id or type == "\ + updaterules" # User changed # Update used/allowed space if Page.site_info.cert_user_id - Page.cmd "fileRules", "data/users/#{Page.site_info.auth_address}/content.json", (rules) => + Page.cmd "fileRules", + "data/users/#{Page.site_info.auth_address}/content.json", (rules) => @rules = rules if rules.max_size @setCurrentSize(rules.current_size) @@ -134,8 +154,10 @@ class Comments extends Class setCurrentSize: (current_size) -> if current_size current_size_kb = current_size/1000 - $(".user-size").text("used: #{current_size_kb.toFixed(1)}k/#{Math.round(@rules.max_size/1000)}k") - $(".user-size-used").css("width", Math.round(70*current_size/@rules.max_size)) + $(".user-size").text("used: #{current_size_kb.toFixed(1)}k/#{Math.round( + @rules.max_size/1000)}k") + $(".user-size-used").css("width", + Math.round(70*current_size/@rules.max_size)) else $(".user-size").text("") From 4b427d0e93e08265666dc9713773739307d8fa8d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Apr 2016 12:12:14 +0800 Subject: [PATCH 07/17] add Comments.hide() ,to support TOC pages --- js/Comments.coffee | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/js/Comments.coffee b/js/Comments.coffee index a1121ef..d6c3171 100644 --- a/js/Comments.coffee +++ b/js/Comments.coffee @@ -190,6 +190,12 @@ class Comments extends Class @setCurrentSize(current_size) if elem.height() > 0 then elem.trigger "input" else elem.height("48px") + + + + hide: () -> + $(".comment").hide() + $("#Comments").hide() window.Comments = new Comments() From b99b3553dc3dd28115383fc6599146a2ba34ed86 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 22 Apr 2016 12:13:36 +0800 Subject: [PATCH 08/17] support TOC by date function add an entry in routeUrl to dispatch ?Toc= type url, then pageToc query all blogs and generate a simple index for all, these index grouped by same month (empty month skipped) order by date. page query not implemented currently --- index.html | 2 ++ js/ZeroBlog.coffee | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/index.html b/index.html index bd2f975..550e986 100644 --- a/index.html +++ b/index.html @@ -52,6 +52,8 @@

Follow in NewsfeedFollowing

+ TOC by date +
diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 80bed71..025d8d1 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -232,6 +232,9 @@ class ZeroBlog extends ZeroFrame $("body").addClass("page-post") @post_id = parseInt(match[1]) @pagePost() + else if match = url.match /Toc=(\w+)/ + $("body").addClass("page-post") + @pageToc(match[1]) else $("body").addClass("page-main") if match = url.match /page=([0-9]+)/ @@ -239,6 +242,70 @@ class ZeroBlog extends ZeroFrame @pageMain() # - Pages - + pageToc: (type) -> + @log "Toc by:", type + if type != "dateDesc" + return + @cmd "dbQuery", ["SELECT post_id,date_published,title FROM post + ORDER BY date_published DESC"], (res) => + parse_res = (res) => + + + post_id = 99999999999 + #id is needed when applyPostdata + + if res.length is 0 + + @applyPostdata($(".post-full"), + title:"no post" + post_id:post_id + votes:-1 + comments:-1 + body:"no post at all" + ,true) + return + + # makes next month + month = new Date(new Date().getTime()+31*24*60*60*1000) + markdown = "" + + for post in res + #in current range + post_date = new Date(post.date_published*1000) + if post_date s = (+ new Date) From 026b0749986523d76c769a49e7c98f5f734df927 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 23 Apr 2016 17:59:23 +0000 Subject: [PATCH 09/17] add tag table to ZeroBlog,to support blog tags, index by tag blog can have a space splited tag list,these value-post_id map stored in tag table. I think best way to implement this should be insert trigger and CTE(requires sqlite 3.8.3), but zeronet didn't assume this, so this implements by maintain tag table in frontend (coffeescript) --- dbschema.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dbschema.json b/dbschema.json index 471a745..b400d06 100644 --- a/dbschema.json +++ b/dbschema.json @@ -14,7 +14,7 @@ "to_keyvalue": [ "cert_user_id" ] }, "data.json": { - "to_table": [ "post" ], + "to_table": [ "post" ,"tag"], "to_keyvalue": [ "title", "description", "links", "next_post_id", "demo", "modified" ] } @@ -51,6 +51,15 @@ "indexes": ["CREATE UNIQUE INDEX post_uri ON post(json_id, post_id)", "CREATE INDEX post_id ON post(post_id)"], "schema_changed": 1426195823 }, + "tag": { + "cols":[ + ["value","TEXT NOT NULL CHECK(length(value)!=0)"], + ["post_id","REFERENCES post(post_id)"], + ["json_id", "INTEGER REFERENCES json (json_id)"] + ], + "indexes": ["CREATE UNIQUE INDEX tag_post_uri ON tag(value,post_id)"], + "schema_changed": 1426195822 + }, "post_vote": { "cols": [ ["post_id", "INTEGER"], From cf721cb0a335a32eb0d6976391413dcf092ae650 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 23 Apr 2016 18:12:45 +0000 Subject: [PATCH 10/17] moves order and limit closure in SQL as a variable ,for later use --- js/ZeroBlog.coffee | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 025d8d1..7489bf9 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -334,6 +334,13 @@ class ZeroBlog extends ZeroFrame pageMain: -> limit = 15 + + order_limit_closure = """ + ORDER BY date_published DESC + LIMIT #{(@page-1)*limit}, #{limit} """ + + + query = """ SELECT COUNT(*) as post_id, NULL as title,NULL as body,NULL as date_published, @@ -348,8 +355,7 @@ class ZeroBlog extends ZeroFrame FROM post LEFT JOIN comment USING (post_id) GROUP BY post_id - ORDER BY date_published DESC - LIMIT #{(@page-1)*limit}, #{limit} + #{order_limit_closure} ) """ @cmd "dbQuery", [query], (res) => From 64b24ac5550b487eeaaa27feace4a347d60530fa Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 23 Apr 2016 18:21:04 +0000 Subject: [PATCH 11/17] implements tag display and edit in post page, add a "taglist" under detail div, every tag is showed as a link to query page , untagged post also has a link to query all untagged posts(query implemented in next commit).this field can be edit just like title and body --- index.html | 4 ++ js/ZeroBlog.coffee | 154 ++++++++++++++++++++++++++++++++++++--------- js/lib/helper.js | 24 +++++++ 3 files changed, 154 insertions(+), 28 deletions(-) create mode 100644 js/lib/helper.js diff --git a/index.html b/index.html index 550e986..7fd8082 100644 --- a/index.html +++ b/index.html @@ -95,6 +95,8 @@

^0 ^1 --> +
+ tag:
Body
Read more @@ -110,6 +112,8 @@

21 hours ago · 2 min read +
+ tag:
diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 7489bf9..a02e81e 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -312,9 +312,13 @@ class ZeroBlog extends ZeroFrame @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) => - parse_res = (res) => + parse_res = (res,tag_res) => if res.length post = res[0] + #post.tag is in data["tag"] table, must query and add them manually + post.tag=[] + for tag in tag_res + post.tag.push(tag.value) @applyPostdata($(".post-full"), post, true) $(".post-full .like").attr("id", "post_like_#{post.post_id}") .off("click").off("click").on "click", @submitPostVote @@ -324,12 +328,26 @@ class ZeroBlog extends ZeroFrame @pageLoaded() Comments.checkCert() - # Temporary dbschema bug workaround - if res.error - @cmd "dbQuery", ["SELECT *, -1 AS votes FROM post - WHERE post_id = #{@post_id} LIMIT 1"], parse_res - else - parse_res(res) + + tag_query = """ + SELECT value FROM tag + WHERE post_id=#{@post_id} + """ + + deal_post = (post_res,tag_res) -> + # Temporary dbschema bug workaround + if res.error + @cmd "dbQuery", ["SELECT *, -1 AS votes FROM post + WHERE post_id = #{@post_id} LIMIT 1"], (res)->parse_res(res,tag_res) + else + parse_res(res,tag_res) + + @cmd "dbQuery", [tag_query], (tag_res) -> + if tag_res.error + deal_post(res,[]) + else + deal_post(res,tag_res) + pageMain: -> @@ -359,7 +377,7 @@ class ZeroBlog extends ZeroFrame ) """ @cmd "dbQuery", [query], (res) => - parse_res = (res) => + parse_res = (res,tags) => total = res[0].post_id res = res[1..] s = (+ new Date) @@ -368,6 +386,13 @@ class ZeroBlog extends ZeroFrame res.reverse() for post in res + + post.tag =[] + + for tag in tags + if post.post_id == tag.post_id + post.tag.push(tag.value) + elem = $("#post_#{post.post_id}") if elem.length == 0 # Not exits yet elem = $(".post.template").clone().removeClass("template") @@ -402,21 +427,38 @@ class ZeroBlog extends ZeroFrame @writeData(data) return false - # Temporary dbschema bug workaround - if res.error - query = """ - SELECT - post.*, COUNT(comment_id) AS comments, - -1 AS votes - FROM post - LEFT JOIN comment USING (post_id) - GROUP BY post_id - ORDER BY date_published DESC - LIMIT #{(@page-1)*limit}, #{limit+1} - """ - @cmd "dbQuery", [query], parse_res - else - parse_res(res) + tag_query = """ + SELECT tag.* FROM tag + LEFT JOIN ( + SELECT post_id FROM post + #{order_limit_closure} + ) AS post USING (post_id) + """ + + + + deal_post = (post_res,tag_res) -> + if res.error + # Temporary dbschema bug workaround + query = """ + SELECT + post.*, COUNT(comment_id) AS comments, + -1 AS votes + FROM post + LEFT JOIN comment USING (post_id) + GROUP BY post_id + ORDER BY date_published DESC + LIMIT #{(@page-1)*limit}, #{limit+1} + """ + @cmd "dbQuery", [query], (res)-> parse_res(res,tag_res) + else + parse_res(res,tag_res) + + @cmd "dbQuery", [tag_query], (tag_res) -> + if tag_res.error + deal_post(res,[]) + else + deal_post(res,tag_res) # - EOF Pages - @@ -468,6 +510,15 @@ class ZeroBlog extends ZeroFrame # Apply from data to post html element applyPostdata: (elem, post, full=false) -> + + #tag is passed as post property (to display tag links), + #but it's not saved to post.tag in json + #so must delete this property after use + tag=post.tag + if not tag + tag=[] + delete post.tag + title_hash = post.title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") elem.data("object", "Post:"+post.post_id) $(".title .editable", elem).html(post.title) @@ -482,6 +533,13 @@ class ZeroBlog extends ZeroFrame .attr("href", "?Post:#{post.post_id}:#{title_hash}") $(".details .published", elem).html(date_published) .data("content", post.date_published) + + + $(".details .tag",elem).append(tagToHtml(tag)) + + $(".details .tag",elem).data("content",(tag.join(" "))) + + # Comments num if post.comments > 0 $(".details .comments-num", elem).css("display", "inline") @@ -590,19 +648,51 @@ class ZeroBlog extends ZeroFrame @cmd "fileGet", ["data/data.json"], (res) => data = JSON.parse(res) if type == "Post" - post = (post for post in data.post when post.post_id == id)[0] - if elem.data("editable-mode") == "timestamp" # Time parse to timestamp - content = Time.timestamp(content) - post[elem.data("editable")] = content + changeKey=elem.data("editable") + #tag is maintained by data["tag"],not post["tag"] + #so must exclude tag property changes. + if changeKey != "tag" + post = (post for post in data.post when post.post_id == id)[0] + + if elem.data("editable-mode") == "timestamp" # Time parse to timestamp + content = Time.timestamp(content) + + post[changeKey] = content + + else + + #db not allow duplicate tag of same post nor empty tag + temp = {} + dedup = [] + for val,idx in content.split(" ") + if val != "" + temp[val]=idx + for k,v of temp + dedup.push(k) + + + #exclude old tag + tag_index = (tag for tag in data.tag when tag.post_id != id) + data["tag"] = tag_index + #add new tag + for tag in dedup + data["tag"].push( + value:tag + post_id:id) + else if type == "Site" data[elem.data("editable")] = content @writeData data, (res) -> if cb if res == true # OK - if elem.data("editable-mode") == "simple" # No markdown + if elem.data("editable") == "tag" + # tag list appears as links + # reserve leading text + cb($(".post.template span.tag").text()+tagToHtml(dedup)) + else if elem.data("editable-mode") == "simple" # No markdown cb(content) else if elem.data("editable-mode") == "timestamp" # Format timestamp cb(Time.since(content)) @@ -645,6 +735,14 @@ class ZeroBlog extends ZeroFrame if type == "Post" post = (post for post in data.post when post.post_id == id)[0] if not post then return false # No post found for this id + + if not data.tag + data.tag=[] + + #remove all tag index from json + tag_index = (tag for tag in data.tag when tag.post_id != id) + data["tag"] = tag_index + data.post.splice(data.post.indexOf(post), 1) # Remove from data @writeData data, (res) -> diff --git a/js/lib/helper.js b/js/lib/helper.js new file mode 100644 index 0000000..8fa3a34 --- /dev/null +++ b/js/lib/helper.js @@ -0,0 +1,24 @@ + +//I don't know how to write this function as coffee script +//it's been called at two place and I don't know how to use +//-> => @ correctly +function tagToHtml(tag){ + if (typeof tag === 'string'){ + //input parameter must not empty nor have duplicate + tag = tag.split(" "); + } + + if (tag.length==0) { + return "not tagged"; + } + + + var ret = ""; + + for (i in tag){ + value = tag[i] + ret+=(""+value+" "); + } + return ret; +} + From 4927b1f0882a65ebbd1484a4c0148225a26d5b32 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 23 Apr 2016 18:32:40 +0000 Subject: [PATCH 12/17] implements auto TOC by tag function support three type TOC: index.html has link to "?Toc=tagAll", which generated a overview tag index page as: tag1: n post(s) ---> links to ?Toc=tag:tag1,shows all posts has tag1 tag2: m post(s) ... untagged: x post(s) ---> links to Toc=tagNone, shows all posts without tags. same post may belongs to many tag,leads too many duplicate, so tagAll didn't show every posts. user can jump to extactly tag list from the "tag list" part of every post (in previous commit) --- index.html | 2 + js/ZeroBlog.coffee | 157 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 137 insertions(+), 22 deletions(-) diff --git a/index.html b/index.html index 7fd8082..e42387b 100644 --- a/index.html +++ b/index.html @@ -53,6 +53,8 @@

Follow in NewsfeedFollowing

TOC by date +
+ TOC by tag
diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index a02e81e..fb1ec61 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -232,8 +232,7 @@ class ZeroBlog extends ZeroFrame $("body").addClass("page-post") @post_id = parseInt(match[1]) @pagePost() - else if match = url.match /Toc=(\w+)/ - $("body").addClass("page-post") + else if match = url.match /Toc=(.*)/ @pageToc(match[1]) else $("body").addClass("page-main") @@ -241,11 +240,137 @@ class ZeroBlog extends ZeroFrame @page = parseInt(match[1]) @pageMain() + pageToc:(tocType) -> + $("body").addClass("page-post") + + if tocType.match /^dateDesc/ + @pageTocDateDesc() + else if tocType.match /^tagAll/ + @pageTocTagAll() + else if tocType.match /^tag/ + @pageTocByTag(tocType.split("&")[0].substring(3)) + + @pageLoaded() + #TOC page didn't show details row nor allow edit,comments + $(".post .details").hide() + $(".editable-edit").hide() + Comments.hide() + + emptyTocPage: (title,body) -> + @applyPostdata($(".post-full"), + title:title + post_id:9999999 + votes:-1 + comments:-1 + body:body + ,true) + + pageTocByTag:(tagType) -> + queryString = "" + tag ="" + #query untagged + if tagType.match /^None/ + tag="all untagged" + queryString = """SELECT date_published,title,post_id FROM post + WHERE post_id NOT IN (SELECT DISTINCT (post_id) FROM tag) + ORDER BY date_published DESC""" + else + + tag = decodeURIComponent(tagType.substring(1)) + @log "Toc by tag:", tag + #by tag + queryString = """SELECT post.date_published AS date_published, + post_id,post.title AS title FROM tag + JOIN (SELECT date_published,title,post_id FROM post) AS post + USING (post_id) WHERE value="#{tag}" + ORDER BY date_published DESC + """ + @cmd "dbQuery", [queryString], (res) => + parse_res = (res) => + if res.length is 0 + @emptyTocPage("#{tag}","no posts found") + return + + markdown="" + for i in res + date = new Date(i.date_published*1000) + markdown += "- [#{date.getFullYear()}-\ + #{date.getMonth()+1}-#{date.getDate()}:#{i.title}](?Post:#{i.post_id})\n" + + post_id = 99999999 + + @applyPostdata($(".post-full"), + title:"posts of tag:"+tag + post_id:post_id + votes:-1 + comments:-1 + body:markdown + ,true) + if res.error + @emptyTocPage("error when getting index","error when getting index") + else + parse_res(res) + + + + + pageTocTagAll: () -> + #list all tags only. to avoid too many duplicate post with different tag + #first row is total post number + #second row is tagged number + #follow rows is tagged value,count + @cmd "dbQuery", ["""SELECT "all" AS value,COUNT(*) AS count FROM post + UNION ALL + SELECT "tagged" AS value,COUNT(DISTINCT post_id) AS count + FROM tag + UNION ALL + SELECT value, COUNT(post_id) + FROM tag GROUP BY value"""], (res) => + parse_res = (res) => + + total_post = res[0].count + if total_post is 0 + emptyTocPage("no post","no post at all") + return + + markdown = "" + tagged = res[2..] + + if tagged.length > 0 + markdown += "tagged:\n\n" + + for one in tagged + escaped = encodeURIComponent(one.value) + markdown += "[#{one.value}:#{one.count} post(s)]\ + (?Toc=tag:#{escaped})\n" + + untagged=total_post - res[1].count + + if untagged != 0 + markdown += "\n[untagged:#{untagged} post(s)]\ + (?Toc=tagNone)" + + post_id = 99999999 + + @applyPostdata($(".post-full"), + title:"index by tag" + post_id:post_id + votes:-1 + comments:-1 + body:markdown + ,true) + if res.error + @emptyTocPage("error when getting index","sorry, error happened") + else + parse_res(res) + + + + + # - Pages - - pageToc: (type) -> - @log "Toc by:", type - if type != "dateDesc" - return + pageTocDateDesc: () -> + @log "Toc by date desc" @cmd "dbQuery", ["SELECT post_id,date_published,title FROM post ORDER BY date_published DESC"], (res) => parse_res = (res) => @@ -256,13 +381,7 @@ class ZeroBlog extends ZeroFrame if res.length is 0 - @applyPostdata($(".post-full"), - title:"no post" - post_id:post_id - votes:-1 - comments:-1 - body:"no post at all" - ,true) + @emptyTocPage("no post","no post at all") return # makes next month @@ -294,17 +413,9 @@ class ZeroBlog extends ZeroFrame ,true) if res.error - @applyPostdata($(".post-full"), - title:"error when getting index" - post_id:post_id - votes:-1 - comments:-1 - body:"error happened" - ,true) + @emptyTocPage("error","error while getting index") else parse_res(res) - @pageLoaded() - Comments.hide() pagePost: () -> @@ -673,6 +784,8 @@ class ZeroBlog extends ZeroFrame dedup.push(k) + if not data.tag + data.tag = [] #exclude old tag tag_index = (tag for tag in data.tag when tag.post_id != id) data["tag"] = tag_index From ae34a0940bb8b75e61187ca3ef59f79ddbe5edd6 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 24 Apr 2016 04:30:35 +0000 Subject: [PATCH 13/17] when access index page, not add href to title.also clean applyPostdata --- index.html | 4 ++-- js/ZeroBlog.coffee | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index e42387b..3f9746c 100644 --- a/index.html +++ b/index.html @@ -88,7 +88,7 @@

Latest comments:

-

Title

+

Title

21 hours ago · 2 min read ·
3 comments
@@ -110,7 +110,7 @@

-

Title

+

Title

21 hours ago · 2 min read diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index fb1ec61..6e67691 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -259,7 +259,7 @@ class ZeroBlog extends ZeroFrame emptyTocPage: (title,body) -> @applyPostdata($(".post-full"), title:title - post_id:9999999 + post_id:-1 votes:-1 comments:-1 body:body @@ -297,11 +297,10 @@ class ZeroBlog extends ZeroFrame markdown += "- [#{date.getFullYear()}-\ #{date.getMonth()+1}-#{date.getDate()}:#{i.title}](?Post:#{i.post_id})\n" - post_id = 99999999 @applyPostdata($(".post-full"), title:"posts of tag:"+tag - post_id:post_id + post_id:-1 votes:-1 comments:-1 body:markdown @@ -350,11 +349,10 @@ class ZeroBlog extends ZeroFrame markdown += "\n[untagged:#{untagged} post(s)]\ (?Toc=tagNone)" - post_id = 99999999 @applyPostdata($(".post-full"), title:"index by tag" - post_id:post_id + post_id:-1 votes:-1 comments:-1 body:markdown @@ -376,7 +374,6 @@ class ZeroBlog extends ZeroFrame parse_res = (res) => - post_id = 99999999999 #id is needed when applyPostdata if res.length is 0 @@ -406,7 +403,7 @@ class ZeroBlog extends ZeroFrame @applyPostdata($(".post-full"), title:"index by date" - post_id:post_id + post_id:-1 votes:-1 comments:-1 body:markdown @@ -633,8 +630,11 @@ class ZeroBlog extends ZeroFrame title_hash = post.title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") elem.data("object", "Post:"+post.post_id) $(".title .editable", elem).html(post.title) - .attr("href", "?Post:#{post.post_id}:#{title_hash}") .data("content", post.title) + #valid post_id + if post.post_id > 0 + $(".title .editable", elem).attr("href", "?Post:#{post.post_id}:#{title_hash}") + date_published = Time.since(post.date_published) # Published date if post.body.match /^---/m # Has more over fold From 752636d9ce6054e2ff598d867246b29c9fa85a37 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 12 May 2016 09:17:12 +0800 Subject: [PATCH 14/17] fix outer this ref --- js/ZeroBlog.coffee | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 6e67691..956ba37 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -441,12 +441,13 @@ class ZeroBlog extends ZeroFrame SELECT value FROM tag WHERE post_id=#{@post_id} """ + self = @ deal_post = (post_res,tag_res) -> # Temporary dbschema bug workaround if res.error - @cmd "dbQuery", ["SELECT *, -1 AS votes FROM post - WHERE post_id = #{@post_id} LIMIT 1"], (res)->parse_res(res,tag_res) + self.cmd "dbQuery", ["SELECT *, -1 AS votes FROM post + WHERE post_id = #{self.post_id} LIMIT 1"], (res)->parse_res(res,tag_res) else parse_res(res,tag_res) @@ -545,6 +546,7 @@ class ZeroBlog extends ZeroFrame + self = @ deal_post = (post_res,tag_res) -> if res.error # Temporary dbschema bug workaround @@ -556,9 +558,9 @@ class ZeroBlog extends ZeroFrame LEFT JOIN comment USING (post_id) GROUP BY post_id ORDER BY date_published DESC - LIMIT #{(@page-1)*limit}, #{limit+1} + LIMIT #{(self.page-1)*limit}, #{limit+1} """ - @cmd "dbQuery", [query], (res)-> parse_res(res,tag_res) + self.cmd "dbQuery", [query], (res)-> parse_res(res,tag_res) else parse_res(res,tag_res) From 545ef717d98be1a31607c19956e6abf799102afb Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 12 May 2016 09:49:28 +0800 Subject: [PATCH 15/17] fix outer this ref --- js/ZeroBlog.coffee | 20 ++++++++++++++++++-- js/lib/helper.js | 24 ------------------------ 2 files changed, 18 insertions(+), 26 deletions(-) delete mode 100644 js/lib/helper.js diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 956ba37..81d8d9c 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -648,7 +648,7 @@ class ZeroBlog extends ZeroFrame .data("content", post.date_published) - $(".details .tag",elem).append(tagToHtml(tag)) + $(".details .tag",elem).append(@tagToHtml(tag)) $(".details .tag",elem).data("content",(tag.join(" "))) @@ -800,13 +800,14 @@ class ZeroBlog extends ZeroFrame else if type == "Site" data[elem.data("editable")] = content + self = @ @writeData data, (res) -> if cb if res == true # OK if elem.data("editable") == "tag" # tag list appears as links # reserve leading text - cb($(".post.template span.tag").text()+tagToHtml(dedup)) + cb($(".post.template span.tag").text()+self.tagToHtml(dedup)) else if elem.data("editable-mode") == "simple" # No markdown cb(content) else if elem.data("editable-mode") == "timestamp" # Format timestamp @@ -999,5 +1000,20 @@ class ZeroBlog extends ZeroFrame if $("body").hasClass("page-main") then @pageMain() if $("body").hasClass("page-post") then @pagePost() + tagToHtml:(tag) -> + if typeof tag is 'string' + #input parameter must not empty nor have duplicate + tag = tag.split(" ") + + if tag.length is 0 + return "not tagged" + + ret = "" + + for i in tag + ret+=(""+i+" ") + return ret + + window.Page = new ZeroBlog() diff --git a/js/lib/helper.js b/js/lib/helper.js deleted file mode 100644 index 8fa3a34..0000000 --- a/js/lib/helper.js +++ /dev/null @@ -1,24 +0,0 @@ - -//I don't know how to write this function as coffee script -//it's been called at two place and I don't know how to use -//-> => @ correctly -function tagToHtml(tag){ - if (typeof tag === 'string'){ - //input parameter must not empty nor have duplicate - tag = tag.split(" "); - } - - if (tag.length==0) { - return "not tagged"; - } - - - var ret = ""; - - for (i in tag){ - value = tag[i] - ret+=(""+value+" "); - } - return ret; -} - From 27cd3d44a59148314a2c49c7574fc79baf2d8cee Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 12 May 2016 09:58:36 +0800 Subject: [PATCH 16/17] fix possible duplicate tags --- js/ZeroBlog.coffee | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 81d8d9c..50f3490 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -648,7 +648,7 @@ class ZeroBlog extends ZeroFrame .data("content", post.date_published) - $(".details .tag",elem).append(@tagToHtml(tag)) + $(".details .tag",elem).html(@tagToHtml(tag)) $(".details .tag",elem).data("content",(tag.join(" "))) @@ -806,8 +806,7 @@ class ZeroBlog extends ZeroFrame if res == true # OK if elem.data("editable") == "tag" # tag list appears as links - # reserve leading text - cb($(".post.template span.tag").text()+self.tagToHtml(dedup)) + cb(self.tagToHtml(dedup)) else if elem.data("editable-mode") == "simple" # No markdown cb(content) else if elem.data("editable-mode") == "timestamp" # Format timestamp @@ -1006,9 +1005,9 @@ class ZeroBlog extends ZeroFrame tag = tag.split(" ") if tag.length is 0 - return "not tagged" + return "tag:not tagged" - ret = "" + ret = "tag:" for i in tag ret+=(""+i+" ") From 05623a6aa525b5d67b7d7eba03e59ec7384f435d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 12 May 2016 11:39:12 +0800 Subject: [PATCH 17/17] fix coffeelint --- js/ZeroBlog.coffee | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/js/ZeroBlog.coffee b/js/ZeroBlog.coffee index 50f3490..07a0305 100644 --- a/js/ZeroBlog.coffee +++ b/js/ZeroBlog.coffee @@ -295,7 +295,8 @@ class ZeroBlog extends ZeroFrame for i in res date = new Date(i.date_published*1000) markdown += "- [#{date.getFullYear()}-\ - #{date.getMonth()+1}-#{date.getDate()}:#{i.title}](?Post:#{i.post_id})\n" + #{date.getMonth()+1}-#{date.getDate()}:\ + #{i.title}](?Post:#{i.post_id})\n" @applyPostdata($(".post-full"), @@ -447,7 +448,8 @@ class ZeroBlog extends ZeroFrame # Temporary dbschema bug workaround if res.error self.cmd "dbQuery", ["SELECT *, -1 AS votes FROM post - WHERE post_id = #{self.post_id} LIMIT 1"], (res)->parse_res(res,tag_res) + WHERE post_id = #{self.post_id} LIMIT 1"], + (res)->parse_res(res,tag_res) else parse_res(res,tag_res) @@ -635,7 +637,8 @@ class ZeroBlog extends ZeroFrame .data("content", post.title) #valid post_id if post.post_id > 0 - $(".title .editable", elem).attr("href", "?Post:#{post.post_id}:#{title_hash}") + $(".title .editable", elem).attr("href", + "?Post:#{post.post_id}:#{title_hash}") date_published = Time.since(post.date_published) # Published date