Skip to content

Instantly share code, notes, and snippets.

@ttscoff
Last active September 7, 2019 16:02
Show Gist options
  • Save ttscoff/4452428 to your computer and use it in GitHub Desktop.
Save ttscoff/4452428 to your computer and use it in GitHub Desktop.
Marked Custom Processor for Jekyll/Octopress previews with img/gist Liquid tags
#!/usr/bin/ruby
# encoding: utf-8
# Version 1
#
# Example custom processor for use with Marked <http://markedapp.com> and Jekyll _posts
# It's geared toward my personal set of plugins and tags, but you'll get the idea.
# It turns
# {% img alignright /images/heythere.jpg 100 100 "Hey there" "hi" %}
# into
# <img src="../images/heythere.jpg" alt="Hey there" class="alignright" title="hi" />
#
# replaces alignleft and alignright classes with appropriate style attribute
# ---
# Replaces {% gist XXXXX filename.rb %} with appropriate script tag
#
# Replace various other OctoPress, Jekyll and custom tags
#
# Processes final output with /usr/bin/kramdown (install kramdown as system gem: `sudo gem install kramdown`)
#
# Be sure to run *without* stripping YAML headers in Marked Behavior preferences.
# full path to image folder
full_image_path = "/Users/ttscoff/Sites/dev/bt/source/"
require "rubygems"
require "rubypants"
require "shellwords"
def class_exists?(class_name)
klass = Module.const_get(class_name)
return klass.is_a?(Class)
rescue NameError
return false
end
if class_exists? 'Encoding'
Encoding.default_external = Encoding::UTF_8 if Encoding.respond_to?('default_external')
Encoding.default_internal = Encoding::UTF_8 if Encoding.respond_to?('default_internal')
end
begin
content = STDIN.read.force_encoding('utf-8')
rescue
content = STDIN.read
end
parts = content.split(/^---\s*$/)
# Read YAML headers as needed before cutting them out
post_title = parts[1].match(/^title:\s+(\!\s*)?["']?(.*?)["']?\s*$/i)[2].strip
# Remove YAML
# content.sub!(/^---.*?---\s*$/m,'')
content = parts[2]
# Fenced code
content.gsub!(/^```(\w+)?\s*\n(.*?)\n```/m,"<pre><code class=\"\\1\">\\2</code></pre>")
# Update image urls to point to absolute file path
content.gsub!(/([\("])\/uploads\/(\d+\/.*?)([ \)"])/,"\\1#{full_image_path}\\2\\3")
# Process image Liquid tags
content.gsub!(/\{% img (.*?) %\}/) {|img|
if img =~ /\{% img (\S.*\s+)?(https?:\/\/\S+|\/\S+|\S+\/\s+)(\s+\d+\s+\d+)?(\s+.+)? %\}/i
classes = $1.strip if $1
src = $2
size = $3
title = $4
if /(?:"|')([^"']+)?(?:"|')\s+(?:"|')([^"']+)?(?:"|')/ =~ title
title = $1
alt = $2
else
alt = title.gsub!(/"/, '&#34;') if title
end
classes.gsub!(/"/, '') if classes
end
style = %Q{ style="float:right;margin:0 0 10px 10px"} if classes =~ /alignright/
style = %Q{ style="float:left;margin:0 10px 10px 0"} if classes =~ /alignleft/
%Q{<img src="#{File.join(full_image_path,src)}" alt="#{alt}" class="#{classes}" title="#{title}"#{style} />}
}
# Process gist tags
content.gsub!(/\{% gist(.*?) %\}/) {|gist|
if parts = gist.match(/\{% gist ([\d]*) (.*?)?%\}/)
gist_id = parts[1].strip
file = parts[2].nil? ? '' : "?file-#{parts[2].strip}"
%Q{<script src="https://gist.github.com/#{gist_id}.js#{file}"></script>}
else
""
end
}
# Replace YouTube tags with a placeholder block
# <http://brettterpstra.com/2013/01/20/jekyll-tag-plugin-for-responsive-youtube-video-embeds/>
content.gsub!(/\{% youtube (\S+) ((\d+) (\d+) )?%\}/) {|vid|
id = $1
width, height = $2.nil? ? [640, 480] : [$3, $4] # width:#{width}px;height:#{height}px;
style = "position:relative;padding-bottom:56.25%;padding-top:30px;height:0;overflow:hidden;background:#666;"
%Q{<div class="bt-video-container" style="#{style}"><h3 style="text-align:center;margin-top:25%;">YouTube Video</h3></div>}
}
# HTML5 semantic pullquote plugin
content.gsub!(/\{% pullquote(.*?) %\}(.*)\{% endpullquote %\}/m) {|q|
quoteblock = $2
if quoteblock =~ /\{"\s*(.+?)\s*"\}/m
quote = RubyPants.new($1).to_html
"<span class='pullquote' data-pullquote='#{quote}'>#{quoteblock.gsub(/\{"\s*|\s*"\}/, '')}</span>"
else
quoteblock
end
}
# Custom downloads manager plugin shiv
content.gsub!(/\{% download(.*?) %\}/,%Q{<div class="download"><h4>A download</h4><p class="dl_icon"><a href="#"><img src="/Volumes/Raptor/Users/ttscoff/Sites/dev/octopress/source/images/serviceicon.jpg"></a></p><div class="dl_body"><p class="dl_link"><a href="#">Download this download</a></p><p class="dl_description">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p><p class="dl_updated">Updated Today</p><p class="dl_info"><a href="#" title="More information on this download">More info&hellip;</a></p></div></div>})
content = "## #{post_title}\n\n#{content}"
puts %x{echo #{Shellwords.escape(content)}|/usr/bin/kramdown}
#!/usr/bin/ruby
# Version 2 (02-03-2015)
#
# Example custom processor for use with Marked <http://markedapp.com> and Jekyll _posts
# It's geared toward my personal set of plugins and tags, but you'll get the idea.
# It turns
# {% img alignright /images/heythere.jpg 100 100 "Hey there" "hi" %}
# into
# <img src="../images/heythere.jpg" alt="Hey there" class="alignright" title="hi" />
#
# replaces alignleft and alignright classes with appropriate style attribute
# ---
# Replaces {% gist XXXXX filename.rb %} with appropriate script tag
#
# Replace various other OctoPress, Jekyll and custom tags
#
# Processes final output with /usr/bin/kramdown (install kramdown as system gem: `sudo gem install kramdown`)
#
# Be sure to run *without* stripping YAML headers in Marked Behavior preferences.
# full path to image folder
full_image_path = "/Users/ttscoff/Sites/dev/bt/source/"
require "rubygems"
require "shellwords"
require "kramdown"
def class_exists?(class_name)
klass = Module.const_get(class_name)
return klass.is_a?(Class)
rescue NameError
return false
end
if class_exists? 'Encoding'
Encoding.default_external = Encoding::UTF_8 if Encoding.respond_to?('default_external')
Encoding.default_internal = Encoding::UTF_8 if Encoding.respond_to?('default_internal')
end
begin
content = STDIN.read.force_encoding('utf-8')
rescue
content = STDIN.read
end
if ENV['MARKED_EXT'].downcase == "taskpaper" || content =~ /@taskpaper/
header = content.scan(/Format\: .*$/) || []
output = ""
prevlevel = 0
begin
content.split("\n").each {|line|
if line =~ /^(\t+)?(.*?):(\s.*)?$/
tabs = $1
project = $2
if tabs.nil?
output += "\n## #{project} ##\n\n"
prevlevel = 0
else
output += "#{tabs.gsub(/^\t/,"")}* **#{project.gsub(/^\s*-\s*/,'')}**\n"
prevlevel = tabs.length
end
elsif line =~ /^(\t+)?\- (.*)$/
task = $2
tabs = $1.nil? ? '' : $1
task = "*<del>#{task}</del>*" if task =~ /@done/
if tabs.length - prevlevel > 1
tabs = "\t"
prevlevel.times {|i| tabs += "\t"}
end
tabs = '' if prevlevel == 0 && tabs.length > 1
output += "#{tabs.gsub(/^\t/,'')}* #{task.strip}\n"
prevlevel = tabs.length
else
next if line =~ /^\s*$/
tabs = ""
prevlevel-1.times {|i| tabs += "\t"} unless prevlevel == 0
output += "\n#{tabs}*#{line.strip}*\n"
end
}
final = header.nil? ? "" : header.join("\n") + "\n\n"
final += "<style>.tag strong {font-weight:normal;color:#555} .tag a {text-decoration:none;border:none;color:#777}</style>\n\n"
# title = File.basename(ENV['MARKED_PATH'],'.taskpaper') || "TaskPaper Preview"
final += output
final.gsub!(/\[\[(.*?)\]\]/,"<a href=\"nvalt://find/\\1\">\\1</a>")
final.gsub!(/(@[^ \n\r\(]+)((\()([^\)]+)(\)))?/,"<em class=\"tag\"><a href=\"nvalt://find/\\0\">\\1\\3<strong>\\4</strong>\\5</a></em>")
final.gsub!(/\|/,'\|')
$stdout.puts Kramdown::Document.new(final).to_html
Process.exit
rescue => err
$stderr.puts err
$stderr.print err.backtrace
raise
Process.exit 1
end
end
def no_custom
$stdout.print "NOCUSTOM"
Process.exit
end
no_custom if ENV['MARKED_EXT'].downcase != "md"
parts = content.split(/^---\s*$/)
no_custom if parts.length != 3
# Read YAML headers as needed before cutting them out
post_title = parts[1].match(/^title:\s+(\!\s*)?["']?(.*?)["']?\s*$/i)
post_title = post_title.nil? ? "" : post_title[2].strip
no_custom if post_title == ""
# Remove YAML
# content.sub!(/^---.*?---\s*$/m,'')
content = parts[2]
# Fenced code
content.gsub!(/^```(\w+)?\s*\n(.*?)\n```/m,"<pre><code class=\"\\1\">\\2</code></pre>")
# Update image urls to point to absolute file path
content.gsub!(/([\("])\/uploads\/(\d+\/.*?)([ \)"])/,"\\1#{full_image_path}\\2\\3")
# Process image Liquid tags
content.gsub!(/\{% img (.*?) %\}/) {|img|
if img =~ /\{% img (\S.*\s+)?(https?:\/\/\S+|\/\S+|\S+\/\s+)(\s+\d+\s+\d+)?(\s+.+)? %\}/i
classes = $1.strip if $1
src = $2
size = $3
title = $4
if /(?:"|')([^"']+)?(?:"|')\s+(?:"|')([^"']+)?(?:"|')/ =~ title
title = $1
alt = $2
else
alt = title.gsub!(/"/, '&#34;') if title
end
classes.gsub!(/"/, '') if classes
end
style = %Q{ style="float:right;margin:0 0 10px 10px"} if classes =~ /alignright/
style = %Q{ style="float:left;margin:0 10px 10px 0"} if classes =~ /alignleft/
%Q{<img src="#{File.join(full_image_path,src)}" alt="#{alt}" class="#{classes}" title="#{title}"#{style} />}
}
# Process gist tags
content.gsub!(/\{% gist(.*?) %\}/) {|gist|
if gistparts = gist.match(/\{% gist ([\d]*) (.*?)?%\}/)
gist_id = gistparts[1].strip
file = gistparts[2].nil? ? '' : "?file-#{gistparts[2].strip}"
%Q{<script src="https://gist.github.com/#{gist_id}.js#{file}"></script>}
else
""
end
}
# Replace YouTube tags with a placeholder block
# <http://brettterpstra.com/2013/01/20/jekyll-tag-plugin-for-responsive-youtube-video-embeds/>
content.gsub!(/\{% youtube (\S+) ((\d+) (\d+) )?(".*?" )?%\}/) {|vid|
id = $1
width, height = $2.nil? ? [640, 480] : [$3, $4] # width:#{width}px;height:#{height}px;
style = "position:relative;padding-bottom:56.25%;padding-top:30px;height:0;overflow:hidden;background:#666;"
%Q{<div class="bt-video-container" style="#{style}"><h3 style="text-align:center;margin-top:25%;">YouTube Video</h3></div>}
}
# HTML5 semantic pullquote plugin
content.gsub!(/\{% pullquote(.*?) %\}(.*)\{% endpullquote %\}/m) {|q|
quoteblock = $2
if quoteblock =~ /\{"\s*(.+?)\s*"\}/m
quote = RubyPants.new($1).to_html
"<span class='pullquote' data-pullquote='#{quote}'>#{quoteblock.gsub(/\{"\s*|\s*"\}/, '')}</span>"
else
quoteblock
end
}
# Custom downloads manager plugin shiv
content.gsub!(/\{% download(.*?) %\}/,%Q{<div class="download"><h4>A download</h4><p class="dl_icon"><a href="#"><img src="/Users/ttscoff/Sites/dev/bt/source/images/serviceicon.jpg"></a></p><div class="dl_body"><p class="dl_link"><a href="#">Download this download</a></p><p class="dl_description">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p><p class="dl_updated">Updated Today</p><p class="dl_info"><a href="#" title="More information on this download">More info&hellip;</a></p></div></div>})
script = ""
# example: add a script to the output (Marked loads jQuery by default)
# script = "<script>(function($){$('#wrapper').css('background','blue');})(jQuery);</script>"
header =<<EOH
title: "#{post_title}"
date: #{Time.now.strftime('%Y-%m-%d %H:%M')}
marked css: BrettTerpstra.com
EOH
content =<<EOC
## #{post_title}
#{content}
#{script}
EOC
# puts header
# %x{open 'marked://style/#{ENV['MARKED_PATH']}?css=brettterpstra.com'}
puts Kramdown::Document.new(content).to_html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment