Added test for LP approach

Added test for LP approach

require "busted"
require "tests.mock_love"

require "LunoPunk.LP"

describe "LP", ->
	it "sign", ->
		assert.are.equal -1, LP.sign -1
		assert.are.equal -1, LP.sign -15
		assert.are_not.equal -1, LP.sign 0
		assert.are_not.equal 0, LP.sign -1
		assert.are.equal 0, LP.sign 0
		assert.are_not.equal 0, LP.sign 1
		assert.are.equal 1, LP.sign 1
		assert.are.equal 1, LP.sign 15

	it "clamp", ->
		-- contained
		assert.are.equal 1.5, LP.clamp 1.5, 0, 2
		assert.are.equal 1.9999, LP.clamp 1.9999, 0, 2
		-- Max
		assert.are.equal 2, LP.clamp 2, 0, 2
		assert.are.equal 2, LP.clamp 3, 0, 2
		-- Min
		assert.are.equal 0, LP.clamp 0, 0, 1
		assert.are.equal 0, LP.clamp -1, 0, 1

	it "convertColor", ->
		rgb = LP.convertColor 0x1f2f3f
		assert.are.equal rgb[1], 0x1f
		assert.are.equal rgb[2], 0x2f
		assert.are.equal rgb[3], 0x3f

	it "getColorRGB", ->
		assert.are.equal 0x1f2f3f, LP.getColorRGB 0x1f, 0x2f, 0x3f

	it "lerp", ->
		assert.are.equal 1, LP.lerp 1, 2, 0
		assert.are.equal 2, LP.lerp 1, 2, 1
		assert.are.equal 1.5, LP.lerp 1, 2, .5

	it "colorLerp", ->
		color1 = 0x000000
		color2 = 0x101010
		assert.are.equal 0x000000, LP.colorLerp color1, color2, 0
		assert.are.equal 0x101010, LP.colorLerp color1, color2, 1

	it "approach", ->
		assert.are.equal 1, LP.approach 1, 2, 0
		assert.are.equal 1, LP.approach 0, 2, 1
		assert.are.equal 2, LP.approach 0, 2, 2
		assert.are.equal 2, LP.approach 0, 2, 3

require "busted"
require "tests.mock_love"

require "LunoPunk.LP"

describe "LP", ->
	it "sign", ->
		assert.are.equal -1, LP.sign -1
		assert.are.equal -1, LP.sign -15
		assert.are_not.equal -1, LP.sign 0
		assert.are_not.equal 0, LP.sign -1
		assert.are.equal 0, LP.sign 0
		assert.are_not.equal 0, LP.sign 1
		assert.are.equal 1, LP.sign 1
		assert.are.equal 1, LP.sign 15

	it "clamp", ->
		-- contained
		assert.are.equal 1.5, LP.clamp 1.5, 0, 2
		assert.are.equal 1.9999, LP.clamp 1.9999, 0, 2
		-- Max
		assert.are.equal 2, LP.clamp 2, 0, 2
		assert.are.equal 2, LP.clamp 3, 0, 2
		-- Min
		assert.are.equal 0, LP.clamp 0, 0, 1
		assert.are.equal 0, LP.clamp -1, 0, 1

	it "convertColor", ->
		rgb = LP.convertColor 0x1f2f3f
		assert.are.equal rgb[1], 0x1f
		assert.are.equal rgb[2], 0x2f
		assert.are.equal rgb[3], 0x3f

	it "getColorRGB", ->
		assert.are.equal 0x1f2f3f, LP.getColorRGB 0x1f, 0x2f, 0x3f

	it "lerp", ->
		assert.are.equal 1, LP.lerp 1, 2, 0
		assert.are.equal 2, LP.lerp 1, 2, 1
		assert.are.equal 1.5, LP.lerp 1, 2, .5

	it "colorLerp", ->
		color1 = 0x000000
		color2 = 0x101010
		assert.are.equal 0x000000, LP.colorLerp color1, color2, 0
		assert.are.equal 0x101010, LP.colorLerp color1, color2, 1
		assert.are.equal 0x080808, LP.colorLerp color1, color2, .5


Remove erroneous debug output

Remove erroneous debug output

lapis = require "lapis"
config = (require "lapis.config").get!
content = require "content"
logger = require "lapis.logging"
console = require "lapis.console" if config._name == 'development' or config._name == 'development-perftest'
csrf = require "lapis.csrf"
submit = require "submit_application"
submit_newsletter = require "submit_newsletter"
lfs = require "lfs"

import after_dispatch from require "lapis.nginx.context"
import to_json from require "lapis.util"
import capture_errors, assert_error, yield_error, respond_to from require "lapis.application"
import json_requested from require "utils"

capture_form_errors = (fn) ->
    capture_errors {
        on_error: (err, trace) =>
            err or= @errors
            if @json
                resp = if @m and @m.form and @m.form.responses and @m.form.responses.default
                    @m.form.responses.default
                else
                    '
A server error has occurred, and the error text could not be fetched.
'

                print to_json err
                { status: 500, json: { response: resp, errors: { } } }
            else
                @app.handle_error self, err, trace, 500

        fn
    }

safe_route = (fn) ->
    capture_errors {
        on_error: =>
            @app.handle_error self, @errors

        =>
            fn self
    }

class CSWeek extends lapis.Application
    layout: require "views.layout"
    error_page: require "views.error"

    handle_error: (err, trace, code = 500) =>
        r = @app.Request @app, @req, @res

        with r
            .m, errpg = content\get "error"
            if errpg then
                .m = { }
                .m[code] = "Could not load the error page (code #{code}): #{errpg}
