Commits

nemunemu committed 4255ccf

Initial commmit

Comments (0)

Files changed (50)

+{
+    "directory": "app/bower_components"
+}
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+
+# Change these settings to your own preference
+indent_style = space
+indent_size = 4
+
+# We recommend you to keep these unchanged
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
+* text=auto
+node_modules
+dist
+test/temp
+.sass-cache
+app/bower_components
+.tmp
+test/bower_components/
+{
+    "node": true,
+    "browser": true,
+    "esnext": true,
+    "bitwise": true,
+    "camelcase": true,
+    "curly": true,
+    "eqeqeq": true,
+    "immed": true,
+    "indent": 4,
+    "latedef": true,
+    "newcap": true,
+    "noarg": true,
+    "quotmark": "single",
+    "regexp": true,
+    "undef": true,
+    "unused": true,
+    "strict": true,
+    "trailing": true,
+    "smarttabs": true,
+    "jquery": true
+}
+{
+  "generator-backbone": {
+    "appPath": "app",
+    "coffee": true,
+    "testFramework": "mocha",
+    "templateFramework": "lodash",
+    "compassBootstrap": true
+  }
+}
+"use strict"
+LIVERELOAD_PORT = 35729
+SERVER_PORT = 9000
+# lrSnippet = require("connect-livereload")(port: LIVERELOAD_PORT)
+# mountFolder = (connect, dir) ->
+#   connect.static require("path").resolve(dir)
+
+
+# # Globbing
+# for performance reasons we're only matching one level down:
+# 'test/spec/{,*/}*.js'
+# use this if you want to match all subfolders:
+# 'test/spec/**/*.js'
+# templateFramework: 'lodash'
+module.exports = (grunt) ->
+
+  # show elapsed time at the end
+  require("time-grunt") grunt
+
+  # load all grunt tasks
+  require("load-grunt-tasks") grunt
+
+  # configurable paths
+  yeomanConfig =
+    app: "app"
+    dist: "dist"
+
+  grunt.initConfig
+    yeoman: yeomanConfig
+    watch:
+      options:
+        nospawn: true
+        livereload: true
+
+      coffee:
+        files: ["<%= yeoman.app %>/scripts/{,*/}*.coffee"]
+        tasks: ["coffee:dist"]
+
+      coffeeTest:
+        files: ["test/spec/{,*/}*.coffee"]
+        tasks: ["coffee:test"]
+
+      compass:
+        files: ["<%= yeoman.app %>/styles/{,*/}*.{scss,sass}"]
+        tasks: ["compass"]
+
+      livereload:
+        options:
+          livereload: LIVERELOAD_PORT
+
+        files: [
+          "<%= yeoman.app %>/*.html"
+          "{.tmp,<%= yeoman.app %>}/styles/{,*/}*.css"
+          "{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js"
+          "<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}"
+          "<%= yeoman.app %>/scripts/templates/*.{ejs,mustache,hbs}"
+          "test/spec/**/*.js"
+        ]
+
+      jst:
+        files: ["<%= yeoman.app %>/scripts/templates/*.ejs"]
+        tasks: ["jst"]
+
+      test:
+        files: [
+          "<%= yeoman.app %>/scripts/{,*/}*.js"
+          "test/spec/**/*.js"
+        ]
+        tasks: ["test:true"]
+
+    express:
+      options:
+        port: SERVER_PORT
+        hostname: 'localhost'
+        script: 'app/app.coffee'
+        cmd: 'coffee'
+
+      dev: {}
+
+      test:
+        options:
+          port: 9001
+
+    # connect:
+    #   options:
+    #     port: SERVER_PORT
+
+    #     # change this to '0.0.0.0' to access the server from outside
+    #     hostname: "localhost"
+
+    #   livereload:
+    #     options:
+    #       middleware: (connect) ->
+    #         [
+    #           lrSnippet
+    #           mountFolder(connect, ".tmp")
+    #           mountFolder(connect, yeomanConfig.app)
+    #         ]
+
+    #   test:
+    #     options:
+    #       port: 9001
+    #       middleware: (connect) ->
+    #         [
+    #           lrSnippet
+    #           mountFolder(connect, ".tmp")
+    #           mountFolder(connect, "test")
+    #           mountFolder(connect, yeomanConfig.app)
+    #         ]
+
+    #   dist:
+    #     options:
+    #       middleware: (connect) ->
+    #         [mountFolder(connect, yeomanConfig.dist)]
+
+    open:
+      server:
+        path: "http://localhost:<%= express.options.port %>"
+
+      test:
+        path: "http://localhost:<%= express.test.options.port %>"
+
+    clean:
+      dist: [
+        ".tmp"
+        "<%= yeoman.dist %>/*"
+      ]
+      server: ".tmp"
+
+    jshint:
+      options:
+        jshintrc: ".jshintrc"
+        reporter: require("jshint-stylish")
+
+      all: [
+        "Gruntfile.js"
+        "<%= yeoman.app %>/scripts/{,*/}*.js"
+        "!<%= yeoman.app %>/scripts/vendor/*"
+        "test/spec/{,*/}*.js"
+      ]
+
+    mocha:
+      all:
+        options:
+          run: true
+          urls: ["http://localhost:<%= connect.test.options.port %>/index.html"]
+
+    coffee:
+      dist:
+        files: [
+
+          # rather than compiling multiple files here you should
+          # require them into your main .coffee file
+          expand: true
+          cwd: "<%= yeoman.app %>/scripts"
+          src: "{,*/}*.coffee"
+          dest: ".tmp/scripts"
+          ext: ".js"
+        ]
+
+      test:
+        files: [
+          expand: true
+          cwd: "test/spec"
+          src: "{,*/}*.coffee"
+          dest: ".tmp/spec"
+          ext: ".js"
+        ]
+
+    compass:
+      options:
+        sassDir: "<%= yeoman.app %>/styles"
+        cssDir: ".tmp/styles"
+        imagesDir: "<%= yeoman.app %>/images"
+        javascriptsDir: "<%= yeoman.app %>/scripts"
+        fontsDir: "<%= yeoman.app %>/styles/fonts"
+        importPath: "<%= yeoman.app %>/bower_components"
+        relativeAssets: true
+
+      dist: {}
+      server:
+        options:
+          debugInfo: true
+
+    # requirejs:
+    #   dist:
+
+    #     # Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js
+    #     options:
+
+    #       # `name` and `out` is set by grunt-usemin
+    #       baseUrl: ".tmp/scripts"
+    #       optimize: "none"
+    #       paths:
+    #         templates: "../../.tmp/scripts/templates"
+    #         jquery: "../../app/bower_components/jquery/jquery"
+    #         underscore: "../../app/bower_components/underscore/underscore"
+    #         backbone: "../../app/bower_components/backbone/backbone"
+    #         ace: "../../app/bower_components/ace/ace"
+
+
+    #       # TODO: Figure out how to make sourcemaps work with grunt-usemin
+    #       # https://github.com/yeoman/grunt-usemin/issues/30
+    #       #generateSourceMaps: true,
+    #       # required to support SourceMaps
+    #       # http://requirejs.org/docs/errors.html#sourcemapcomments
+    #       preserveLicenseComments: false
+    #       useStrict: true
+    #       wrap: true
+
+    # bower:
+    #   all:
+    #     rjsConfig: "<%= yeoman.app %>/scripts/main.js"
+
+    jst:
+      options:
+        amd: true
+
+      compile:
+        files:
+          ".tmp/scripts/templates.js": ["<%= yeoman.app %>/scripts/templates/*.ejs"]
+
+  grunt.registerTask 'dryice', ->
+    { buildAce } = require("#{yeomanConfig.app}/bower_components/ace/Makefile.dryice.js")
+    buildAce({})
+
+  grunt.registerTask "createDefaultTemplate", ->
+    grunt.file.write ".tmp/scripts/templates.js", "this.JST = this.JST || {};"
+    return
+
+  grunt.registerTask "server", ->
+    grunt.log.warn "The `server` task has been deprecated. Use `grunt serve` to start a server."
+    grunt.task.run ["serve:" + target]
+    return
+
+  grunt.registerTask "serve", (target) ->
+    if target is "dist"
+      return grunt.task.run([
+        "build"
+        "open:server"
+        #   "connect:dist:keepalive"
+      ])
+    if target is "test"
+      return grunt.task.run([
+        "clean:server"
+        "coffee"
+        "createDefaultTemplate"
+        "jst"
+        "compass:server"
+        # "connect:test"
+        "open:test"
+        "watch:livereload"
+      ])
+    grunt.task.run [
+      "clean:server"
+      "coffee:dist"
+      "createDefaultTemplate"
+      "jst"
+      "compass:server"
+      #    "connect:livereload"
+      'express:dev'
+      # "open:server"
+      "watch"
+    ]
+    return
+
+  grunt.registerTask "test", (isConnected) ->
+    isConnected = Boolean(isConnected)
+    testTasks = [
+      "clean:server"
+      "coffee"
+      "createDefaultTemplate"
+      "jst"
+      "compass"
+      # "connect:test"
+      "mocha"
+      "watch:test"
+    ]
+    unless isConnected
+      grunt.task.run testTasks
+    else
+
+      # already connected so not going to connect again, remove the connect:test task
+  #    testTasks.splice testTasks.indexOf("connect:test"), 1
+      grunt.task.run testTasks
+
+  grunt.registerTask "build", [
+    "clean:dist"
+    "coffee"
+    "createDefaultTemplate"
+    "jst"
+    "compass:dist"
+    # "useminPrepare"
+    "requirejs"
+    #   "imagemin"
+    #   "htmlmin"
+    "concat"
+    # "cssmin"
+    # "uglify"
+    # "copy"
+    # "rev"
+    # "usemin"
+  ]
+  grunt.registerTask "default", [
+    "jshint"
+    "test"
+    "build"
+  ]
+  return

