├── README.md ├── img ├── bg.png └── loading.gif ├── data-default ├── data.json └── users │ └── content-default.json ├── js ├── utils │ ├── RateLimit.coffee │ ├── Class.coffee │ ├── Time.coffee │ ├── ZeroFrame.coffee │ ├── Text.coffee │ └── InlineEditor.coffee ├── lib │ ├── jquery.cssanim.coffee │ ├── jquery.csslater.coffee │ ├── identicon.js │ ├── pnglib.js │ ├── marked.min.js │ └── highlight.pack.js ├── Comments.coffee └── Gifs.coffee ├── data └── users │ └── content.json ├── updater ├── cleanup.py └── updater.py ├── dbschema.json ├── css ├── icons.css ├── Comments.css └── Gifs.css ├── index.html └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # ReactionGIFs 2 | ReactionGIFs ZeroNet site 3 | -------------------------------------------------------------------------------- /img/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ReactionGIFs/master/img/bg.png -------------------------------------------------------------------------------- /img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloZeroNet/ReactionGIFs/master/img/loading.gif -------------------------------------------------------------------------------- /data-default/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "MyZeroBlog", 3 | "description": "My ZeroBlog.", 4 | "links": "- [Source code](https://github.com/HelloZeroNet)", 5 | "next_post_id": 2, 6 | "demo": false, 7 | "modified": 1432515193, 8 | "post": [ 9 | { 10 | "post_id": 1, 11 | "title": "Congratulations!", 12 | "date_published": 1433033779.604, 13 | "body": "Your zeronet blog has been successfully created!" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /js/utils/RateLimit.coffee: -------------------------------------------------------------------------------- 1 | limits = {} 2 | call_after_interval = {} 3 | window.RateLimit = (interval, fn) -> 4 | if not limits[fn] 5 | call_after_interval[fn] = false 6 | fn() # First call is not delayed 7 | limits[fn] = setTimeout (-> 8 | if call_after_interval[fn] 9 | fn() 10 | delete limits[fn] 11 | delete call_after_interval[fn] 12 | ), interval 13 | else # Called within iterval, delay the call 14 | call_after_interval[fn] = true 15 | -------------------------------------------------------------------------------- /js/utils/Class.coffee: -------------------------------------------------------------------------------- 1 | class Class 2 | trace: true 3 | 4 | log: (args...) -> 5 | return unless @trace 6 | return if typeof console is 'undefined' 7 | args.unshift("[#{@.constructor.name}]") 8 | console.log(args...) 9 | @ 10 | 11 | logStart: (name, args...) -> 12 | return unless @trace 13 | @logtimers or= {} 14 | @logtimers[name] = +(new Date) 15 | @log "#{name}", args..., "(started)" if args.length > 0 16 | @ 17 | 18 | logEnd: (name, args...) -> 19 | ms = +(new Date)-@logtimers[name] 20 | @log "#{name}", args..., "(Done in #{ms}ms)" 21 | @ 22 | 23 | window.Class = Class -------------------------------------------------------------------------------- /data/users/content.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": {}, 3 | "ignore": ".*", 4 | "modified": 1447023686.64, 5 | "signs": { 6 | "1Gif7PqWTzVWDQ42Mo7np3zXmGAo3DXc7h": "HE/+syGEMkrg20HJsDPCDfM8IkauzrkfoL0N3Nw9O1u9H+II1V6+RYA3SthoMAVF7p+zot6Chczb1zDZHsX02K8=" 7 | }, 8 | "user_contents": { 9 | "cert_signers": { 10 | "zeroid.bit": [ "1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz" ] 11 | }, 12 | "permission_rules": { 13 | ".*": { 14 | "files_allowed": "data.json", 15 | "max_size": 10000 16 | }, 17 | "bitid/.*@zeroid.bit": { "max_size": 40000 }, 18 | "bitmsg/.*@zeroid.bit": { "max_size": 15000 } 19 | }, 20 | "permissions": { 21 | "banexample@zeroid.bit": false, 22 | "nofish@zeroid.bit": { "max_size": 20000 } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /data-default/users/content-default.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": {}, 3 | "ignore": ".*", 4 | "modified": 1432466966.003, 5 | "signs": { 6 | "1BLogC9LN4oPDcruNz3qo1ysa133E9AGg8": "HChU28lG4MCnAiui6wDAaVCD4QUrgSy4zZ67+MMHidcUJRkLGnO3j4Eb1N0AWQ86nhSBwoOQf08Rha7gRyTDlAk=" 7 | }, 8 | "user_contents": { 9 | "cert_signers": { 10 | "zeroid.bit": [ "1iD5ZQJMNXu43w1qLB8sfdHVKppVMduGz" ] 11 | }, 12 | "permission_rules": { 13 | ".*": { 14 | "files_allowed": "data.json", 15 | "max_size": 10000 16 | }, 17 | "bitid/.*@zeroid.bit": { "max_size": 40000 }, 18 | "bitmsg/.*@zeroid.bit": { "max_size": 15000 } 19 | }, 20 | "permissions": { 21 | "banexample@zeroid.bit": false, 22 | "nofish@zeroid.bit": { "max_size": 20000 } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /js/lib/jquery.cssanim.coffee: -------------------------------------------------------------------------------- 1 | jQuery.fn.cssSlideDown = -> 2 | elem = @ 3 | elem.css({"opacity": 0, "margin-bottom": 0, "margin-top": 0, "padding-bottom": 0, "padding-top": 0, "display": "none", "transform": "scale(0.8)"}) 4 | setTimeout (-> 5 | elem.css("display", "") 6 | height = elem.outerHeight() 7 | elem.css({"height": 0, "display": ""}).cssLater("transition", "all 0.3s ease-out", 20) 8 | elem.cssLater({"height": height, "opacity": 1, "margin-bottom": "", "margin-top": "", "padding-bottom": "", "padding-top": "", "transform": "scale(1)"}, null, 40) 9 | elem.cssLater("transition", "", 1000, "noclear") 10 | ), 10 11 | return @ 12 | 13 | 14 | jQuery.fn.fancySlideDown = -> 15 | elem = @ 16 | elem.css({"opacity": 0, "transform":"scale(0.9)"}).slideDown().animate({"opacity": 1, "scale": 1}, {"duration": 600, "queue": false, "easing": "easeOutBack"}) 17 | 18 | 19 | jQuery.fn.fancySlideUp = -> 20 | elem = @ 21 | elem.delay(600).slideUp(600).animate({"opacity": 0, "scale": 0.9}, {"duration": 600, "queue": false, "easing": "easeOutQuad"}) 22 | -------------------------------------------------------------------------------- /js/utils/Time.coffee: -------------------------------------------------------------------------------- 1 | class Time 2 | since: (time) -> 3 | now = +(new Date)/1000 4 | secs = now - time 5 | if secs < 60 6 | back = "Just now" 7 | else if secs < 60*60 8 | back = "#{Math.round(secs/60)} minutes ago" 9 | else if secs < 60*60*24 10 | back = "#{Math.round(secs/60/60)} hours ago" 11 | else if secs < 60*60*24*3 12 | back = "#{Math.round(secs/60/60/24)} days ago" 13 | else 14 | back = "on "+@date(time) 15 | back = back.replace(/1 ([a-z]+)s/, "1 $1") # 1 days ago fix 16 | return back 17 | 18 | 19 | date: (timestamp, format="short") -> 20 | parts = (new Date(timestamp*1000)).toString().split(" ") 21 | if format == "short" 22 | display = parts.slice(1, 4) 23 | else 24 | display = parts.slice(1, 5) 25 | return display.join(" ").replace(/( [0-9]{4})/, ",$1") 26 | 27 | 28 | timestamp: (date="") -> 29 | if date == "now" or date == "" 30 | return parseInt(+(new Date)/1000) 31 | else 32 | return parseInt(Date.parse(date)/1000) 33 | 34 | 35 | # Get elistamated read time for post 36 | readtime: (text) -> 37 | chars = text.length 38 | if chars > 1500 39 | return parseInt(chars/1500)+" min read" 40 | else 41 | return "less than 1 min read" 42 | 43 | 44 | window.Time = new Time -------------------------------------------------------------------------------- /updater/cleanup.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import re 3 | import json 4 | import os 5 | import time 6 | 7 | db = sqlite3.connect("../data/zeroblog.db") 8 | db.row_factory = sqlite3.Row 9 | c = db.cursor() 10 | 11 | query = """ 12 | SELECT 13 | post.*, COUNT(comment_id) AS comments, MAX(comment.date_added) AS last_comment, 14 | (SELECT COUNT(*) FROM post_vote WHERE post_vote.post_id = post.post_id) AS votes 15 | FROM post 16 | LEFT JOIN comment USING (post_id) 17 | WHERE date_published < strftime('%s', 'now','-20 day') 18 | GROUP BY post_id 19 | HAVING comments = 0 AND votes < 4 20 | ORDER BY date_published 21 | LIMIT 500 22 | """ 23 | deleted = 0 24 | data = json.load(open("../data/data.json", "rb")) 25 | print "Posts:", len(data["post"]) 26 | for row in c.execute(query): 27 | days = float(time.time() - row["date_published"]) / (60*60*24) 28 | if days < 20: 29 | print "! Skipping, days: %.3f" % days 30 | continue 31 | deleted += 1 32 | mp4_file = re.match('.*?src="(.*?)"', row["body"]).group(1) 33 | data["post"] = filter(lambda post: post["post_id"] != row["post_id"], data["post"]) 34 | print u"Deleting %s / %s" % (row["source"], repr(row["title"])), mp4_file, round(days, 2) 35 | try: 36 | os.unlink("../" + mp4_file) 37 | except Exception, err: 38 | print "Error deleting: %s" % err 39 | raw_input("Continue?") 40 | 41 | print "Deleted:", deleted, 42 | print "Posts:", len(data["post"]) 43 | json.dump(data, open("../data/data.json-new", "wb"), indent=1, sort_keys=True) 44 | -------------------------------------------------------------------------------- /js/utils/ZeroFrame.coffee: -------------------------------------------------------------------------------- 1 | class ZeroFrame extends Class 2 | constructor: (url) -> 3 | @url = url 4 | @waiting_cb = {} 5 | @connect() 6 | @next_message_id = 1 7 | @init() 8 | 9 | 10 | init: -> 11 | @ 12 | 13 | 14 | connect: -> 15 | @target = window.parent 16 | window.addEventListener("message", @onMessage, false) 17 | @cmd("innerReady") 18 | 19 | 20 | onMessage: (e) => 21 | message = e.data 22 | cmd = message.cmd 23 | if cmd == "response" 24 | if @waiting_cb[message.to]? 25 | @waiting_cb[message.to](message.result) 26 | else 27 | @log "Websocket callback not found:", message 28 | else if cmd == "wrapperReady" # Wrapper inited later 29 | @cmd("innerReady") 30 | else if cmd == "ping" 31 | @response message.id, "pong" 32 | else if cmd == "wrapperOpenedWebsocket" 33 | @onOpenWebsocket() 34 | else if cmd == "wrapperClosedWebsocket" 35 | @onCloseWebsocket() 36 | else 37 | @onRequest cmd, message 38 | 39 | 40 | onRequest: (cmd, message) => 41 | @log "Unknown request", message 42 | 43 | 44 | response: (to, result) -> 45 | @send {"cmd": "response", "to": to, "result": result} 46 | 47 | 48 | cmd: (cmd, params={}, cb=null) -> 49 | @send {"cmd": cmd, "params": params}, cb 50 | 51 | 52 | send: (message, cb=null) -> 53 | message.id = @next_message_id 54 | @next_message_id += 1 55 | @target.postMessage(message, "*") 56 | if cb 57 | @waiting_cb[message.id] = cb 58 | 59 | 60 | onOpenWebsocket: => 61 | @log "Websocket open" 62 | 63 | 64 | onCloseWebsocket: => 65 | @log "Websocket close" 66 | 67 | 68 | 69 | window.ZeroFrame = ZeroFrame 70 | -------------------------------------------------------------------------------- /js/utils/Text.coffee: -------------------------------------------------------------------------------- 1 | class Renderer extends marked.Renderer 2 | image: (href, title, text) -> 3 | return ("![#{text}](#{href})") 4 | 5 | class Text 6 | toColor: (text) -> 7 | hash = 0 8 | for i in [0..text.length-1] 9 | hash = text.charCodeAt(i) + ((hash << 5) - hash) 10 | color = '#' 11 | return "hsl(" + (hash % 360) + ",30%,70%)"; 12 | for i in [0..2] 13 | value = (hash >> (i * 8)) & 0xFF 14 | color += ('00' + value.toString(16)).substr(-2) 15 | return color 16 | 17 | 18 | renderMarked: (text, options={}) -> 19 | options["gfm"] = true 20 | options["breaks"] = true 21 | if options.sanitize 22 | options["renderer"] = renderer # Dont allow images 23 | text = @fixReply(text) 24 | text = marked(text, options) 25 | return @fixHtmlLinks text 26 | 27 | 28 | # Convert zeronet html links to relaitve 29 | fixHtmlLinks: (text) -> 30 | if window.is_proxy 31 | return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="http://zero') 32 | else 33 | return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="') 34 | 35 | 36 | # Convert a single link to relative 37 | fixLink: (link) -> 38 | if window.is_proxy 39 | return link.replace(/http:\/\/(127.0.0.1|localhost):43110/, 'http://zero') 40 | else 41 | return link.replace(/http:\/\/(127.0.0.1|localhost):43110/, '') 42 | 43 | 44 | fixReply: (text) -> 45 | return text.replace(/(>.*\n)([^\n>])/gm, "$1\n$2") 46 | 47 | 48 | toUrl: (text) => 49 | return text.replace(/[^A-Za-z0-9]/g, "+").replace(/[+]+/g, "+").replace(/[+]+$/, "") 50 | 51 | window.is_proxy = (window.location.pathname == "/") 52 | window.renderer = new Renderer() 53 | window.Text = new Text() 54 | -------------------------------------------------------------------------------- /js/lib/jquery.csslater.coffee: -------------------------------------------------------------------------------- 1 | jQuery.fn.readdClass = (class_name) -> 2 | elem = @ 3 | elem.removeClass class_name 4 | setTimeout ( -> 5 | elem.addClass class_name 6 | ), 1 7 | return @ 8 | 9 | jQuery.fn.removeLater = (time = 500) -> 10 | elem = @ 11 | setTimeout ( -> 12 | elem.remove() 13 | ), time 14 | return @ 15 | 16 | jQuery.fn.hideLater = (time = 500) -> 17 | @.cssLater("display", "none", time) 18 | return @ 19 | 20 | jQuery.fn.addClassLater = (class_name, time=5, mode="clear") -> 21 | elem = @ 22 | elem[0].timers ?= {} 23 | timers = elem[0].timers 24 | 25 | if timers[class_name] and mode == "clear" then clearInterval(timers[class_name]) 26 | timers[class_name] = setTimeout ( -> 27 | elem.addClass(class_name) 28 | ), time 29 | return @ 30 | 31 | jQuery.fn.removeClassLater = (class_name, time=500, mode="clear") -> 32 | elem = @ 33 | elem[0].timers ?= {} 34 | timers = elem[0].timers 35 | 36 | if timers[class_name] and mode == "clear" then clearInterval(timers[class_name]) 37 | timers[class_name] = setTimeout ( -> 38 | elem.removeClass(class_name) 39 | ), time 40 | return @ 41 | 42 | jQuery.fn.cssLater = (name, val, time=500, mode="clear") -> 43 | elem = @ 44 | elem[0].timers ?= {} 45 | timers = elem[0].timers 46 | 47 | if timers[name] and mode == "clear" then clearInterval(timers[name]) 48 | if time == "now" 49 | elem.css name, val 50 | else 51 | timers[name] = setTimeout ( -> 52 | elem.css name, val 53 | ), time 54 | return @ 55 | 56 | 57 | jQuery.fn.toggleClassLater = (name, val, time=10, mode="clear") -> 58 | elem = @ 59 | elem[0].timers ?= {} 60 | timers = elem[0].timers 61 | 62 | if timers[name] and mode == "clear" then clearInterval(timers[name]) 63 | timers[name] = setTimeout ( -> 64 | elem.toggleClass name, val 65 | ), time 66 | return @ -------------------------------------------------------------------------------- /dbschema.json: -------------------------------------------------------------------------------- 1 | { 2 | "db_name": "ZeroBlog", 3 | "db_file": "data/zeroblog.db", 4 | "version": 2, 5 | "maps": { 6 | "users/.+/data.json": { 7 | "to_table": [ 8 | "comment", 9 | {"node": "comment_vote", "table": "comment_vote", "key_col": "comment_uri", "val_col": "vote"}, 10 | {"node": "post_vote", "table": "post_vote", "key_col": "post_id", "val_col": "vote"} 11 | ] 12 | }, 13 | "users/.+/content.json": { 14 | "to_keyvalue": [ "cert_user_id" ] 15 | }, 16 | "data.json": { 17 | "to_table": [ "post" ], 18 | "to_keyvalue": [ "title", "description", "links", "next_post_id", "demo", "modified" ] 19 | } 20 | 21 | }, 22 | "tables": { 23 | "comment": { 24 | "cols": [ 25 | ["comment_id", "INTEGER"], 26 | ["post_id", "INTEGER"], 27 | ["body", "TEXT"], 28 | ["date_added", "INTEGER"], 29 | ["json_id", "INTEGER REFERENCES json (json_id)"] 30 | ], 31 | "indexes": ["CREATE UNIQUE INDEX comment_key ON comment(json_id, comment_id)", "CREATE INDEX comment_post_id ON comment(post_id)"], 32 | "schema_changed": 1426195823 33 | }, 34 | "comment_vote": { 35 | "cols": [ 36 | ["comment_uri", "TEXT"], 37 | ["vote", "INTEGER"], 38 | ["json_id", "INTEGER REFERENCES json (json_id)"] 39 | ], 40 | "indexes": ["CREATE INDEX comment_vote_comment_uri ON comment_vote(comment_uri)", "CREATE INDEX comment_vote_json_id ON comment_vote(json_id)"], 41 | "schema_changed": 1426195822 42 | }, 43 | "post": { 44 | "cols": [ 45 | ["post_id", "INTEGER"], 46 | ["title", "TEXT"], 47 | ["body", "TEXT"], 48 | ["source", "TEXT"], 49 | ["date_published", "INTEGER"], 50 | ["json_id", "INTEGER REFERENCES json (json_id)"] 51 | ], 52 | "indexes": ["CREATE UNIQUE INDEX post_uri ON post(json_id, post_id)", "CREATE INDEX post_id ON post(post_id)", "CREATE INDEX post_source ON post(source)"], 53 | "schema_changed": 1426195824 54 | }, 55 | "post_vote": { 56 | "cols": [ 57 | ["post_id", "INTEGER"], 58 | ["vote", "INTEGER"], 59 | ["json_id", "INTEGER REFERENCES json (json_id)"] 60 | ], 61 | "indexes": ["CREATE INDEX post_vote_post_id ON post_vote(post_id)", "CREATE INDEX post_vote_json_id ON post_vote(post_id)"], 62 | "schema_changed": 1426195826 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /css/icons.css: -------------------------------------------------------------------------------- 1 | .icon { display: inline-block; vertical-align: text-bottom; background-repeat: no-repeat; } 2 | .icon-profile { font-size: 6px; top: 0em; border-radius: 0.7em 0.7em 0 0; background: #FFFFFF; width: 1.5em; height: 0.7em; position: relative; display: inline-block; margin-right: 4px } 3 | .icon-profile:before { position: absolute; content: ""; top: -1em; left: 0.38em; width: 0.8em; height: 0.85em; border-radius: 50%; background: #FFFFFF; } 4 | 5 | .icon-comment { width: 16px; height: 10px; border-radius: 2px; background: #464545; margin-top: 0px; display: inline-block; position: relative; top: 0px; } 6 | .icon-comment:after { left: 9px; border: 2px solid transparent; border-top-color: #464545; border-left-color: #333; background: transparent; content: ""; display: block; margin-top: 10px; width: 0px; margin-left: 7px; } 7 | 8 | .icon-edit { 9 | width: 16px; height: 16px; background-repeat: no-repeat; background-position: 20px center; 10 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAOVBMVEUAAAC9w8e9w8e9w8e9w8e/xMi9w8e9w8e+w8e9w8e9w8e9w8e9w8e9w8e9w8e+w8e/xMi9w8e9w8fvY4+KAAAAEnRSTlMASPv3WQbwOTCkt4/psX4YDMWr+RRCAAAAUUlEQVQY06XLORKAMAxDUTs7kA3d/7AYGju0UfffjIgoHkxm0vB5bZyxKHx9eX0FJw0Y4bcXKQ4/CTtS5yqp5GFFOjGpVGl00k1pNDIb3Nv9AHC7BOZC4ZjvAAAAAElFTkSuQmCC+d0ckOwyAMRVGHUOO0gUyd+P8f7WApz4Iki9wFmyOEATrXLZcFp5LrGogPOxKp6zfFf9fZ1/I/cY7YZSS3U6S3XFZJmGBwL+FuJX/F1K0wUUlZyZGlXgXESthTEs4B8fh7xoVUDPGYJnsfkCRarKAgz8cAKbpD6pqDPz3XB8K6HdUEeN9NAAAAAElFTkSuQmCC); 11 | } 12 | .icon-reply { 13 | width: 16px; height: 16px; 14 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAIVBMVEUAAABmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYs5FxxAAAAC3RSTlMAgBFwYExAMHgoCDJmUTYAAAA3SURBVAjXY8APGGEMQZgAjCEoKBwEEQCCAoiIh6AQVM1kMaguJhGYOSJQjexiUMbiAChDCclCAOHqBBdHpwQTAAAAAElFTkSuQmCC); 15 | } 16 | .icon-heart { position: absolute; width: 17px; height: 13px; margin-top: 5px } 17 | .icon-heart:before, .icon-heart:after { 18 | position: absolute; content: ""; left: 8px; top: 0; width: 8px; height: 13px; 19 | background: #FA6C8D; /*border-radius: 25px 25px 0 0;*/ transform: rotate(-45deg); transform-origin: 0 100% 20 | } 21 | .icon-heart:after { left: 0; transform: rotate(45deg); transform-origin :100% 100% } 22 | .icon-up { font-weight: normal !important; font-size: 15px; font-family: Tahoma; vertical-align: -4px; padding-right: 5px; display: inline; height: 1px; } -------------------------------------------------------------------------------- /css/Comments.css: -------------------------------------------------------------------------------- 1 | h2#Comments { clear: both } 2 | .comments { margin-bottom: 60px } 3 | .comment { background-color: transparent; padding: 25px 0px; margin: 1px; border-top: 1px solid #35373C } 4 | .comment .user_name { font-size: 14px; font-weight: bold } 5 | .comment .added { color: #AAA } 6 | .comment .reply { color: #CCC; opacity: 0; transition: opacity 0.3s; border: none; position: relative; } 7 | .comment:hover .reply { opacity: 1 } 8 | .comment .reply .icon { opacity: 0.3 } 9 | .comment .reply:hover { border-bottom: none; color: #666 } 10 | .comment .reply:hover .icon { opacity: 1 } 11 | .comment .info { font-size: 12px; color: #AAA; margin-bottom: 7px } 12 | .comment .info .score { margin-left: 5px } 13 | .comment .comment-body { line-height: 1.5em; margin-top: 0.5em; margin-bottom: 0.5em } 14 | .comment .comment-body p { margin-bottom: 0px; margin-top: 0.5em; } 15 | .comment .comment-body p:first-child { margin: 0px; margin-top: 0px; } 16 | .comment .comment-body.editor { margin-top: 0.5em !important; margin-bottom: 0.5em !important } 17 | .comment .comment-body h1, .comment .body h2, .comment .body h3 { font-size: 110% } 18 | .comment .comment-body blockquote { padding: 1px 15px; border-left: 2px solid #3A3939; margin: 0px; margin-top: 30px } 19 | .comment .comment-body blockquote:first-child { margin-top: 0px } 20 | .comment .comment-body blockquote p { margin: 0px; color: #999; font-size: 90% } 21 | .comment .comment-body blockquote a { color: #FFF; font-weight: normal; border-bottom: 0px } 22 | .comment .comment-body blockquote a:hover { border-bottom: 1px solid #999 } 23 | .comment .editable-edit { margin-top: -5px } 24 | 25 | .comment-new { margin-bottom: 5px; border-top: 0px } 26 | .comment-new .button-submit { 27 | margin: 0px; font-weight: normal; padding: 5px 15px; display: inline-block; 28 | background-color: #FFDA00; border-bottom: 2px solid #CDBD1E; font-size: 15px; line-height: 30px 29 | } 30 | .comment-new h2 { margin-bottom: 25px } 31 | 32 | /* Input */ 33 | .comment-new textarea { 34 | line-height: 1.5em; width: 100%; padding: 10px; font-family: 'Roboto', sans-serif; font-size: 16px; color: white; 35 | transition: border 0.3s; border: 1px solid #222; box-sizing: border-box; overflow-y: auto; background-color: #1D1D1D; 36 | } 37 | input.text:focus, textarea:focus { border-color: #28404A; outline: none; background-color: #111 } 38 | 39 | .comment-nocert textarea { opacity: 0.5; pointer-events: none } 40 | .comment-nocert .info { opacity: 0.1; pointer-events: none } 41 | .comment-nocert .button-submit-comment { opacity: 0.1; pointer-events: none } 42 | .comment-nocert .button.button-certselect { display: inherit } 43 | .button.button-certselect { 44 | position: absolute; left: 50%; white-space: nowrap; transform: translateX(-50%); z-index: 99; 45 | margin-top: 13px; background-color: #007AFF; color: white; border-bottom-color: #3543F9; display: none 46 | } 47 | .button.button-certselect:hover { background-color: #3396FF; color: white; border-bottom-color: #5D68FF; } 48 | .button.button-certselect:active { position: absolute; transform: translateX(-50%) translateY(1px); top: auto; } 49 | 50 | .user-size { font-size: 11px; margin-top: 6px; box-sizing: border-box; text-transform: uppercase; display: inline-block; color: #AAA } 51 | .user-size-used { position: absolute; color: #B10DC9; overflow: hidden; width: 40px; white-space: nowrap } 52 | -------------------------------------------------------------------------------- /js/lib/identicon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Identicon.js v1.0 3 | * http://github.com/stewartlord/identicon.js 4 | * 5 | * Requires PNGLib 6 | * http://www.xarg.org/download/pnglib.js 7 | * 8 | * Copyright 2013, Stewart Lord 9 | * Released under the BSD license 10 | * http://www.opensource.org/licenses/bsd-license.php 11 | */ 12 | 13 | (function() { 14 | Identicon = function(hash, size, margin){ 15 | this.hash = hash; 16 | this.size = size || 64; 17 | this.margin = margin || .08; 18 | } 19 | 20 | Identicon.prototype = { 21 | hash: null, 22 | size: null, 23 | margin: null, 24 | 25 | render: function(){ 26 | var hash = this.hash, 27 | size = this.size, 28 | margin = Math.floor(size * this.margin), 29 | cell = Math.floor((size - (margin * 2)) / 5), 30 | image = new PNGlib(size, size, 256); 31 | 32 | // light-grey background 33 | var bg = image.color(240, 240, 240); 34 | 35 | // foreground is last 7 chars as hue at 50% saturation, 70% brightness 36 | var rgb = this.hsl2rgb(parseInt(hash.substr(-7), 16) / 0xfffffff, .5, .7), 37 | fg = image.color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255); 38 | 39 | // the first 15 characters of the hash control the pixels (even/odd) 40 | // they are drawn down the middle first, then mirrored outwards 41 | var i, color; 42 | for (i = 0; i < 15; i++) { 43 | color = parseInt(hash.charAt(i), 16) % 2 ? bg : fg; 44 | if (i < 5) { 45 | this.rectangle(2 * cell + margin, i * cell + margin, cell, cell, color, image); 46 | } else if (i < 10) { 47 | this.rectangle(1 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image); 48 | this.rectangle(3 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image); 49 | } else if (i < 15) { 50 | this.rectangle(0 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image); 51 | this.rectangle(4 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image); 52 | } 53 | } 54 | 55 | return image; 56 | }, 57 | 58 | rectangle: function(x, y, w, h, color, image) { 59 | var i, j; 60 | for (i = x; i < x + w; i++) { 61 | for (j = y; j < y + h; j++) { 62 | image.buffer[image.index(i, j)] = color; 63 | } 64 | } 65 | }, 66 | 67 | // adapted from: https://gist.github.com/aemkei/1325937 68 | hsl2rgb: function(h, s, b){ 69 | h *= 6; 70 | s = [ 71 | b += s *= b < .5 ? b : 1 - b, 72 | b - h % 1 * s * 2, 73 | b -= s *= 2, 74 | b, 75 | b + h % 1 * s, 76 | b + s 77 | ]; 78 | 79 | return[ 80 | s[ ~~h % 6 ], // red 81 | s[ (h|16) % 6 ], // green 82 | s[ (h|8) % 6 ] // blue 83 | ]; 84 | }, 85 | 86 | toString: function(){ 87 | return this.render().getBase64(); 88 | } 89 | } 90 | 91 | window.Identicon = Identicon; 92 | })(); -------------------------------------------------------------------------------- /js/utils/InlineEditor.coffee: -------------------------------------------------------------------------------- 1 | class InlineEditor 2 | constructor: (@elem, @getContent, @saveContent, @getObject) -> 3 | @edit_button = $("") 4 | @edit_button.on "click", @startEdit 5 | @elem.addClass("editable").before(@edit_button) 6 | @editor = null 7 | @elem.on "mouseenter", (e) => 8 | @edit_button.css("opacity", "0.4") 9 | # Keep in display 10 | scrolltop = $(window).scrollTop() 11 | top = @edit_button.offset().top-parseInt(@edit_button.css("margin-top")) 12 | if scrolltop > top 13 | @edit_button.css("margin-top", scrolltop-top+e.clientY-20) 14 | else 15 | @edit_button.css("margin-top", "") 16 | @elem.on "mouseleave", => 17 | @edit_button.css("opacity", "") 18 | 19 | if @elem.is(":hover") then @elem.trigger "mouseenter" 20 | 21 | 22 | startEdit: => 23 | @content_before = @elem.html() # Save current to restore on cancel 24 | 25 | @editor = $("") 26 | #@editor.css("outline", "10000px solid rgba(255,255,255,0)").cssLater("transition", "outline 0.3s", 5).cssLater("outline", "10000px solid rgba(255,255,255,0.9)", 10) # Animate other elements fadeout 27 | @editor.val @getContent(@elem, "raw") 28 | @elem.after(@editor) 29 | 30 | @elem.html [1..50].join("fill the width") # To make sure we span the editor as far as we can 31 | @copyStyle(@elem, @editor) # Copy elem style to editor 32 | @elem.html @content_before # Restore content 33 | 34 | 35 | @autoExpand(@editor) # Set editor to autoexpand 36 | @elem.css("display", "none") # Hide elem 37 | 38 | if $(window).scrollTop() == 0 # Focus textfield if scroll on top 39 | @editor[0].selectionEnd = 0 40 | @editor.focus() 41 | 42 | $(".editable-edit").css("display", "none") # Hide all edit button until its not finished 43 | 44 | $(".editbar").css("display", "inline-block").addClassLater("visible", 10) 45 | $(".publishbar").css("opacity", 0) # Hide publishbar 46 | $(".editbar .object").text @getObject(@elem).data("object")+"."+@elem.data("editable") 47 | $(".editbar .button").removeClass("loading") 48 | 49 | $(".editbar .save").off("click").on "click", @saveEdit 50 | $(".editbar .delete").off("click").on "click", @deleteObject 51 | $(".editbar .cancel").off("click").on "click", @cancelEdit 52 | 53 | # Deletable button show/hide 54 | if @getObject(@elem).data("deletable") 55 | $(".editbar .delete").css("display", "").html("Delete "+@getObject(@elem).data("object").split(":")[0]) 56 | else 57 | $(".editbar .delete").css("display", "none") 58 | 59 | window.onbeforeunload = -> 60 | return 'Your unsaved blog changes will be lost!' 61 | 62 | return false 63 | 64 | 65 | stopEdit: => 66 | @editor.remove() 67 | @editor = null 68 | @elem.css("display", "") 69 | 70 | $(".editable-edit").css("display", "") # Show edit buttons 71 | 72 | $(".editbar").cssLater("display", "none", 1000).removeClass("visible") # Hide editbar 73 | $(".publishbar").css("opacity", 1) # Show publishbar 74 | 75 | window.onbeforeunload = null 76 | 77 | 78 | saveEdit: => 79 | content = @editor.val() 80 | $(".editbar .save").addClass("loading") 81 | @saveContent @elem, content, (content_html) => 82 | if content_html # File write ok 83 | $(".editbar .save").removeClass("loading") 84 | @stopEdit() 85 | if typeof content_html == "string" # Returned the new content 86 | @elem.html content_html 87 | 88 | $('pre code').each (i, block) -> # Higlight code blocks 89 | hljs.highlightBlock(block) 90 | else 91 | $(".editbar .save").removeClass("loading") 92 | 93 | return false 94 | 95 | 96 | deleteObject: => 97 | object_type = @getObject(@elem).data("object").split(":")[0] 98 | Page.cmd "wrapperConfirm", ["Are you sure you sure to delete this #{object_type}?", "Delete"], (confirmed) => 99 | $(".editbar .delete").addClass("loading") 100 | Page.saveContent @getObject(@elem), null, => 101 | @stopEdit() 102 | return false 103 | 104 | 105 | cancelEdit: => 106 | @stopEdit() 107 | @elem.html @content_before 108 | 109 | $('pre code').each (i, block) -> # Higlight code blocks 110 | hljs.highlightBlock(block) 111 | 112 | return false 113 | 114 | 115 | copyStyle: (elem_from, elem_to) -> 116 | elem_to.addClass(elem_from[0].className) 117 | from_style = getComputedStyle(elem_from[0]) 118 | 119 | elem_to.css 120 | fontFamily: from_style.fontFamily 121 | fontSize: from_style.fontSize 122 | fontWeight: from_style.fontWeight 123 | marginTop: from_style.marginTop 124 | marginRight: from_style.marginRight 125 | marginBottom: from_style.marginBottom 126 | marginLeft: from_style.marginLeft 127 | paddingTop: from_style.paddingTop 128 | paddingRight: from_style.paddingRight 129 | paddingBottom: from_style.paddingBottom 130 | paddingLeft: from_style.paddingLeft 131 | lineHeight: from_style.lineHeight 132 | textAlign: from_style.textAlign 133 | backgroundColor:from_style.backgroundColor 134 | color: from_style.color 135 | letterSpacing: from_style.letterSpacing 136 | 137 | if elem_from.innerWidth() < 1000 # inline elems fix 138 | elem_to.css "minWidth", elem_from.innerWidth() 139 | 140 | 141 | autoExpand: (elem) -> 142 | editor = elem[0] 143 | # Autoexpand 144 | elem.height(1) 145 | elem.on "input", -> 146 | if editor.scrollHeight > elem.height() 147 | elem.height(1).height(editor.scrollHeight + parseFloat(elem.css("borderTopWidth")) + parseFloat(elem.css("borderBottomWidth"))) 148 | elem.trigger "input" 149 | 150 | # Tab key support 151 | elem.on 'keydown', (e) -> 152 | if e.which == 9 153 | e.preventDefault() 154 | s = this.selectionStart 155 | val = elem.val() 156 | elem.val(val.substring(0,this.selectionStart) + "\t" + val.substring(this.selectionEnd)) 157 | this.selectionEnd = s+1; 158 | 159 | 160 | window.InlineEditor = InlineEditor -------------------------------------------------------------------------------- /js/lib/pnglib.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A handy class to calculate color values. 3 | * 4 | * @version 1.0 5 | * @author Robert Eisele 6 | * @copyright Copyright (c) 2010, Robert Eisele 7 | * @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ 8 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License 9 | * 10 | */ 11 | 12 | (function() { 13 | 14 | // helper functions for that ctx 15 | function write(buffer, offs) { 16 | for (var i = 2; i < arguments.length; i++) { 17 | for (var j = 0; j < arguments[i].length; j++) { 18 | buffer[offs++] = arguments[i].charAt(j); 19 | } 20 | } 21 | } 22 | 23 | function byte2(w) { 24 | return String.fromCharCode((w >> 8) & 255, w & 255); 25 | } 26 | 27 | function byte4(w) { 28 | return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255); 29 | } 30 | 31 | function byte2lsb(w) { 32 | return String.fromCharCode(w & 255, (w >> 8) & 255); 33 | } 34 | 35 | window.PNGlib = function(width,height,depth) { 36 | 37 | this.width = width; 38 | this.height = height; 39 | this.depth = depth; 40 | 41 | // pixel data and row filter identifier size 42 | this.pix_size = height * (width + 1); 43 | 44 | // deflate header, pix_size, block headers, adler32 checksum 45 | this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4; 46 | 47 | // offsets and sizes of Png chunks 48 | this.ihdr_offs = 0; // IHDR offset and size 49 | this.ihdr_size = 4 + 4 + 13 + 4; 50 | this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size 51 | this.plte_size = 4 + 4 + 3 * depth + 4; 52 | this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size 53 | this.trns_size = 4 + 4 + depth + 4; 54 | this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size 55 | this.idat_size = 4 + 4 + this.data_size + 4; 56 | this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size 57 | this.iend_size = 4 + 4 + 4; 58 | this.buffer_size = this.iend_offs + this.iend_size; // total PNG size 59 | 60 | this.buffer = new Array(); 61 | this.palette = new Object(); 62 | this.pindex = 0; 63 | 64 | var _crc32 = new Array(); 65 | 66 | // initialize buffer with zero bytes 67 | for (var i = 0; i < this.buffer_size; i++) { 68 | this.buffer[i] = "\x00"; 69 | } 70 | 71 | // initialize non-zero elements 72 | write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03"); 73 | write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE'); 74 | write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS'); 75 | write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT'); 76 | write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND'); 77 | 78 | // initialize deflate header 79 | var header = ((8 + (7 << 4)) << 8) | (3 << 6); 80 | header+= 31 - (header % 31); 81 | 82 | write(this.buffer, this.idat_offs + 8, byte2(header)); 83 | 84 | // initialize deflate block headers 85 | for (var i = 0; (i << 16) - 1 < this.pix_size; i++) { 86 | var size, bits; 87 | if (i + 0xffff < this.pix_size) { 88 | size = 0xffff; 89 | bits = "\x00"; 90 | } else { 91 | size = this.pix_size - (i << 16) - i; 92 | bits = "\x01"; 93 | } 94 | write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size)); 95 | } 96 | 97 | /* Create crc32 lookup table */ 98 | for (var i = 0; i < 256; i++) { 99 | var c = i; 100 | for (var j = 0; j < 8; j++) { 101 | if (c & 1) { 102 | c = -306674912 ^ ((c >> 1) & 0x7fffffff); 103 | } else { 104 | c = (c >> 1) & 0x7fffffff; 105 | } 106 | } 107 | _crc32[i] = c; 108 | } 109 | 110 | // compute the index into a png for a given pixel 111 | this.index = function(x,y) { 112 | var i = y * (this.width + 1) + x + 1; 113 | var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i; 114 | return j; 115 | } 116 | 117 | // convert a color and build up the palette 118 | this.color = function(red, green, blue, alpha) { 119 | 120 | alpha = alpha >= 0 ? alpha : 255; 121 | var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue; 122 | 123 | if (typeof this.palette[color] == "undefined") { 124 | if (this.pindex == this.depth) return "\x00"; 125 | 126 | var ndx = this.plte_offs + 8 + 3 * this.pindex; 127 | 128 | this.buffer[ndx + 0] = String.fromCharCode(red); 129 | this.buffer[ndx + 1] = String.fromCharCode(green); 130 | this.buffer[ndx + 2] = String.fromCharCode(blue); 131 | this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha); 132 | 133 | this.palette[color] = String.fromCharCode(this.pindex++); 134 | } 135 | return this.palette[color]; 136 | } 137 | 138 | // output a PNG string, Base64 encoded 139 | this.getBase64 = function() { 140 | 141 | var s = this.getDump(); 142 | 143 | var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 144 | var c1, c2, c3, e1, e2, e3, e4; 145 | var l = s.length; 146 | var i = 0; 147 | var r = ""; 148 | 149 | do { 150 | c1 = s.charCodeAt(i); 151 | e1 = c1 >> 2; 152 | c2 = s.charCodeAt(i+1); 153 | e2 = ((c1 & 3) << 4) | (c2 >> 4); 154 | c3 = s.charCodeAt(i+2); 155 | if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); } 156 | if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; } 157 | r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4); 158 | } while ((i+= 3) < l); 159 | return r; 160 | } 161 | 162 | // output a PNG string 163 | this.getDump = function() { 164 | 165 | // compute adler32 of output pixels + row filter bytes 166 | var BASE = 65521; /* largest prime smaller than 65536 */ 167 | var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ 168 | var s1 = 1; 169 | var s2 = 0; 170 | var n = NMAX; 171 | 172 | for (var y = 0; y < this.height; y++) { 173 | for (var x = -1; x < this.width; x++) { 174 | s1+= this.buffer[this.index(x, y)].charCodeAt(0); 175 | s2+= s1; 176 | if ((n-= 1) == 0) { 177 | s1%= BASE; 178 | s2%= BASE; 179 | n = NMAX; 180 | } 181 | } 182 | } 183 | s1%= BASE; 184 | s2%= BASE; 185 | write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1)); 186 | 187 | // compute crc32 of the PNG chunks 188 | function crc32(png, offs, size) { 189 | var crc = -1; 190 | for (var i = 4; i < size-4; i += 1) { 191 | crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff); 192 | } 193 | write(png, offs+size-4, byte4(crc ^ -1)); 194 | } 195 | 196 | crc32(this.buffer, this.ihdr_offs, this.ihdr_size); 197 | crc32(this.buffer, this.plte_offs, this.plte_size); 198 | crc32(this.buffer, this.trns_offs, this.trns_size); 199 | crc32(this.buffer, this.idat_offs, this.idat_size); 200 | crc32(this.buffer, this.iend_offs, this.iend_size); 201 | 202 | // convert PNG to string 203 | return "\211PNG\r\n\032\n"+this.buffer.join(''); 204 | } 205 | } 206 | 207 | })(); 208 | -------------------------------------------------------------------------------- /js/Comments.coffee: -------------------------------------------------------------------------------- 1 | class Comments extends Class 2 | pagePost: (post_id, cb=false) -> 3 | @post_id = post_id 4 | @rules = {} 5 | $(".button-submit-comment").on "click", => 6 | @submitComment() 7 | return false 8 | @loadComments("noanim", cb) 9 | @autoExpand $(".comment-textarea") 10 | 11 | $(".certselect").on "click", => 12 | if Page.server_info.rev < 160 13 | Page.cmd "wrapperNotification", ["error", "Comments requires at least ZeroNet 0.3.0 Please upgade!"] 14 | else 15 | Page.cmd "certSelect", [["zeroid.bit", "zeroverse.bit"]] 16 | return false 17 | 18 | 19 | loadComments: (type="show", cb=false) -> 20 | query = "SELECT comment.*, json_content.json_id AS content_json_id, keyvalue.value AS cert_user_id, json.directory, 21 | (SELECT COUNT(*) FROM comment_vote WHERE comment_vote.comment_uri = comment.comment_id || '@' || json.directory)+1 AS votes 22 | FROM comment 23 | LEFT JOIN json USING (json_id) 24 | LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') 25 | LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') 26 | WHERE post_id = #{@post_id} ORDER BY date_added DESC" 27 | 28 | Page.cmd "dbQuery", query, (comments) => 29 | $(".comments-num").text(comments.length) 30 | for comment in comments 31 | user_address = comment.directory.replace("users/", "") 32 | comment_address = "#{comment.comment_id}_#{user_address}" 33 | elem = $("#comment_"+comment_address) 34 | if elem.length == 0 # Create if not exits 35 | elem = $(".comment.template").clone().removeClass("template").attr("id", "comment_"+comment_address).data("post_id", @post_id) 36 | if type != "noanim" 37 | elem.cssSlideDown() 38 | $(".reply", elem).on "click", (e) => # Reply link 39 | return @buttonReply $(e.target).parents(".comment") 40 | @applyCommentData(elem, comment) 41 | elem.appendTo(".comments") 42 | setTimeout (-> 43 | Page.addInlineEditors(".comments") 44 | ), 1000 45 | 46 | 47 | applyCommentData: (elem, comment) -> 48 | [user_name, cert_domain] = comment.cert_user_id.split("@") 49 | user_address = comment.directory.replace("users/", "") 50 | $(".comment-body", elem).html Text.renderMarked(comment.body, {"sanitize": true}) 51 | $(".user_name", elem).text(user_name).css("color": Text.toColor(comment.cert_user_id)).attr("title", "#{user_name}@#{cert_domain}: #{user_address}") 52 | $(".added", elem).text(Time.since(comment.date_added)).attr("title", Time.date(comment.date_added, "long")) 53 | #$(".cert_domain", elem).html("@#{cert_domain}").css("display", "none") 54 | # Add inline editor 55 | if user_address == Page.site_info.auth_address 56 | $(elem).attr("data-object", "Comment:#{comment.comment_id}").attr("data-deletable", "yes") 57 | $(".comment-body", elem).attr("data-editable", "body").data("content", comment.body) 58 | 59 | 60 | buttonReply: (elem) -> 61 | @log "Reply to", elem 62 | user_name = $(".user_name", elem).text() 63 | post_id = elem.attr("id") 64 | body_add = "> [#{user_name}](\##{post_id}): " 65 | elem_quote = $(".comment-body", elem).clone() 66 | $("blockquote", elem_quote).remove() # Remove other people's quotes 67 | body_add+= elem_quote.text().trim("\n").replace(/\n/g, "\n> ") 68 | body_add+= "\n\n" 69 | $(".comment-new .comment-textarea").val( $(".comment-new .comment-textarea").val()+body_add ) 70 | $(".comment-new .comment-textarea").trigger("input").focus() # Autosize 71 | return false 72 | 73 | 74 | submitComment: -> 75 | if not Page.site_info.cert_user_id # Not registered 76 | Page.cmd "wrapperNotification", ["info", "Please, select your account."] 77 | return false 78 | 79 | body = $(".comment-new .comment-textarea").val() 80 | if not body 81 | $(".comment-new .comment-textarea").focus() 82 | return false 83 | 84 | $(".comment-new .button-submit").addClass("loading") 85 | inner_path = "data/users/#{Page.site_info.auth_address}/data.json" 86 | Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => 87 | if data 88 | data = JSON.parse(data) 89 | else # Default data 90 | data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, "topic_vote": {} } 91 | 92 | data.comment.push { 93 | "comment_id": data.next_comment_id, 94 | "body": body, 95 | "post_id": @post_id, 96 | "date_added": Time.timestamp() 97 | } 98 | data.next_comment_id += 1 99 | json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) 100 | Page.writePublish inner_path, btoa(json_raw), (res) => 101 | $(".comment-new .button-submit").removeClass("loading") 102 | @loadComments() 103 | setTimeout (-> 104 | Page.loadLastcomments() 105 | ), 1000 106 | @checkCert("updaterules") 107 | @log "Writepublish result", res 108 | if res != false 109 | $(".comment-new .comment-textarea").val("") 110 | 111 | 112 | checkCert: (type) -> 113 | last_cert_user_id = $(".comment-new .user_name").text() 114 | if Page.site_info.cert_user_id 115 | $(".comment-new").removeClass("comment-nocert") 116 | $(".comment-new .user_name").text(Page.site_info.cert_user_id) 117 | else 118 | $(".comment-new").addClass("comment-nocert") 119 | $(".comment-new .user_name").text("Please sign in") 120 | 121 | if $(".comment-new .user_name").text() != last_cert_user_id or type == "updaterules" # User changed 122 | # Update used/allowed space 123 | if Page.site_info.cert_user_id 124 | Page.cmd "fileRules", "data/users/#{Page.site_info.auth_address}/content.json", (rules) => 125 | @rules = rules 126 | if rules.max_size 127 | @setCurrentSize(rules.current_size) 128 | else 129 | @setCurrentSize(0) 130 | else 131 | @setCurrentSize(0) 132 | 133 | 134 | setCurrentSize: (current_size) -> 135 | if current_size 136 | current_size_kb = current_size/1000 137 | $(".user-size").text("used: #{current_size_kb.toFixed(1)}k/#{Math.round(@rules.max_size/1000)}k") 138 | $(".user-size-used").css("width", Math.round(70*current_size/@rules.max_size)) 139 | else 140 | $(".user-size").text("") 141 | 142 | 143 | autoExpand: (elem) -> 144 | editor = elem[0] 145 | # Autoexpand 146 | if elem.height() > 0 then elem.height(1) 147 | 148 | elem.on "input", => 149 | if editor.scrollHeight > elem.height() 150 | old_height = elem.height() 151 | elem.height(1) 152 | new_height = editor.scrollHeight 153 | new_height += parseFloat elem.css("borderTopWidth") 154 | new_height += parseFloat elem.css("borderBottomWidth") 155 | new_height -= parseFloat elem.css("paddingTop") 156 | new_height -= parseFloat elem.css("paddingBottom") 157 | 158 | min_height = parseFloat(elem.css("lineHeight"))*2 # 2 line minimum 159 | if new_height < min_height then new_height = min_height+4 160 | 161 | elem.height(new_height-4) 162 | # Update used space 163 | if @rules.max_size 164 | if elem.val().length > 0 165 | current_size = @rules.current_size + elem.val().length + 90 166 | else 167 | current_size = @rules.current_size 168 | @setCurrentSize(current_size) 169 | if elem.height() > 0 then elem.trigger "input" 170 | else elem.height("48px") 171 | 172 | 173 | window.Comments = new Comments() -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GIFs 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 34 | ? Editing: Post:21.body Save Delete Cancel 35 |
36 | 37 | 38 | 39 | 40 |
41 | Content changed Sign & Publish new content 42 |
43 | 44 | 45 |
46 | Sections: 47 | DevopsReactions 48 | r/Gifs 49 | r/NSFW_gifs 50 |
51 | 52 |
53 | <DevopsReactions> 54 | Most liked 55 | Last commented 56 |
57 | 58 |
59 | r/gifs 60 | Most liked 61 | Last commented 62 |
63 | 64 |
65 | r/nsfw_gifs 66 | Most liked 67 | Last commented 68 |
69 | 70 | 71 |
72 |
73 |