When handling #{err}
#{trace}"

            .m[code] = .m[code] or .m.default or { }
            .page_id = "error"
            .status = code
            .error = err
            .traceback = trace
            .m.title = .m[code].title

            \write {
                status: code
                content_type: "text/html"
                render: "error"
            }
            \render!
            logger.request r
        r

    try_render: (template, context) =>
        context = context or self
        with context
            .nav = assert_error content\get "nav"
            .footer = assert_error content\get "footer"
        render: template

    respond_to_form: (err, model, page_id, template) =>
        resp = { }

        status = if not err
                200
            elseif err[1] == 'internal_error'
                500
            elseif err[1] == 'duplicate_application'
                409
            elseif err[1] == 'too_frequent'
                403
            elseif err[1] == 'file_too_big'
                419
            else
                400

        resp.response = model.form.responses[status] or model.form.responses.default if model.form.responses

        if status == 400
            resp.errors = { }
            for e in *err
                table.insert resp.errors, model.form.validation_errors[e] or 'unknown error'
        if @json
            { :status, json: resp }
        else
            @resp = resp
            @page_id = page_id
            ret = @app\try_render template, self
            ret.status = status
            ret

    @before_filter =>
        if #[n for n in *{'production-perftest', 'development-perftest'} when n == config._name] > 0
            after_dispatch ->
                print to_json(ngx.ctx.performance)

    [home: "/"]: safe_route =>
        @page_id = "home"
        @m = assert_error content\get "home"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "home", self

    [about: "/o-nedelji"]: safe_route =>
        @page_id = "about"
        @m = assert_error content\get "about"
        @app\try_render "about", self

    [lecturers: "/predavaci"]: safe_route =>
        @page_id = "lecturers"
        @m = assert_error content\get "lecturers_page"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "lecturers", self

    [lecturer: "/predavaci/:name"]: safe_route =>
        @lecturers_page = assert_error content\get "lecturers_page"
        @page_id = "lecturer"
        lecturers = assert_error content\get "lecturers"
        results = [l for l in *lecturers when l.id == @params.name]
        if #results > 1
            yield_error "Multiple lecturer id's found for id `#{@params.name}`"
        else if #results == 0
            @app.handle_error self, "No such lecturer: `#{@params.name}`", nil, 404
            return

        @m = results[1]
        @app\try_render "lecturer", self

    [apply: "/prijava"]: respond_to {
        GET: safe_route =>
            print 'hey'
            @page_id = "apply"
            @csrf_token = csrf.generate_token @
            if config.applications_enabled
                @m = assert_error content\get "apply"
                mtime = lfs.attributes 'static/resources/test.pdf', 'modification'
                if mtime
                    @last_updated = os.date '%d.%m.%y. %H:%M', mtime
                @app\try_render "apply", self
            else
                @m = assert_error content\get "apply-newsletter"
                @app\try_render "apply-newsletter", self

        POST: capture_form_errors =>
            @json = json_requested @
            model_name = if config.applications_enabled
                'apply'
            else
                'apply-newsletter'

            model = assert_error content\get model_name
            local succ, ret, err

            @m = model
            if not csrf.validate_token @
                @app.respond_to_form self, { 'bad_token' }, model, model_name, 'apply-result'
            else
                this = self

                if config.applications_enabled
                    succ, ret, err = pcall ->
                        submit\submit this.res.req.params_post, model, (...) ->
                            this\build_url ...
                else
                    succ, ret, err = pcall ->
                        submit_newsletter\submit this.res.req.params_post, model, (...) ->
                            this\build_url ...

                yield_error ret if not succ
                @app.respond_to_form self, err, model, model_name, 'apply-result'
    }

    [apply_upload: "/prijava/upload/*"]: respond_to {
        GET: safe_route =>
            if config.applications_enabled
                @page_id = "apply"
                @m = assert_error content\get "apply-upload"
                @a = assert_error content\get "apply"
                @csrf_token = csrf.generate_token @
                if @application = submit\get_application @params.splat
                    @app\try_render "apply-upload", self
                else
                    redirect_to: '/prijava'
            else
                redirect_to: '/prijava'

        POST: capture_form_errors =>
            if config.applications_enabled
                @json = json_requested @
                model = assert_error content\get 'apply-upload'
                apply_model = assert_error content\get 'apply'
                @m = model

                this = self
                succ, ret, err = pcall ->
                    submit\upload this.res.req.params_post, @params.splat, model, apply_model.tasks

                yield_error ret if not succ

                @app.respond_to_form self, err, model, 'apply', 'apply-result'
            else
                redirect_to: '/prijava'
    }

    [console: "/console"]: console.make!

    [materials: "/materijali"]: safe_route =>
        @page_id = "materials"
        @m = assert_error content\get "materials"
        @app\try_render "materials", self

    handle_404: =>
        @app.handle_error self, "Route `#{self.req.parsed_url.path or 'unknown'}` not found", nil, 404

lapis = require "lapis"
config = (require "lapis.config").get!
content = require "content"
logger = require "lapis.logging"
console = require "lapis.console" if config._name == 'development' or config._name == 'development-perftest'
csrf = require "lapis.csrf"
submit = require "submit_application"
submit_newsletter = require "submit_newsletter"
lfs = require "lfs"

import after_dispatch from require "lapis.nginx.context"
import to_json from require "lapis.util"
import capture_errors, assert_error, yield_error, respond_to from require "lapis.application"
import json_requested from require "utils"

capture_form_errors = (fn) ->
    capture_errors {
        on_error: (err, trace) =>
            err or= @errors
            if @json
                resp = if @m and @m.form and @m.form.responses and @m.form.responses.default
                    @m.form.responses.default
                else
                    '
A server error has occurred, and the error text could not be fetched.
'

                print to_json err
                { status: 500, json: { response: resp, errors: { } } }
            else
                @app.handle_error self, err, trace, 500

        fn
    }

safe_route = (fn) ->
    capture_errors {
        on_error: =>
            @app.handle_error self, @errors

        =>
            fn self
    }

class CSWeek extends lapis.Application
    layout: require "views.layout"
    error_page: require "views.error"

    handle_error: (err, trace, code = 500) =>
        r = @app.Request @app, @req, @res

        with r
            .m, errpg = content\get "error"
            if errpg then
                .m = { }
                .m[code] = "Could not load the error page (code #{code}): #{errpg}