app/api/controllers/controller.coffee

+module.exports = ->
+  return class @Controller
+    constructor: ->
+      @beforeActions.forEach ({ condition, action }) =>
+        (condition.methods || []).forEach (method) =>
+          original_method = @[method]
+          @["#{method}_without_filter"] = original_method
+          @[method] = @_filter(original_method, action)
+
+    @beforeAction: (methods, action) ->
+      @prototype.beforeActions ||= []
+      filter = @_makeActionRule(methods, action)
+      @prototype.beforeActions.push(filter)
+
+    @_makeActionRule: (methods, action) ->
+      methods = [methods] unless methods instanceof Array
+      condition:
+        methods: methods
+      action: action
+
+    _filter: (method, action) ->
+      =>
+        action.apply(this, arguments)
+        method.apply(this, arguments)
+

app/api/controllers/directories_controller.coffee

+module.exports = ->
+  Directory = @requireModel('Directory')
+  Controller = @requireController('Controller')
+  ErrorPageException = @requireUtil('ErrorPageException')
+
+  return class @Controller.DirectoriesController extends Controller
+    show: (_uuid) ->
+      @directory.toJsonWithEntries()
+
+    getUuid: (_uuid, entryName) ->
+      if entry = @directory.getUuidOfEntry(entryName)
+        uuid: entry.uuid
+      else
+        throw new ErrorPageException
+          message: 'entry.notFound'
+
+    @beforeAction ['show', 'getUuid'], (uuid) ->
+      unless @directory = Directory.findByUuid(uuid)
+        throw new ErrorPageException
+          message: 'directory.notFound'
+

app/api/controllers/sources_controller.coffee

+module.exports = ->
+  Source = @requireModel('Source')
+  Controller = @requireController('Controller')
+  ErrorPageException = @requireUtil('ErrorPageException')
+
+  class @Controller.SourcesController extends Controller
+    show: (uuid) ->
+      @source.toJsonWithContent()
+
+    @beforeAction ['show'], (uuid) ->
+      unless @source = Source.findByUuid(uuid)
+        throw new ErrorPageException
+          message: 'notFound'
+

app/api/models/directory.coffee

+module.exports = ->
+  fs = require('fs')
+  path = require('path')
+  File = @requireModel('File')
+
+  class @Model.Directory extends File
+    isDirectory: true
+
+    toJsonWithEntries: ->
+      @toJson(@jsonKeys.concat('entries'))
+
+    @property 'entries',
+      get: ->
+        _ = require('underscore')
+        @_entries ||= _.map(fs.readdirSync(@path), (basepath) =>
+          File.build(path.join(@path, basepath))
+        )
+

app/api/models/file.coffee

+module.exports = ->
+  fs = require('fs')
+  glass = this
+
+  UuidModule = @requireUtil('UuidModule')
+
+  return class @Model.File extends UuidModule
+    @findByUuid: (uuid) ->
+      @build(super)
+
+    @build: (path) ->
+      console.log("reading: #{path}")
+      if fs.existsSync(path)
+        stat = fs.statSync(path)
+        if stat.isDirectory()
+          Directory = glass.requireModel('Directory')
+          new Directory(path)
+        else
+          Source = glass.requireModel('Source')
+          new Source(path)
+
+    constructor: (@path) ->
+
+    toJson: (jsonKeys = @jsonKeys) ->
+      _ = require('underscore')
+      _.chain(jsonKeys)
+        .reduce((json, key) ->
+          value = @[key]
+          json[key] =
+            if _.isArray(value)
+              _.map(value, (v) -> v.toJson?() || v)
+            else
+              value.toJson?() || value
+          json
+        , {}, this)
+        .value()
+
+
+    getJsonKeys: -> [
+      'path'
+      'uuid'
+      'isDirectory'
+    ]
+
+    isDirectory: false
+
+    @property 'uuid',
+      get: ->
+        @constructor.uuidFromPath(@path)
+
+    @property 'jsonKeys',
+      get: -> @getJsonKeys()
+

app/api/models/source.coffee

+
+module.exports = ->
+  File = @requireModel('File')
+  class @Model.Source extends File
+    fs = require('fs')
+
+    @property 'content',
+      get: ->
+        @_content ||= fs.readFileSync(@path, 'utf8')
+
+    toJsonWithContent: ->
+      @toJson(@jsonKeys.concat('content'))
+
+path = require 'path'
+
+options =
+  port: process.env.PORT or 3000
+  appRoot: __dirname
+  root: path.join(__dirname, '..')
+
+app = require('./config/server')(options)
+Glass = require('./config/glass')(app, options)
+require('./config/routes')(Glass, options)
+
+Glass.app.listen app.get('port'), ->
+  console.log "Express listening on port #{app.get('port')}"

