diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..d7c18ec
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,13 @@
+source "http://rubygems.org/"
+
+# Framework
+gem "camping"
+
+# Template engine
+gem "tilt"
+gem "haml"
+
+# Maruku for content
+gem "maruku"
+gem "ultraviolet"
+
diff --git a/config.ru b/config.ru
new file mode 100644
index 0000000..b05c428
--- /dev/null
+++ b/config.ru
@@ -0,0 +1,2 @@
+require 'timeless'
+run Timeless
diff --git a/maruku-uv.rb b/maruku-uv.rb
new file mode 100644
index 0000000..af183d7
--- /dev/null
+++ b/maruku-uv.rb
@@ -0,0 +1,45 @@
+require 'uv'
+require 'maruku'
+
+module MaRuKu::Out::HTML
+ def to_html_code;
+ source = self.raw_code
+ lang = self.attributes[:lang] || @doc.attributes[:code_lang]
+ lang = 'xml' if lang=='html'
+
+ use_syntax = get_setting :html_use_syntax
+
+ element =
+ if lang
+ begin
+ # eliminate trailing newlines otherwise Syntax crashes
+ source = source.gsub(/\n*\Z/,'')
+
+ html = Uv.parse(source, "xhtml", lang, false, "idle")
+ html = html.gsub(/\'/,''') # IE bug
+ html = html.gsub(/'/,''') # IE bug
+
+ code = Document.new(html, {:respect_whitespace =>:all}).root
+ code.name = 'code'
+ code.attributes['class'] = lang
+ code.attributes['lang'] = lang
+
+ pre = Element.new 'pre'
+ pre << code
+ pre
+ rescue Object => e
+ maruku_error"Error while using the syntax library for code:\n#{source.inspect}"+
+ "Lang is #{lang} object is: \n"+
+ self.inspect +
+ "\nException: #{e.class}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
+
+ tell_user("Using normal PRE because the syntax library did not work.")
+ to_html_code_using_pre(source)
+ end
+ else
+ to_html_code_using_pre(source)
+ end
+
+ add_ws element
+ end
+end
diff --git a/timeless.rb b/timeless.rb
new file mode 100644
index 0000000..33bb06f
--- /dev/null
+++ b/timeless.rb
@@ -0,0 +1,108 @@
+require 'bundler'
+Bundler.setup
+
+require 'yaml'
+require 'time'
+
+require 'camping'
+require 'tilt'
+require 'haml'
+require 'maruku'
+require './maruku-uv'
+
+Camping.goes :Timeless
+
+require './timeless/models/entry'
+require './timeless/models/change'
+
+class NotFound < StandardError; end
+
+module Timeless
+ set :views, File.dirname(__FILE__) + '/timeless/views'
+ set :dynamic_templates, true
+
+ set :haml, {
+ :format => :html5,
+ :escape_html => true,
+ :ugly => true
+ }
+
+ def service(*a)
+ super
+ rescue NotFound
+ @status = 404
+ @method = :r404
+ super(@env["PATH_INFO"])
+ end
+end
+
+module Timeless::Controllers
+ class Index
+ def get
+ @change = Change.last
+ render :index
+ end
+
+ def main_class; "frontpage" end
+ end
+
+ class Stylesheet < R '/style\.css'
+ def get
+ @headers['Content-Type'] = "text/css"
+ File.read("public/style.css")
+ end
+ end
+
+ class Changelog < R '/changelog'
+ def get
+ @changes = Change.all
+ render :changes
+ end
+ end
+
+ class ChangelogN
+ def get(id)
+ @change = Change.new(id)
+ case @change.type
+ when :entry
+ redirect(Entry, @change.entry)
+ when :text
+ render :layout, :layout => false do
+ render :_change, :locals => { :change => @change }
+ end
+ end
+ end
+ end
+
+ class Feed < R '/changelog\.xml'
+ def get
+ @headers['Content-Type'] = 'text/xml'
+ @changes = Change.all.reject { |c| c["feed"] == false }
+ render :feed, :layout => false
+ end
+ end
+
+ class Entry < R '/(.+?)'
+ def get(name)
+ @entry = Models::Entry.new(name)
+
+ if @entry.file?
+ @headers['Content-Type'] = Rack::Mime.mime_type(File.extname(@entry.name))
+ @entry.to_html
+ else
+ render :entry
+ end
+ end
+ end
+end
+
+module Timeless::Helpers
+ def main_class; false end
+
+ def entry_index
+ entries = Timeless::Models::Entry.all.reject { |e| e.file? }
+ half = entries.size / 2
+ [entries[0...half], entries[half..-1]]
+ end
+end
+
diff --git a/timeless/models/change.rb b/timeless/models/change.rb
new file mode 100644
index 0000000..6500987
--- /dev/null
+++ b/timeless/models/change.rb
@@ -0,0 +1,50 @@
+module Timeless::Models
+ class Change
+ attr_reader :id
+
+ def self.last
+ new(Dir["changes/*.yaml"].sort.last[/\d+/])
+ end
+
+ def self.all(options = {})
+ limit = (options[:limit] || 0) - 1
+ Dir["changes/*.yaml"].sort.reverse[0..limit].map { |c| new(c[/\d+/]) }
+ end
+
+ def initialize(id)
+ @id = id.to_i
+ raise NotFound unless exists?
+ end
+
+ def filename; "changes/#{@id}.yaml"; end
+ def exists?; File.exists?(filename) end
+
+ def content
+ @content ||= YAML.load_file(filename)
+ end
+
+ def [](key); content[key.to_s]; end
+ def created_at; Time.parse(self["created_at"]) end
+
+ def type
+ content.has_key?("entry") ? :entry : :text
+ end
+
+ def title
+ type == :entry ? entry.title : content["title"]
+ end
+
+ def to_html
+ type == :entry ? entry.to_snip : Maruku.new(content["text"]).to_html
+ end
+
+ def url
+ type == :entry ? "/#{entry.name}" : "/changelog/#{@id}"
+ end
+
+ def entry
+ @entry ||= Entry.new(content["entry"])
+ end
+ end
+end
+
diff --git a/timeless/models/entry.rb b/timeless/models/entry.rb
new file mode 100644
index 0000000..7dd45db
--- /dev/null
+++ b/timeless/models/entry.rb
@@ -0,0 +1,54 @@
+module Timeless::Models
+ class Entry
+ attr_reader :name
+
+ def self.all
+ entries.map { |name| new(name) }
+ end
+
+ def self.entries
+ Dir["content/*"].reject { |f| f =~ /~/ }.map { |f| File.basename(f) }
+ end
+
+ def initialize(name)
+ @name = name
+ raise NotFound unless exists?
+ end
+
+ def to_param; @name end
+ def <=>(o); title <=> o.title end
+ def filename; "content/#{@name}" end
+ def file?; @name.include?(".") end
+ def exists?; File.exists?(filename) end
+
+ def to_html
+ file? ? content : maruku.to_html
+ end
+
+ def title
+ file? ? @name : maruku.get_setting(:title) || @name
+ end
+
+ def subtitle
+ file? ? "" : maruku.get_setting(:subtitle) || ""
+ end
+
+ def to_snip
+ Maruku.new(content(true)).to_html
+ end
+
+ def content(snip = false)
+ content = File.read(filename)
+ if snip
+ content =~ /\(snip\)/
+ $` || content
+ else
+ content.gsub('(snip)', '')
+ end
+ end
+
+ def maruku
+ @maruku ||= Maruku.new(content)
+ end
+ end
+end
diff --git a/timeless/views/_change.haml b/timeless/views/_change.haml
new file mode 100644
index 0000000..c4cd92b
--- /dev/null
+++ b/timeless/views/_change.haml
@@ -0,0 +1,9 @@
+%article
+ %header
+ %h1
+ %a{:href => change.url}= change.title
+
+ != change.to_html
+ - if change.type == :entry
+ %p
+ %a{:href => change.url} Continue to full post.
diff --git a/timeless/views/changes.haml b/timeless/views/changes.haml
new file mode 100644
index 0000000..51b8363
--- /dev/null
+++ b/timeless/views/changes.haml
@@ -0,0 +1,7 @@
+%header
+ %h1 Recent Changes
+
+%div(class="changelog")
+ - @changes.each do |change|
+ != render :_change, :locals => { :change => change }
+
diff --git a/timeless/views/entry.haml b/timeless/views/entry.haml
new file mode 100644
index 0000000..7ab7f9e
--- /dev/null
+++ b/timeless/views/entry.haml
@@ -0,0 +1,4 @@
+%header
+ %h1= @entry.title
+
+%p!= @entry.to_html
diff --git a/timeless/views/feed.erb b/timeless/views/feed.erb
new file mode 100644
index 0000000..a972875
--- /dev/null
+++ b/timeless/views/feed.erb
@@ -0,0 +1,25 @@
+
+
+ Magnus Holm's timeless repository
+ Thoughts on life, internet and programming
+ tag:judofyr.net,1992-10-15:/
+
+
+ <%= Time.now.xmlschema %>
+
+ Magnus Holm
+ judofyr@gmail.com
+ http://judofyr.net
+
+<% @changes.each do |change|;c=change.created_at.utc %>
+
+ <%= change.title %>
+ tag:timeless.judofyr.net,<%=c.xmlschema[0..9]%>:<%=c.to_i%>
+
+ <%= c.xmlschema %>
+ <%= c.xmlschema %>
+ ]]>
+
+<% end %>
+
+
diff --git a/timeless/views/index.haml b/timeless/views/index.haml
new file mode 100644
index 0000000..4ade841
--- /dev/null
+++ b/timeless/views/index.haml
@@ -0,0 +1,28 @@
+%div(class="changelog")
+ != render :_change, :locals => { :change => @change }
+
+%hr
+
+%p(class="front-buttons")
+ %a(href="http://feeds.feedburner.com/MagnusHolm" class="d") Subscribe to the feed
+ %a(href="/timeless" class="a") What kind of blog is this really?
+ %a(href="#index" class="b") Check out all the articles below ↓
+ %a(href="/changelog" class="c") Have a look at the recent changes
+ %div(style="clear:both")
+ %p(style="clear:both")
+
+%hr
+
+%h2 All articles
+
+%nav(class="index" id="index")
+ - entry_index.each do |entries|
+ %div(class="column")
+ %ul
+ - entries.each do |entry|
+ %li
+ %a{:href => R(Entry, entry)}
+ = entry.title
+ %p= entry.subtitle
+ %div(style="clear:both")
+
diff --git a/timeless/views/layout.haml b/timeless/views/layout.haml
new file mode 100644
index 0000000..20d997c
--- /dev/null
+++ b/timeless/views/layout.haml
@@ -0,0 +1,22 @@
+!!!
+%head
+ %meta(charset="utf-8")
+ %title Timeless from Magnus Holm
+ %link{:rel => "stylesheet", :href => R(Stylesheet)}
+
+%body(class=main_class)
+ %header(class="main")
+ %h1
+ %a{:href => R(Index)}
+ Magnus Holm's timeless repository
+
+ %article
+ != yield
+
+ %footer
+ %p
+ 2010 ©
+ %a(href="mailto:judofyr@gmail.com") Magnus Holm
+ —
+ %a{:href => R(Entry, 'comments')} Leave a comment
+