require 'ostruct' require 'pp' require 'date' require 'livetext' require 'runeblog' errfile = File.new("/tmp/liveblog.out", "w") STDERR.reopen(errfile) def init_liveblog # FIXME - a lot of this logic sucks here = Dir.pwd dir = here loop { dir = Dir.pwd; break if File.exist?("config"); Dir.chdir("..") } Dir.chdir(here) @blog = $_blog = RuneBlog.new(dir) @root = @blog.root @view = @blog.view @view_name = @blog.view.name @vdir = @blog.view.dir @version = RuneBlog::VERSION @theme = @vdir + "/themes/standard/" end def post @meta = OpenStruct.new @meta.num = _args[0] _out " \n " end def quote _passthru "
" _passthru _body _passthru "
" _optional_blank_line end def categories # does nothing right now end def style fname = _args[0] _passthru %[] end # Move elsewhere later! def h1; _passthru "

#{@_data}

"; end def h2; _passthru "

#{@_data}

"; end def h3; _passthru "

#{@_data}

"; end def h4; _passthru "

#{@_data}

"; end def h5; _passthru "
#{@_data}
"; end def h6; _passthru "
#{@_data}
"; end def hr; _passthru "
"; end def list _out "" _optional_blank_line end def list! _out "" _optional_blank_line end def html_body(file) file.puts "\n " yield file.puts " \n" end def make_magic_links # FIXME remember strings may not be safe line = _data.chomp input, cardfile, mainfile, card_title = *line.split(" ", 4) pairs = File.readlines(input).map {|line| line.chomp.split(",", 2) } # HTML for main area (iframe) File.open("#{mainfile}.html", "w") do |f| html_body(f) do f.puts "

#{card_title}