app/assets/javascripts/application.js

+//= require config
+//= require glass
+
+//= require_tree ./backbone/models
+//= require_tree ./backbone/views
+//= require_tree ./backbone/routers
+//= require_tree ./backbone/collections
+
+$(document).ready(function () {
+    Backbone.history.start();
+});

app/assets/javascripts/backbone/collections/file_list.js.coffee

+#= require backbone/models/file
+
+namespace = Glass.namespace('collections')
+
+class namespace.FileList extends Backbone.Collection
+  model: Glass.models.File
+

app/assets/javascripts/backbone/models/file.js.coffee

+namespace = Glass.namespace('models')
+
+class namespace.File extends Backbone.Model
+  urlRoot: '/file'
+  idAttribute: 'uuid'
+
+  constructor: (attrs) ->
+    if @constructor is namespace.File
+      if attrs.isDirectory
+        return new namespace.files.Directory(arguments...)
+      else
+        return new namespace.files.Source(arguments...)
+    else
+      super
+
+  @property 'content',
+    get: ->
+
+  @property 'basepath',
+    get: -> _.last(@path.split('/'))
+    set: ->
+
+  @property 'path',
+    get: -> unescape(@uuid)
+
+  @property 'uuid',
+    get: -> @get('uuid')
+
+  @property 'viewId',
+    get: ->
+      uuid = @uuid.replace(/[^\w\-]/g, '-')
+      "file-#{uuid}"

app/assets/javascripts/backbone/models/files/directory.js.coffee

+#= require backbone/models/file
+#= require backbone/collections/file_list
+
+namespace = Glass.namespace('models.files')
+
+class namespace.Directory extends Glass.models.File
+  urlRoot: '/directory'
+  isDirectory: true
+
+  initialize: ->
+    @entries = new Glass.collections.FileList
+
+  set: (key, val, options) ->
+    if typeof key is 'object'
+      attrs = key
+      options = val
+    else
+      (attrs = {})[key] = val
+    options ||= {}
+
+    if entries = attrs['entries']
+      @entries.set(entries)
+    super(_.omit(attrs, 'entries'), options)
+

app/assets/javascripts/backbone/models/files/source.js.coffee

+namespace = Glass.namespace('models.files')
+
+class namespace.Source extends Glass.models.File
+  urlRoot: '/source'
+
+  @property 'content',
+    get: -> @get('content')
+

app/assets/javascripts/backbone/routers/home_router.js.coffee

+#= require backbone/views/viewer_view
+#= require backbone/views/filemenu_view
+#= require backbone/models/files/directory
+
+class HomeRouter extends Backbone.Router
+  routes:
+    '': 'home'
+
+  home: ->
+    @editor = new Glass.views.ViewerView
+      el: $('div#viewer')
+
+    $('#toggle-sidr').sidr
+      name: 'filemenu'
+      source: -> ''
+
+    homedir = JSON.parse($('#homedir').html())
+
+    @filemenu = new Glass.views.FilemenuView
+      model: new Glass.models.files.Directory(homedir)
+      el: '#filemenu'
+
+Glass.namespace('routers').HomeRouter = new HomeRouter

app/assets/javascripts/backbone/views/filemenu/directory_view.js.coffee

+#= require backbone/models/files/directory
+#= require ./file_view.js.coffee
+
+namespace = Glass.namespace('views.filemenu')
+
+class namespace.DirectoryView extends namespace.FileView
+  events:
+    'click > div.title': 'toggle'
+
+  initialize: ->
+    @model.entries.on('add', @addChild, this)
+
+  addChild: (model) ->
+    html = "<li class=\"#{model.viewId}\"></li>"
+    childView = namespace.FileView.build
+      model: model
+      el: $(html)
+
+    @childViews ||= []
+    @childViews.push(childView)
+
+    @$el.children('ul.children').append(childView.render().el)
+
+  expand: ->
+    return if @isExpanded
+    @render()
+    @model.fetch()
+    @isExpanded = true
+    @$el.children('ul.children').show()
+
+  collapse: ->
+    return unless @isExpanded
+    @isExpanded = false
+    @$el.children('ul.children').hide()
+
+  toggle: ->
+    if @isExpanded then @collapse() else @expand()
+
+  render: ->
+    if @$el.html()
+      @$el.children('div.title').html(@model.basepath)
+    else
+      @$el.html("<div class=\"title\">#{@model.basepath}</div>")
+      @$el.append($("<ul class=\"children\"></ul>").hide())
+    this
+

app/assets/javascripts/backbone/views/filemenu/file_view.js.coffee

+namespace = Glass.namespace('views.filemenu')
+
+class namespace.FileView extends Backbone.View
+  @build: (options) ->
+    if options.model.get('isDirectory')
+      new namespace.DirectoryView(options)
+    else
+      new namespace.SourceView(options)
+
+  @property 'viewId',
+    get: ->
+      uuid = @uuid.replace(/[^\w\-]/g, '-')
+      "file-#{uuid}"

app/assets/javascripts/backbone/views/filemenu/source_view.js.coffee

+#= require backbone/models/files/source
+#= require ./file_view.js.coffee
+
+namespace = Glass.namespace('views.filemenu')
+
+class namespace.SourceView extends namespace.FileView
+  events:
+    'click > div.title': 'open'
+
+  open: ->
+
+  render: ->
+    if @$el.html()
+      @$el.children('div.title').html(@model.basepath)
+    else
+      @$el.html("<div class=\"title\">#{@model.basepath}</div>")
+    this
+

app/assets/javascripts/backbone/views/filemenu_view.js.coffee

+#= require_tree ./filemenu
+
+namespace = Glass.namespace('views')
+
+class namespace.FilemenuView extends Backbone.View
+  initialize: ->
+    @render()
+
+  buildRootView: ->
+    @rootView ||= namespace.filemenu.FileView.build
+      model: @model
+      el: $("<li id=\"#{@model.viewId}\"></li>")
+
+  render: ->
+    unless @$el.html()
+      @$el.html("<h1 class=\"filemenu-root\">#{@model.path}</h1>")
+      @$el.append($("<ul class=\"root\"></ul>"))
+      @$el.children('ul.root').append(@buildRootView().render().el)
+    this

app/assets/javascripts/backbone/views/viewer_view.js.coffee

+namespace = Glass.namespace('views')
+
+class namespace.ViewerView extends Backbone.View
+  initialize: ->
+    @editor = ace.edit(@el)
+    @editor.container.style.opacity = ''
+    @editor.setOptions
+      maxLines: 30,
+      autoScrollEditorIntoView: true
+      # mode: 'ace/mode/javascript',

app/assets/javascripts/config.js.coffee

+property = (prop, desc) ->
+  Object.defineProperty @prototype, prop, desc
+
+Function::property = property
+

app/assets/javascripts/glass.js.coffee