When handling #{err}
#{trace}"

            .m[code] = .m[code] or .m.default or { }
            .page_id = "error"
            .status = code
            .error = err
            .traceback = trace
            .m.title = .m[code].title

            \write {
                status: code
                content_type: "text/html"
                render: "error"
            }
            \render!
            logger.request r
        r

    try_render: (template, context) =>
        context = context or self
        with context
            .nav = assert_error content\get "nav"
            .footer = assert_error content\get "footer"
        render: template

    respond_to_form: (err, model, page_id, template) =>
        resp = { }

        status = if not err
                200
            elseif err[1] == 'internal_error'
                500
            elseif err[1] == 'duplicate_application'
                409
            elseif err[1] == 'too_frequent'
                403
            elseif err[1] == 'file_too_big'
                419
            else
                400

        resp.response = model.form.responses[status] or model.form.responses.default if model.form.responses

        if status == 400
            resp.errors = { }
            for e in *err
                table.insert resp.errors, model.form.validation_errors[e] or 'unknown error'
        if @json
            { :status, json: resp }
        else
            @resp = resp
            @page_id = page_id
            ret = @app\try_render template, self
            ret.status = status
            ret

    @before_filter =>
        if #[n for n in *{'production-perftest', 'development-perftest'} when n == config._name] > 0
            after_dispatch ->
                print to_json(ngx.ctx.performance)

    [home: "/"]: safe_route =>
        @page_id = "home"
        @m = assert_error content\get "home"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "home", self

    [about: "/o-nedelji"]: safe_route =>
        @page_id = "about"
        @m = assert_error content\get "about"
        @app\try_render "about", self

    [lecturers: "/predavaci"]: safe_route =>
        @page_id = "lecturers"
        @m = assert_error content\get "lecturers_page"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "lecturers", self

    [lecturer: "/predavaci/:name"]: safe_route =>
        @lecturers_page = assert_error content\get "lecturers_page"
        @page_id = "lecturer"
        lecturers = assert_error content\get "lecturers"
        results = [l for l in *lecturers when l.id == @params.name]
        if #results > 1
            yield_error "Multiple lecturer id's found for id `#{@params.name}`"
        else if #results == 0
            @app.handle_error self, "No such lecturer: `#{@params.name}`", nil, 404
            return

        @m = results[1]
        @app\try_render "lecturer", self

    [apply: "/prijava"]: respond_to {
        GET: safe_route =>
            print 'hey'
            @page_id = "apply"
            @csrf_token = csrf.generate_token @
            if config.applications_enabled
                @m = assert_error content\get "apply"
                mtime = lfs.attributes 'static/resources/test.pdf', 'modification'
                if mtime
                    @last_updated = os.date '%d.%m.%y. %H:%M', mtime
                @app\try_render "apply", self
            else
                @m = assert_error content\get "apply-newsletter"
                @app\try_render "apply-newsletter", self

        POST: capture_form_errors =>
            @json = json_requested @
            model_name = if config.applications_enabled
                'apply'
            else
                'apply-newsletter'

            model = assert_error content\get model_name
            local succ, ret, err

            @m = model
            if not csrf.validate_token @
                @app.respond_to_form self, { 'bad_token' }, model, model_name, 'apply-result'
            else
                this = self

                if config.applications_enabled
                    succ, ret, err = pcall ->
                        submit\submit this.res.req.params_post, model, (...) ->
                            this\build_url ...
                else
                    succ, ret, err = pcall ->
                        moon.p submit_newsletter
                        submit_newsletter\submit this.res.req.params_post, model, (...) ->
                            this\build_url ...

                yield_error ret if not succ
                @app.respond_to_form self, err, model, model_name, 'apply-result'
    }

    [apply_upload: "/prijava/upload/*"]: respond_to {
        GET: safe_route =>
            if config.applications_enabled
                @page_id = "apply"
                @m = assert_error content\get "apply-upload"
                @a = assert_error content\get "apply"
                @csrf_token = csrf.generate_token @
                if @application = submit\get_application @params.splat
                    @app\try_render "apply-upload", self
                else
                    redirect_to: '/prijava'
            else
                redirect_to: '/prijava'

        POST: capture_form_errors =>
            if config.applications_enabled
                @json = json_requested @
                model = assert_error content\get 'apply-upload'
                apply_model = assert_error content\get 'apply'
                @m = model

                this = self
                succ, ret, err = pcall ->
                    submit\upload this.res.req.params_post, @params.splat, model, apply_model.tasks

                yield_error ret if not succ

                @app.respond_to_form self, err, model, 'apply', 'apply-result'
            else
                redirect_to: '/prijava'
    }

    [console: "/console"]: console.make!

    [materials: "/materijali"]: safe_route =>
        @page_id = "materials"
        @m = assert_error content\get "materials"
        @app\try_render "materials", self

    handle_404: =>
        @app.handle_error self, "Route `#{self.req.parsed_url.path or 'unknown'}` not found", nil, 404


improved addChild, added removeChild

improved addChild, added removeChild

import graphics from love
import floor from math
import insert, remove from table
tonumber = tonumber

class element
    new: (parent) =>
        @parent = parent
        @child = {}

        @w = 0
        @h = 0

        @margin = 0

        if parent
            @x = parent.x
            @y = parent.y
            --@horizontal = parent.horizontal
            --@vertical = parent.vertical
            --@align!
        else
            @x = 0
            @y = 0
            --@horizontal = "left"
            --@vertical = "top"

        @horizontal = "left"
        @vertical = "top"

    debugDraw: =>
        graphics.setLineWidth 0.5
        graphics.setColor 0, 0, 0, 100
        graphics.rectangle "fill", @x, @y, @w, @h
        graphics.setColor 0, 200, 0, 200
        graphics.rectangle "line", @x, @y, @w, @h
        graphics.setColor 200, 255, 200, 255
        graphics.print "e", @x, @y

        return @

    addChild: (child) =>
        -- make sure we don't duplicate references
        if child.parent
            child.parent\removeChild(child)

        insert @child, child
        child.parent = @

        return @

    -- remove child by index and return it OR remove child by reference
    removeChild: (child) =>
        if tonumber(child) == child
            -- remove indexed child, return it
            @child[child].parent = false
            return remove @child, child
        else
            for k,v in ipairs @child
                if v == child
                    remove @child, k
                    return @
            error "Element \"#{child}\" is not a child of element \"#{@}\". Cannot remove it."

    getChildren: =>
        return @child

    move: (x, y) =>
        if x
            @x = @x + x
        if y
            @y = @y + y

        for i = 1, #@child
            unless @child[i].excludeMovement
                @child[i]\move x, y

        return @

    setPosition: (x, y) =>
        oldX = @x
        oldY = @y

        if x
            switch @horizontal
                when "left"
                    @x = x
                when "center"
                    @x = x - @w/2
                when "right"
                    @x = x - @w
        else
            x = oldX

        if y
            switch @vertical
                when "top"
                    @y = y
                when "center"
                    @y = y - @h/2
                when "bottom"
                    @y = y - @h
        else
            y = oldY

        for i = 1, #@child
            unless @child[i].excludeMovement
                @child[i]\move x - oldX, y - oldY

        return @

    getPosition: =>
        resultX = @x
        resultY = @y

        switch @horizontal
            when "center"
                resultX += @w/2
            when "right"
                resultY += @w

        switch @vertical
            when "center"
                resultY += @h/2
            when "bottom"
                resultY += @h

        return resultX, resultY

    setSize: (w, h) =>
        if w
            switch @horizontal
                when "center"
                    @x -= (w - @w)/2
                when "right"
                    @x -= w - @w

            @w = w

        if h
            switch @vertical
                when "center"
                    @y -= (h - @h)/2
                when "bottom"
                    @y -= h - @h

            @h = h

        return @

    getSize: =>
        return @w, @h

    setWidth: (w) =>
        switch @horizontal
            when "center"
                @x -= (w - @w)/2
            when "right"
                @x -= w - @w

        @w = w

        return @

    getWidth: =>
        return @w

    setHeight: (h) =>
        switch @vertical
            when "center"
                @y -= (h - @h)/2
            when "bottom"
                @y -= h - @h

        @h = h

        return @

    getHeight: =>
        return @h

    adjustSize: (w, h) =>
        W, H = @getSize!

        if w
            W += w
        if h
            H += h

        @setSize W, H

        return @

    --TODO note that align requires a parent!
    align: (horizontal, vertical, toPixel=true) =>
        @setAlignment horizontal, vertical

        @x = @parent.x
        @y = @parent.y

        switch @horizontal
            when "left"
                @x += @margin
            when "center"
                @x += (@parent.w - @w)/2
            when "right"
                @x += @parent.w - @w - @margin

        switch @vertical
            when "top"
                @y += @margin
            when "center"
                @y += (@parent.h - @h)/2
            when "bottom"
                @y += @parent.h - @h - @margin

        if toPixel
            @x = floor @x
            @y = floor @y

        return @

    alignTo: (element, horizontal, vertical, toPixel=true) =>
        parent = @parent
        @parent = element

        @align horizontal, vertical, toPixel

        @parent = parent

        return @

    setAlignment: (horizontal, vertical) =>
        if horizontal
            @horizontal = horizontal
        if vertical
            @vertical = vertical

        return @

    getAlignment: =>
        return @horizontal, @vertical

    setMargin: (margin) =>
        @margin = margin
        @align!
        return @

    getMargin: =>
        return @margin

    fill: =>
        @x = @parent.x + @margin
        @y = @parent.y + @margin
        @w = @parent.w - @margin*2
        @h = @parent.h - @margin*2