74 |

75 |
76 | 77 | 78 | 79 |
80 |

Latest comments:

81 |
    82 |
  • 83 | nofish: 84 | WebGL under linux can be problematic, here is some tips... 85 |
    86 | @ 87 | Changelog, August 16, 2015 88 |
    89 |
  • 90 |
91 |
92 | 93 | 94 |
95 | 96 | 97 | 98 | 99 |
100 | 101 | 102 | 103 |
104 | Add new post 105 | 106 | 107 |
108 |
109 |

Title

110 | 21 hours ago · 2 min read 111 |  
3 comments

112 | 115 | 119 |
120 |
121 | Read more 122 |
123 |
124 | 125 | 126 |
127 | 128 | 129 | 130 |
131 |
132 |

Title

133 | 21 hours ago · 2 min read
134 | 135 |
136 | 137 |
138 | 139 |

0 Comments:

140 | 141 |
142 |
143 | Please sign in 144 | ━ 145 | new comment 146 |
147 |
148 |
Sign in as...
149 | 150 | Submit comment 151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | 159 | 160 |
161 | 162 |
163 |
164 | user_name 165 | 166 | ━ 167 | 1 day ago 168 |
Reply
169 |
170 |
Body
171 |
172 | 173 |
174 |
175 | 176 | 177 | 178 |
179 | 180 | 181 |
182 | 183 | 184 |
185 | 186 | 187 | 188 |
189 | 190 |
Sources: DevOps Reactions, The coding love
191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /updater/updater.py: -------------------------------------------------------------------------------- 1 | import urllib2 2 | import re 3 | import os 4 | import json 5 | import time 6 | import subprocess 7 | import sys 8 | 9 | opener = urllib2.build_opener() 10 | opener.addheaders = [('User-agent', 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.41 Safari/537.36')] 11 | 12 | class Updater: 13 | def __init__(self): 14 | pass 15 | 16 | def getVideos(self, pages=1): 17 | videos = [] 18 | for page in range(pages): 19 | videos += self.getPage(page) 20 | return videos 21 | 22 | 23 | class Devopreactions(Updater): 24 | def getPage(self, page=0): 25 | print "- Getting Devopreactions page %s..." % page 26 | data = opener.open("http://devopsreactions.tumblr.com/page/%s" % page).read() 27 | open("temp/devopsreactions.html", "w").write(data) 28 | # data = open(".temp/devopsreactions.html").read() 29 | videos = [] 30 | for part in data.split('item_content'): 31 | match = re.match(""".*?post_title.*?href.*?>(?P.*?)</a>.*?src=["'](?P<gif_url>.*?gif)["']""", part, re.DOTALL) 32 | if match: 33 | video = match.groupdict() 34 | video["source"] = "DevopsReactions" 35 | videos.append(video) 36 | return videos 37 | 38 | 39 | class RedditGifs(Updater): 40 | def getPage(self, page=0): 41 | if page > 0: return [] 42 | print "- Getting RedditGifs page %s..." % page 43 | data = json.load(opener.open("https://www.reddit.com/r/gifs/hot.json")) 44 | videos = [] 45 | for row in data["data"]["children"]: 46 | if not row: continue 47 | if "reddit.com/" in row["data"]["url"]: continue 48 | 49 | if row["data"]["score"] > 4000: 50 | video = {"title": row["data"]["title"], "source": "RedditGifs"} 51 | url = row["data"]["url"] 52 | if url.endswith(".mp4"): 53 | video["gif_url"] = url 54 | elif url.endswith(".gifv"): 55 | video["gif_url"] = url.replace(".gifv", ".mp4") 56 | elif url.endswith(".gif") and "imgur.com" in url: 57 | video["gif_url"] = url.replace(".gif", ".mp4") 58 | elif "gfycat.com" in url and not url.endswith(".mp4"): 59 | video["gif_url"] = url.replace("www.gfycat.com", "zippy.gfycat.com")+".mp4" 60 | else: 61 | video["gif_url"] = url 62 | videos.append(video) 63 | return videos 64 | 65 | 66 | class RedditNsfwGifs(Updater): 67 | def getPage(self, page=0): 68 | if page > 0: return [] 69 | print "- Getting RedditNsfwGifs page %s..." % page 70 | data = json.load(opener.open("https://www.reddit.com/r/nsfw_gifs/hot.json")) 71 | videos = [] 72 | for row in data["data"]["children"]: 73 | if not row: continue 74 | if "reddit.com/" in row["data"]["url"]: continue 75 | 76 | if row["data"]["score"] > 200: 77 | video = {"title": row["data"]["title"], "source": "RedditNsfwGifs"} 78 | url = row["data"]["url"] 79 | if url.endswith(".mp4"): 80 | video["gif_url"] = url 81 | elif url.endswith(".gifv"): 82 | video["gif_url"] = url.replace(".gifv", ".mp4") 83 | elif url.endswith(".gif") and "imgur.com" in url: 84 | video["gif_url"] = url.replace(".gif", ".mp4") 85 | else: 86 | video["gif_url"] = url 87 | videos.append(video) 88 | return videos 89 | 90 | 91 | def download(gif_url, gif_path): 92 | req = urllib2.Request(gif_url) 93 | req.add_header("Referer", gif_url) 94 | data = urllib2.urlopen(req).read() 95 | if "<html" in data: 96 | new_url = re.search("(http[s]{0,1}://[^\"\']*?mp4)[\"']", data).group(1) 97 | print "- Html source, redirecting to %s" % new_url 98 | req = urllib2.Request(new_url) 99 | req.add_header("Referer", new_url) 100 | data = urllib2.urlopen(new_url).read() 101 | 102 | open(gif_path, "wb").write(data) 103 | return gif_path 104 | 105 | 106 | def convertToMp4(gif_path, mp4_path): 107 | if os.path.isfile(mp4_path): 108 | os.unlink(mp4_path) 109 | cmd = [ 110 | "ffmpeg/bin/ffmpeg", 111 | "-i", gif_path, 112 | "-c:v", "libx264", 113 | "-crf", "28", 114 | "-pix_fmt", "yuv420p", 115 | "-movflags", "faststart", 116 | "-force_key_frames", "00:00:00.100", 117 | "-filter:v", "scale=trunc(iw/2)*2:trunc(ih/2)*2", 118 | "-vf", "scale=floor(iw*min(1\,if(gt(iw\,ih)\,600/iw\,(500*sar)/ih))/2)*2:(floor((ow/dar)/2))*2", 119 | mp4_path 120 | ] 121 | process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 122 | output = process.stderr.read() 123 | size_match = re.search("Video: h264.*, ([0-9]+)x([0-9]+)", output) 124 | if size_match: 125 | return size_match.groups() 126 | else: 127 | print output 128 | return False 129 | 130 | 131 | def getMp4Dimensions(mp4_path): 132 | raise "Unsupported yet" 133 | 134 | 135 | def updateSites(): 136 | added = 0 137 | data = json.load(open("../data/data.json")) 138 | titles = [post["title"] for post in data["post"]] 139 | 140 | sites = [RedditGifs(), Devopreactions(), RedditNsfwGifs()] 141 | videos = [] 142 | for site in sites: 143 | videos += site.getVideos(pages=3) 144 | 145 | source_added = {} 146 | 147 | for video in reversed(videos): 148 | if video["source"] in source_added: 149 | print "! Only one video per section per update" 150 | continue 151 | 152 | if video["title"] in titles: 153 | print "* Already exist, skipping: %s / %s" % (video["source"], video["title"]) 154 | continue 155 | if video.get("mp4_url"): 156 | raise "Unsupported" 157 | """print "- Downloading MP4 %s..." % video["mp4_url"] 158 | mp4_path = download(video["mp4_url"], "temp/last.mp4") 159 | print "- Getting dimensions of %s..." % mp4_path 160 | video_size = getMp4Dimensions(mp4_path)""" 161 | else: 162 | print "- Downloading GIF %s..." % video["gif_url"] 163 | try: 164 | gif_path = download(video["gif_url"], "temp/last.gif") 165 | except Exception, err: 166 | print "Error downloading video: %s, skipping" % err 167 | continue 168 | mp4_path = "../data/mp4-%s/%s.mp4" % (video["source"].lower(), data["next_post_id"]) 169 | print "- Converting %s to %s..." % (gif_path, mp4_path) 170 | video_size = convertToMp4(gif_path, mp4_path) 171 | 172 | if not os.path.isfile(mp4_path): 173 | print "! Converting failed, skipping" 174 | continue 175 | 176 | if os.path.getsize(mp4_path) > 1024*1024 or os.path.getsize(mp4_path) < 30: 177 | print "! Too large or too small, skipping: %s" % os.path.getsize(mp4_path) 178 | os.unlink(mp4_path) 179 | continue 180 | 181 | if video_size: 182 | # 90 158 183 | print "- Adding to data.json..." 184 | width, height = map(float, video_size) 185 | width_resized = 600 186 | height_resized = round(height * (600.0 / width)) 187 | if height_resized > 500: 188 | height_resized = 500 189 | width_resized = round(width * (500.0 / height)) 190 | 191 | post = { 192 | "post_id": data["next_post_id"], 193 | "title": video["title"], 194 | "date_published": time.time(), 195 | "source": video["source"], 196 | "body": 197 | '<video src="data/mp4-%s/%s.mp4" width=%.0f height=%.0f loop muted preload="auto"></video>' % 198 | (video["source"].lower(), data["next_post_id"], width_resized, height_resized) 199 | } 200 | data["post"].insert(0, post) 201 | data["next_post_id"] += 1 202 | json.dump(data, open("../data/data.json", "w"), indent=1) 203 | titles.append(video["title"]) 204 | 205 | source_added[video["source"]] = True 206 | added += 1 207 | else: 208 | print "! Error converting gif to mp4" 209 | continue 210 | return added 211 | 212 | if __name__ == "__main__": 213 | added = updateSites() 214 | if added: 215 | os.chdir(sys.argv[1]) 216 | os.system("python zeronet.py siteSign 1Gif7PqWTzVWDQ42Mo7np3zXmGAo3DXc7h --publish") 217 | print "Done, added: %s" % added 218 | #download("http://imgur.com/CDWxtM7", "temp/last.gif") 219 | #print convertToMp4("temp/last.gif", "temp/last.mp4") 220 | -------------------------------------------------------------------------------- /css/Gifs.css: -------------------------------------------------------------------------------- 1 | /* Design based on medium */ 2 | 3 | body { color: #FFF; margin: 10px; padding: 0px; font-family: 'Roboto', sans-serif; height: 15000px; overflow: hidden; backface-visibility: hidden; #191A1F; background: #2D2E32 url(../img/bg.png) no-repeat; } 4 | body.loaded { height: auto; overflow: auto } 5 | h1, h2, h3, h4 { font-family: 'Roboto', sans-serif; font-weight: normal; margin: 0px; padding: 0px } 6 | h1 { font-size: 24px; line-height: 1.2em; font-weight: lighter; letter-spacing: -0.5px; margin-bottom: 5px; color: #FFDB00 } 7 | h2 { margin-top: 2em } 8 | h3 { font-size: 24px; margin-top: 2em } 9 | h1 + h2, h2 + h3 { margin-top: inherit } 10 | 11 | p { margin-top: 0.9em; margin-bottom: 0.9em } 12 | hr { margin: 20px 0px; border: none; border-bottom: 1px solid #eee; margin-left: auto; margin-right: auto; width: 120px; } 13 | small { font-size: 80%; color: #999; } 14 | 15 | a { border-bottom: 1px solid #3498db; text-decoration: none; color: white; font-weight: bold } 16 | a.nolink { border-bottom: none } 17 | a:hover { color: #FFF } 18 | 19 | .button { 20 | padding: 5px 10px; margin-left: 10px; background-color: #FFDA00; border-bottom: 2px solid #999998; background-position: left center; 21 | border-radius: 0px; text-decoration: none; transition: all 0.5s ease-out; color: #333 22 | } 23 | .button:hover { background-color: #fff400; border-color: #fff400; border-bottom: 2px solid #fff400; transition: none; color: black } 24 | .button:active { position: relative; top: 1px } 25 | 26 | /*.button-delete { background-color: #e74c3c; border-bottom-color: #A83F34; color: white }*/ 27 | .button-outline { background-color: transparent; color: #464545; border: 1px solid #3A3A3A; } 28 | 29 | .button-delete:hover { background-color: #FF5442; border: 1px solid #FF5442; color: white } 30 | .button-ok:hover { background-color: #27AE60; border: 1px solid #27AE60; color: white } 31 | 32 | .button.loading { 33 | color: rgba(0,0,0,0); background: #999 url(../img/loading.gif) no-repeat center center; 34 | transition: all 0.5s ease-out; pointer-events: none; border-bottom: 2px solid #666 35 | } 36 | 37 | .cancel { margin-left: 10px; font-size: 80%; color: #999; } 38 | 39 | 40 | .template { display: none } 41 | 42 | /* Editable */ 43 | .editable { outline: none } 44 | .editable-edit:hover { opacity: 1; border: none; color: #333 } 45 | .editable-edit { 46 | opacity: 0; float: left; margin-top: 0px; margin-left: -40px; padding: 8px 20px; transition: all 0.3s; width: 0px; display: inline-block; padding-right: 0px; 47 | color: rgba(100,100,100,0.5); text-decoration: none; font-size: 18px; font-weight: normal; border: none; 48 | } 49 | /*.editing { white-space: pre-wrap; z-index: 1; position: relative; outline: 10000px solid rgba(255,255,255,0.9) !important; } 50 | .editing p { margin: 0px; padding: 0px }*/ /* IE FIX */ 51 | .editor { width: 100%; display: block; overflow :hidden; resize: none; border: none; box-sizing: border-box; z-index: 900; position: relative } 52 | .editor:focus { border: 0px; outline-offset: 0px } 53 | 54 | 55 | /* -- Editbar -- */ 56 | 57 | .bottombar { 58 | display: none; position: fixed; padding: 10px 20px; opacity: 0; background-color: rgba(255,255,255,0.9); 59 | right: 30px; bottom: 0px; z-index: 999; transition: all 0.3s; transform: translateY(50px) 60 | } 61 | .bottombar.visible { transform: translateY(0px); opacity: 1 } 62 | .publishbar { z-index: 990} 63 | .publishbar.visible { display: inline-block; } 64 | .editbar { perspective: 900px } 65 | .markdown-help { 66 | position: absolute; bottom: 30px; transform: translateX(0px) rotateY(-40deg); transform-origin: right; right: 0px; 67 | list-style-type: none; background-color: rgba(255,255,255,0.9); padding: 10px; opacity: 0; transition: all 0.6s; display: none 68 | } 69 | .markdown-help.visible { transform: none; opacity: 1 } 70 | .markdown-help li { margin: 10px 0px } 71 | .markdown-help code { font-size: 100% } 72 | .icon-help { border: 1px solid #EEE; padding: 2px; display: inline-block; width: 17px; text-align: center; font-size: 13px; margin-right: 6px; vertical-align: 1px } 73 | .icon-help.active { background-color: #EEE } 74 | 75 | /* -- Head -- */ 76 | 77 | .sections { position: absolute; top: 0px; display: block; padding: 9px 0px; font-size: 13px; background-color: #171719; width: 100%; left: 0px; padding-left: 15px; box-sizing: border-box; } 78 | .sections a { margin-left: 0px; margin-left: 20px; margin-right: 15px; border: 0px; border-bottom: 2px solid #FFDB00; opacity: 0.8; transition: opacity 0.6s } 79 | .sections a:hover { opacity: 1; transition: 0s } 80 | .sections a:before { content: "\2022"; color: #FFDB00; display: inline-block; position: absolute; margin-left: -13px; font-size: 22px; line-height: 24px; } 81 | .sections a.section-RedditGifs { border-bottom-color: #86E7F5; } 82 | .sections a.section-RedditGifs:before { color: #86E7F5; } 83 | .sections a.section-RedditNsfwGifs { border-bottom-color: #FB754D; } 84 | .sections a.section-RedditNsfwGifs:before { color: #FB754D; } 85 | 86 | .head { font-size: 30px; left: 0%; position: absolute; top: 60px; display: none } 87 | .head a { font-family: Monospace; font-weight: normal; border: 0px; color: black; transition: 1s; background-color: #FFDB00; padding: 5px 20px; display: block; float: left } 88 | .head a:hover, .head-link.active { background-color: #FFFA22; color: black; transition: 0s } 89 | .head .head-link { 90 | box-shadow: inset 15px 0px 55px -40px black; font-size: 14px; font-family: Roboto; text-decoration: underline; 91 | line-height: 36px; font-weight: bold; text-transform: uppercase; color: rgba(0, 0, 0, 0.83); 92 | } 93 | .page-DevopsReactions .head-DevopsReactions { display: inline-block; } 94 | .page-DevopsReactions .section-DevopsReactions { opacity: 1 } 95 | 96 | .page-RedditGifs .head-RedditGifs { display: inline-block; } 97 | .page-RedditGifs .head-RedditGifs a { background-color: #86E7F5 } 98 | .page-RedditGifs .head-RedditGifs a:hover { background-color: #4DDBFF } 99 | .page-RedditGifs .post .title a { color: #86E7F5 } 100 | .page-RedditGifs .section-RedditGifs { opacity: 1 } 101 | 102 | .page-RedditNsfwGifs .head-RedditNsfwGifs { display: inline-block; } 103 | .page-RedditNsfwGifs .head-RedditNsfwGifs a { background-color: #FB754D } 104 | .page-RedditNsfwGifs .head-RedditNsfwGifs a:hover { background-color: #FC9D4F } 105 | .page-RedditNsfwGifs .post .title a { color: #FB754D } 106 | .page-RedditNsfwGifs .section-RedditNsfwGifs { opacity: 1 } 107 | 108 | /* -- Left -- */ 109 | 110 | .left { float: left; position: absolute; width: 220px; padding-left: 20px; padding-right: 20px; margin-top: 60px; text-align: right; display: none } 111 | .right { float: left; padding-left: 30px; margin-left: 0px; max-width: 1000px; padding-right: 60px; padding-top: 160px } 112 | 113 | .left .avatar { 114 | background-color: #F0F0F0; width: 60px; height: 60px; border-radius: 100%; margin-bottom: 10px; 115 | background-position: center center; background-size: 70%; display: inline-block; image-rendering: pixelated; 116 | } 117 | .left h1 a.nolink { font-family: Tinos; display: inline-block; padding: 1px } 118 | .left h1 a.editable-edit { float: none } 119 | .left h2 { font-size: 15px; font-family: Tinos; line-height: 1.6em; color: #AAA; margin-top: 14px; letter-spacing: 0.2px } 120 | .left ul, .left li { padding: 0px; margin: 0px; list-style-type: none; line-height: 2em } 121 | .left hr { margin-left: 100px; margin-right: 0px; width: auto } 122 | /*.left .links { width: 230px; margin-left: -60px }*/ 123 | .left .links.editor { text-align: left !important } 124 | 125 | .lastcomments { background-color: #FAFAFA; margin-left: -25px; padding-right: 15px; padding-bottom: 5px; padding-top: 3px; margin-top: 40px; margin-right: -15px; display: none } 126 | .lastcomments h3 { margin-top: 20px; } 127 | .lastcomments .lastcomment { font-size: 85%; margin-top: 20px; margin-bottom: 30px; margin-left: 20px; } 128 | .lastcomments .lastcomment .user_name { font-weight: bold; } 129 | .lastcomments .lastcomment .details { font-size: 90%; } 130 | .lastcomments .lastcomment .details .at { color: #999 } 131 | .lastcomments .lastcomment .details .postlink { border-bottom: 1px solid #BBB; } 132 | 133 | /* -- Post -- */ 134 | 135 | .posts .new { display: none; position: absolute; top: -50px; margin-left: 310px; left: 50%; transform: translateX(-50%); } 136 | 137 | .posts, .post-full { display: none; position: relative; } 138 | .page-main .posts { display: block } 139 | .page-post.loaded .post-full { display: block; border-bottom: none } 140 | 141 | 142 | .post { margin-bottom: 50px; padding-bottom: 50px; border-bottom: 0px solid #eee; min-width: 500px; width: 1100px } 143 | .post .title a { text-decoration: none; color: inherit; display: inline-block; border-bottom: none; font-weight: inherit; #fff400; clear: both; } 144 | .post h1 { width: 100% } 145 | .post .details { color: #BBB; margin-top: 5px; margin-bottom: 20px; margin-right: 20px; margin-left: -20px; width: 500px; float: left; font-size: 10px; text-align: right } 146 | .post .details .published { text-transform: uppercase; font-weight: bold; color: #5A5D62; } 147 | .post .details .comments-num { border: none; color: #BBB; font-weight: normal; } 148 | .post .details .comments-num .num { border-bottom: 1px solid #35373C } 149 | .post .details .comments-num:hover .num { border-bottom: 1px solid #95979C; } 150 | 151 | .post .details .like { 152 | padding: 5px 5px; margin-top: 12px; font-weight: normal; transition: all 0.3s; 153 | display: block; clear: both; float: right; border: 1px solid #3A3A3A; padding: 11px 15px; border-radius: 0px; 154 | } 155 | .post .details .like .icon-heart:before, .post .details .like .icon-heart:after { background-color: #464545; transition: all 0.3s } 156 | .post .details .like .icon-heart { transform-style: preserve-3d; transition: all 0.3s } 157 | .post .details .like:hover .icon-heart { transform: scale(1.1) } 158 | .post .details .like:hover .icon-heart:before, .post .details .like:hover .icon-heart:after { background-color: #088FD3 } 159 | .post .details .like.active .icon-heart:before, .post .details .like.active .icon-heart:after { background-color: #088FD3 } 160 | .post .details .like.active .icon-heart-anim { transform: scale(1.8); opacity: 0; transition: all 1s ease-in-out } 161 | .post .details .like .num { margin-left: 25px; color: #CCC; transition: all 0.3s; font-size: 15px } 162 | .post .details .like:hover { border-color: #088FD3; transition: 0s } 163 | .post .details .like:hover .num { } 164 | .post .details .like.loading { pointer-events: none; animation: bounce .3s infinite alternate ease-out; animation-delay: 1s; } 165 | 166 | .post .body { font-size: 21.5px; line-height: 1.6; font-family: Tinos; float: left; max-width: 600px } 167 | .post .body video { box-shadow: 0px 7px 20px 1px rgba(0,0,0,0.5); transition: 0.3s all; max-height: 500px; background-color: black; } 168 | .post .body video.playing { opacity: 1 } 169 | .post .body video { opacity: 0.6 } 170 | 171 | .post .body h1 { text-align: center; margin-top: 50px } 172 | .post .body h1:before { content: " "; border-top: 1px solid #EEE; width: 120px; display: block; margin-left: auto; margin-right: auto; margin-bottom: 50px; } 173 | 174 | .post .body p + ul { margin-top: -0.5em } 175 | .post .body li { margin-top: 0.5em; margin-bottom: 0.5em } 176 | .post .body hr:first-of-type { display: none } 177 | 178 | .post .body a img { margin-bottom: -8px } 179 | .post .body img { max-width: 100% } 180 | 181 | code { 182 | background-color: #f5f5f5; border: 1px solid #ccc; padding: 0px 5px; overflow: auto; border-radius: 2px; display: inline-block; 183 | color: #444; font-weight: normal; font-size: 60%; vertical-align: text-bottom; border-bottom-width: 2px; 184 | } 185 | .post .body pre { table-layout: fixed; width: auto; display: table; white-space: normal; } 186 | .post .body pre code { padding: 10px 20px; white-space: pre; max-width: 850px } 187 | 188 | blockquote { border-left: 3px solid #333; margin-left: 0px; padding-left: 1em } 189 | /*.post .more { 190 | display: inline-block; border: 1px solid #eee; padding: 10px 25px; border-radius: 26px; font-size: 11px; color: #AAA; font-weight: normal; 191 | left: 50%; position: relative; transform: translateX(-50%); 192 | }*/ 193 | 194 | .post .more { border: 2px solid #333; padding: 10px 20px; font-size: 15px; margin-top: 30px; display: none; transition: all 0.3s } 195 | .post .more .readmore { } 196 | .post .more:hover { color: white; box-shadow: inset 150px 0px 0px 0px #333; } 197 | .post .more:active { color: white; box-shadow: inset 150px 0px 0px 0px #FFDA00; transition: none; border-color: #FFDA00 } 198 | 199 | .pager { margin-bottom: 200px; padding-left: 600px } 200 | .pager a { border: 2px solid #333; padding: 10px 20px; font-size: 15px; display: none; transition: all 0.3s } 201 | .pager a:hover { color: white; box-shadow: inset 150px 0px 0px 0px #333; } 202 | .pager a:active { color: black; box-shadow: inset 150px 0px 0px 0px #FFDA00; transition: none; border-color: #FFDA00 } 203 | .pager .next { float: right } 204 | .pager .prev:hover { box-shadow: inset -150px 0px 0px 0px #333; } 205 | .pager .prev:active { box-shadow: inset -150px 0px 0px 0px #FFDA00; } 206 | 207 | .foot { text-align: right; font-size: 80%; opacity: 0.5; display: none } 208 | 209 | /* Score */ 210 | /* 211 | .score { 212 | border: 1px solid #eee; margin-right: 5px; margin-left: 5px; padding: 2px 7px; border-radius: 10px; color: #AAA; background-position: left center; font-weight: bold; font-size: 12px; 213 | display: inline-block; height: 1.2em; overflow: hidden; vertical-align: -0.5em; line-height: 7px; transition: background-color 0.3s; height: 20px; box-sizing: border-box 214 | } 215 | .score-active, .score-inactive { transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); display: inline-block; margin-top: 3px } 216 | .score-active { display: block; margin-top: 5px } 217 | .score.active { background-color: #2ecc71; color: white } 218 | .score.loading { 219 | color: rgba(255,255,255,0) !important; background: #2ecc71 url(../img/loading.gif) no-repeat center center; 220 | transition: background 0.5s ease-out; pointer-events: none; border: 1px solid #2ecc71 221 | } 222 | 223 | .score.active .score-inactive, .score:hover .score-inactive { transform: translateY(-14px); opacity: 0.5 } 224 | .score.active .score-active, .score:hover .score-active { transform: translateY(-14px) } 225 | .score:hover, .score.active { border: 1px solid #2ecc71; color: #AAA; } 226 | 227 | .score:active { border: 1px solid #AAA !important; transform: translateY(1px) } 228 | 229 | .noscore .score-inactive .icon-up { margin-left: 6px } 230 | .noscore .score-inactive .score-num { display: none } 231 | */ 232 | 233 | @keyframes bounce { 234 | 0% { transform: translateY(0); } 235 | 100% { transform: translateY(-3px); } 236 | } 237 | -------------------------------------------------------------------------------- /js/lib/marked.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * marked - a markdown parser 3 | * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) 4 | * https://github.com/chjj/marked 5 | */ 6 | (function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",/<!--[\s\S]*?-->/)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,paragraph:/^/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1","\\2")+"|"+block.list.source.replace("\\1","\\3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].split(/ *\| */)}this.tokens.push(item);continue}if(cap=this.rules.lheading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[2]==="="?1:2,text:cap[1]});continue}if(cap=this.rules.hr.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"hr"});continue}if(cap=this.rules.blockquote.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"blockquote_start"});cap=cap[0].replace(/^ *> ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i<l;i++){item=cap[i];space=item.length;item=item.replace(/^ *([*+-]|\d+\.) +/,"");if(~item.indexOf("\n ")){space-=item.length;item=!this.options.pedantic?item.replace(new RegExp("^ {1,"+space+"}","gm"),""):item.replace(/^ {1,4}/gm,"")}if(this.options.smartLists&&i!==l-1){b=block.bullet.exec(cap[i+1])[0];if(bull!==b&&!(bull.length>1&&b.length>1)){src=cap.slice(i+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:cap[1]==="pre"||cap[1]==="script"||cap[1]==="style",text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].replace(/^ *\| *| *\| *$/g,"").split(/ *\| */)}this.tokens.push(item);continue}if(top&&(cap=this.rules.paragraph.exec(src))){src=src.substring(cap[0].length);this.tokens.push({type:"paragraph",text:cap[1].charAt(cap[1].length-1)==="\n"?cap[1].slice(0,-1):cap[1]});continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"text",text:cap[0]});continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return this.tokens};var inline={escape:/^\\([\\`*{}\[\]()#+\-.!_>])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/};inline._inside=/(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;inline._href=/\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^<a /i.test(cap[0])){this.inLink=true}else if(this.inLink&&/^<\/a>/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=escape(this.smartypants(cap[0]));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/--/g,"—").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){var out="",l=text.length,i=0,ch;for(;i<l;i++){ch=text.charCodeAt(i);if(Math.random()>.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"<pre><code>"+(escaped?code:escape(code,true))+"\n</code></pre>"}return'<pre><code class="'+this.options.langPrefix+escape(lang,true)+'">'+(escaped?code:escape(code,true))+"\n</code></pre>\n"};Renderer.prototype.blockquote=function(quote){return"<blockquote>\n"+quote+"</blockquote>\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"<h"+level+' id="'+this.options.headerPrefix+raw.toLowerCase().replace(/[^\w]+/g,"-")+'">'+text+"</h"+level+">\n"};Renderer.prototype.hr=function(){return this.options.xhtml?"<hr/>\n":"<hr>\n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+"</"+type+">\n"};Renderer.prototype.listitem=function(text){return"<li>"+text+"</li>\n"};Renderer.prototype.paragraph=function(text){return"<p>"+text+"</p>\n"};Renderer.prototype.table=function(header,body){return"<table>\n"+"<thead>\n"+header+"</thead>\n"+"<tbody>\n"+body+"</tbody>\n"+"</table>\n"};Renderer.prototype.tablerow=function(content){return"<tr>\n"+content+"</tr>\n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"</"+type+">\n"};Renderer.prototype.strong=function(text){return"<strong>"+text+"</strong>"};Renderer.prototype.em=function(text){return"<em>"+text+"</em>"};Renderer.prototype.codespan=function(text){return"<code>"+text+"</code>"};Renderer.prototype.br=function(){return this.options.xhtml?"<br/>":"<br>"};Renderer.prototype.del=function(text){return"<del>"+text+"</del>"};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0){return""}}var out='<a href="'+href+'"';if(title){out+=' title="'+title+'"'}out+=">"+text+"</a>";return out};Renderer.prototype.image=function(href,title,text){var out='<img src="'+href+'" alt="'+text+'"';if(title){out+=' title="'+title+'"'}out+=this.options.xhtml?"/>":">";return out};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="\n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i<this.token.header.length;i++){flags={header:true,align:this.token.align[i]};cell+=this.renderer.tablecell(this.inline.output(this.token.header[i]),{header:true,align:this.token.align[i]})}header+=this.renderer.tablerow(cell);for(i=0;i<this.token.cells.length;i++){row=this.token.cells[i];cell="";for(j=0;j<row.length;j++){cell+=this.renderer.tablecell(this.inline.output(row[j]),{header:false,align:this.token.align[j]})}body+=this.renderer.tablerow(cell)}return this.renderer.table(header,body)}case"blockquote_start":{var body="";while(this.next().type!=="blockquote_end"){body+=this.tok()}return this.renderer.blockquote(body)}case"list_start":{var body="",ordered=this.token.ordered;while(this.next().type!=="list_end"){body+=this.tok()}return this.renderer.list(body,ordered)}case"list_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.token.type==="text"?this.parseText():this.tok()}return this.renderer.listitem(body)}case"loose_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.tok()}return this.renderer.listitem(body)}case"html":{var html=!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;return this.renderer.html(html)}case"paragraph":{return this.renderer.paragraph(this.inline.output(this.token.text))}case"text":{return this.renderer.paragraph(this.parseText())}}};function escape(html,encode){return html.replace(!encode?/&(?!#?\w+;)/g:/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function unescape(html){return html.replace(/&([#\w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;i<arguments.length;i++){target=arguments[i];for(key in target){if(Object.prototype.hasOwnProperty.call(target,key)){obj[key]=target[key]}}}return obj}function marked(src,opt,callback){if(callback||typeof opt==="function"){if(!callback){callback=opt;opt=null}opt=merge({},marked.defaults,opt||{});var highlight=opt.highlight,tokens,pending,i=0;try{tokens=Lexer.lex(src,opt)}catch(e){return callback(e)}pending=tokens.length;var done=function(err){if(err){opt.highlight=highlight;return callback(err)}var out;try{out=Parser.parse(tokens,opt)}catch(e){err=e}opt.highlight=highlight;return err?callback(err):callback(null,out)};if(!highlight||highlight.length<3){return done()}delete opt.highlight;if(!pending)return done();for(;i<tokens.length;i++){(function(token){if(token.type!=="code"){return--pending||done()}return highlight(token.text,token.lang,function(err,code){if(err)return done(err);if(code==null||code===token.text){return--pending||done()}token.text=code;token.escaped=true;--pending||done()})})(tokens[i])}return}try{if(opt)opt=merge({},marked.defaults,opt);return Parser.parse(Lexer.lex(src,opt),opt)}catch(e){e.message+="\nPlease report this to https://github.com/chjj/marked.";if((opt||marked.defaults).silent){return"<p>An error occured:</p><pre>"+escape(e.message+"",true)+"</pre>"}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof module!=="undefined"&&typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}()); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/> 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /js/Gifs.coffee: -------------------------------------------------------------------------------- 1 | class ZeroBlog extends ZeroFrame 2 | init: -> 3 | @data = {} 4 | @site_info = null 5 | @server_info = null 6 | @page = 1 7 | @orderby = null 8 | @section = "DevopsReactions" 9 | @sql_orderby = "date_published DESC" 10 | @my_post_votes = {} 11 | @lazy_videos = [] 12 | 13 | @event_page_load = $.Deferred() 14 | @event_site_info = $.Deferred() 15 | 16 | # Editable items on own site 17 | $.when(@event_page_load, @event_site_info).done => 18 | if @site_info.settings.own or @data.demo 19 | @addInlineEditors() 20 | @checkPublishbar() 21 | $(".publishbar").on "click", @publish 22 | $(".posts .button.new").css("display", "inline-block") 23 | $(".editbar .icon-help").on "click", => 24 | $(".editbar .markdown-help").css("display", "block") 25 | $(".editbar .markdown-help").toggleClassLater("visible", 10) 26 | $(".editbar .icon-help").toggleClass("active") 27 | return false 28 | $(window).on "scroll", => 29 | RateLimit(200, @checkLazyVideos) 30 | 31 | 32 | $(".foot").css("display", "block") 33 | 34 | $.when(@event_site_info).done => 35 | @log "event site info" 36 | # Set avatar 37 | imagedata = new Identicon(@site_info.address, 70).toString(); 38 | $("body").append("<style>.avatar { background-image: url(data:image/png;base64,#{imagedata}) }</style>") 39 | 40 | @log "inited!" 41 | 42 | addLazyVideos: => 43 | @lazy_videos = [] 44 | for video in $("video") 45 | video = $(video) 46 | video.off("click").on "click", -> 47 | if this.paused 48 | this.play() 49 | else 50 | this.pause() 51 | @lazy_videos.push([video, video.offset().top, video.innerHeight()]) 52 | 53 | checkLazyVideos: => 54 | scroll_y = $(window).scrollTop() 55 | inner_h = window.innerHeight 56 | if scroll_y == 0 # Always play the first video on top scroll 57 | center = 0 58 | else 59 | center = scroll_y + inner_h/2 60 | closest_video = null 61 | closest_distance = 1000 62 | # Only play one video that is closest to center 63 | for lazy in @lazy_videos 64 | [video, video_top, video_height] = lazy 65 | distance = Math.abs(video_top + (video_height/2) - center) 66 | if distance < closest_distance 67 | closest_video = video 68 | closest_distance = distance 69 | 70 | for lazy in @lazy_videos 71 | video = lazy[0] 72 | if closest_video == video 73 | video[0].play() 74 | video.addClass("playing") 75 | else 76 | if not video[0].paused 77 | video[0].pause() 78 | video.removeClass("playing") 79 | 80 | 81 | loadData: (query="new") -> 82 | # Get blog parameters 83 | if query == "old" # Old type query for pre 0.3.0 84 | query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE path = 'data.json'" 85 | else 86 | query = "SELECT key, value FROM json LEFT JOIN keyvalue USING (json_id) WHERE directory = '' AND file_name = 'data.json'" 87 | @cmd "dbQuery", [query], (res) => 88 | @data = {} 89 | if res 90 | for row in res 91 | @data[row.key] = row.value 92 | $(".left h1 a:not(.editable-edit)").html(@data.title).data("content", @data.title) 93 | $(".left h2").html(Text.renderMarked(@data.description)).data("content", @data.description) 94 | $(".left .links").html(Text.renderMarked(@data.links)).data("content", @data.links) 95 | 96 | loadLastcomments: (type="show", cb=false) -> 97 | return false # No need on reactiongifs 98 | query = " 99 | SELECT comment.*, json_content.json_id AS content_json_id, keyvalue.value AS cert_user_id, json.directory, post.title AS post_title 100 | FROM comment 101 | LEFT JOIN json USING (json_id) 102 | LEFT JOIN json AS json_content ON (json_content.directory = json.directory AND json_content.file_name='content.json') 103 | LEFT JOIN keyvalue ON (keyvalue.json_id = json_content.json_id AND key = 'cert_user_id') 104 | LEFT JOIN post ON (comment.post_id = post.post_id) 105 | WHERE post.title IS NOT NULL 106 | ORDER BY date_added DESC LIMIT 3" 107 | 108 | @cmd "dbQuery", [query], (res) => 109 | if res.length 110 | $(".lastcomments").css("display", "block") 111 | res.reverse() 112 | for lastcomment in res 113 | elem = $("#lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") 114 | if elem.length == 0 # Not exits yet 115 | elem = $(".lastcomment.template").clone().removeClass("template").attr("id", "lastcomment_#{lastcomment.json_id}_#{lastcomment.comment_id}") 116 | if type != "noanim" 117 | elem.cssSlideDown() 118 | elem.prependTo(".lastcomments ul") 119 | @applyLastcommentdata(elem, lastcomment) 120 | if cb then cb() 121 | 122 | applyLastcommentdata: (elem, lastcomment) -> 123 | elem.find(".user_name").text(lastcomment.cert_user_id.replace(/@.*/, "")+":") 124 | 125 | body = Text.renderMarked(lastcomment.body) 126 | body = body.replace /[\r\n]/g, " " # Remove whitespace 127 | body = body.replace /\<blockquote\>.*?\<\/blockquote\>/g, " " # Remove quotes 128 | body = body.replace /\<.*?\>/g, " " # Remove html codes 129 | if body.length > 60 # Strip if too long 130 | body = body[0..60].replace(/(.*) .*?$/, "$1") + " ..." # Keep the last 60 character and strip back until last space 131 | elem.find(".body").html(body) 132 | 133 | title_hash = lastcomment.post_title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") 134 | elem.find(".postlink").text(lastcomment.post_title).attr("href", "?#{@section}&Post:#{lastcomment.post_id}:#{title_hash}") 135 | 136 | applyPagerdata: (page, limit, has_next) -> 137 | pager = $(".pager") 138 | if page > 1 139 | pager.find(".prev").css("display", "inline-block").attr "href", "?#{@section}&page=#{page-1}" + (if @orderby then "&orderby=#{@orderby}" else "") 140 | if has_next 141 | pager.find(".next").css("display", "inline-block").attr "href", "?#{@section}&page=#{page+1}" + (if @orderby then "&orderby=#{@orderby}" else "") 142 | 143 | routeUrl: (url) -> 144 | @log "Routing url:", url 145 | if match = url.match("^(DevopsReactions|RedditGifs|RedditNsfwGifs)") 146 | @section = match[1] 147 | $("body").addClass("page-#{@section}") 148 | 149 | if match = url.match /Post:([0-9]+)/ 150 | $("body").addClass("page-post") 151 | @post_id = parseInt(match[1]) 152 | @pagePost() 153 | else 154 | $("body").addClass("page-main") 155 | if match = url.match /page=([0-9]+)/ 156 | @page = parseInt(match[1]) 157 | if match = url.match /orderby=([a-z_]+)/ 158 | @orderby = match[1] 159 | if match[1] == "like" 160 | @sql_orderby = "votes DESC" 161 | $(".head-link-mostliked").addClass("active") 162 | else if match[1] == "last_comment" 163 | @sql_orderby = "last_comment DESC" 164 | $(".head-link-mostcommented").addClass("active") 165 | $("body").on "keyup", (e) => 166 | if e.keyCode == 37 # Left 167 | $("a.prev")[0].click() 168 | if e.keyCode == 39 # Right 169 | $("a.next")[0].click() 170 | @pageMain() 171 | 172 | # - Pages - 173 | 174 | pagePost: () -> 175 | s = (+ new Date) 176 | @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) => 177 | parse_res = (res) => 178 | if res.length 179 | post = res[0] 180 | @applyPostdata($(".post-full"), post, true) 181 | $(".post-full .like").attr("id", "post_like_#{post.post_id}").on "click", @submitPostVote 182 | Comments.pagePost(@post_id) 183 | else 184 | $(".post-full").html("<h1>Not found</h1>") 185 | @pageLoaded() 186 | Comments.checkCert() 187 | 188 | # Temporary dbschema bug workaround 189 | if res.error 190 | @cmd "dbQuery", ["SELECT *, -1 AS votes FROM post WHERE post_id = #{@post_id} LIMIT 1"], parse_res 191 | else 192 | parse_res(res) 193 | 194 | 195 | pageMain: (type="normal") -> 196 | limit = 15 197 | query = """ 198 | SELECT 199 | post.*, COUNT(comment_id) AS comments, MAX(comment.date_added) AS last_comment, 200 | (SELECT COUNT(*) FROM post_vote WHERE post_vote.post_id = post.post_id) AS votes 201 | FROM post 202 | LEFT JOIN comment USING (post_id) 203 | WHERE source = '#{@section}' 204 | GROUP BY post_id 205 | ORDER BY #{@sql_orderby} 206 | LIMIT #{(@page-1)*limit}, #{limit+1} 207 | """ 208 | @cmd "dbQuery", [query], (res) => 209 | if @server_info and @server_info.rev < 570 and type == "normal" 210 | @cmd "wrapperNotification", ["error", "This site requires ZeroNet 0.3.2 rev570 or newer!<br>Please update!"] 211 | return 212 | 213 | parse_res = (res) => 214 | s = (+ new Date) 215 | if res.length > limit # Has next page 216 | res.pop() 217 | @applyPagerdata(@page, limit, true) 218 | else 219 | @applyPagerdata(@page, limit, false) 220 | for post, i in res 221 | elem = $("#post_#{post.post_id}") 222 | if elem.length == 0 # Not exits yet 223 | elem = $(".post.template").clone().removeClass("template").attr("id", "post_#{post.post_id}") 224 | elem.appendTo(".posts") 225 | # elem.find(".score").attr("id", "post_score_#{post.post_id}").on "click", @submitPostVote # Submit vote 226 | elem.find(".like").attr("id", "post_like_#{post.post_id}").on "click", @submitPostVote 227 | if i > 3 228 | delay = 800 229 | else 230 | delay = 0 231 | @applyPostdata(elem, post, full=false, delay=delay) 232 | @pageLoaded() 233 | setTimeout ( => @addLazyVideos() ), 1000 # Add delayed videos to lazy loading 234 | @log "Posts loaded in", ((+ new Date)-s),"ms" 235 | 236 | if type == "normal" 237 | $(".posts .new").on "click", => # Create new blog post 238 | @cmd "fileGet", ["data/data.json"], (res) => 239 | data = JSON.parse(res) 240 | # Add to data 241 | data.post.unshift 242 | post_id: data.next_post_id 243 | title: "New blog post" 244 | date_published: (+ new Date)/1000 245 | body: "Blog post body" 246 | data.next_post_id += 1 247 | 248 | # Create html elements 249 | elem = $(".post.template").clone().removeClass("template") 250 | @applyPostdata(elem, data.post[0]) 251 | elem.hide() 252 | elem.prependTo(".posts").slideDown() 253 | @addInlineEditors(elem) 254 | 255 | @writeData(data) 256 | return false 257 | 258 | # Temporary dbschema bug workaround 259 | if res.error 260 | query = """ 261 | SELECT 262 | post.*, COUNT(comment_id) AS comments, 263 | -1 AS votes 264 | FROM post 265 | LEFT JOIN comment USING (post_id) 266 | GROUP BY post_id 267 | ORDER BY date_published DESC 268 | LIMIT #{(@page-1)*limit}, #{limit+1} 269 | """ 270 | @cmd "dbQuery", [query], parse_res 271 | else 272 | parse_res(res) 273 | 274 | 275 | # - EOF Pages - 276 | 277 | 278 | # All page content loaded 279 | pageLoaded: => 280 | $("body").addClass("loaded") # Back/forward button keep position support 281 | $('pre code').each (i, block) -> # Higlight code blocks 282 | hljs.highlightBlock(block) 283 | @addLazyVideos() 284 | @checkLazyVideos() 285 | @event_page_load.resolve() 286 | @cmd "innerLoaded", true 287 | 288 | 289 | addInlineEditors: (parent) -> 290 | @logStart "Adding inline editors" 291 | elems = $("[data-editable]:visible", parent) 292 | for elem in elems 293 | elem = $(elem) 294 | if not elem.data("editor") and not elem.hasClass("editor") 295 | editor = new InlineEditor(elem, @getContent, @saveContent, @getObject) 296 | elem.data("editor", editor) 297 | @logEnd "Adding inline editors" 298 | 299 | 300 | # Check if publishing is necessary 301 | checkPublishbar: -> 302 | if not @data["modified"] or @data["modified"] > @site_info.content.modified 303 | $(".publishbar").addClass("visible") 304 | else 305 | $(".publishbar").removeClass("visible") 306 | 307 | 308 | # Sign and Publish site 309 | publish: => 310 | if @site_info.privatekey # Privatekey stored in users.json 311 | @cmd "sitePublish", ["stored"], (res) => 312 | @log "Publish result:", res 313 | else 314 | @cmd "wrapperPrompt", ["Enter your private key:", "password"], (privatekey) => # Prompt the private key 315 | $(".publishbar .button").addClass("loading") 316 | @cmd "sitePublish", [privatekey], (res) => 317 | $(".publishbar .button").removeClass("loading") 318 | @log "Publish result:", res 319 | 320 | return false # Ignore link default event 321 | 322 | 323 | # Apply from data to post html element 324 | applyPostdata: (elem, post, full=false, delay=0) -> 325 | title_hash = post.title.replace(/[#?& ]/g, "+").replace(/[+]+/g, "+") 326 | elem.data("object", "Post:"+post.post_id) 327 | $(".title .editable", elem).html(post.title).attr("href", "?#{@section}&Post:#{post.post_id}:#{title_hash}").data("content", post.title) 328 | date_published = Time.since(post.date_published) 329 | # Published date 330 | if post.body.match /^---/m # Has more over fold 331 | date_published += " · #{Time.readtime(post.body)}" # If has break add readtime 332 | $(".more", elem).css("display", "inline-block").attr("href", "?#{@section}&Post:#{post.post_id}:#{title_hash}") 333 | $(".details .published", elem).html(date_published).data("content", post.date_published) 334 | # Comments num 335 | if post.comments > 0 336 | $(".details .comments-num", elem).css("display", "inline").attr("href", "?#{@section}&Post:#{post.post_id}:#{title_hash}") 337 | $(".details .comments-num .num", elem).text("#{post.comments} comments") 338 | else 339 | $(".details .comments-num", elem).css("display", "none") 340 | 341 | ### 342 | if @my_post_votes[post.post_id] # Voted on it 343 | $(".score-inactive .score-num", elem).text post.votes-1 344 | $(".score-active .score-num", elem).text post.votes 345 | $(".score", elem).addClass("active") 346 | else # Not voted on it 347 | $(".score-inactive .score-num", elem).text post.votes 348 | $(".score-active .score-num", elem).text post.votes+1 349 | 350 | if post.votes == 0 351 | $(".score", elem).addClass("noscore") 352 | else 353 | $(".score", elem).removeClass("noscore") 354 | ### 355 | if post.votes > 0 356 | $(".like .num", elem).text post.votes+" Likes" 357 | else if post.votes == -1 # DB bug 358 | $(".like", elem).css("display", "none") 359 | else 360 | $(".like .num", elem).text "Like!" 361 | 362 | if @my_post_votes[post.post_id] # Voted on it 363 | $(".like", elem).addClass("active") 364 | 365 | 366 | if full 367 | body = post.body 368 | else # On main page only show post until the first --- hr separator 369 | body = post.body.replace(/^([\s\S]*?)\n---\n[\s\S]*$/, "$1") 370 | 371 | if $(".body", elem).data("content") != post.body 372 | if delay 373 | setTimeout ( -> 374 | $(".body", elem).html(Text.renderMarked(body)).data("content", post.body) 375 | ), delay 376 | else 377 | $(".body", elem).html(Text.renderMarked(body)).data("content", post.body) 378 | 379 | 380 | # Wrapper websocket connection ready 381 | onOpenWebsocket: (e) => 382 | @loadData() 383 | @cmd "siteInfo", {}, (site_info) => 384 | @setSiteinfo(site_info) 385 | query_my_votes = """ 386 | SELECT 387 | 'post_vote' AS type, 388 | post_id AS uri 389 | FROM json 390 | LEFT JOIN post_vote USING (json_id) 391 | WHERE directory = 'users/#{@site_info.auth_address}' AND file_name = 'data.json' 392 | """ 393 | @cmd "dbQuery", [query_my_votes], (res) => 394 | for row in res 395 | @my_post_votes[row["uri"]] = 1 396 | @routeUrl(window.location.search.substring(1)) 397 | 398 | @cmd "serverInfo", {}, (ret) => # Get server info 399 | @server_info = ret 400 | if @server_info.rev < 160 401 | @loadData("old") 402 | @loadLastcomments("noanim") 403 | 404 | 405 | # Returns the elem parent object 406 | getObject: (elem) => 407 | return elem.parents("[data-object]:first") 408 | 409 | 410 | # Get content from data.json 411 | getContent: (elem, raw=false) => 412 | [type, id] = @getObject(elem).data("object").split(":") 413 | id = parseInt(id) 414 | content = elem.data("content") 415 | if elem.data("editable-mode") == "timestamp" # Convert to time 416 | content = Time.date(content, "full") 417 | 418 | if elem.data("editable-mode") == "simple" or raw # No markdown 419 | return content 420 | else 421 | return Text.renderMarked(content) 422 | 423 | 424 | # Save content to data.json 425 | saveContent: (elem, content, cb=false) => 426 | if elem.data("deletable") and content == null then return @deleteObject(elem, cb) # Its a delete request 427 | elem.data("content", content) 428 | [type, id] = @getObject(elem).data("object").split(":") 429 | id = parseInt(id) 430 | if type == "Post" or type == "Site" 431 | @saveSite(elem, type, id, content, cb) 432 | else if type == "Comment" 433 | @saveComment(elem, type, id, content, cb) 434 | 435 | 436 | 437 | saveSite: (elem, type, id, content, cb) -> 438 | @cmd "fileGet", ["data/data.json"], (res) => 439 | data = JSON.parse(res) 440 | if type == "Post" 441 | post = (post for post in data.post when post.post_id == id)[0] 442 | 443 | if elem.data("editable-mode") == "timestamp" # Time parse to timestamp 444 | content = Time.timestamp(content) 445 | 446 | post[elem.data("editable")] = content 447 | else if type == "Site" 448 | data[elem.data("editable")] = content 449 | 450 | @writeData data, (res) => 451 | if cb 452 | if res == true # OK 453 | if elem.data("editable-mode") == "simple" # No markdown 454 | cb(content) 455 | else if elem.data("editable-mode") == "timestamp" # Format timestamp 456 | cb(Time.since(content)) 457 | else 458 | cb(Text.renderMarked(content)) 459 | else # Error 460 | cb(false) 461 | 462 | 463 | 464 | saveComment: (elem, type, id, content, cb) -> 465 | @log "Saving comment...", id 466 | @getObject(elem).css "height", "auto" 467 | inner_path = "data/users/#{Page.site_info.auth_address}/data.json" 468 | Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => 469 | data = JSON.parse(data) 470 | comment = (comment for comment in data.comment when comment.comment_id == id)[0] 471 | comment[elem.data("editable")] = content 472 | json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) 473 | @writePublish inner_path, btoa(json_raw), (res) => 474 | if res == true 475 | Comments.checkCert("updaterules") 476 | if cb then cb(Text.renderMarked(content, {"sanitize": true})) 477 | else 478 | @cmd "wrapperNotification", ["error", "File write error: #{res}"] 479 | if cb then cb(false) 480 | 481 | 482 | 483 | 484 | deleteObject: (elem, cb=False) -> 485 | [type, id] = elem.data("object").split(":") 486 | id = parseInt(id) 487 | 488 | if type == "Post" 489 | @cmd "fileGet", ["data/data.json"], (res) => 490 | data = JSON.parse(res) 491 | if type == "Post" 492 | post = (post for post in data.post when post.post_id == id)[0] 493 | if not post then return false # No post found for this id 494 | data.post.splice(data.post.indexOf(post), 1) # Remove from data 495 | 496 | @writeData data, (res) => 497 | if cb then cb() 498 | if res == true then elem.slideUp() 499 | else if type == "Comment" 500 | inner_path = "data/users/#{Page.site_info.auth_address}/data.json" 501 | @cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => 502 | data = JSON.parse(data) 503 | comment = (comment for comment in data.comment when comment.comment_id == id)[0] 504 | data.comment.splice(data.comment.indexOf(comment), 1) 505 | json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) 506 | @writePublish inner_path, btoa(json_raw), (res) => 507 | if res == true 508 | elem.slideUp() 509 | if cb then cb() 510 | 511 | 512 | 513 | writeData: (data, cb=null) -> 514 | if not data 515 | return @log "Data missing" 516 | @data["modified"] = data.modified = Time.timestamp() 517 | json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) # Encode to json, encode utf8 518 | @cmd "fileWrite", ["data/data.json", btoa(json_raw)], (res) => # Convert to to base64 and send 519 | if res == "ok" 520 | if cb then cb(true) 521 | else 522 | @cmd "wrapperNotification", ["error", "File write error: #{res}"] 523 | if cb then cb(false) 524 | @checkPublishbar() 525 | 526 | # Updating title in content.json 527 | @cmd "fileGet", ["content.json"], (content) => 528 | content = content.replace /"title": ".*?"/, "\"title\": \"#{data.title}\"" # Load as raw html to prevent js bignumber problems 529 | @cmd "fileWrite", ["content.json", btoa(content)], (res) => 530 | if res != "ok" 531 | @cmd "wrapperNotification", ["error", "Content.json write error: #{res}"] 532 | 533 | # If the privatekey is stored sign the new content 534 | if @site_info["privatekey"] 535 | @cmd "siteSign", ["stored", "content.json"], (res) => 536 | @log "Sign result", res 537 | 538 | 539 | writePublish: (inner_path, data, cb) -> 540 | @cmd "fileWrite", [inner_path, data], (res) => 541 | if res != "ok" # fileWrite failed 542 | @cmd "wrapperNotification", ["error", "File write error: #{res}"] 543 | cb(false) 544 | return false 545 | 546 | @cmd "sitePublish", {"inner_path": inner_path}, (res) => 547 | if res == "ok" 548 | cb(true) 549 | else 550 | cb(res) 551 | 552 | submitPostVote: (e) => 553 | if not Page.site_info.cert_user_id # No selected cert 554 | Page.cmd "certSelect", [["zeroid.bit", "zeroverse.bit"]] 555 | return false 556 | 557 | elem = $(e.currentTarget) 558 | elem.toggleClass("active").addClass("loading") 559 | inner_path = "data/users/#{@site_info.auth_address}/data.json" 560 | Page.cmd "fileGet", {"inner_path": inner_path, "required": false}, (data) => 561 | if data 562 | data = JSON.parse(data) 563 | else # Default data 564 | data = {"next_comment_id": 1, "comment": [], "comment_vote": {}, "post_vote": {} } 565 | 566 | if not data.post_vote 567 | data.post_vote = {} 568 | post_id = elem.attr("id").match("_([0-9]+)$")[1] 569 | 570 | if elem.hasClass("active") 571 | data.post_vote[post_id] = 1 572 | else 573 | delete data.post_vote[post_id] 574 | json_raw = unescape(encodeURIComponent(JSON.stringify(data, undefined, '\t'))) 575 | 576 | current_num = parseInt elem.find(".num").text() 577 | if not current_num 578 | current_num = 0 579 | if elem.hasClass("active") 580 | elem.find(".num").text(current_num+1+" Likes") 581 | else 582 | elem.find(".num").text(current_num-1+" Likes") 583 | 584 | Page.writePublish inner_path, btoa(json_raw), (res) => 585 | elem.removeClass("loading") 586 | @log "Writepublish result", res 587 | 588 | return false 589 | 590 | # Parse incoming requests 591 | onRequest: (cmd, message) -> 592 | if cmd == "setSiteInfo" # Site updated 593 | @actionSetSiteInfo(message) 594 | else 595 | @log "Unknown command", message 596 | 597 | 598 | # Siteinfo changed 599 | actionSetSiteInfo: (message) => 600 | @setSiteinfo(message.params) 601 | @checkPublishbar() 602 | 603 | 604 | setSiteinfo: (site_info) => 605 | @site_info = site_info 606 | @event_site_info.resolve(site_info) 607 | if $("body").hasClass("page-post") then Comments.checkCert() # Update if username changed 608 | # User commented 609 | if site_info.event?[0] == "file_done" and site_info.event[1].match /.*users.*data.json$/ 610 | if $("body").hasClass("page-post") 611 | @pagePost() 612 | Comments.loadComments() # Post page, reload comments 613 | @loadLastcomments() 614 | if $("body").hasClass("page-main") 615 | RateLimit 500, => 616 | @pageMain("update") 617 | @loadLastcomments() 618 | else if site_info.event?[0] == "file_done" and site_info.event[1] == "data/data.json" 619 | @loadData() 620 | if $("body").hasClass("page-main") then @pageMain("update") 621 | if $("body").hasClass("page-post") then @pagePost() 622 | 623 | 624 | window.Page = new ZeroBlog() 625 | -------------------------------------------------------------------------------- /js/lib/highlight.pack.js: -------------------------------------------------------------------------------- 1 | !function(e){"undefined"!=typeof exports?e(exports):(window.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return window.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&").replace(/</gm,"<").replace(/>/gm,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){var n=(e.className+" "+(e.parentNode?e.parentNode.className:"")).split(/\s+/);return n=n.map(function(e){return e.replace(/^lang(uage)?-/,"")}),n.filter(function(e){return N(e)||/no(-?)highlight/.test(e)})[0]}function o(e,n){var t={};for(var r in e)t[r]=e[r];if(n)for(var r in n)t[r]=n[r];return t}function i(e){var n=[];return function r(e,a){for(var o=e.firstChild;o;o=o.nextSibling)3==o.nodeType?a+=o.nodeValue.length:1==o.nodeType&&(n.push({event:"start",offset:a,node:o}),a=r(o,a),t(o).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:o}));return a}(e,0),n}function c(e,r,a){function o(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset<r[0].offset?e:r:"start"==r[0].event?e:r:e.length?e:r}function i(e){function r(e){return" "+e.nodeName+'="'+n(e.value)+'"'}l+="<"+t(e)+Array.prototype.map.call(e.attributes,r).join("")+">"}function c(e){l+="</"+t(e)+">"}function u(e){("start"==e.event?i:c)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=o();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){f.reverse().forEach(c);do u(g.splice(0,1)[0]),g=o();while(g==e&&g.length&&g[0].offset==s);f.reverse().forEach(i)}else"start"==g[0].event?f.push(g[0].node):f.pop(),u(g.splice(0,1)[0])}return l+n(a.substr(s))}function u(e){function n(e){return e&&e.source||e}function t(t,r){return RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var c={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");c[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):Object.keys(a.k).forEach(function(e){u(e,a.k[e])}),a.k=c}a.lR=t(a.l||/\b[A-Za-z0-9_]+\b/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function s(e,t,a,o){function i(e,n){for(var t=0;t<n.c.length;t++)if(r(n.c[t].bR,e))return n.c[t]}function c(e,n){return r(e.eR,n)?e:e.eW?c(e.parent,n):void 0}function f(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=x.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":E.classPrefix,o='<span class="'+a,i=t?"":"</span>";return o+=e+'">',o+n+i}function d(){if(!w.k)return n(y);var e="",t=0;w.lR.lastIndex=0;for(var r=w.lR.exec(y);r;){e+=n(y.substr(t,r.index-t));var a=g(w,r);a?(B+=a[1],e+=p(a[0],n(r[0]))):e+=n(r[0]),t=w.lR.lastIndex,r=w.lR.exec(y)}return e+n(y.substr(t))}function h(){if(w.sL&&!R[w.sL])return n(y);var e=w.sL?s(w.sL,y,!0,L[w.sL]):l(y);return w.r>0&&(B+=e.r),"continuous"==w.subLanguageMode&&(L[w.sL]=e.top),p(e.language,e.value,!1,!0)}function v(){return void 0!==w.sL?h():d()}function b(e,t){var r=e.cN?p(e.cN,"",!0):"";e.rB?(M+=r,y=""):e.eB?(M+=n(t)+r,y=""):(M+=r,y=t),w=Object.create(e,{parent:{value:w}})}function m(e,t){if(y+=e,void 0===t)return M+=v(),0;var r=i(t,w);if(r)return M+=v(),b(r,t),r.rB?0:t.length;var a=c(w,t);if(a){var o=w;o.rE||o.eE||(y+=t),M+=v();do w.cN&&(M+="</span>"),B+=w.r,w=w.parent;while(w!=a.parent);return o.eE&&(M+=n(t)),y="",a.starts&&b(a.starts,""),o.rE?0:t.length}if(f(t,w))throw new Error('Illegal lexeme "'+t+'" for mode "'+(w.cN||"<unnamed>")+'"');return y+=t,t.length||1}var x=N(e);if(!x)throw new Error('Unknown language: "'+e+'"');u(x);for(var w=o||x,L={},M="",k=w;k!=x;k=k.parent)k.cN&&(M=p(k.cN,"",!0)+M);var y="",B=0;try{for(var C,j,I=0;;){if(w.t.lastIndex=I,C=w.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}m(t.substr(I));for(var k=w;k.parent;k=k.parent)k.cN&&(M+="</span>");return{r:B,value:M,language:e,top:w}}catch(A){if(-1!=A.message.indexOf("Illegal"))return{r:0,value:n(t)};throw A}}function l(e,t){t=t||E.languages||Object.keys(R);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(N(n)){var t=s(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function f(e){return E.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,E.tabReplace)})),E.useBR&&(e=e.replace(/\n/g,"<br>")),e}function g(e,n,t){var r=n?x[n]:t,a=[e.trim()];return e.match(/(\s|^)hljs(\s|$)/)||a.push("hljs"),r&&a.push(r),a.join(" ").trim()}function p(e){var n=a(e);if(!/no(-?)highlight/.test(n)){var t;E.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ \/]*>/g,"\n")):t=e;var r=t.textContent,o=n?s(n,r,!0):l(r),u=i(t);if(u.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=o.value,o.value=c(u,i(p),r)}o.value=f(o.value),e.innerHTML=o.value,e.className=g(e.className,n,o.language),e.result={language:o.language,re:o.r},o.second_best&&(e.second_best={language:o.second_best.language,re:o.second_best.r})}}function d(e){E=o(E,e)}function h(){if(!h.called){h.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",h,!1),addEventListener("load",h,!1)}function b(n,t){var r=R[n]=t(e);r.aliases&&r.aliases.forEach(function(e){x[e]=n})}function m(){return Object.keys(R)}function N(e){return R[e]||R[x[e]]}var E={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},R={},x={};return e.highlight=s,e.highlightAuto=l,e.fixMarkup=f,e.highlightBlock=p,e.configure=d,e.initHighlighting=h,e.initHighlightingOnLoad=v,e.registerLanguage=b,e.listLanguages=m,e.getLanguage=N,e.inherit=o,e.IR="[a-zA-Z][a-zA-Z0-9_]*",e.UIR="[a-zA-Z_][a-zA-Z0-9_]*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/},e.CLCM={cN:"comment",b:"//",e:"$",c:[e.PWM]},e.CBCM={cN:"comment",b:"/\\*",e:"\\*/",c:[e.PWM]},e.HCM={cN:"comment",b:"#",e:"$",c:[e.PWM]},e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("cpp",function(t){var i={keyword:"false int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using true class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue wchar_t inline delete alignof char16_t char32_t constexpr decltype noexcept nullptr static_assert thread_local restrict _Bool complex _Complex _Imaginaryintmax_t uintmax_t int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_tint_least8_t uint_least8_t int_least16_t uint_least16_t int_least32_t uint_least32_tint_least64_t uint_least64_t int_fast8_t uint_fast8_t int_fast16_t uint_fast16_t int_fast32_tuint_fast32_t int_fast64_t uint_fast64_t intptr_t uintptr_t atomic_bool atomic_char atomic_scharatomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llongatomic_ullong atomic_wchar_t atomic_char16_t atomic_char32_t atomic_intmax_t atomic_uintmax_tatomic_intptr_t atomic_uintptr_t atomic_size_t atomic_ptrdiff_t atomic_int_least8_t atomic_int_least16_tatomic_int_least32_t atomic_int_least64_t atomic_uint_least8_t atomic_uint_least16_t atomic_uint_least32_tatomic_uint_least64_t atomic_int_fast8_t atomic_int_fast16_t atomic_int_fast32_t atomic_int_fast64_tatomic_uint_fast8_t atomic_uint_fast16_t atomic_uint_fast32_t atomic_uint_fast64_t",built_in:"std string cin cout cerr clog stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf"};return{aliases:["c","h","c++","h++"],k:i,i:"</",c:[t.CLCM,t.CBCM,t.QSM,{cN:"string",b:"'\\\\?.",e:"'",i:"."},{cN:"number",b:"\\b(\\d+(\\.\\d*)?|\\.\\d+)(u|U|l|L|ul|UL|f|F)"},t.CNM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line pragma",c:[{b:'include\\s*[<"]',e:'[>"]',k:"include",i:"\\n"},t.CLCM]},{cN:"stl_container",b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:i,c:["self"]},{b:t.IR+"::"},{bK:"new throw return",r:0},{cN:"function",b:"("+t.IR+"\\s+)+"+t.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:i,c:[{b:t.IR+"\\s*\\(",rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:i,r:0,c:[t.CBCM]},t.CLCM,t.CBCM]}]}});hljs.registerLanguage("ruby",function(e){var b="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r="and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",c={cN:"yardoctag",b:"@[A-Za-z]+"},a={cN:"value",b:"#<",e:">"},s={cN:"comment",v:[{b:"#",e:"$",c:[c]},{b:"^\\=begin",e:"^\\=end",c:[c],r:10},{b:"^__END__",e:"\\n$"}]},n={cN:"subst",b:"#\\{",e:"}",k:r},t={cN:"string",c:[e.BE,n],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/}]},i={cN:"params",b:"\\(",e:"\\)",k:r},d=[t,a,s,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]},s]},{cN:"function",bK:"def",e:" |$|;",r:0,c:[e.inherit(e.TM,{b:b}),i,s]},{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":",c:[t,{b:b}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:[a,s,{cN:"regexp",c:[e.BE,n],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}],r:0}];n.c=d,i.c=d;var l="[>?]>",u="[\\w#]+\\(\\w+\\):\\d+:\\d+>",N="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",o=[{b:/^\s*=>/,cN:"status",starts:{e:"$",c:d}},{cN:"prompt",b:"^("+l+"|"+u+"|"+N+")",starts:{e:"$",c:d}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,c:[s].concat(o).concat(d)}});hljs.registerLanguage("apache",function(e){var r={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"tag",b:"</?",e:">"},{cN:"keyword",b:/\w+/,r:0,k:{common:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"sqbracket",b:"\\s\\[",e:"\\]$"},{cN:"cbracket",b:"[\\$%]\\{",e:"\\}",c:["self",r]},r,e.QSM]}}],i:/\S/}});hljs.registerLanguage("python",function(e){var r={cN:"prompt",b:/^(>>>|\.\.\.) /},b={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[r],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[r],r:10},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},e.ASM,e.QSM]},l={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},c={cN:"params",b:/\(/,e:/\)/,c:["self",r,l,b]};return{aliases:["py","gyp"],k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},i:/(<\/|->|\?)/,c:[r,l,b,e.HCM,{v:[{cN:"function",bK:"def",r:10},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n]/,c:[e.UTM,c]},{cN:"decorator",b:/@/,e:/$/},{b:/\b(print|exec)\(/}]}});hljs.registerLanguage("javascript",function(r){return{aliases:["js"],k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document"},c:[{cN:"pi",r:10,v:[{b:/^\s*('|")use strict('|")/},{b:/^\s*('|")use asm('|")/}]},r.ASM,r.QSM,r.CLCM,r.CBCM,r.CNM,{b:"("+r.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[r.CLCM,r.CBCM,r.RM,{b:/</,e:/>;/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[r.inherit(r.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[r.CLCM,r.CBCM],i:/["'\(]/}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+r.IR,r:0}]}});hljs.registerLanguage("coffeescript",function(e){var c={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",reserved:"case default function var void with const let enum export import native __hasProp __extends __slice __bind __indexOf",built_in:"npm require console print module global window document"},n="[A-Za-z$_][0-9A-Za-z$_]*",t={cN:"subst",b:/#\{/,e:/}/,k:c},r=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,t]},{b:/"/,e:/"/,c:[e.BE,t]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[t,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{cN:"property",b:"@"+n},{b:"`",e:"`",eB:!0,eE:!0,sL:"javascript"}];t.c=r;var i=e.inherit(e.TM,{b:n}),s="(\\(.*\\))?\\s*\\B[-=]>",o={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:c,c:["self"].concat(r)}]};return{aliases:["coffee","cson","iced"],k:c,i:/\/\*/,c:r.concat([{cN:"comment",b:"###",e:"###",c:[e.PWM]},e.HCM,{cN:"function",b:"^\\s*"+n+"\\s*=\\s*"+s,e:"[-=]>",rB:!0,c:[i,o]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:s,e:"[-=]>",rB:!0,c:[o]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[i]},i]},{cN:"attribute",b:n+":",e:":",rB:!0,rE:!0,r:0}])}});hljs.registerLanguage("http",function(){return{i:"\\S",c:[{cN:"status",b:"^HTTP/[0-9\\.]+",e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{cN:"request",b:"^[A-Z]+ (.*?) HTTP/[0-9\\.]+$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{cN:"string",e:"$"}},{b:"\\n\\n",starts:{sL:"",eW:!0}}]}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",a={cN:"function",b:c+"\\(",rB:!0,eE:!0,e:"\\("};return{cI:!0,i:"[=/|']",c:[e.CBCM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[a,e.ASM,e.QSM,e.CSSNM]}]},{cN:"tag",b:c,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[e.CBCM,{cN:"rule",b:"[^\\s]",rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:!0,i:"[^\\s]",starts:{cN:"value",eW:!0,eE:!0,c:[a,e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"hexcolor",b:"#[0-9A-Fa-f]+"},{cN:"important",b:"!important"}]}}]}]}]}});hljs.registerLanguage("ini",function(e){return{cI:!0,i:/\S/,c:[{cN:"comment",b:";",e:"$"},{cN:"title",b:"^\\[",e:"\\]"},{cN:"setting",b:"^[a-z0-9\\[\\]_-]+[ \\t]*=[ \\t]*",e:"$",c:[{cN:"value",eW:!0,k:"on off true false yes no",c:[e.QSM,e.NM],r:0}]}]}});hljs.registerLanguage("objectivec",function(e){var t={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"NSString NSData NSDictionary CGRect CGPoint UIButton UILabel UITextView UIWebView MKMapView NSView NSViewController NSWindow NSWindowController NSSet NSUUID NSIndexSet UISegmentedControl NSObject UITableViewDelegate UITableViewDataSource NSThread UIActivityIndicator UITabbar UIToolBar UIBarButtonItem UIImageView NSAutoreleasePool UITableView BOOL NSInteger CGFloat NSException NSLog NSMutableString NSMutableArray NSMutableDictionary NSURL NSIndexPath CGSize UITableViewCell UIView UIViewController UINavigationBar UINavigationController UITabBarController UIPopoverController UIPopoverControllerDelegate UIImage NSNumber UISearchBar NSFetchedResultsController NSFetchedResultsChangeType UIScrollView UIScrollViewDelegate UIEdgeInsets UIColor UIFont UIApplication NSNotFound NSNotificationCenter NSNotification UILocalNotification NSBundle NSFileManager NSTimeInterval NSDate NSCalendar NSUserDefaults UIWindow NSRange NSArray NSError NSURLRequest NSURLConnection NSURLSession NSURLSessionDataTask NSURLSessionDownloadTask NSURLSessionUploadTask NSURLResponseUIInterfaceOrientation MPMoviePlayerController dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},o=/[a-zA-Z@][a-zA-Z0-9_]*/,a="@interface @class @protocol @implementation";return{aliases:["m","mm","objc","obj-c"],k:t,l:o,i:"</",c:[e.CLCM,e.CBCM,e.CNM,e.QSM,{cN:"string",v:[{b:'@"',e:'"',i:"\\n",c:[e.BE]},{b:"'",e:"[^\\\\]'",i:"[^\\\\][^']"}]},{cN:"preprocessor",b:"#",e:"$",c:[{cN:"title",v:[{b:'"',e:'"'},{b:"<",e:">"}]}]},{cN:"class",b:"("+a.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:a,l:o,c:[e.UTM]},{cN:"variable",b:"\\."+e.UIR,r:0}]}});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)\}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/-?[a-z\.]+/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",operator:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"shebang",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,e.NM,s,a,t]}});hljs.registerLanguage("markdown",function(){return{aliases:["md","mkdown","mkd"],c:[{cN:"header",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"blockquote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{cN:"horizontal_rule",b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"link_label",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link_url",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"link_reference",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:"^\\[.+\\]:",rB:!0,c:[{cN:"link_reference",b:"\\[",e:"\\]:",eB:!0,eE:!0,starts:{cN:"link_url",e:"$"}}]}]}});hljs.registerLanguage("java",function(e){var a=e.UIR+"(<"+e.UIR+">)?",t="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private",c="(\\b(0b[01_]+)|\\b0[xX][a-fA-F0-9_]+|(\\b[\\d_]+(\\.[\\d_]*)?|\\.[\\d_]+)([eE][-+]?\\d+)?)[lLfF]?",r={cN:"number",b:c,r:0};return{aliases:["jsp"],k:t,i:/<\//,c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return",r:0},{cN:"function",b:"("+a+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:t,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:t,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},r,{cN:"annotation",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("diff",function(){return{aliases:["patch"],c:[{cN:"chunk",r:10,v:[{b:/^\@\@ +\-\d+,\d+ +\+\d+,\d+ +\@\@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"header",v:[{b:/Index: /,e:/$/},{b:/=====/,e:/=====$/},{b:/^\-\-\-/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+\+\+/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}});hljs.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},s={b:"->{",e:"}"},n={cN:"variable",v:[{b:/\$\d/},{b:/[\$\%\@](\^\w\b|#\w+(\:\:\w+)*|{\w+}|\w+(\:\:\w*)*)/},{b:/[\$\%\@][^\s\w{]/,r:0}]},o={cN:"comment",b:"^(__END__|__DATA__)",e:"\\n$",r:5},i=[e.BE,r,n],c=[n,e.HCM,o,{cN:"comment",b:"^\\=\\w",e:"\\=cut",eW:!0},s,{cN:"string",c:i,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,o,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",r:5},{cN:"operator",b:"-\\w\\b",r:0}];return r.c=c,s.c=c,{aliases:["pl"],k:t,c:c}});hljs.registerLanguage("makefile",function(e){var a={cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]};return{aliases:["mk","mak"],c:[e.HCM,{b:/^\w+\s*\W*=/,rB:!0,r:0,starts:{cN:"constant",e:/\s*\W*=/,eE:!0,starts:{e:/$/,r:0,c:[a]}}},{cN:"title",b:/^[\w]+:\s*$/},{cN:"phony",b:/^\.PHONY:/,e:/$/,k:".PHONY",l:/[\.\w]+/},{b:/^\t+/,e:/$/,r:0,c:[e.QSM,a]}]}});hljs.registerLanguage("cs",function(e){var r="abstract as base bool break byte case catch char checked const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long null object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while async protected public private internal ascending descending from get group into join let orderby partial select set value var where yield",t=e.IR+"(<"+e.IR+">)?";return{aliases:["csharp"],k:r,i:/::/,c:[{cN:"comment",b:"///",e:"$",rB:!0,c:[{cN:"xmlDocTag",v:[{b:"///",r:0},{b:"<!--|-->"},{b:"</?",e:">"}]}]},e.CLCM,e.CBCM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},e.ASM,e.QSM,e.CNM,{bK:"class namespace interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"new return throw await",r:0},{cN:"function",b:"("+t+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:r,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:r,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}});hljs.registerLanguage("json",function(e){var t={literal:"true false null"},i=[e.QSM,e.CNM],l={cN:"value",e:",",eW:!0,eE:!0,c:i,k:t},c={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:!0,eE:!0,c:[e.BE],i:"\\n",starts:l}],i:"\\S"},n={b:"\\[",e:"\\]",c:[e.inherit(l,{cN:null})],i:"\\S"};return i.splice(i.length,0,c,n),{c:i,k:t,i:"\\S"}});hljs.registerLanguage("nginx",function(e){var r={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},b={eW:!0,l:"[a-z/_]+",k:{built_in:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,r],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{cN:"url",b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[r]},{cN:"regexp",c:[e.BE,r],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},r]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"title",b:e.UIR,starts:b}],r:0}],i:"[^\\s\\}]"}});hljs.registerLanguage("sql",function(e){var t={cN:"comment",b:"--",e:"$"};return{cI:!0,i:/[<>]/,c:[{cN:"operator",bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate savepoint release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup",e:/;/,eW:!0,k:{keyword:"abs absolute acos action add adddate addtime aes_decrypt aes_encrypt after aggregate all allocate alter analyze and any are as asc ascii asin assertion at atan atan2 atn2 authorization authors avg backup before begin benchmark between bin binlog bit_and bit_count bit_length bit_or bit_xor both by cache call cascade cascaded case cast catalog ceil ceiling chain change changed char_length character_length charindex charset check checksum checksum_agg choose close coalesce coercibility collate collation collationproperty column columns columns_updated commit compress concat concat_ws concurrent connect connection connection_id consistent constraint constraints continue contributors conv convert convert_tz corresponding cos cot count count_big crc32 create cross cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime data database databases datalength date_add date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts datetimeoffsetfromparts day dayname dayofmonth dayofweek dayofyear deallocate declare decode default deferrable deferred degrees delayed delete des_decrypt des_encrypt des_key_file desc describe descriptor diagnostics difference disconnect distinct distinctrow div do domain double drop dumpfile each else elt enclosed encode encrypt end end-exec engine engines eomonth errors escape escaped event eventdata events except exception exec execute exists exp explain export_set extended external extract fast fetch field fields find_in_set first first_value floor flush for force foreign format found found_rows from from_base64 from_days from_unixtime full function get get_format get_lock getdate getutcdate global go goto grant grants greatest group group_concat grouping grouping_id gtid_subset gtid_subtract handler having help hex high_priority hosts hour ident_current ident_incr ident_seed identified identity if ifnull ignore iif ilike immediate in index indicator inet6_aton inet6_ntoa inet_aton inet_ntoa infile initially inner innodb input insert install instr intersect into is is_free_lock is_ipv4 is_ipv4_compat is_ipv4_mapped is_not is_not_null is_used_lock isdate isnull isolation join key kill language last last_day last_insert_id last_value lcase lead leading least leaves left len lenght level like limit lines ln load load_file local localtime localtimestamp locate lock log log10 log2 logfile logs low_priority lower lpad ltrim make_set makedate maketime master master_pos_wait match matched max md5 medium merge microsecond mid min minute mod mode module month monthname mutex name_const names national natural nchar next no no_write_to_binlog not now nullif nvarchar oct octet_length of old_password on only open optimize option optionally or ord order outer outfile output pad parse partial partition password patindex percent_rank percentile_cont percentile_disc period_add period_diff pi plugin position pow power pragma precision prepare preserve primary prior privileges procedure procedure_analyze processlist profile profiles public publishingservername purge quarter query quick quote quotename radians rand read references regexp relative relaylog release release_lock rename repair repeat replace replicate reset restore restrict return returns reverse revoke right rlike rollback rollup round row row_count rows rpad rtrim savepoint schema scroll sec_to_time second section select serializable server session session_user set sha sha1 sha2 share show sign sin size slave sleep smalldatetimefromparts snapshot some soname soundex sounds_like space sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sql_variant_property sqlstate sqrt square start starting status std stddev stddev_pop stddev_samp stdev stdevp stop str str_to_date straight_join strcmp string stuff subdate substr substring subtime subtring_index sum switchoffset sysdate sysdatetime sysdatetimeoffset system_user sysutcdatetime table tables tablespace tan temporary terminated tertiary_weights then time time_format time_to_sec timediff timefromparts timestamp timestampadd timestampdiff timezone_hour timezone_minute to to_base64 to_days to_seconds todatetimeoffset trailing transaction translation trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse ucase uncompress uncompressed_length unhex unicode uninstall union unique unix_timestamp unknown unlock update upgrade upped upper usage use user user_resources using utc_date utc_time utc_timestamp uuid uuid_short validate_password_strength value values var var_pop var_samp variables variance varp version view warnings week weekday weekofyear weight_string when whenever where with work write xml xor year yearweek zon",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int integer interval number numeric real serial smallint varchar varying int8 serial8 text"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}});hljs.registerLanguage("xml",function(){var t="[A-Za-z0-9\\._:-]+",e={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"},c={eW:!0,i:/</,r:0,c:[e,{cN:"attribute",b:t,r:0},{b:"=",r:0,c:[{cN:"value",c:[e],v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"<!--",e:"-->",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:"style"},c:[c],starts:{e:"</style>",rE:!0,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:"script"},c:[c],starts:{e:"</script>",rE:!0,sL:"javascript"}},e,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:/[^ \/><\n\t]+/,r:0},c]}]}});hljs.registerLanguage("php",function(e){var c={cN:"variable",b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},i={cN:"preprocessor",b:/<\?(php)?|\?>/},a={cN:"string",c:[e.BE,i],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},n={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.CLCM,e.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"},i]},{cN:"comment",b:"__halt_compiler.+?;",eW:!0,k:"__halt_compiler",l:e.UIR},{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[e.BE]},i,c,{b:/->+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",c,e.CBCM,a,n]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},a,n]}}); --------------------------------------------------------------------------------