+Glass =
+  namespace: (namespace) ->
+    parts = namespace.split('.')
+    _.reduce(parts, (layer, part) ->
+      layer[part] ||= {}
+    , this)
+
+window.Glass = Glass

app/config/glass.coffee

+module.exports = (app, options) ->
+  _ = require('underscore')
+
+  Glass =
+    utils: {}
+    Model: {}
+    Controller: {}
+
+    request: (controllerName, method, params, req, res) ->
+      controller = new (Glass.requireController(controllerName))()
+      ErrorPageException = @requireUtil('ErrorPageException')
+
+      try
+        if typeof controller[method] is 'function'
+          res.send(controller[method](params...))
+        else
+          throw new ErrorPageException
+            message: "unknown controller method: #{method}"
+      catch error
+        if error instanceof ErrorPageException
+          console.log("error #{error.status}: (#{error.message})")
+          if error.status is 404
+            @render404(req, res)
+          else
+            res.status(error.status)
+            res.send(error.message)
+        else
+          throw error
+
+    requireController: (controller) ->
+      @requireComponent("api.controllers.#{controller}")
+
+    requireModel: (model) ->
+      @requireComponent("api.models.#{model}")
+
+    requireUtil: (util) ->
+      @requireComponent("utils.#{util}")
+
+    requireComponent: (namespace) ->
+      @loadedModules ||= {}
+      @loadingModules ||= {}
+      loadPath = "#{@appRoot}/#{@namespaceToModulePath(namespace)}"
+
+      # load module's definition once
+      if @loadedModules[loadPath]
+        @loadedModules[loadPath]
+      else
+        if @loadingModules[loadPath]
+          throw new Error("Circular dependency detected: #{loadPath}")
+        @loadingModules[loadPath] = true
+        @loadedModules[loadPath] = require(loadPath).apply(this)
+        @loadingModules[loadPath] = undefined
+        @loadedModules[loadPath]
+
+    importUtils: ->
+      @requireUtil('property').import()
+
+    namespaceToModulePath: (name) ->
+      _.map(name.split("\."), (str) => @underscore(str)).join("/")
+
+    underscore: (str) ->
+      str
+        .replace(/[A-Z]/g, (str) -> "_#{str}")
+        .replace(/^\_/g, '')
+        .toLowerCase()
+
+    resetModules: ->
+      @loadingModules = {}
+
+    render404: (req, res) ->
+      res.status(404)
+      res.render('404.html')
+
+  _.extend(Glass, options)
+  Glass.app = app
+  Glass.importUtils()
+
+  Directory = Glass.requireModel('Directory')
+  Glass.homedir = new Directory(process.env.HOME)
+
+  return Glass

app/config/routes.coffee

+express = require 'express'
+path = require 'path'
+_ = require 'underscore'
+Mincer = require 'mincer'
+
+Mincer.CoffeeEngine.configure
+  bare: false
+
+Mincer.logger.use
+  log:    console.log
+  debug:  console.log
+  info:   console.log
+  warn:   console.log
+  error:  console.log
+
+module.exports = (Glass, options) ->
+  Glass.app.use express.logger('dev')
+
+  Glass.app.get '/', (req, res) ->
+    res.render 'index.ect',
+      homedir: Glass.homedir
+
+  Glass.app.get '/hello', (req, res) ->
+    res.send {message : "Hello World"}
+
+  Glass.app.get '/directory/:uuid', (req, res, next) ->
+    { uuid } = req.params
+    Glass.request('DirectoriesController', 'show', [uuid], req, res)
+
+  Glass.app.get '/source/:uuid', (req, res, next) ->
+    { uuid } = req.params
+    Glass.request('SourcesController', 'show', [uuid], req, res)
+
+  Glass.app.use express.static(path.join(options.root, '.tmp'))
+  Glass.app.use express.static(path.join(options.root, 'public'))
+  Glass.app.use(
+    '/bower_components',
+    express.static(path.join(options.appRoot, 'bower_components')))
+
+  environment = new Mincer.Environment()
+  environment.appendPath 'app/assets/javascripts'
+  Glass.app.use('/assets', Mincer.createServer(environment))
+
+  Glass.app.use (req, res) ->
+    Glass.render404(req, res)
+

app/config/server.coffee

+express = require 'express'
+path = require 'path'
+ECT = require 'ect'
+
+module.exports = (options) ->
+  viewRoot = path.join(options.appRoot, 'views')
+  ectRender = ECT(
+    watch: true
+    root: viewRoot
+    # ext: '.ect'
+  ).render
+
+  app = express()
+
+  app.configure ->
+
+    app.use express.favicon()
+    app.use express.static("#{__dirname}/app")
+    app.use app.router
+    app.set 'port', options.port or 3000
+    app.set 'views', viewRoot
+
+    app.engine('ect', ectRender)
+    app.engine('html', ectRender)
+
+    app.set('view engine', 'ect')
+
+  return app
+
+body {
+    background: #fafafa;
+}
+
+.hero-unit {
+    margin: 50px auto 0 auto;
+    width: 300px;
+}
+$icon-font-path: "/bower_components/sass-bootstrap/fonts/";
+
+@import 'sass-bootstrap/lib/bootstrap';
+@import 'sidr/src/scss/jquery.sidr.dark';
+
+.sidr ul.children {
+  padding-left: 10px;
+}
+
+.browsehappy {
+    margin: 0.2em 0;
+    background: #ccc;
+    color: #000;
+    padding: 0.2em 0;
+}
+
+/* Space out content a bit */
+body {
+    padding-top: 20px;
+    padding-bottom: 20px;
+}
+
+/* Everything but the jumbotron gets side spacing for mobile first views */
+.header,
+.marketing,
+.footer {
+    padding-left: 15px;
+    padding-right: 15px;
+}
+
+/* Custom page header */
+.header {
+    border-bottom: 1px solid #e5e5e5;
+}
+
+/* Make the masthead heading the same height as the navigation */
+.header h3 {
+    margin-top: 0;
+    margin-bottom: 0;
+    line-height: 40px;
+    padding-bottom: 19px;
+}
+
+/* Custom page footer */
+.footer {
+    padding-top: 19px;
+    color: #777;
+    border-top: 1px solid #e5e5e5;
+}
+
+.container-narrow > hr {
+    margin: 30px 0;
+}
+
+/* Main marketing message and sign up button */
+.jumbotron {
+    text-align: center;
+    border-bottom: 1px solid #e5e5e5;
+}
+
+.jumbotron .btn {
+    font-size: 21px;
+    padding: 14px 24px;
+}
+
+/* Supporting marketing content */
+.marketing {
+    margin: 40px 0;
+}
+
+.marketing p + h4 {
+    margin-top: 28px;
+}
+
+/* Responsive: Portrait tablets and up */
+@media screen and (min-width: 768px) {
+    .container {
+        max-width: 730px;
+    }
+
+    /* Remove the padding we set earlier */
+    .header,
+    .marketing,
+    .footer {
+        padding-left: 0;
+        padding-right: 0;
+    }
+    /* Space out the masthead */
+    .header {
+        margin-bottom: 30px;
+    }
+    /* Remove the bottom border on the jumbotron for visual effect */
+    .jumbotron {
+        border-bottom: 0;
+    }
+}