import graphics from love
import floor from math

class element
    new: (parent) =>
        @parent = parent
        @child = {}

        @w = 0
        @h = 0

        @margin = 0

        if parent
            @x = parent.x
            @y = parent.y
            --@horizontal = parent.horizontal
            --@vertical = parent.vertical
            --@align!
        else
            @x = 0
            @y = 0
            --@horizontal = "left"
            --@vertical = "top"

        @horizontal = "left"
        @vertical = "top"

    debugDraw: =>
        graphics.setLineWidth 0.5
        graphics.setColor 0, 0, 0, 100
        graphics.rectangle "fill", @x, @y, @w, @h
        graphics.setColor 0, 200, 0, 200
        graphics.rectangle "line", @x, @y, @w, @h
        graphics.setColor 200, 255, 200, 255
        graphics.print "e", @x, @y

        return @

    addChild: (child) =>
        @child[#@child+1] = child
        child.parent = @

        return @

    getChildren: =>
        return @child

    move: (x, y) =>
        if x
            @x = @x + x
        if y
            @y = @y + y

        for i = 1, #@child
            unless @child[i].excludeMovement
                @child[i]\move x, y

        return @

    setPosition: (x, y) =>
        oldX = @x
        oldY = @y

        if x
            switch @horizontal
                when "left"
                    @x = x
                when "center"
                    @x = x - @w/2
                when "right"
                    @x = x - @w
        else
            x = oldX

        if y
            switch @vertical
                when "top"
                    @y = y
                when "center"
                    @y = y - @h/2
                when "bottom"
                    @y = y - @h
        else
            y = oldY

        for i = 1, #@child
            unless @child[i].excludeMovement
                @child[i]\move x - oldX, y - oldY

        return @

    getPosition: =>
        resultX = @x
        resultY = @y

        switch @horizontal
            when "center"
                resultX += @w/2
            when "right"
                resultY += @w

        switch @vertical
            when "center"
                resultY += @h/2
            when "bottom"
                resultY += @h

        return resultX, resultY

    setSize: (w, h) =>
        if w
            switch @horizontal
                when "center"
                    @x -= (w - @w)/2
                when "right"
                    @x -= w - @w

            @w = w

        if h
            switch @vertical
                when "center"
                    @y -= (h - @h)/2
                when "bottom"
                    @y -= h - @h

            @h = h

        return @

    getSize: =>
        return @w, @h

    setWidth: (w) =>
        switch @horizontal
            when "center"
                @x -= (w - @w)/2
            when "right"
                @x -= w - @w

        @w = w

        return @

    getWidth: =>
        return @w

    setHeight: (h) =>
        switch @vertical
            when "center"
                @y -= (h - @h)/2
            when "bottom"
                @y -= h - @h

        @h = h

        return @

    getHeight: =>
        return @h

    adjustSize: (w, h) =>
        W, H = @getSize!

        if w
            W += w
        if h
            H += h

        @setSize W, H

        return @

    --TODO note that align requires a parent!
    align: (horizontal, vertical, toPixel=true) =>
        @setAlignment horizontal, vertical

        @x = @parent.x
        @y = @parent.y

        switch @horizontal
            when "left"
                @x += @margin
            when "center"
                @x += (@parent.w - @w)/2
            when "right"
                @x += @parent.w - @w - @margin

        switch @vertical
            when "top"
                @y += @margin
            when "center"
                @y += (@parent.h - @h)/2
            when "bottom"
                @y += @parent.h - @h - @margin

        if toPixel
            @x = floor @x
            @y = floor @y

        return @

    alignTo: (element, horizontal, vertical, toPixel=true) =>
        parent = @parent
        @parent = element

        @align horizontal, vertical, toPixel

        @parent = parent

        return @

    setAlignment: (horizontal, vertical) =>
        if horizontal
            @horizontal = horizontal
        if vertical
            @vertical = vertical

        return @

    getAlignment: =>
        return @horizontal, @vertical

    setMargin: (margin) =>
        @margin = margin
        @align!
        return @

    getMargin: =>
        return @margin

    fill: =>
        @x = @parent.x + @margin
        @y = @parent.y + @margin
        @w = @parent.w - @margin*2
        @h = @parent.h - @margin*2


Big oops

Big oops

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

class NetworkChangeState
	new: (key = '', keyValid = '', newValue, obj, len = 24, ply = NULL) =>
		@key = key
		@keyValid = keyValid
		@oldValue = obj[key]
		@newValue = newValue
		@ply = ply
		@time = CurTime()
		@rtime = RealTime()
		@stime = SysTime()
		@obj = obj
		@objID = obj.netID
		@len = len
		@rlen = len - 24 -- ID - 16 bits, variable id - 8 bits
		@networkChange = true
	GetPlayer: => @ply
	ChangedByClient: => not @networkChange or IsValid(@ply)
	ChangedByPlayer: => not @networkChange or IsValid(@ply)
	ChangedByServer: => not @networkChange or not IsValid(@ply)
	GetKey: => @keyValid
	GetVariable: => @keyValid
	GetVar: => @keyValid
	GetKeyInternal: => @key
	GetVariableInternal: => @key
	GetVarInternal: => @key
	GetNewValue: => @newValue
	GetValue: => @newValue
	NewValue: => @newValue
	GetOldValue: => @oldValue
	OldValue: => @oldValue
	CurTime: => @time
	GetCurTime: => @time
	GetReceiveTime: => @time
	GetReceiveStamp: => @time
	RealTime: => @rtime
	GetRealTime: => @rtime
	SysTime: => @stime
	GetSysTime: => @stime
	GetObject: => @obj
	GetNWObject: => @obj
	GetNetworkedObject: => @obj
	GetLength: => @rlen
	GetRealLength: => @len
	ChangedByNetwork: => @networkChange

	Revert: => @obj[@key] = @oldValue
	Apply: => @obj[@key] = @newValue

class NetworkedObject
	@Setup = =>
		@NW_Vars = {} if @NW_Vars == nil
		@NW_VarsTable = {}
		@NW_Objects = {}
		@NW_Waiting = {}
		@NW_WaitID = -1
		@NW_Setup = true
		@NW_NextVarID = -1 if @NW_NextVarID == nil
		@NW_Create = "PPM2.NW.Created.#{@__name}"
		@NW_Modify = "PPM2.NW.Modified.#{@__name}"
		@NW_Remove = "PPM2.NW.Removed.#{@__name}"
		@NW_ReceiveID = "PPM2.NW.ReceiveID.#{@__name}"
		@NW_NextObjectID = 0

		if SERVER
			util.AddNetworkString(@NW_Create)
			util.AddNetworkString(@NW_Modify)
			util.AddNetworkString(@NW_Remove)
			util.AddNetworkString(@NW_ReceiveID)

		net.Receive @NW_Create, (len = 0, ply = NULL) -> @OnNetworkedCreated(ply, len)
		net.Receive @NW_Modify, (len = 0, ply = NULL) -> @OnNetworkedModify(ply, len)
		net.Receive @NW_Remove, (len = 0, ply = NULL) -> @OnNetworkedDelete(ply, len)
		net.Receive @NW_ReceiveID, (len = 0, ply = NULL) ->
			return if SERVER
			waitID = net.ReadUInt(16)
			netID = net.ReadUInt(16)
			obj = @NW_Waiting[netID]
			@NW_Waiting[netID] = nil
			return unless obj
			obj.NETWORKED = true
			obj.netID = netID
	-- @__inherited = (child) => child.Setup(child)

	@AddNetworkVar = (getName = 'Var', readFunc = (->), writeFunc = (->), defValue) =>
		strName = "_NW_#{getName}"
		error("No more free slots! Can't add #{getName} to the table") if @NW_NextVarID > 254
		@NW_NextVarID += 1
		id = @NW_NextVarID
		tab = {:strName, :readFunc, :getName, :writeFunc, :defValue, :id}
		table.insert(@NW_Vars, tab)
		@NW_VarsTable[id] = tab
		@__base[strName] = defValue
		@__base["Get#{getName}"] = => @[strName]
		@__base["Set#{getName}"] = (val = defValue, networkNow = true) =>
			oldVal = @[strName]
			@[strName] = val
			state = NetworkChangeState(strName, getName, val, @)
			state.networkChange = false
			@SetLocalChange(state)
			if networkNow and @NETWORKED and (CLIENT and @@NW_ClientsideCreation or SERVER)
				net.Start(@@NW_Modify)
				net.WriteUInt(@GetNetworkID(), 16)
				net.WriteUInt(id, 8)
				writeFunc(@[strName])
				if CLIENT
					net.SendToServer()
				else
					net.Broadcast()
	@NetworkVar = (...) => @AddNetworkVar(...)

	@NW_ClientsideCreation = false
	@NW_RemoveOnPlayerLeave = true
	@OnNetworkedCreated = (ply = NULL, len = 0) =>
		return if SERVER and not @NW_ClientsideCreation
		if CLIENT
			netID = net.ReadUInt(16)
			obj = @NW_Objects[netID] or @(netID)
			obj.NETWORKED = true
			obj.NETWORKED_PREDICT = true
			obj\ReadNetworkData()
			@OnNetworkedCreatedCallback(obj, ply, len)
		else
			waitID = net.ReadUInt(16)
			obj = @()
			obj.NW_Player = ply
			obj.NETWORKED_PREDICT = true
			obj\ReadNetworkData()
			obj\Create()
			net.Start(@NW_ReceiveID)
			net.WriteUInt(waitID, 16)
			net.WriteUInt(obj.netID, 16)
			net.Send(ply)
			@OnNetworkedCreatedCallback(obj, ply, len)
	@OnNetworkedCreatedCallback = (obj, ply = NULL, len = 0) => -- Override

	@OnNetworkedModify = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		id = net.ReadUInt(16)
		obj = @NW_Objects[id]
		return unless obj
		return if IsValid(ply) and obj.NW_Player ~= ply
		varID = net.ReadUInt(8)
		varData = @NW_VarsTable[varID]
		return unless varData
		{:strName, :getName, :readFunc, :writeFunc} = varData
		newVal = readFunc()
		state = NetworkChangeState(strName, getName, newVal, obj, len, ply)
		state\Apply()
		obj\NetworkDataChanges(state)
		if SERVER
			net.Start(@NW_Modify)
			net.WriteUInt(id, 16)
			net.WriteUInt(varID, 8)
			writeFunc(newVal)
			net.SendOmit(ply)
		@OnNetworkedModifyCallback(state)
	@OnNetworkedModifyCallback = (state) => -- Override

	@OnNetworkedDelete = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		id = net.ReadUInt(16)
		obj = @NW_Objects[id]
		return unless obj
		obj\Remove(true)
		@OnNetworkedDeleteCallback(obj, ply, len)
	@OnNetworkedDeleteCallback = (obj, ply = NULL, len = 0) => -- Override

	@ReadNetworkData = =>
		output = {strName, {getName, readFunc()} for {:getName, :strName, :readFunc} in *@NW_Vars}
		return output

	new: (netID = @@NW_NextObjectID, localObject = false) =>
		@valid = true
		@NETWORKED = false
		@NETWORKED_PREDICT = false

		if SERVER
			@netID = @@NW_NextObjectID
			@@NW_NextObjectID += 1
			print @netID
		else
			@netID = netID

		@@NW_Objects[@netID] = @
		@NW_Player = NULL
		@isLocal = localObject
		@NW_Player = LocalPlayer() if localObject

	IsValid: => @valid
	IsNetworked: => @NETWORKED
	IsGoingToNetwork: => @NETWORKED_PREDICT
	SetIsGoingToNetwork: (val = @NETWORKED) => @NETWORKED_PREDICT = val
	IsLocal: => @isLocal
	IsLocalObject: => @isLocal
	GetNetworkID: => @netID
	NetworkID: => @netID
	NetID: => @netID
	Remove: (byClient = false) =>
		@@NW_Objects[@netID] = nil
		@valid = false
		if CLIENT and @isLocal and @NETWORKED and @@NW_ClientsideCreation
			net.Start(@@NW_Remove)
			net.WriteUInt(@netID, 16)
			net.SendToServer()
		elseif SERVER and @NETWORKED
			net.Start(@@NW_Remove)
			net.WriteUInt(@netID, 16)
			if not IsValid(@NW_Player) or not byClient
				net.Broadcast()
			else
				net.SendOmit(@NW_Player)

	NetworkDataChanges: (state) => -- Override
	SetLocalChange: (state) => -- Override
	ReadNetworkData: (len = 24, ply = NULL, silent = false) =>
		data = @@ReadNetworkData()
		states = [NetworkChangeState(key, keyValid, newVal, @, len, ply) for key, {keyValid, newVal} in pairs data]
		for state in *states
			state\Apply()
			@NetworkDataChanges(state) unless silent

	WriteNetworkData: => writeFunc(@[strName]) for {:strName, :writeFunc} in *@@NW_Vars

	SendVar: (Var = '') =>
		return if @[Var] == nil

	Create: =>
		return if @NETWORKED
		return if CLIENT and not @@NW_ClientsideCreation
		@NETWORKED = true
		@NETWORKED_PREDICT = true
		if SERVER
			net.Start(@@NW_Create)
			net.WriteUInt(@netID, 16)
			@WriteNetworkData()
			filter = RecipientFilter()
			filter\AddAllPlayers()
			filter\RemovePlayer(@NW_Player) if IsValid(@NW_Player)
			net.Send(filter)
		else
			@@NW_WaitID += 1
			net.Start(@@NW_Create)
			net.WriteUInt(@@NW_WaitID, 16)
			@WriteNetworkData()
			net.SendToServer()
			@@NW_Waiting[@@NW_WaitID] = @
	NetworkTo: (targets = {}) =>
		net.Start(@@NW_Create)
		net.WriteUInt(@netID, 16)
		@WriteNetworkData()
		net.Send(targets)

PPM2.NetworkedObject = NetworkedObject

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

class NetworkChangeState
	new: (key = '', keyValid = '', newValue, obj, len = 24, ply = NULL) =>
		@key = key
		@keyValid = keyValid
		@oldValue = obj[key]
		@newValue = newValue
		@ply = ply
		@time = CurTime()
		@rtime = RealTime()
		@stime = SysTime()
		@obj = obj
		@objID = obj.netID
		@len = len
		@rlen = len - 24 -- ID - 16 bits, variable id - 8 bits
		@networkChange = true
	GetPlayer: => @ply
	ChangedByClient: => not @networkChange or IsValid(@ply)
	ChangedByPlayer: => not @networkChange or IsValid(@ply)
	ChangedByServer: => not @networkChange or not IsValid(@ply)
	GetKey: => @keyValid
	GetVariable: => @keyValid
	GetVar: => @keyValid
	GetKeyInternal: => @key
	GetVariableInternal: => @key
	GetVarInternal: => @key
	GetNewValue: => @newValue
	GetValue: => @newValue
	NewValue: => @newValue
	GetOldValue: => @oldValue
	OldValue: => @oldValue
	CurTime: => @time
	GetCurTime: => @time
	GetReceiveTime: => @time
	GetReceiveStamp: => @time
	RealTime: => @rtime
	GetRealTime: => @rtime
	SysTime: => @stime
	GetSysTime: => @stime
	GetObject: => @obj
	GetNWObject: => @obj
	GetNetworkedObject: => @obj
	GetLength: => @rlen
	GetRealLength: => @len
	ChangedByNetwork: => @networkChange

	Revert: => @obj[@key] = @oldValue
	Apply: => @obj[@key] = @newValue

class NetworkedObject
	@Setup = =>
		@NW_Vars = {} if @NW_Vars == nil
		@NW_VarsTable = {}
		@NW_Objects = {}
		@NW_Waiting = {}
		@NW_WaitID = -1
		@NW_Setup = true
		@NW_NextVarID = -1 if @NW_NextVarID == nil
		@NW_Create = "PPM2.NW.Created.#{@__name}"
		@NW_Modify = "PPM2.NW.Modified.#{@__name}"
		@NW_Remove = "PPM2.NW.Removed.#{@__name}"
		@NW_ReceiveID = "PPM2.NW.ReceiveID.#{@__name}"
		@NW_NextObjectID = 0

		if SERVER
			util.AddNetworkString(@NW_Create)
			util.AddNetworkString(@NW_Modify)
			util.AddNetworkString(@NW_Remove)
			util.AddNetworkString(@NW_ReceiveID)

		net.Receive @NW_Create, (len = 0, ply = NULL) -> @OnNetworkedCreated(ply, len)
		net.Receive @NW_Modify, (len = 0, ply = NULL) -> @OnNetworkedModify(ply, len)
		net.Receive @NW_Remove, (len = 0, ply = NULL) -> @OnNetworkedDelete(ply, len)
		net.Receive @NW_ReceiveID, (len = 0, ply = NULL) ->
			return if SERVER
			waitID = net.ReadUInt(16)
			netID = net.ReadUInt(16)
			obj = @NW_Waiting[netID]
			@NW_Waiting[netID] = nil
			return unless obj
			obj.NETWORKED = true
			obj.netID = netID
	-- @__inherited = (child) => child.Setup(child)

	@AddNetworkVar = (getName = 'Var', readFunc = (->), writeFunc = (->), defValue) =>
		strName = "_NW_#{getName}"
		error("No more free slots! Can't add #{getName} to the table") if @NW_NextVarID > 254
		@NW_NextVarID += 1
		id = @NW_NextVarID
		tab = {:strName, :readFunc, :getName, :writeFunc, :defValue, :id}
		table.insert(@NW_Vars, tab)
		@NW_VarsTable[id] = tab
		@__base[strName] = defValue
		@__base["Get#{getName}"] = => @[strName]
		@__base["Set#{getName}"] = (val = defValue, networkNow = true) =>
			oldVal = @[strName]
			@[strName] = val
			state = NetworkChangeState(strName, getName, val, @)
			state.networkChange = false
			@SetLocalChange(state)
			if networkNow and @NETWORKED and (CLIENT and @@NW_ClientsideCreation or SERVER)
				net.Start(@@NW_Modify)
				net.WriteUInt(@GetNetworkID(), 16)
				net.WriteUInt(id, 8)
				writeFunc(@[strName])
				if CLIENT
					net.SendToServer()
				else
					net.Broadcast()
	@NetworkVar = (...) => @AddNetworkVar(...)

	@NW_ClientsideCreation = false
	@NW_RemoveOnPlayerLeave = true
	@OnNetworkedCreated = (ply = NULL, len = 0) =>
		return if SERVER and not @NW_ClientsideCreation
		if CLIENT
			netID = net.ReadUInt(16)
			obj = @NW_Objects[netID] or @(netID)
			obj.NETWORKED = true
			obj.NETWORKED_PREDICT = true
			obj\ReadNetworkData()
			@OnNetworkedCreatedCallback(obj, ply, len)
		else
			waitID = net.ReadUInt(16)
			obj = @()
			obj.NW_Player = ply
			obj.NETWORKED = true
			obj.NETWORKED_PREDICT = true
			obj\ReadNetworkData()
			obj\Create()
			net.Start(@NW_ReceiveID)
			net.WriteUInt(waitID, 16)
			net.WriteUInt(obj.netID, 16)
			net.Send(ply)
			@OnNetworkedCreatedCallback(obj, ply, len)
	@OnNetworkedCreatedCallback = (obj, ply = NULL, len = 0) => -- Override

	@OnNetworkedModify = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		id = net.ReadUInt(16)
		obj = @NW_Objects[id]
		return unless obj
		return if IsValid(ply) and obj.NW_Player ~= ply
		varID = net.ReadUInt(8)
		varData = @NW_VarsTable[varID]
		return unless varData
		{:strName, :getName, :readFunc, :writeFunc} = varData
		newVal = readFunc()
		state = NetworkChangeState(strName, getName, newVal, obj, len, ply)
		state\Apply()
		obj\NetworkDataChanges(state)
		if SERVER
			net.Start(@NW_Modify)
			net.WriteUInt(id, 16)
			net.WriteUInt(varID, 8)
			writeFunc(newVal)
			net.SendOmit(ply)
		@OnNetworkedModifyCallback(state)
	@OnNetworkedModifyCallback = (state) => -- Override

	@OnNetworkedDelete = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		id = net.ReadUInt(16)
		obj = @NW_Objects[id]
		return unless obj
		obj\Remove(true)
		@OnNetworkedDeleteCallback(obj, ply, len)
	@OnNetworkedDeleteCallback = (obj, ply = NULL, len = 0) => -- Override

	@ReadNetworkData = =>
		output = {strName, {getName, readFunc()} for {:getName, :strName, :readFunc} in *@NW_Vars}
		return output

	new: (netID = @@NW_NextObjectID, localObject = false) =>
		@valid = true
		@NETWORKED = false
		@NETWORKED_PREDICT = false

		if SERVER
			@netID = @@NW_NextObjectID
			@@NW_NextObjectID += 1
		else
			@netID = netID

		@@NW_Objects[@netID] = @
		@NW_Player = NULL
		@isLocal = localObject
		@NW_Player = LocalPlayer() if localObject

	IsValid: => @valid
	IsNetworked: => @NETWORKED
	IsGoingToNetwork: => @NETWORKED_PREDICT
	SetIsGoingToNetwork: (val = @NETWORKED) => @NETWORKED_PREDICT = val
	IsLocal: => @isLocal
	IsLocalObject: => @isLocal
	GetNetworkID: => @netID
	NetworkID: => @netID
	NetID: => @netID
	Remove: (byClient = false) =>
		@@NW_Objects[@netID] = nil
		@valid = false
		if CLIENT and @isLocal and @NETWORKED and @@NW_ClientsideCreation
			net.Start(@@NW_Remove)
			net.WriteUInt(@netID, 16)
			net.SendToServer()
		elseif SERVER and @NETWORKED
			net.Start(@@NW_Remove)
			net.WriteUInt(@netID, 16)
			if not IsValid(@NW_Player) or not byClient
				net.Broadcast()
			else
				net.SendOmit(@NW_Player)

	NetworkDataChanges: (state) => -- Override
	SetLocalChange: (state) => -- Override
	ReadNetworkData: (len = 24, ply = NULL, silent = false) =>
		data = @@ReadNetworkData()
		states = [NetworkChangeState(key, keyValid, newVal, @, len, ply) for key, {keyValid, newVal} in pairs data]
		for state in *states
			state\Apply()
			@NetworkDataChanges(state) unless silent

	WriteNetworkData: => writeFunc(@[strName]) for {:strName, :writeFunc} in *@@NW_Vars

	SendVar: (Var = '') =>
		return if @[Var] == nil

	Create: =>
		return if @NETWORKED
		return if CLIENT and not @@NW_ClientsideCreation
		@NETWORKED = true
		@NETWORKED_PREDICT = true
		if SERVER
			net.Start(@@NW_Create)
			net.WriteUInt(@netID, 16)
			@WriteNetworkData()
			if not IsValid(@NW_Player)
				net.Broadcast()
			else
				net.SendOmit(@NW_Player)
		else
			@@NW_WaitID += 1
			net.Start(@@NW_Create)
			net.WriteUInt(@@NW_WaitID, 16)
			@WriteNetworkData()
			net.SendToServer()
			@@NW_Waiting[@@NW_WaitID] = @
	NetworkTo: (targets = {}) =>
		net.Start(@@NW_Create)
		net.WriteUInt(@netID, 16)
		@WriteNetworkData()
		net.Send(targets)

PPM2.NetworkedObject = NetworkedObject


Refactor to capture_form_errors

Refactor to capture_form_errors

Since we're having another form, this makes sense :)

lapis = require "lapis"
config = (require "lapis.config").get!
content = require "content"
logger = require "lapis.logging"
csrf = require "lapis.csrf"
submit = require "submit_application"

import after_dispatch from require "lapis.nginx.context"
import to_json from require "lapis.util"
import capture_errors, assert_error, yield_error, respond_to from require "lapis.application"
import json_requested from require "utils"

capture_form_errors = (fn) ->
    capture_errors {
        on_error: (err, trace) =>
            err or= @errors
            if @json
                resp = if @m and @m.form and @m.form.responses and @m.form.responses.default
                    @m.form.responses.default
                else
                    '
A server error has occurred, and the error text could not be fetched.
'

                print to_json err
                { status: 500, json: { response: resp, errors: { } } }
            else
                @app.handle_error self, err, trace, 500

        fn
    }

class CSWeek extends lapis.Application
    layout: require "views.layout"
    error_page: require "views.error"

    handle_error: (err, trace, code = 500) =>
        r = @app.Request @app, @req, @res

        with r
            .m, errpg = content\get "error"
            if errpg then
                .m = { }
                .m[code] = "Could not load the error page (code #{code}): #{errpg}
When handling #{err}
#{trace}"

            .m[code] = .m[code] or .m.default or { }
            .page_id = "error"
            .status = code
            .error = err
            .traceback = trace
            .m.title = .m[code].title

            \write {
                status: code
                content_type: "text/html"
                render: "error"
            }
            \render!
            logger.request r
        r

    try_render: (template, context) =>
        context = context or self
        with context
            .nav = assert_error content\get "nav"
            .footer = assert_error content\get "footer"
        render: template

    @before_filter =>
        if #[n for n in *{'production-perftest', 'development-perftest'} when n == config._name] > 0
            after_dispatch ->
                print to_json(ngx.ctx.performance)

    [home: "/"]: =>
        @page_id = "home"
        @m = assert_error content\get "home"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "home", self

    [about: "/o-nedelji"]: =>
        @page_id = "about"
        @m = assert_error content\get "about"
        @app\try_render "about", self

    [lecturers: "/predavaci"]: =>
        @page_id = "lecturers"
        @m = assert_error content\get "lecturers_page"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "lecturers", self

    [lecturer: "/predavaci/:name"]: =>
        @lecturers_page = assert_error content\get "lecturers_page"
        @page_id = "lecturer"
        lecturers = assert_error content\get "lecturers"
        results = [l for l in *lecturers when l.id == @params.name]
        if #results > 1
            yield_error "Multiple lecturer id's found for id `#{@params.name}`"
        else if #results == 0
            @app.handle_error self, "No such lecturer: `#{@params.name}`", nil, 404
            return

        @m = results[1]
        @app\try_render "lecturer", self

    [apply: "/prijava"]: respond_to {
        GET: =>
            @page_id = "apply"
            @m = assert_error content\get "apply"
            @csrf_token = csrf.generate_token @
            @app\try_render "apply", self

        POST: capture_form_errors =>
                @json = json_requested @
                model = assert_error content\get "apply"
                @m = model

                this = self
                succ, ret, err = pcall ->
                    submit\submit this.res.req.params_post, model, (...) ->
                        this\build_url ...

                yield_error ret if not succ

                resp = { }

                status = if ret
                        200
                    elseif err[1] == 'internal_error'
                        500
                    elseif err[1] == 'duplicate_application'
                        409
                    elseif err[1] == 'too_frequent'
                        403
                    else
                        400

                resp.response = model.form.responses[status] or model.form.responses.default
                if status == 400
                    resp.errors = { }
                    for e in *err
                        table.insert resp.errors, model.form.validation_errors[e] or 'unknown error'
                if @json
                    { :status, json: resp }
                else
                    @resp = resp
                    @page_id = 'apply'
                    ret = @app\try_render 'apply-result', self
                    ret.status = status
                    ret
    }

    [apply_upload: "/prijava/upload/*"]: respond_to {
        GET: =>
            @page_id = "apply"
            @m = assert_error content\get "apply-upload"
            @a = assert_error content\get "apply"
            @csrf_token = csrf.generate_token @
            if @application = submit\upload_form @params.splat
                @app\try_render "apply-upload", self
            else
                redirect_to: '/prijava'

        POST: capture_form_errors =>
            json = json_requested @
            model = assert_error content\get 'apply-upload'
            @m = model

            this = self
            succ, ret, err = pcall ->
                submit\upload this.res.req.params_post, model, (...) ->
                    this\build_url ...

            yield_error ret if not succ

            resp = { }

    }

    handle_404: =>
        @app.handle_error self, "Route `#{self.req.parsed_url.path or 'unknown'}` not found", nil, 404

lapis = require "lapis"
config = (require "lapis.config").get!
content = require "content"
logger = require "lapis.logging"
csrf = require "lapis.csrf"
submit = require "submit_application"

import after_dispatch from require "lapis.nginx.context"
import to_json from require "lapis.util"
import capture_errors, assert_error, yield_error, respond_to from require "lapis.application"
import json_requested from require "utils"

class CSWeek extends lapis.Application
    layout: require "views.layout"
    error_page: require "views.error"

    handle_error: (err, trace, code = 500) =>
        r = @app.Request @app, @req, @res

        with r
            .m, errpg = content\get "error"
            if errpg then
                .m = { }
                .m[code] = "Could not load the error page (code #{code}): #{errpg}
When handling #{err}
#{trace}"

            .m[code] = .m[code] or .m.default or { }
            .page_id = "error"
            .status = code
            .error = err
            .traceback = trace
            .m.title = .m[code].title

            \write {
                status: code
                content_type: "text/html"
                render: "error"
            }
            \render!
            logger.request r
        r

    try_render: (template, context) =>
        context = context or self
        with context
            .nav = assert_error content\get "nav"
            .footer = assert_error content\get "footer"
        render: template

    @before_filter =>
        if #[n for n in *{'production-perftest', 'development-perftest'} when n == config._name] > 0
            after_dispatch ->
                print to_json(ngx.ctx.performance)

    [home: "/"]: =>
        @page_id = "home"
        @m = assert_error content\get "home"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "home", self

    [about: "/o-nedelji"]: =>
        @page_id = "about"
        @m = assert_error content\get "about"
        @app\try_render "about", self

    [lecturers: "/predavaci"]: =>
        @page_id = "lecturers"
        @m = assert_error content\get "lecturers_page"
        @lecturers = assert_error content\get "lecturers"
        @app\try_render "lecturers", self

    [lecturer: "/predavaci/:name"]: =>
        @lecturers_page = assert_error content\get "lecturers_page"
        @page_id = "lecturer"
        lecturers = assert_error content\get "lecturers"
        results = [l for l in *lecturers when l.id == @params.name]
        if #results > 1
            yield_error "Multiple lecturer id's found for id `#{@params.name}`"
        else if #results == 0
            @app.handle_error self, "No such lecturer: `#{@params.name}`", nil, 404
            return

        @m = results[1]
        @app\try_render "lecturer", self

    [apply: "/prijava"]: respond_to {
        GET: =>
            @page_id = "apply"
            @m = assert_error content\get "apply"
            @csrf_token = csrf.generate_token @
            @app\try_render "apply", self

        POST: capture_errors {
            on_error: (err, trace) =>
                err or= @errors
                if @json
                    resp = if @m and @m.form and @m.form.responses and @m.form.responses.default
                        @m.form.responses.default
                    else
                        '
A server error has occurred, and the error text could not be fetched.
'

                    { status: 500, json: { response: resp, errors: { } } }
                else
                    @app.handle_error self, err, trace, 500

            =>
                @json = json_requested @
                model = assert_error content\get "apply"
                @m = model

                this = self
                succ, ret, err = pcall ->
                    submit\submit this.res.req.params_post, model, (...) ->
                        this\build_url ...

                yield_error ret if not succ

                resp = { }

                status = if ret
                        200
                    elseif err[1] == 'internal_error'
                        500
                    elseif err[1] == 'duplicate_application'
                        409
                    elseif err[1] == 'too_frequent'
                        403
                    else
                        400

                resp.response = model.form.responses[status] or model.form.responses.default
                if status == 400
                    resp.errors = { }
                    for e in *err
                        table.insert resp.errors, model.form.validation_errors[e] or 'unknown error'
                if @json
                    { :status, json: resp }
                else
                    @resp = resp
                    @page_id = 'apply'
                    ret = @app\try_render 'apply-result', self
                    ret.status = status
                    ret
        }
    }

    [apply_upload: "/prijava/upload/*"]: respond_to {
        GET: =>
            @page_id = "apply"
            @m = assert_error content\get "apply-upload"
            @a = assert_error content\get "apply"
            @csrf_token = csrf.generate_token @
            if @application = submit\upload_form @params.splat
                @app\try_render "apply-upload", self
            else
                redirect_to: '/prijava'

        POST: =>
            json = json_requested @
            model = assert_error content\get 'apply-upload'
            @m = model

            this = self
            succ, ret, err = pcall ->
                submit\upload this.res.req.params_post, model, (...) ->
                    this\build_url ...

            yield_error ret if not succ

            resp = { }

    }

    handle_404: =>
        @app.handle_error self, "Route `#{self.req.parsed_url.path or 'unknown'}` not found", nil, 404