Skip to content
This repository
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 269 lines (229 sloc) 7.381 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
require 'wunderbar/script'
require 'wunderbar/jquery'
require 'wunderbar/markdown'
require 'wunderbar/pagedown'
require 'ruby2js/filter/functions'
require 'digest/md5'
require 'escape'

Dir.chdir WIKIDATA

# parse request
%r{/(?<file>\w[-\w]+)((?<flag>/)(?<rev>\w*))?$} =~ env['PATH_INFO']
flag ||= '?' if env['REQUEST_URI'].to_s.include? '?'
file ||= 'index'
file.untaint
rev.untaint

_html _width: $WIDTH do
  _head_ do
    _title file
    _style %{
body {background-color: #{(flag=='?') ? '#E0D8D8' : '#FFF'}}

.status {border: 2px solid #000; border-radius: 1em; background: #FAFAD2; padding: 0.5em}
._stdin:before {content: '$ '}
._stdin {color: #9400D3; margin-bottom: 0}
._stdout {margin: 0}
._stderr {margin: 0; color: red}

h1 {text-align: center; margin: 0}

.input, .output {border: 1px solid #888; position: relative; width: 47.5%; height: 400px; overflow: auto}
.input {float: left; left: 1.5%}
.output {float: right; right: 1.5%; background-color: #6C6666; color: #FFF}

.buttons {clear: both; text-align: center; padding-top: 0.5em}
#message {position: absolute; left: 2%; color: #9400d3}
.buttons:hover #message {opacity: 0.7; color: black; display:inline !important}
form {clear: both}
.buttons form {display: inline}
}
  end

  _body? do

    # determine markup
    if _.post? and @markup
      _header.status do
        _h1 'Status'
        if File.exist?(file) and Digest::MD5.hexdigest(File.read(file)) != @hash
          _p 'Write conflict'
        else
          File.open(file, 'w') {|fh| fh.write @markup}
          _.system 'git init' unless Dir.exist? '.git'
          if `git status --porcelain #{file}`.empty?
            _p 'Nothing changed'
          else
            _.system "git add #{file}"

            commit = %w(git commit)
            commit << '--author' << $EMAIL if defined? $EMAIL and $EMAIL
            commit << '--message' << @comment
            commit << file

            _.system commit
          end
        end
      end

    elsif File.exist? file
      # existing file
      if !rev or rev.empty?
        @markup = File.read(file)
      else
        @markup = `git show #{rev}:#{file}`
        flag = nil
      end

    else
      # new file: go directly into edit mode
      @markup = "#{file}\n#{'-'*file.length}\n\nEnter your text here..."
      flag = '?'
    end

    # produce HTML
    if file == '_index'

      # index
      index = Hash[`git ls-tree HEAD --name-only`.scan(/(\w+)()/)].
        merge Hash[*`git status --porcelain`.scan(/(..) (\w+)/).flatten.reverse]
      _table do
        _tbody do
          index.sort.each do |name, status|
            _tr do
              _td status
              _td! {_a name, href: name}
            end
          end
        end
      end

    elsif flag == '?'

      # edit mode
      _header do
        _h1 "~~~ #{file} ~~~"
        _span 'Input', style: 'float: left; margin-left: 2em'
        _span 'Output', style: 'float: right; margin-right: 2em'
      end

      _form_ action: file, method: 'post' do
        _textarea.input @markup, name: 'markup'
        _input type: 'hidden', name: 'hash',
          value: Digest::MD5.hexdigest(@markup)
        _div.output do
          _markdown @markup
        end

        _div.buttons do
          _span.message!
          _input.comment! name: 'comment', placeholder: 'commit message'
          _input.save! type: 'submit', value: 'save'
        end
      end

    elsif flag == '/'

      # revision history
      _h2 "Revision history for #{file}"
      _ul do
        `git log --format="%H|%ai|%an|%s" #{file}`.lines.each do |line|
          hash, date, author, subject = line.split('|')
          _li! {_a date, href: hash; _ " #{subject} by #{author}"}
        end
      end

    else

      #display
      _div_.content do
        _markdown @markup
      end

      _div_.buttons do
        _span.message!
        if !rev or rev.empty?
          _form action: "#{file}?", method: 'post' do
            _input type: 'submit', value: 'edit'
          end
        end
        _form action: "#{file}/" do
          _input type: 'submit', value: 'history'
        end
      end
    end

    @uri = env['REQUEST_URI']

    _script do
      # autosave every 5 seconds
      dirty = false
      setInterval 5000 do
        return unless dirty
        dirty = false

        params = {
          markup: ~'textarea[name=markup]'.val,
          hash: ~'input[name=hash]'.val
        }

        $$.post(@uri, params) do |response|
          ~'input[name=hash]'.val = response.hash
          if response.time
            time = Date.new(response.time).toLocaleTimeString()
            ~'#message'.text("Autosaved at #{time}").show.fadeOut(5000)
          else
            ~'.input'.val(response.markup).readonly = true
            ~'#message'.css(fontWeight: 'bold').text(response.error).show
          end

          ~'#save'.disabled = false if ~'#comment'.val != ''
        end
      end

      # regenerate output every second
      updated = false
      setInterval 1000 do
        return unless updated
        updated = false
        ~'.output'.html = converter.makeHtml(~'.input'.val)
      end

      # update output pane and mark dirty whenever input changes
      converter = Markdown::Converter.new()
      ~'.input'.on(:input) do
        updated = dirty = true
        ~'#save'.disabled = true
      end

      # watch for updates
      watch = ~'<input type="submit" value="watch"/>'
      watch.click do
        watcher = proc do
          $$.ajax(url: @uri, ifModified: true,
            dataType: 'text', accepts: {text: 'text/plain'},
            success: proc do |markup|
              ~'.content'.html = converter.makeHtml(markup)
              time = Date.new().toLocaleTimeString()
              ~'#message'.text("Updated at #{time}").show.fadeOut(5000)
            end
          )
        end
        watcher()
        setInterval(watcher, 10000)
        ~this.hide
        return false
      end
      ~'.buttons form'.first.prepend(watch)

      # disable save button until there is a commit message
      ~'#save'.disabled = true
      ~'#comment'.on(:input) do
        ~'#save'.disabled = false unless dirty
      end

      # resize based on window size
      reserve = ~'header'.height * 2.5 + ~'.buttons'.height
      ~window.resize {
        ~'.input,.output'.height(~window.height - reserve)
      }.trigger(:resize)
    end
  end
end

# process autosave requests
_json do
  hash = Digest::MD5.hexdigest(@markup)
  if File.exist?(file) and Digest::MD5.hexdigest(File.read(file)) != @hash
    _error "Write conflict"
    _markup File.read(file)
  else
    File.open(file, 'w') {|fh| fh.write @markup} unless @hash == hash
    _time Time.now.to_i*1000
  end
  _hash hash
end

# allow the raw markdown to be fetched
_text do
  _ File.read(file) if File.exist?(file)
end

__END__
# Customize where the wiki data is stored
WIKIDATA = '/full/path/to/data/directory'

# Width to wrap lines in output HTML produced (remove to disable wrapping)
$WIDTH = 80

# git author e-mail address override
require 'wunderbar'
require 'etc'
begin
name = Etc.getpwnam($USER).gecos.split(',').first
$EMAIL = "#{name} <#{$USER}@#{ENV['SERVER_NAME']}>"
$EMAIL = nil if %w(www-data _www).include?($USER)
rescue
end
Something went wrong with that request. Please try again.