app/utils/error_page_exception.coffee

+module.exports = ->
+  @Exceptions ||= {}
+
+  return class @Exceptions.ErrorPageException
+    status: 404
+    message: 'exception'
+
+    constructor: (options = {}) ->
+      @status = options.status || @status
+      @message = options.message || @message
+

app/utils/glass.coffee

+module.exports = Glass =
+  Controller: require('./glass/controller')
+  import: (global) ->
+    global.Glass = Glass

app/utils/glass/namespace.coffee

+module.exports =
+  namespace: (namespace) ->
+    parts = namespace.split('.')
+    _.reduce(parts, (layer, part) ->
+      layer[part] ||= {}
+    , this)

app/utils/mixin.coffee

+module.exports = ->
+  return mixin = (superclass, modules...) ->
+    class SuperClass extends superclass
+    modules.each (module) ->
+      _.mixin(SuperClass, module)
+      _.mixin(SuperClass.prototype, module.prototype)
+    SuperClass
+

app/utils/namespace.coffee

+Glass =
+  namespace: (namespace) ->
+    parts = namespace.split('.')
+    _.reduce(parts, (layer, part) ->
+      layer[part] ||= {}
+    , this)

app/utils/property.coffee

+module.exports = ->
+  property = (prop, desc) ->
+    Object.defineProperty @prototype, prop, desc
+
+  property: property
+  import: ->
+    Function::property = property
+

app/utils/uuid_module.coffee