" pairs.each {|file, title| f.puts %[#{title}
] } end end # HTML for sidebar card File.open("#{cardfile}.html", "w") do |f| f.puts <<-EOS
#{card_title}
EOS pairs.each do |file, title| f.puts <<-EOS
  • #{title}
  • EOS end f.puts <<-EOS
    EOS end end ### inset def inset lines = _body box = "" lines.each do |line| line = line.dup if line[0] == "/" # Only into inset line[0] = ' ' box << line.dup + " " line.replace(" ") end if line[0] == "|" # Into inset and body line[0] = ' ' box << line.dup + " " end _passthru(line) end lr = _args.first wide = _args[1] || "25" _passthru "
    " _passthru '' _passthru box _passthru_noline '
    ' _optional_blank_line end def _errout(*args) ::STDERR.puts *args end def _passthru(line) return if line.nil? line = _format(line) _out line + "\n" _out "

    " if line.empty? && ! @_nopara end def _passthru_noline(line) return if line.nil? line = _format(line) _out line _out "

    " if line.empty? && ! @_nopara end def title raise "'post' was not called" unless @meta title = @_data.chomp @meta.title = title setvar :title, title _out %[

    #{title}


    ] _optional_blank_line end def pubdate raise "'post' was not called" unless @meta _debug "data = #@_data" # Check for discrepancy? match = /(\d{4}).(\d{2}).(\d{2})/.match @_data junk, y, m, d = match.to_a y, m, d = y.to_i, m.to_i, d.to_i @meta.date = ::Date.new(y, m, d) @meta.pubdate = "%04d-%02d-%02d" % [y, m, d] _optional_blank_line end def image # primitive so far _debug "img: huh? " fname = _args.first path = "assets/#{fname}" _out "" _optional_blank_line end def tags raise "'post' was not called" unless @meta _debug "args = #{_args}" @meta.tags = _args.dup || [] _optional_blank_line end def views raise "'post' was not called" unless @meta _debug "data = #{_args}" @meta.views = _args.dup _optional_blank_line end def pin raise "'post' was not called" unless @meta _debug "data = #{_args}" # verify only already-specified views? @meta.pinned = _args.dup _optional_blank_line end def write_post raise "'post' was not called" unless @meta save = Dir.pwd @postdir.gsub!(/\/\//, "/") # FIXME unneeded? Dir.mkdir(@postdir) unless Dir.exist?(@postdir) # FIXME remember assets! Dir.chdir(@postdir) @meta.views = @meta.views.join(" ") if @meta.views.is_a? Array @meta.tags = @meta.tags.join(" ") if @meta.tags.is_a? Array File.write("teaser.txt", @meta.teaser) fields = [:num, :title, :date, :pubdate, :views, :tags] fname2 = "metadata.txt" f2 = File.open(fname2, "w") do |f2| fields.each {|fld| f2.puts "#{fld}: #{@meta.send(fld)}" } end Dir.chdir(save) rescue => err puts "err = #{err}" puts err.backtrace.join("\n") end def teaser raise "'post' was not called" unless @meta @meta.teaser = _body_text setvar :teaser, @meta.teaser _out @meta.teaser + "\n" # FIXME end def finalize unless @meta puts @live.body return end if @blog.nil? return @meta end @slug = @blog.make_slug(@meta) slug_dir = @slug @postdir = @blog.view.dir + "/posts/#{slug_dir}" STDERR.puts "--- finalize: pwd = #{Dir.pwd} postdir = #@postdir" write_post @meta end $Dot = self # Clunky! for dot commands called from Functions class # Find a better way to do this? class Livetext::Functions def br(n="1") # Thought: Maybe make a way for functions to "simply" call the # dot command of the same name?? Is this trivial?? n = n.empty? ? 1 : n.to_i "
    "*n end def h1(param); "

    #{param}

    "; end def h2(param); "

    #{param}

    "; end def h3(param); "

    #{param}

    "; end def h4(param); "

    #{param}

    "; end def h5(param); "
    #{param}
    "; end def h6(param); "
    #{param}
    "; end def hr(param=nil) $Dot.hr end def image(param) "" end end ###### experimental... class Livetext::Functions def _var(name) ::Livetext::Vars[name] || "[:#{name} is undefined]" end def link file, cdata = self.class.param.split("||", 2) %[] end end ### def _var(name) # FIXME scope issue! ::Livetext::Vars[name] || "[:#{name} is undefined]" end def head # Does NOT output tags args = _args args.each do |inc| self.data = inc _include end # Depends on vars: title, desc, host defaults = {} defaults = { "charset" => %[], "http-equiv" => %[], "title" => %[\n #{_var(:blog)} | #{_var("blog.desc")}\n ], "generator" => %[], "og:title" => %[], "og:locale" => %[], "description" => %[], "og:description" => %[], "linkc" => %[], "og:url" => %[], "og:site_name" => %[], "style" => %[], "feed" => %[], "favicon" => %[\n ] } result = {} lines = _body lines.each do |line| line.chomp word, remain = line.split(" ", 2) case word when "viewport" result["viewport"] = %[] when "script" # FIXME this is broken file = remain text = File.read(file) result["script"] = Livetext.new.transform(text) when "style" result["style"] = %[] # Later: allow other overrides when ""; break else STDERR.puts "-- got '#{word}'; old value = #{result[word].inspect}" if defaults[word] result[word] = %[] STDERR.puts "-- new value = #{result[word].inspect}" else puts "Unknown tag '#{word}'" end end end hash = defaults.dup.update(result) # FIXME collisions? # _out " " hash.each_value {|x| _out " " + x } _out "" end ########## newer stuff... def meta args = _args enum = args.each str = "" _out str end def recent_posts # side-effect _out %[
    ] all_teasers _out %[
    ] end def sidebar _out %[
    ] _body do |line| tag = line.chomp.strip.downcase Dir.chdir("widgets/#{tag}") do source = "#{tag}.lt3" livetext source, "/dev/null" _include_file "card-#{source}.html" end end _out %[
    ] end def sidebar! _out %[
    ] _args do |line| tag = line.chomp.strip source = "sidebar/#{tag.downcase}.lt3" unless File.exist?(source) source = "widgets/#{tag.downcase}/main.lt3" end _include_file source end _out %[
    ] end def stylesheet lines = _body url = lines[0] integ = lines[1] cross = lines[2] || "anonymous" _out %[] end def script lines = _body url = lines[0] integ = lines[1] cross = lines[2] || "anonymous" _out %[] end ### How this next bit works: ### ### all_teasers will call _find_recent_posts ### ### _find_recent_posts will search higher in the directory structure ### for where the posts are (0001, 0002, ...) NOTE: This implies you ### must be in some specific place when this code is run. ### It returns the 20 most recent posts. ### ### all_teasers will then pick a small number of posts and call _teaser ### on each one. (The code in _teaser really belongs in a small template ### somewhere.) ### def _find_recent_posts @vdir = _var(:FileDir).match(%r[(^.*/views/.*?)/])[1] posts = nil dir_posts = @vdir + "/posts" entries = Dir.entries(dir_posts) posts = entries.grep(/^\d\d\d\d/).map {|x| dir_posts + "/" + x } posts.select! {|x| File.directory?(x) } # directories that start with four digits posts = posts.sort {|a, b| b.to_i <=> a.to_i } # sort descending posts[0..19] # return 20 at most end def all_teasers open = <<-HTML
    HTML close = <<-HTML
    HTML text = <<-HTML HTML posts = _find_recent_posts wanted = [5, posts.size].min # estimate how many we want? enum = posts.each wanted.times do postid = File.basename(enum.next) postid = postid.to_i text << _teaser(postid) # side effect! calls _out end text << "" File.write("recent.html", text) _out %[] end def _post_lookup(postid) # side-effect # .. = templates, ../.. = views/thisview slug = title = date = teaser_text = nil dir_posts = @vdir + "/posts" posts = Dir.entries(dir_posts).grep(/^\d\d\d\d/).map {|x| dir_posts + "/" + x } posts.select! {|x| File.directory?(x) } post = posts.select {|x| File.basename(x).to_i == postid } raise "Error: More than one post #{postid}" if post.size > 1 postdir = post.first vp = RuneBlog::ViewPost.new(@blog.view, postdir) vp end def _interpolate(str, context) # FIXME move this later wrapped = "%[" + str.dup + "]" # could fail... eval(wrapped, context) end def _teaser(slug) id = slug.to_i text = nil post_entry_name = @theme + "blog/post_entry.lt3" @_post_entry ||= File.read(post_entry_name) vp = _post_lookup(id) nslug, aslug, title, date, teaser_text = vp.nslug, vp.aslug, vp.title, vp.date, vp.teaser_text path = vp.path url = "#{path}/#{aslug}.html" # Should be relative to .blogs!! FIXME date = Date.parse(date) date = date.strftime("%B %e
    %Y") text = _interpolate(@_post_entry, binding) text end def _card_generic(card_title:, middle:, extra: "") front = <<-HTML
    #{card_title}
    HTML tail = <<-HTML
    HTML text = front + middle + tail _out text + "\n " end def card_iframe title, lines = _data, _body lines.map!(&:chomp) url = lines[0].chomp stuff = lines[1..-1].join(" ") # FIXME later middle = <<-HTML HTML _card_generic(card_title: title, middle: middle, extra: "bg-dark text-white") end def _main(url) %[href="javascript: void(0)" onclick="javascript:open_main('#{url}')"] end def card1 title, lines = _data, _body lines.map!(&:chomp) card_text = lines[0] url, classname, cdata = lines[1].split(",", 4) main = _main(url) middle = <<-HTML

    #{card_text}

    #{cdata} HTML _card_generic(card_title: title, middle: middle, extra: "bg-dark text-white") end def card2 str = _data file, card_title = str.chomp.split(" ", 2) card_title = %[#{card_title}] # FIXME is this wrong?? open = <<-HTML
    #{card_title}
      HTML _out open _body do |line| url, cdata = line.chomp.split(",", 3) main = _main(url) _out %[
    • #{cdata}
    • ] end close = %[
    \n
    \n
    ] _out close end def tag_cloud title = _data title = "Tag Cloud" if title.empty? open = <<-HTML
    #{title}
    HTML _out open _body do |line| line.chomp! url, classname, cdata = line.split(",", 4) main = _main(url) _out %[#{cdata}] end end def navbar title = _var(:blog) open = <<-HTML HTML first = true _out open _body do |line| href, cdata = line.chomp.strip.split(" ", 2) main = _main(href) if first first = false _out %[] else main = _main("navbar/#{href}") _out %[] end end _out close end