+
+module.exports = ->
+  qs = require 'querystring'
+
+  return class @utils.UuidModule
+    @findByUuid: (uuid) ->
+      @pathFromUuid(uuid)
+
+    @pathFromUuid: (uuid) ->
+      qs.unescape(uuid)
+
+    @uuidFromPath: (path) ->
+      qs.escape(path)
+
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>Page Not Found :(</title>
+        <style>
+            ::-moz-selection {
+                background: #b3d4fc;
+                text-shadow: none;
+            }
+
+            ::selection {
+                background: #b3d4fc;
+                text-shadow: none;
+            }
+
+            html {
+                padding: 30px 10px;
+                font-size: 20px;
+                line-height: 1.4;
+                color: #737373;
+                background: #f0f0f0;
+                -webkit-text-size-adjust: 100%;
+                -ms-text-size-adjust: 100%;
+            }
+
+            html,
+            input {
+                font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+            }
+
+            body {
+                max-width: 500px;
+                _width: 500px;
+                padding: 30px 20px 50px;
+                border: 1px solid #b3b3b3;
+                border-radius: 4px;
+                margin: 0 auto;
+                box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
+                background: #fcfcfc;
+            }
+
+            h1 {
+                margin: 0 10px;
+                font-size: 50px;
+                text-align: center;
+            }
+
+            h1 span {
+                color: #bbb;
+            }
+
+            h3 {
+                margin: 1.5em 0 0.5em;
+            }
+
+            p {
+                margin: 1em 0;
+            }
+
+            ul {
+                padding: 0 0 0 40px;
+                margin: 1em 0;
+            }
+
+            .container {
+                max-width: 380px;
+                _width: 380px;
+                margin: 0 auto;
+            }
+
+            /* google search */
+
+            #goog-fixurl ul {
+                list-style: none;
+                padding: 0;
+                margin: 0;
+            }
+
+            #goog-fixurl form {
+                margin: 0;
+            }
+
+            #goog-wm-qt,
+            #goog-wm-sb {
+                border: 1px solid #bbb;
+                font-size: 16px;
+                line-height: normal;
+                vertical-align: top;
+                color: #444;
+                border-radius: 2px;
+            }
+
+            #goog-wm-qt {
+                width: 220px;
+                height: 20px;
+                padding: 5px;
+                margin: 5px 10px 0 0;
+                box-shadow: inset 0 1px 1px #ccc;
+            }
+
+            #goog-wm-sb {
+                display: inline-block;
+                height: 32px;
+                padding: 0 10px;
+                margin: 5px 0 0;
+                white-space: nowrap;
+                cursor: pointer;
+                background-color: #f5f5f5;
+                background-image: -webkit-linear-gradient(rgba(255,255,255,0), #f1f1f1);
+                background-image: -moz-linear-gradient(rgba(255,255,255,0), #f1f1f1);
+                background-image: -ms-linear-gradient(rgba(255,255,255,0), #f1f1f1);
+                background-image: -o-linear-gradient(rgba(255,255,255,0), #f1f1f1);
+                -webkit-appearance: none;
+                -moz-appearance: none;
+                appearance: none;
+                *overflow: visible;
+                *display: inline;
+                *zoom: 1;
+            }
+
+            #goog-wm-sb:hover,
+            #goog-wm-sb:focus {
+                border-color: #aaa;
+                box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+                background-color: #f8f8f8;
+            }
+
+            #goog-wm-qt:hover,
+            #goog-wm-qt:focus {
+                border-color: #105cb6;
+                outline: 0;
+                color: #222;
+            }
+
+            input::-moz-focus-inner {
+                padding: 0;
+                border: 0;
+            }
+        </style>
+    </head>
+    <body>
+        <div class="container">
+            <h1>Not found <span>:(</span></h1>
+            <p>Sorry, but the page you were trying to view does not exist.</p>
+            <p>It looks like this was the result of either:</p>
+            <ul>
+                <li>a mistyped address</li>
+                <li>an out-of-date link</li>
+            </ul>
+            <script>
+                var GOOG_FIXURL_LANG = (navigator.language || '').slice(0,2),GOOG_FIXURL_SITE = location.host;
+            </script>
+            <script src="//linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script>
+        </div>
+    </body>
+</html>
+<!doctype html>
+<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
+<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
+<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
+<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
+    <head>
+        <meta charset="utf-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <title>glass</title>
+        <meta name="description" content="">
+        <meta name="viewport" content="width=device-width">
+        <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
+        <!-- build:css(.tmp) styles/main.css -->
+        <link rel="stylesheet" href="styles/main.css">
+        <!-- endbuild -->
+        <!-- build:js scripts/vendor/modernizr.js -->
+        <script src="bower_components/modernizr/modernizr.js"></script>
+        <!-- endbuild -->
+    </head>
+    <body>
+        <!--[if lt IE 10]>
+            <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
+        <![endif]-->
+
+
+        <div class="container">
+            <div class="header">
+                <ul class="nav nav-pills pull-right">
+                    <li class="active"><a href="#">Home</a></li>
+                    <li><a href="#">About</a></li>
+                    <li><a href="#">Contact</a></li>
+                </ul>
+                <button id="toggle-sidr" class="button">toggle file menu</button>
+                <h3 class="text-muted">glass</h3>
+            </div>
+
+            <div class="main">
+              <!--
+                <div class="jumbotron">
+                    <h1>'Allo, 'Allo!</h1>
+                    <p class="lead">Always a pleasure scaffolding your apps.</p>
+                    <p><a class="btn btn-lg btn-success" href="#">Splendid!</a></p>
+                </div>
+              -->
+
+              <div id="viewer-view">
+                <div id="viewer">
+                  // example code
+                  function hello() {
+                    console.log('hello world!')
+                  };
+                  hello();
+                </div>
+              </div>
+
+              <!--
+              <div class="row text-left">
+                  <div class="col-md-offset-4">
+                      <h2>Now Try,</h2>
+                      <h4><b>yo backbone:router</b> &lt;router&gt;</h4>
+                      <h4><b>yo backbone:collection</b> &lt;collection&gt;</h4>
+                      <h4><b>yo backbone:model </b> &lt;model&gt;</h4>
+                      <h4><b>yo backbone:view </b> &lt;view&gt;</h4>
+                  </div>
+              </div>
+              -->
+
+              <div class="row marketing">
+                  <div class="col-lg-6">
+                      <h4>HTML5 Boilerplate</h4>
+                      <p>HTML5 Boilerplate is a professional front-end template for building fast, robust, and adaptable web apps or sites.</p>
+
+                      <h4>Bootstrap</h4>
+                      <p>Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development.</p>
+
+                      <h4>Modernizr</h4>
+                      <p>Modernizr is an open-source JavaScript library that helps you build the next generation of HTML5 and CSS3-powered websites.</p>
+
+                  </div>
+                  <div class="col-lg-6">
+                      <h4>Backbone.js</h4>
+                      <p>Backbone.js gives structure to web applications by providing models, collections, views and connects it all to your existing API over a RESTful JSON interface.</p>
+
+                      <h4>Underscore.js</h4>
+                      <p>Underscore.js is a utility-belt library for JavaScript that provides a lot of the functional programming support</p>
+
+                  </div>
+              </div>
+            </div>
+
+            <div class="footer">
+                <p>♥ from the Yeoman team</p>
+            </div>
+
+        </div>
+
+
+        <!-- Google Analytics: change UA-XXXXX-X to be your site's ID.
+        <script>
+            (function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=
+            function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;
+            e=o.createElement(i);r=o.getElementsByTagName(i)[0];
+            e.src='//www.google-analytics.com/analytics.js';
+            r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
+            ga('create','UA-XXXXX-X');ga('send','pageview');
+        </script>
+        -->
+
+        <script id="homedir" type="application/json">
+          <%- JSON.stringify(@homedir.toJson()) %>
+        </script>
+
+        <!-- build:js scripts/main.js -->
+        <!-- <script data-main="scripts/main" src="bower_components/requirejs/require.js"></script> -->
+        <script src="bower_components/jquery/jquery.js"></script>
+        <script src="bower_components/underscore/underscore.js"></script>
+        <script src="bower_components/backbone/backbone.js"></script>
+
+        <script src="bower_components/ace/build/src/ace.js"></script>
+        <script src="bower_components/sidr/src/jquery.sidr.js"></script>
+
+        <script src="assets/application.js"></script>
+        <!-- endbuild -->
+
+</body>
+</html>
+{
+  "name": "glass",
+  "version": "0.0.0",
+  "dependencies": {
+    "sass-bootstrap": "~3.0.0",
+    "jquery": "~1.9.0",
+    "underscore": "~1.4.3",
+    "backbone": "~1.0.0",
+    "requirejs": "~2.1.5",
+    "requirejs-text": "~2.0.5",
+    "modernizr": "~2.6.2",
+    "ace": "~1.1.3",
+    "sidr": "git://github.com/artberri/sidr.git"
+  },
+  "devDependencies": {}
+}
+{
+  "name": "glass",
+  "version": "0.0.0",
+  "dependencies": {
+    "express": "~3.4.2",
+    "coffee-script": "^1.7.1",
+    "ect": "^0.5.6",
+    "dryice": "^0.4.10",
+    "mincer": "git://github.com/nodeca/mincer",
+    "underscore": "^1.6.0"
+  },
+  "devDependencies": {
+    "grunt": "~0.4.1",
+    "grunt-contrib-copy": "~0.4.0",
+    "grunt-contrib-concat": "~0.3.0",
+    "grunt-contrib-coffee": "~0.7.0",
+    "grunt-contrib-jst": "~0.5.0",
+    "grunt-contrib-uglify": "~0.2.0",
+    "grunt-contrib-compass": "~0.5.0",
+    "grunt-contrib-jshint": "~0.6.3",
+    "grunt-contrib-cssmin": "~0.6.0",
+    "grunt-contrib-connect": "~0.3.0",
+    "grunt-contrib-clean": "~0.5.0",
+    "grunt-contrib-htmlmin": "~0.1.3",
+    "grunt-contrib-imagemin": "~0.2.0",
+    "grunt-contrib-watch": "~0.5.2",
+    "grunt-mocha": "~0.4.1",
+    "grunt-usemin": "~0.1.10",
+    "grunt-bower-requirejs": "~0.7.0",
+    "grunt-requirejs": "~0.4.0",
+    "grunt-rev": "~0.1.0",
+    "grunt-open": "~0.2.0",
+    "load-grunt-tasks": "~0.1.0",
+    "connect-livereload": "~0.2.0",
+    "time-grunt": "~0.2.1",
+    "jshint-stylish": "~0.1.3",
+    "grunt-express-server": "^0.4.13"
+  },
+  "engines": {
+    "node": ">=0.8.0"
+  }
+}
+# Apache configuration file
+# httpd.apache.org/docs/2.2/mod/quickreference.html
+
+# Note .htaccess files are an overhead, this logic should be in your Apache
+# config if possible: httpd.apache.org/docs/2.2/howto/htaccess.html
+
+# Techniques in here adapted from all over, including:
+#   Kroc Camen: camendesign.com/.htaccess
+#   perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/
+#   Sample .htaccess file of CMS MODx: modxcms.com
+
+
+# ----------------------------------------------------------------------
+# Better website experience for IE users
+# ----------------------------------------------------------------------
+
+# Force the latest IE version, in various cases when it may fall back to IE7 mode
+#  github.com/rails/rails/commit/123eb25#commitcomment-118920
+# Use ChromeFrame if it's installed for a better experience for the poor IE folk
+
+<IfModule mod_headers.c>
+  Header set X-UA-Compatible "IE=Edge,chrome=1"
+  # mod_headers can't match by content-type, but we don't want to send this header on *everything*...
+  <FilesMatch "\.(js|css|gif|png|jpe?g|pdf|xml|oga|ogg|m4a|ogv|mp4|m4v|webm|svg|svgz|eot|ttf|otf|woff|ico|webp|appcache|manifest|htc|crx|oex|xpi|safariextz|vcf)$" >
+    Header unset X-UA-Compatible
+  </FilesMatch>
+</IfModule>
+
+
+# ----------------------------------------------------------------------
+# Cross-domain AJAX requests
+# ----------------------------------------------------------------------
+
+# Serve cross-domain Ajax requests, disabled by default.
+# enable-cors.org
+# code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
+
+#  <IfModule mod_headers.c>
+#    Header set Access-Control-Allow-Origin "*"
+#  </IfModule>
+
+
+# ----------------------------------------------------------------------
+# CORS-enabled images (@crossorigin)
+# ----------------------------------------------------------------------
+
+# Send CORS headers if browsers request them; enabled by default for images.
+# developer.mozilla.org/en/CORS_Enabled_Image
+# blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
+# hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
+# wiki.mozilla.org/Security/Reviews/crossoriginAttribute
+
+<IfModule mod_setenvif.c>
+  <IfModule mod_headers.c>
+    # mod_headers, y u no match by Content-Type?!
+    <FilesMatch "\.(gif|png|jpe?g|svg|svgz|ico|webp)$">
+      SetEnvIf Origin ":" IS_CORS
+      Header set Access-Control-Allow-Origin "*" env=IS_CORS
+    </FilesMatch>
+  </IfModule>
+</IfModule>
+
+
+# ----------------------------------------------------------------------
+# Webfont access
+# ----------------------------------------------------------------------
+
+# Allow access from all domains for webfonts.
+# Alternatively you could only whitelist your
+# subdomains like "subdomain.example.com".
+
+<IfModule mod_headers.c>
+  <FilesMatch "\.(ttf|ttc|otf|eot|woff|font.css)$">
+    Header set Access-Control-Allow-Origin "*"
+  </FilesMatch>
+</IfModule>
+
+
+# ----------------------------------------------------------------------
+# Proper MIME type for all files
+# ----------------------------------------------------------------------
+
+# JavaScript
+#   Normalize to standard type (it's sniffed in IE anyways)
+#   tools.ietf.org/html/rfc4329#section-7.2
+AddType application/javascript         js jsonp
+AddType application/json               json
+
+# Audio
+AddType audio/ogg                      oga ogg
+AddType audio/mp4                      m4a f4a f4b
+
+# Video
+AddType video/ogg                      ogv
+AddType video/mp4                      mp4 m4v f4v f4p
+AddType video/webm                     webm
+AddType video/x-flv                    flv
+
+# SVG
+#   Required for svg webfonts on iPad
+#   twitter.com/FontSquirrel/status/14855840545
+AddType     image/svg+xml              svg svgz
+AddEncoding gzip                       svgz
+
+# Webfonts
+AddType application/vnd.ms-fontobject  eot
+AddType application/x-font-ttf         ttf ttc
+AddType font/opentype                  otf
+AddType application/x-font-woff        woff
+
+# Assorted types
+AddType image/x-icon                        ico
+AddType image/webp                          webp
+AddType text/cache-manifest                 appcache manifest
+AddType text/x-component                    htc
+AddType application/xml                     rss atom xml rdf
+AddType application/x-chrome-extension      crx
+AddType application/x-opera-extension       oex
+AddType application/x-xpinstall             xpi
+AddType application/octet-stream            safariextz
+AddType application/x-web-app-manifest+json webapp
+AddType text/x-vcard                        vcf
+AddType application/x-shockwave-flash       swf
+AddType text/vtt                            vtt
+
+
+# ----------------------------------------------------------------------
+# Allow concatenation from within specific js and css files
+# ----------------------------------------------------------------------
+
+# e.g. Inside of script.combined.js you could have
+#   <!--#include file="libs/jquery-1.5.0.min.js" -->
+#   <!--#include file="plugins/jquery.idletimer.js" -->
+# and they would be included into this single file.
+
+# This is not in use in the boilerplate as it stands. You may
+# choose to use this technique if you do not have a build process.
+
+#<FilesMatch "\.combined\.js$">
+#  Options +Includes
+#  AddOutputFilterByType INCLUDES application/javascript application/json
+#  SetOutputFilter INCLUDES
+#</FilesMatch>
+
+#<FilesMatch "\.combined\.css$">
+#  Options +Includes
+#  AddOutputFilterByType INCLUDES text/css
+#  SetOutputFilter INCLUDES
+#</FilesMatch>
+
+
+# ----------------------------------------------------------------------
+# Gzip compression
+# ----------------------------------------------------------------------
+
+<IfModule mod_deflate.c>
+
+  # Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
+  <IfModule mod_setenvif.c>
+    <IfModule mod_headers.c>
+      SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
+      RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
+    </IfModule>
+  </IfModule>
+
+  # HTML, TXT, CSS, JavaScript, JSON, XML, HTC:
+  <IfModule filter_module>
+    FilterDeclare   COMPRESS
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/html
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/css
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/plain
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/xml
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/x-component
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/javascript
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/json
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xml
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xhtml+xml
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/rss+xml
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/atom+xml
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/vnd.ms-fontobject
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/svg+xml
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/x-icon
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/x-font-ttf
+    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $font/opentype
+    FilterChain     COMPRESS
+    FilterProtocol  COMPRESS  DEFLATE change=yes;byteranges=no
+  </IfModule>
+
+  <IfModule !mod_filter.c>
+    # Legacy versions of Apache
+    AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
+    AddOutputFilterByType DEFLATE application/javascript
+    AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
+    AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
+    AddOutputFilterByType DEFLATE image/x-icon image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype
+  </IfModule>
+
+</IfModule>
+
+
+# ----------------------------------------------------------------------
+# Expires headers (for better cache control)
+# ----------------------------------------------------------------------
+
+# These are pretty far-future expires headers.
+# They assume you control versioning with filename-based cache busting
+# Additionally, consider that outdated proxies may miscache
+#   www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
+
+# If you don't use filenames to version, lower the CSS and JS to something like
+# "access plus 1 week".
+
+<IfModule mod_expires.c>
+  ExpiresActive on
+
+# Perhaps better to whitelist expires rules? Perhaps.
+  ExpiresDefault                          "access plus 1 month"
+
+# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
+  ExpiresByType text/cache-manifest       "access plus 0 seconds"
+
+# Your document html
+  ExpiresByType text/html                 "access plus 0 seconds"
+
+# Data
+  ExpiresByType text/xml                  "access plus 0 seconds"
+  ExpiresByType application/xml           "access plus 0 seconds"
+  ExpiresByType application/json          "access plus 0 seconds"
+
+# Feed
+  ExpiresByType application/rss+xml       "access plus 1 hour"
+  ExpiresByType application/atom+xml      "access plus 1 hour"
+
+# Favicon (cannot be renamed)
+  ExpiresByType image/x-icon              "access plus 1 week"
+
+# Media: images, video, audio
+  ExpiresByType image/gif                 "access plus 1 month"
+  ExpiresByType image/png                 "access plus 1 month"
+  ExpiresByType image/jpeg                "access plus 1 month"
+  ExpiresByType video/ogg                 "access plus 1 month"
+  ExpiresByType audio/ogg                 "access plus 1 month"
+  ExpiresByType video/mp4                 "access plus 1 month"
+  ExpiresByType video/webm                "access plus 1 month"
+
+# HTC files  (css3pie)
+  ExpiresByType text/x-component          "access plus 1 month"
+
+# Webfonts
+  ExpiresByType application/x-font-ttf    "access plus 1 month"
+  ExpiresByType font/opentype             "access plus 1 month"
+  ExpiresByType application/x-font-woff   "access plus 1 month"
+  ExpiresByType image/svg+xml             "access plus 1 month"
+  ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
+
+# CSS and JavaScript
+  ExpiresByType text/css                  "access plus 1 year"
+  ExpiresByType application/javascript    "access plus 1 year"
+
+</IfModule>
+
+
+# ----------------------------------------------------------------------
+# Prevent mobile network providers from modifying your site
+# ----------------------------------------------------------------------
+
+# The following header prevents modification of your code over 3G on some
+# European providers.
+# This is the official 'bypass' suggested by O2 in the UK.
+
+# <IfModule mod_headers.c>
+# Header set Cache-Control "no-transform"
+# </IfModule>
+
+
+# ----------------------------------------------------------------------
+# ETag removal
+# ----------------------------------------------------------------------
+
+# FileETag None is not enough for every server.
+<IfModule mod_headers.c>
+  Header unset ETag
+</IfModule>
+
+# Since we're sending far-future expires, we don't need ETags for
+# static content.
+#   developer.yahoo.com/performance/rules.html#etags
+FileETag None
+
+
+# ----------------------------------------------------------------------
+# Stop screen flicker in IE on CSS rollovers
+# ----------------------------------------------------------------------
+
+# The following directives stop screen flicker in IE on CSS rollovers - in
+# combination with the "ExpiresByType" rules for images (see above).
+
+# BrowserMatch "MSIE" brokenvary=1
+# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
+# BrowserMatch "Opera" !brokenvary
+# SetEnvIf brokenvary 1 force-no-vary
+
+
+# ----------------------------------------------------------------------
+# Set Keep-Alive Header
+# ----------------------------------------------------------------------
+
+# Keep-Alive allows the server to send multiple requests through one
+# TCP-connection. Be aware of possible disadvantages of this setting. Turn on
+# if you serve a lot of static content.
+
+# <IfModule mod_headers.c>
+#   Header set Connection Keep-Alive
+# </IfModule>
+
+
+# ----------------------------------------------------------------------
+# Cookie setting from iframes
+# ----------------------------------------------------------------------
+
+# Allow cookies to be set from iframes (for IE only)
+# If needed, specify a path or regex in the Location directive.
+
+# <IfModule mod_headers.c>
+#   Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
+# </IfModule>
+
+
+# ----------------------------------------------------------------------
+# Start rewrite engine
+# ----------------------------------------------------------------------
+
+# Turning on the rewrite engine is necessary for the following rules and
+# features. FollowSymLinks must be enabled for this to work.
+
+# Some cloud hosting services require RewriteBase to be set: goo.gl/HOcPN
+# If using the h5bp in a subdirectory, use `RewriteBase /foo` instead where
+# 'foo' is your directory.
+
+# If your web host doesn't allow the FollowSymlinks option, you may need to
+# comment it out and use `Options +SymLinksOfOwnerMatch`, but be aware of the
+# performance impact: http://goo.gl/Mluzd
+
+<IfModule mod_rewrite.c>
+  Options +FollowSymlinks
+# Options +SymLinksIfOwnerMatch
+  Options +FollowSymlinks
+  RewriteEngine On
+# RewriteBase /
+</IfModule>
+
+
+# ----------------------------------------------------------------------
+# Suppress or force the "www." at the beginning of URLs
+# ----------------------------------------------------------------------
+
+# The same content should never be available under two different URLs -
+# especially not with and without "www." at the beginning, since this can cause
+# SEO problems (duplicate content). That's why you should choose one of the
+# alternatives and redirect the other one.
+
+# By default option 1 (no "www.") is activated.
+# no-www.org/faq.php?q=class_b
+
+# If you'd prefer to use option 2, just comment out all option 1 lines
+# and uncomment option 2.
+
+# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
+
+# ----------------------------------------------------------------------
+
+# Option 1:
+# Rewrite "www.example.com -> example.com".
+
+<IfModule mod_rewrite.c>
+  RewriteCond %{HTTPS} !=on
+  RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
+  RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
+</IfModule>
+
+# ----------------------------------------------------------------------
+
+# Option 2:
+# Rewrite "example.com -> www.example.com".
+# Be aware that the following rule might not be a good idea if you use "real"
+# subdomains for certain parts of your website.
+
+# <IfModule mod_rewrite.c>
+#   RewriteCond %{HTTPS} !=on
+#   RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
+#   RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
+# </IfModule>
+
+
+# ----------------------------------------------------------------------
+# Built-in filename-based cache busting
+# ----------------------------------------------------------------------
+
+# If you're not using the build script to manage your filename version revving,
+# you might want to consider enabling this, which will route requests for
+# /css/style.20110203.css to /css/style.css
+
+# To understand why this is important and a better idea than all.css?v1231,
+# read: github.com/h5bp/html5-boilerplate/wiki/cachebusting
+
+# <IfModule mod_rewrite.c>
+#   RewriteCond %{REQUEST_FILENAME} !-f
+#   RewriteCond %{REQUEST_FILENAME} !-d
+#   RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
+# </IfModule>
+
+
+# ----------------------------------------------------------------------
+# Prevent SSL cert warnings
+# ----------------------------------------------------------------------
+
+# Rewrite secure requests properly to prevent SSL cert warnings, e.g. prevent
+# https://www.example.com when your cert only allows https://secure.example.com
+
+# <IfModule mod_rewrite.c>
+#   RewriteCond %{SERVER_PORT} !^443
+#   RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
+# </IfModule>
+
+
+# ----------------------------------------------------------------------
+# Prevent 404 errors for non-existing redirected folders
+# ----------------------------------------------------------------------
+
+# without -MultiViews, Apache will give a 404 for a rewrite if a folder of the
+# same name does not exist.
+# webmasterworld.com/apache/3808792.htm
+
+Options -MultiViews
+
+
+# ----------------------------------------------------------------------
+# Custom 404 page
+# ----------------------------------------------------------------------
+
+# You can add custom pages to handle 500 or 403 pretty easily, if you like.
+# If you are hosting your site in subdirectory, adjust this accordingly
+#    e.g. ErrorDocument 404 /subdir/404.html
+ErrorDocument 404 /404.html
+
+
+# ----------------------------------------------------------------------
+# UTF-8 encoding
+# ----------------------------------------------------------------------
+
+# Use UTF-8 encoding for anything served text/plain or text/html
+AddDefaultCharset utf-8
+
+# Force UTF-8 for a number of file formats
+AddCharset utf-8 .atom .css .js .json .rss .vtt .xml
+
+
+# ----------------------------------------------------------------------
+# A little more security
+# ----------------------------------------------------------------------
+
+# To avoid displaying the exact version number of Apache being used, add the
+# following to httpd.conf (it will not work in .htaccess):
+# ServerTokens Prod
+
+# "-Indexes" will have Apache block users from browsing folders without a
+# default document Usually you should leave this activated, because you
+# shouldn't allow everybody to surf through every folder on your server (which
+# includes rather private places like CMS system folders).
+<IfModule mod_autoindex.c>
+  Options -Indexes
+</IfModule>
+
+# Block access to "hidden" directories or files whose names begin with a
+# period. This includes directories used by version control systems such as
+# Subversion or Git.
+<IfModule mod_rewrite.c>
+  RewriteCond %{SCRIPT_FILENAME} -d [OR]
+  RewriteCond %{SCRIPT_FILENAME} -f
+  RewriteRule "(^|/)\." - [F]
+</IfModule>
+
+# Block access to backup and source files. These files may be left by some
+# text/html editors and pose a great security danger, when anyone can access
+# them.
+<